PXA270 processors support. Index: qemu/Makefile =================================================================== --- qemu.orig/Makefile 2007-02-12 13:06:49.000000000 +0800 +++ qemu/Makefile 2007-02-12 15:23:30.000000000 +0800 @@ -14,7 +14,7 @@ endif CPPFLAGS += -I. -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE LIBS= -TOOLS=qemu-img$(EXESUF) +TOOLS=qemu-img$(EXESUF) raw2flash$(EXESUF) flash2raw$(EXESUF) ifdef CONFIG_STATIC BASE_LDFLAGS += -static endif @@ -30,6 +30,10 @@ endif endif +MODELS=spitz akita borzoi terrier + +LIBS+=$(AIOLIBS) + all: $(TOOLS) $(DOCS) recurse-all subdir-%: dyngen$(EXESUF) @@ -43,6 +47,13 @@ dyngen$(EXESUF): dyngen.c $(HOST_CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -o $@ $^ +raw2flash$(EXESUF) flash2raw$(EXESUF): raw2flash.c + $(CC) -D$@ $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ + for m in $(MODELS); do \ + [ -e $@.$$m$(EXESUF) ] || \ + ln -s $@ $@.$$m$(EXESUF) ; \ + done + clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h @@ -89,6 +100,11 @@ for d in $(TARGET_DIRS); do \ $(MAKE) -C $$d $@ || exit 1 ; \ done + for m in $(MODELS); do \ + for n in *.$$m; do \ + $(INSTALL) $$n "$(DESTDIR)$(bindir)"; \ + done; \ + done # various test targets test speed test2: all Index: qemu/Makefile.target =================================================================== --- qemu.orig/Makefile.target 2007-02-12 15:18:49.000000000 +0800 +++ qemu/Makefile.target 2007-03-06 10:25:38.000000000 +0800 @@ -65,10 +65,6 @@ endif endif # !CONFIG_USER_ONLY -ifdef CONFIG_STATIC -BASE_LDFLAGS+=-static -endif - # We require -O2 to avoid the stack setup prologue in EXIT_TB OP_CFLAGS = -Wall -O2 -g -fno-strict-aliasing @@ -99,6 +95,7 @@ endif ifeq ($(ARCH),x86_64) +HELPER_CFLAGS+=-fomit-frame-pointer BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld endif @@ -330,7 +327,7 @@ endif SOUND_HW = sb16.o es1370.o -AUDIODRV = audio.o noaudio.o wavaudio.o +AUDIODRV = audio.o noaudio.o wavaudio.o mixeng.o ifdef CONFIG_SDL AUDIODRV += sdlaudio.o endif @@ -362,7 +359,7 @@ VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o # USB layer -VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o +VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o usb-net.o # PCI network cards VL_OBJS+= ne2000.o rtl8139.o pcnet.o @@ -371,20 +368,20 @@ # Hardware support VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o -VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o acpi.o piix_pci.o +VL_OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o VL_OBJS+= usb-uhci.o smbus_eeprom.o -CPPFLAGS += -DHAS_AUDIO +CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE endif ifeq ($(TARGET_BASE_ARCH), ppc) VL_OBJS+= ppc.o ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o -VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o +VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o VL_OBJS+= grackle_pci.o prep_pci.o unin_pci.o -CPPFLAGS += -DHAS_AUDIO +CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE endif ifeq ($(TARGET_ARCH), mips) VL_OBJS+= mips_r4k.o mips_malta.o mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o -VL_OBJS+= ide.o gt64xxx.o pckbd.o ps2.o fdc.o mc146818rtc.o usb-uhci.o acpi.o +VL_OBJS+= ide.o gt64xxx.o pckbd.o ps2.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o VL_OBJS+= piix_pci.o parallel.o mixeng.o cirrus_vga.o $(SOUND_HW) $(AUDIODRV) DEFINES += -DHAS_AUDIO endif @@ -405,6 +402,10 @@ VL_OBJS+= versatile_pci.o VL_OBJS+= arm_gic.o realview.o arm_sysctl.o VL_OBJS+= arm-semi.o +VL_OBJS+= pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o +VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o +VL_OBJS+= ads7846.o sd.o ide.o serial.o nand.o $(AUDIODRV) wm8750.o +CPPFLAGS += -DHAS_AUDIO -DHIGH_LATENCY endif ifeq ($(TARGET_BASE_ARCH), sh4) VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o @@ -432,6 +433,7 @@ endif VL_LDFLAGS= +VL_LIBS=$(AIOLIBS) # specific flags are needed for non soft mmu emulator ifdef CONFIG_STATIC VL_LDFLAGS+=-static @@ -442,7 +444,7 @@ ifndef CONFIG_DARWIN ifndef CONFIG_WIN32 ifndef CONFIG_SOLARIS -VL_LIBS=-lutil -lrt +VL_LIBS+=-lutil endif endif endif @@ -465,7 +467,7 @@ endif $(QEMU_SYSTEM): $(VL_OBJS) libqemu.a - $(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS) + $(CC) $(VL_LDFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS) cocoa.o: cocoa.m $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< Index: qemu/audio/alsaaudio.c =================================================================== --- qemu.orig/audio/alsaaudio.c 2006-07-05 05:47:22.000000000 +0800 +++ qemu/audio/alsaaudio.c 2007-02-24 21:23:06.000000000 +0800 @@ -57,6 +57,8 @@ int period_size_out_overriden; int verbose; } conf = { +#define DEFAULT_BUFFER_SIZE 1024 +#define DEFAULT_PERIOD_SIZE 256 #ifdef HIGH_LATENCY .size_in_usec_in = 1, .size_in_usec_out = 1, @@ -69,8 +71,6 @@ .buffer_size_out = 400000, .period_size_out = 400000 / 4, #else -#define DEFAULT_BUFFER_SIZE 1024 -#define DEFAULT_PERIOD_SIZE 256 .buffer_size_in = DEFAULT_BUFFER_SIZE * 4, .period_size_in = DEFAULT_PERIOD_SIZE * 4, .buffer_size_out = DEFAULT_BUFFER_SIZE, @@ -157,6 +157,12 @@ case AUD_FMT_U16: return SND_PCM_FORMAT_U16_LE; + case AUD_FMT_S32: + return SND_PCM_FORMAT_S32_LE; + + case AUD_FMT_U32: + return SND_PCM_FORMAT_U32_LE; + default: dolog ("Internal logic error: Bad audio format %d\n", fmt); #ifdef DEBUG_AUDIO @@ -199,6 +205,26 @@ *fmt = AUD_FMT_U16; break; + case SND_PCM_FORMAT_S32_LE: + *endianness = 0; + *fmt = AUD_FMT_S32; + break; + + case SND_PCM_FORMAT_U32_LE: + *endianness = 0; + *fmt = AUD_FMT_U32; + break; + + case SND_PCM_FORMAT_S32_BE: + *endianness = 1; + *fmt = AUD_FMT_S32; + break; + + case SND_PCM_FORMAT_U32_BE: + *endianness = 1; + *fmt = AUD_FMT_U32; + break; + default: dolog ("Unrecognized audio format %d\n", alsafmt); return -1; Index: qemu/configure =================================================================== --- qemu.orig/configure 2007-02-12 13:06:50.000000000 +0800 +++ qemu/configure 2007-03-06 10:25:38.000000000 +0800 @@ -159,6 +159,12 @@ fi fi +if [ "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then + AIOLIBS= +else + AIOLIBS="-lrt" +fi + # find source path source_path=`dirname "$0"` if [ -z "$source_path" ]; then @@ -221,7 +227,7 @@ ;; --fmod-inc=*) fmod_inc="$optarg" ;; - --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" ; user="no" + --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" ; linux_user="no" ;; --disable-slirp) slirp="no" ;; @@ -348,9 +354,9 @@ if test "$gcc3_search" = "yes" ; then echo "Looking for gcc 3.x" for compat_cc in $gcc3_list ; do - if check_cc "$compat_cc" ; then + if check_cc "$cross_prefix$compat_cc" ; then echo "Found \"$compat_cc\"" - cc="$compat_cc" + cc="$cross_prefix$compat_cc" found_compat_cc="yes" break fi @@ -559,28 +565,28 @@ fi if test "$mingw32" = "yes" ; then -if test -z "$prefix" ; then - prefix="/c/Program Files/Qemu" -fi -mandir="$prefix" -datadir="$prefix" -docdir="$prefix" -bindir="$prefix" -else -if test -z "$prefix" ; then - prefix="/usr/local" -fi -mandir="$prefix/share/man" -datadir="$prefix/share/qemu" -docdir="$prefix/share/doc/qemu" -bindir="$prefix/bin" + if test -z "$prefix" ; then + prefix="/c/Program Files/Qemu" + fi + mansuffix="" + datasuffix="" + docsuffix="" + binsuffix="" +else + if test -z "$prefix" ; then + prefix="/usr/local" + fi + mansuffix="/share/man" + datasuffix="/share/qemu" + docsuffix="/share/doc/qemu" + binsuffix="/bin" fi echo "Install prefix $prefix" -echo "BIOS directory $datadir" -echo "binary directory $bindir" +echo "BIOS directory $prefix$datasuffix" +echo "binary directory $prefix$binsuffix" if test "$mingw32" = "no" ; then -echo "Manual directory $mandir" +echo "Manual directory $prefix$mansuffix" echo "ELF interp prefix $interp_prefix" fi echo "Source path $source_path" @@ -640,11 +646,11 @@ echo "/* Automatically generated by configure - do not modify */" > $config_h echo "prefix=$prefix" >> $config_mak -echo "bindir=$bindir" >> $config_mak -echo "mandir=$mandir" >> $config_mak -echo "datadir=$datadir" >> $config_mak -echo "docdir=$docdir" >> $config_mak -echo "#define CONFIG_QEMU_SHAREDIR \"$datadir\"" >> $config_h +echo "bindir=\${prefix}$binsuffix" >> $config_mak +echo "mandir=\${prefix}$mansuffix" >> $config_mak +echo "datadir=\${prefix}$datasuffix" >> $config_mak +echo "docdir=\${prefix}$docsuffix" >> $config_mak +echo "#define CONFIG_QEMU_SHAREDIR \"$prefix$datasuffix\"" >> $config_h echo "MAKE=$make" >> $config_mak echo "INSTALL=$install" >> $config_mak echo "CC=$cc" >> $config_mak @@ -658,6 +664,7 @@ echo "CFLAGS=$CFLAGS" >> $config_mak echo "LDFLAGS=$LDFLAGS" >> $config_mak echo "EXESUF=$EXESUF" >> $config_mak +echo "AIOLIBS=$AIOLIBS" >> $config_mak if test "$cpu" = "i386" ; then echo "ARCH=i386" >> $config_mak echo "#define HOST_I386 1" >> $config_h @@ -706,8 +713,14 @@ if test "$mingw32" = "yes" ; then echo "CONFIG_WIN32=yes" >> $config_mak echo "#define CONFIG_WIN32 1" >> $config_h -elif test -f "/usr/include/byteswap.h" ; then - echo "#define HAVE_BYTESWAP_H 1" >> $config_h +else + cat > $TMPC << EOF +#include +int main(void) { return bswap_32(0); } +EOF + if $cc -o $TMPE $TMPC 2> /dev/null ; then + echo "#define HAVE_BYTESWAP_H 1" >> $config_h + fi fi if test "$darwin" = "yes" ; then echo "CONFIG_DARWIN=yes" >> $config_mak Index: qemu/cpu-exec.c =================================================================== --- qemu.orig/cpu-exec.c 2007-02-06 05:41:46.000000000 +0800 +++ qemu/cpu-exec.c 2007-02-12 15:21:33.000000000 +0800 @@ -276,8 +276,8 @@ if (env1->halted) { /* An interrupt wakes the CPU even if the I and F CPSR bits are set. */ - if (env1->interrupt_request - & (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD)) { + if (env1->interrupt_request & + (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB)) { env1->halted = 0; } else { return EXCP_HALTED; @@ -523,6 +523,12 @@ env->exception_index = EXCP_IRQ; do_interrupt(env); } + if (interrupt_request & CPU_INTERRUPT_HALT) { + env->interrupt_request &= ~CPU_INTERRUPT_HALT; + env->halted = 1; + env->exception_index = EXCP_HLT; + cpu_loop_exit(); + } #elif defined(TARGET_SH4) /* XXXXX */ #endif Index: qemu/ecc.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/ecc.h 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,77 @@ +/* + * Calculate Error-correcting Codes. Used by NAND Flash controllers + * (not by NAND chips). + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + */ + +struct ecc_state_s { + uint8_t cp; /* Column parity */ + uint16_t lp[2]; /* Line parity */ + uint16_t count; +}; + +/* + * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux. + */ +static const uint8_t nand_ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, + 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, + 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, + 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, + 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, + 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, + 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, + 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, + 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, + 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, + 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, + 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, + 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, + 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, + 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, + 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, + 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, +}; + +/* Update ECC parity count. */ +static inline uint8_t ecc_digest(struct ecc_state_s *s, uint8_t sample) +{ + uint8_t idx = nand_ecc_precalc_table[sample]; + + s->cp ^= idx & 0x3f; + if (idx & 0x40) { + s->lp[0] ^= ~s->count; + s->lp[1] ^= s->count; + } + s->count ++; + + return sample; +} + +/* Reinitialise the counters. */ +static inline void ecc_reset(struct ecc_state_s *s) +{ + s->lp[0] = 0x0000; + s->lp[1] = 0x0000; + s->cp = 0x00; + s->count = 0; +} Index: qemu/hw/ads7846.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/ads7846.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,131 @@ +/* + * TI ADS7846 chip emulation. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + */ + +#include + +struct ads7846_state_s { + void (*interrupt)(void *opaque, int level); + void *opaque; + + int input[8]; + int pressure; + int noise; + + int cycle; + int output; +}; + +/* Control-byte bitfields */ +#define CB_PD0 (1 << 0) +#define CB_PD1 (1 << 1) +#define CB_SER (1 << 2) +#define CB_MODE (1 << 3) +#define CB_A0 (1 << 4) +#define CB_A1 (1 << 5) +#define CB_A2 (1 << 6) +#define CB_START (1 << 7) + +#define X_AXIS_DMAX 3680 +#define X_AXIS_MIN 150 +#define Y_AXIS_DMAX 3640 +#define Y_AXIS_MIN 190 + +#define ADS_VBAT 2000 +#define ADS_VAUX 2000 +#define ADS_TEMP0 2000 +#define ADS_TEMP1 3000 +#define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15)) +#define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15)) +#define ADS_Z1POS(x, y) 600 +#define ADS_Z2POS(x, y) (600 + 6000 / ADS_XPOS(x, y)) + +static void ads7846_int_update(struct ads7846_state_s *s) +{ + if (s->interrupt) + s->interrupt(s->opaque, s->pressure == 0); +} + +uint32_t ads7846_read(void *opaque) +{ + struct ads7846_state_s *s = (struct ads7846_state_s *) opaque; + + return s->output; +} + +void ads7846_write(void *opaque, uint32_t value) +{ + struct ads7846_state_s *s = (struct ads7846_state_s *) opaque; + + switch (s->cycle ++) { + case 0: + if (!(value & CB_START)) { + s->cycle = 0; + break; + } + + s->output = s->input[(value >> 4) & 7]; + + /* Imitate the ADC noise, some drivers expect this. */ + s->noise = (s->noise + 3) & 7; + switch ((value >> 4) & 7) { + case 1: s->output += s->noise ^ 2; break; + case 3: s->output += s->noise ^ 0; break; + case 4: s->output += s->noise ^ 7; break; + case 5: s->output += s->noise ^ 5; break; + } + + if (value & CB_MODE) + s->output >>= 4; /* 8 bits instead of 12 */ + + break; + case 1: + s->cycle = 0; + break; + } +} + +static void ads7846_ts_event(void *opaque, + int x, int y, int z, int buttons_state) +{ + struct ads7846_state_s *s = opaque; + + if (buttons_state) { + s->input[1] = ADS_YPOS(x, y); + s->input[3] = ADS_Z1POS(x, y); + s->input[4] = ADS_Z2POS(x, y); + s->input[5] = ADS_XPOS(x, y); + } + s->pressure = !!buttons_state; + + ads7846_int_update(s); +} + +struct ads7846_state_s *ads7846_init( + void (*penirq)(void *opaque, int level), void *opaque) +{ + struct ads7846_state_s *s; + s = (struct ads7846_state_s *) + qemu_mallocz(sizeof(struct ads7846_state_s)); + memset(s, 0, sizeof(struct ads7846_state_s)); + + s->interrupt = penirq; + s->opaque = opaque; + + s->input[0] = ADS_TEMP0; /* TEMP0 */ + s->input[2] = ADS_VBAT; /* VBAT */ + s->input[6] = ADS_VAUX; /* VAUX */ + s->input[7] = ADS_TEMP1; /* TEMP1 */ + + /* We want absolute coordinates */ + qemu_add_mouse_event_handler(ads7846_ts_event, s, 1, + "QEMU ADS7846-driven Touchscreen"); + + ads7846_int_update(s); + return s; +} Index: qemu/hw/arm_boot.c =================================================================== --- qemu.orig/hw/arm_boot.c 2007-01-17 16:59:26.000000000 +0800 +++ qemu/hw/arm_boot.c 2007-02-12 15:21:33.000000000 +0800 @@ -66,7 +66,7 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, - int board_id) + int board_id, target_phys_addr_t loader_start) { int kernel_size; int initrd_size; @@ -107,9 +107,11 @@ bootloader[2] |= (board_id >> 8) & 0xff; bootloader[5] = KERNEL_ARGS_ADDR; bootloader[6] = KERNEL_LOAD_ADDR; + bootloader[5] = loader_start + KERNEL_ARGS_ADDR; + bootloader[6] = loader_start + 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); } + set_kernel_args(ram_size, initrd_size, kernel_cmdline); } - Index: qemu/hw/i2c.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/i2c.h 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,159 @@ +/* + * Simplified I2C(tm) bus / SMBus(tm). + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This file is licensed under GNU GPL. + */ + +#define I2C_MAX_MSG 4096 + +struct i2c_slave_s { + uint8_t address; + int (*tx)(void *opaque, uint8_t *data, int len); + void (*start)(void *opaque, int dir); + void (*stop)(void *opaque); + void *opaque; +}; + +struct i2c_bus_s { + struct i2c_slave_s *slave[0x80]; + uint8_t current; + int dir; +}; + +/* I2C master - drives the clock signal on a bus. There can be multiple + * masters on one bus. */ +struct i2c_master_s { + struct i2c_bus_s *bus; + uint8_t message[I2C_MAX_MSG]; + int message_len; + int ack; + + uint8_t data; +}; + +static inline int i2c_bus_start(struct i2c_bus_s *bus, uint8_t byte) +{ + struct i2c_slave_s *slave; + + bus->current = byte >> 1; + bus->dir = byte & 1; + slave = bus->slave[bus->current]; + + return !slave; +} + +static inline int i2c_start_submit(struct i2c_bus_s *bus) +{ + struct i2c_slave_s *slave = bus->slave[bus->current]; + if (!slave) + return 1; + + if (slave->start) + slave->start(slave->opaque, bus->dir); + return 0; +} + +static inline int i2c_stop_submit(struct i2c_bus_s *bus) +{ + struct i2c_slave_s *slave = bus->slave[bus->current]; + if (!slave) + return 1; + + if (slave->stop) + slave->stop(slave->opaque); + return 0; +} + +static inline int i2c_msg_submit(struct i2c_bus_s *bus, + uint8_t message[], int len) +{ + struct i2c_slave_s *slave = bus->slave[bus->current]; + if (!slave) + return 1; + + return slave->tx ? slave->tx(slave->opaque, message, len) : 1; +} + +static inline void i2c_master_submit(struct i2c_master_s *master, + int start, int stop) +{ + int ret = 0; + + if (!master->bus) { + master->ack = 0; + return; + } + + if (start) { + ret = i2c_bus_start(master->bus, master->data); + master->message_len = 0; + + if (master->bus->dir) { /* Master <-- Slave */ + i2c_start_submit(master->bus); + master->message_len = 1; + if (stop) + i2c_msg_submit(master->bus, master->message, 0); + } + } else { + if (!master->bus->dir) { /* Master --> Slave */ + if (master->message_len < I2C_MAX_MSG) + master->message[master->message_len ++] = master->data; + } else { /* Master <-- Slave */ + ret = i2c_msg_submit(master->bus, + master->message, master->message_len); + master->data = master->message[0]; + } + } + + if (stop) { + if (!master->bus->dir) { /* Master --> Slave */ + i2c_start_submit(master->bus); + ret = i2c_msg_submit(master->bus, + master->message, master->message_len); + } + + i2c_stop_submit(master->bus); + } + + master->ack = !ret; +} + +/* Call with zero `addr' to detach. */ +static inline void i2c_slave_attach(struct i2c_bus_s *bus, uint8_t addr, + struct i2c_slave_s *dev) +{ + if (addr >= 0x80) + cpu_abort(cpu_single_env, "bad I2C address"); + + if (dev->address) + bus->slave[dev->address] = 0; + + dev->address = addr; + + if (dev->address) + bus->slave[dev->address] = dev; +} + +static inline void i2c_master_attach(struct i2c_bus_s *bus, + struct i2c_master_s *dev) +{ + dev->bus = bus; +} + +/* max7310.c */ +struct i2c_slave_s *max7310_init(void); +void max7310_reset(struct i2c_slave_s *i2c); +void max7310_gpio_set(struct i2c_slave_s *i2c, int line, int level); +void max7310_gpio_handler_set(struct i2c_slave_s *i2c, int line, + gpio_handler_t handler, void *opaque); + +/* wm8750.c */ +struct i2c_slave_s *wm8750_init(AudioState *audio); +void wm8750_reset(struct i2c_slave_s *i2c); +void wm8750_data_req_set(struct i2c_slave_s *i2c, + void (*data_req)(void *, int, int), void *opaque); +void wm8750_dac_dat(void *opaque, uint32_t sample); +uint32_t wm8750_adc_dat(void *opaque); Index: qemu/hw/ide.c =================================================================== --- qemu.orig/hw/ide.c 2007-01-25 05:35:22.000000000 +0800 +++ qemu/hw/ide.c 2007-02-24 21:24:25.000000000 +0800 @@ -131,6 +131,7 @@ #define WIN_SPECIFY 0x91 /* set drive geometry translation */ #define WIN_DOWNLOAD_MICROCODE 0x92 #define WIN_STANDBYNOW2 0x94 +#define CFA_IDLEIMMEDIATE 0x95 /* force drive to become "ready" */ #define WIN_STANDBY2 0x96 #define WIN_SETIDLE2 0x97 #define WIN_CHECKPOWERMODE2 0x98 @@ -142,7 +143,8 @@ #define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */ #define WIN_QUEUED_SERVICE 0xA2 #define WIN_SMART 0xB0 /* self-monitoring and reporting */ -#define CFA_ERASE_SECTORS 0xC0 +#define CFA_ACCESS_METADATA_STORAGE 0xB8 +#define CFA_ERASE_SECTORS 0xC0 /* microdrives implement as NOP */ #define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/ #define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */ #define WIN_SETMULT 0xC6 /* enable/disable multiple mode */ @@ -176,11 +178,13 @@ #define WIN_IDENTIFY_DMA 0xEE /* same as WIN_IDENTIFY, but DMA */ #define WIN_SETFEATURES 0xEF /* set special drive features */ #define EXABYTE_ENABLE_NEST 0xF0 +#define IBM_SENSE_CONDITION 0xF0 /* measure disk temperature */ #define WIN_SECURITY_SET_PASS 0xF1 #define WIN_SECURITY_UNLOCK 0xF2 #define WIN_SECURITY_ERASE_PREPARE 0xF3 #define WIN_SECURITY_ERASE_UNIT 0xF4 #define WIN_SECURITY_FREEZE_LOCK 0xF5 +#define CFA_WEAR_LEVEL 0xF5 /* microdrives implement as NOP */ #define WIN_SECURITY_DISABLE 0xF6 #define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */ #define WIN_SET_MAX 0xF9 @@ -282,6 +286,12 @@ #define ASC_MEDIUM_NOT_PRESENT 0x3a #define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39 +#define CFA_NO_ERROR 0x00 +#define CFA_MISC_ERROR 0x09 +#define CFA_INVALID_COMMAND 0x20 +#define CFA_INVALID_ADDRESS 0x21 +#define CFA_ADDRESS_OVERFLOW 0x2f + #define SENSE_NONE 0 #define SENSE_NOT_READY 2 #define SENSE_ILLEGAL_REQUEST 5 @@ -295,6 +305,7 @@ typedef struct IDEState { /* ide config */ int is_cdrom; + int is_cf; int cylinders, heads, sectors; int64_t nb_sectors; int mult_sectors; @@ -349,6 +360,12 @@ uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4]; QEMUTimer *sector_write_timer; /* only used for win2k instal hack */ uint32_t irq_count; /* counts IRQs when using win2k install hack */ + /* CF-ATA extended error */ + uint8_t ext_error; + /* CF-ATA metadata storage */ + uint32_t mdata_size; + uint8_t *mdata_storage; + int media_changed; } IDEState; #define BM_STATUS_DMAING 0x01 @@ -544,6 +561,74 @@ s->identify_set = 1; } +static void ide_cfata_identify(IDEState *s) +{ + uint16_t *p; + uint32_t cur_sec; + char buf[20]; + + p = (uint16_t *) s->identify_data; + if (s->identify_set) + goto fill_buffer; + + memset(p, 0, sizeof(s->identify_data)); + + cur_sec = s->cylinders * s->heads * s->sectors; + + put_le16(p + 0, 0x848a); /* CF Storage Card signature */ + put_le16(p + 1, s->cylinders); /* Default cylinders */ + put_le16(p + 3, s->heads); /* Default heads */ + put_le16(p + 6, s->sectors); /* Default sectors per track */ + put_le16(p + 7, s->nb_sectors >> 16); /* Sectors per card */ + put_le16(p + 8, s->nb_sectors); /* Sectors per card */ + snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial); + padstr((uint8_t *)(p + 10), buf, 20); /* Serial number in ASCII */ + put_le16(p + 22, 0x0004); /* ECC bytes */ + padstr((uint8_t *) (p + 23), QEMU_VERSION, 8); /* Firmware Revision */ + padstr((uint8_t *) (p + 27), "QEMU MICRODRIVE", 40);/* Model number */ +#if MAX_MULT_SECTORS > 1 + put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS); +#else + put_le16(p + 47, 0x0000); +#endif + put_le16(p + 49, 0x0f00); /* Capabilities */ + put_le16(p + 51, 0x0002); /* PIO cycle timing mode */ + put_le16(p + 52, 0x0001); /* DMA cycle timing mode */ + put_le16(p + 53, 0x0003); /* Translation params valid */ + put_le16(p + 54, s->cylinders); /* Current cylinders */ + put_le16(p + 55, s->heads); /* Current heads */ + put_le16(p + 56, s->sectors); /* Current sectors */ + put_le16(p + 57, cur_sec); /* Current capacity */ + put_le16(p + 58, cur_sec >> 16); /* Current capacity */ + if (s->mult_sectors) /* Multiple sector setting */ + put_le16(p + 59, 0x100 | s->mult_sectors); + put_le16(p + 60, s->nb_sectors); /* Total LBA sectors */ + put_le16(p + 61, s->nb_sectors >> 16); /* Total LBA sectors */ + put_le16(p + 63, 0x0203); /* Multiword DMA capability */ + put_le16(p + 64, 0x0001); /* Flow Control PIO support */ + put_le16(p + 65, 0x0096); /* Min. Multiword DMA cycle */ + put_le16(p + 66, 0x0096); /* Rec. Multiword DMA cycle */ + put_le16(p + 68, 0x00b4); /* Min. PIO cycle time */ + put_le16(p + 82, 0x400c); /* Command Set supported */ + put_le16(p + 83, 0x7068); /* Command Set supported */ + put_le16(p + 84, 0x4000); /* Features supported */ + put_le16(p + 85, 0x000c); /* Command Set enabled */ + put_le16(p + 86, 0x7044); /* Command Set enabled */ + put_le16(p + 87, 0x4000); /* Features enabled */ + put_le16(p + 91, 0x4060); /* Current APM level */ + put_le16(p + 129, 0x0002); /* Current features option */ + put_le16(p + 130, 0x0005); /* Reassigned sectors */ + put_le16(p + 131, 0x0001); /* Initial power mode */ + put_le16(p + 132, 0x0000); /* User signature */ + put_le16(p + 160, 0x8100); /* Power requirement */ + put_le16(p + 161, 0x8001); /* CF command set */ + + s->identify_set = 1; + +fill_buffer: + memcpy(s->io_buffer, p, sizeof(s->identify_data)); +} + static void ide_set_signature(IDEState *s) { s->select &= 0xf0; /* clear head */ @@ -794,7 +879,7 @@ ret = bdrv_write(s->bs, sector_num, s->io_buffer, n); s->nsector -= n; if (s->nsector == 0) { - /* no more sector to write */ + /* no more sectors to write */ ide_transfer_stop(s); } else { n1 = s->nsector; @@ -1498,6 +1583,59 @@ } } +static void ide_cfata_metadata_inquiry(IDEState *s) +{ + uint16_t *p; + uint32_t spd; + + p = (uint16_t *) s->io_buffer; + memset(p, 0, 0x200); + spd = ((s->mdata_size - 1) >> 9) + 1; + + put_le16(p + 0, 0x0001); /* Data format revision */ + put_le16(p + 1, 0x0000); /* Media property: silicon */ + put_le16(p + 2, s->media_changed); /* Media status */ + put_le16(p + 3, s->mdata_size & 0xffff); /* Capacity in bytes (low) */ + put_le16(p + 4, s->mdata_size >> 16); /* Capacity in bytes (high) */ + put_le16(p + 5, spd & 0xffff); /* Sectors per device (low) */ + put_le16(p + 6, spd >> 16); /* Sectors per device (high) */ +} + +static void ide_cfata_metadata_read(IDEState *s) +{ + uint16_t *p; + + if (((s->hcyl << 16) | s->lcyl) << 9 > s->mdata_size + 2) { + s->status = ERR_STAT; + s->error = ABRT_ERR; + return; + } + + p = (uint16_t *) s->io_buffer; + memset(p, 0, 0x200); + + put_le16(p + 0, s->media_changed); /* Media status */ + memcpy(p + 1, s->mdata_storage + (((s->hcyl << 16) | s->lcyl) << 9), + MIN(MIN(s->mdata_size - (((s->hcyl << 16) | s->lcyl) << 9), + s->nsector << 9), 0x200 - 2)); +} + +static void ide_cfata_metadata_write(IDEState *s) +{ + if (((s->hcyl << 16) | s->lcyl) << 9 > s->mdata_size + 2) { + s->status = ERR_STAT; + s->error = ABRT_ERR; + return; + } + + s->media_changed = 0; + + memcpy(s->mdata_storage + (((s->hcyl << 16) | s->lcyl) << 9), + s->io_buffer + 2, + MIN(MIN(s->mdata_size - (((s->hcyl << 16) | s->lcyl) << 9), + s->nsector << 9), 0x200 - 2)); +} + /* called when the inserted state of the media has changed */ static void cdrom_change_cb(void *opaque) { @@ -1613,7 +1751,10 @@ switch(val) { case WIN_IDENTIFY: if (s->bs && !s->is_cdrom) { - ide_identify(s); + if (!s->is_cf) + ide_identify(s); + else + ide_cfata_identify(s); s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); } else { @@ -1631,12 +1772,16 @@ ide_set_irq(s); break; case WIN_SETMULT: - if (s->nsector > MAX_MULT_SECTORS || + if (s->is_cf && s->nsector == 0) { + /* Disable Read and Write Multiple */ + s->mult_sectors = 0; + s->status = READY_STAT; + } else if ((s->nsector & 0xff) > MAX_MULT_SECTORS || s->nsector == 0 || (s->nsector & (s->nsector - 1)) != 0) { ide_abort_command(s); } else { - s->mult_sectors = s->nsector; + s->mult_sectors = s->nsector & 0xff; s->status = READY_STAT; } ide_set_irq(s); @@ -1664,11 +1809,14 @@ lba48 = 1; case WIN_WRITE: case WIN_WRITE_ONCE: + case CFA_WRITE_SECT_WO_ERASE: + case WIN_WRITE_VERIFY: ide_cmd_lba48_transform(s, lba48); s->error = 0; s->status = SEEK_STAT | READY_STAT; s->req_nb_sectors = 1; ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); + s->media_changed = 1; break; case WIN_MULTREAD_EXT: lba48 = 1; @@ -1682,6 +1830,7 @@ case WIN_MULTWRITE_EXT: lba48 = 1; case WIN_MULTWRITE: + case CFA_WRITE_MULTI_WO_ERASE: if (!s->mult_sectors) goto abort_cmd; ide_cmd_lba48_transform(s, lba48); @@ -1692,6 +1841,7 @@ if (n > s->req_nb_sectors) n = s->req_nb_sectors; ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write); + s->media_changed = 1; break; case WIN_READDMA_EXT: lba48 = 1; @@ -1710,6 +1860,7 @@ goto abort_cmd; ide_cmd_lba48_transform(s, lba48); ide_sector_write_dma(s); + s->media_changed = 1; break; case WIN_READ_NATIVE_MAX_EXT: lba48 = 1; @@ -1720,6 +1871,7 @@ ide_set_irq(s); break; case WIN_CHECKPOWERMODE1: + case WIN_CHECKPOWERMODE2: s->nsector = 0xff; /* device active or idle */ s->status = READY_STAT; ide_set_irq(s); @@ -1733,6 +1885,14 @@ case 0x82: /* write cache disable */ case 0xaa: /* read look-ahead enable */ case 0x55: /* read look-ahead disable */ + case 0x05: /* set advanced power management mode */ + case 0x85: /* disable advanced power management mode */ + case 0x69: /* NOP */ + case 0x67: /* NOP */ + case 0x96: /* NOP */ + case 0x9a: /* NOP */ + case 0x66: /* disable power on reset at soft reset */ + case 0xcc: /* enable power on reset at soft reset */ s->status = READY_STAT | SEEK_STAT; ide_set_irq(s); break; @@ -1772,7 +1932,11 @@ ide_set_irq(s); break; case WIN_STANDBYNOW1: + case WIN_STANDBYNOW2: case WIN_IDLEIMMEDIATE: + case CFA_IDLEIMMEDIATE: + case WIN_SETIDLE1: + case WIN_SETIDLE2: s->status = READY_STAT; ide_set_irq(s); break; @@ -1810,6 +1974,79 @@ ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE, ide_atapi_cmd); break; + /* CF-ATA commands */ + case CFA_REQ_EXT_ERROR_CODE: + if (!s->is_cf) + goto abort_cmd; + s->error = 0x09; /* miscellaneous error */ + s->status = READY_STAT; + ide_set_irq(s); + break; + case CFA_ERASE_SECTORS: + case CFA_WEAR_LEVEL: + if (!s->is_cf) + goto abort_cmd; + if (val == CFA_WEAR_LEVEL) + s->nsector = 0; + if (val == CFA_ERASE_SECTORS) + s->media_changed = 1; + s->error = 0x00; + s->status = READY_STAT; + ide_set_irq(s); + break; + case CFA_TRANSLATE_SECTOR: + if (!s->is_cf) + goto abort_cmd; + s->error = 0x00; + s->status = READY_STAT; + memset(s->io_buffer, 0, 0x200); + s->io_buffer[0x00] = s->hcyl; /* Cyl MSB */ + s->io_buffer[0x01] = s->lcyl; /* Cyl LSB */ + s->io_buffer[0x02] = s->select; /* Head */ + s->io_buffer[0x03] = s->sector; /* Sector */ + s->io_buffer[0x04] = ide_get_sector(s) >> 16; /* LBA MSB */ + s->io_buffer[0x05] = ide_get_sector(s) >> 8; /* LBA */ + s->io_buffer[0x06] = ide_get_sector(s) >> 0; /* LBA LSB */ + s->io_buffer[0x13] = 0x00; /* Erase flag */ + s->io_buffer[0x18] = 0x00; /* Hot count */ + s->io_buffer[0x19] = 0x00; /* Hot count */ + s->io_buffer[0x1a] = 0x01; /* Hot count */ + ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); + ide_set_irq(s); + break; + case CFA_ACCESS_METADATA_STORAGE: + if (!s->is_cf) + goto abort_cmd; + switch (s->feature) { + case 0x02: /* Inquiry Metadata Storage */ + ide_cfata_metadata_inquiry(s); + break; + case 0x03: /* Read Metadata Storage */ + ide_cfata_metadata_read(s); + break; + case 0x04: /* Write Metadata Storage */ + ide_cfata_metadata_write(s); + break; + default: + goto abort_cmd; + } + ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); + s->status = 0x00; /* NOTE: READY is _not_ set */ + ide_set_irq(s); + break; + case IBM_SENSE_CONDITION: + if (!s->is_cf) + goto abort_cmd; + switch (s->feature) { + case 0x01: /* sense temperature in device */ + s->nsector = 0x50; /* +20 C */ + break; + default: + goto abort_cmd; + } + s->status = READY_STAT; + ide_set_irq(s); + break; default: abort_cmd: ide_abort_command(s); @@ -2015,7 +2252,10 @@ static void ide_reset(IDEState *s) { - s->mult_sectors = MAX_MULT_SECTORS; + if (s->is_cf) + s->mult_sectors = 0; + else + s->mult_sectors = MAX_MULT_SECTORS; s->cur_drive = s; s->select = 0xa0; s->status = READY_STAT; @@ -2024,6 +2264,7 @@ accesses */ s->end_transfer_func = ide_dummy_transfer_stop; ide_dummy_transfer_stop(s); + s->media_changed = 0; } struct partition { @@ -2791,3 +3032,8 @@ pmac_ide_write, &ide_if[0]); return pmac_ide_memory; } + +/***********************************************************/ +/* CF-ATA Microdrive */ + +#include "md.c" Index: qemu/hw/integratorcp.c =================================================================== --- qemu.orig/hw/integratorcp.c 2007-01-17 02:54:31.000000000 +0800 +++ qemu/hw/integratorcp.c 2007-03-06 10:25:41.000000000 +0800 @@ -510,13 +510,13 @@ pl110_init(ds, 0xc0000000, pic, 22, 0); arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline, - initrd_filename, 0x113); + initrd_filename, 0x113, 0x0); } static void integratorcp926_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) + const char *initrd_filename, const char *cpu_model) { integratorcp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, kernel_filename, kernel_cmdline, @@ -526,7 +526,7 @@ static void integratorcp1026_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) + const char *initrd_filename, const char *cpu_model) { integratorcp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, kernel_filename, kernel_cmdline, Index: qemu/hw/max111x.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/max111x.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,138 @@ +/* + * MAX1110/1111 ADC chip emulation. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + */ + +#include + +struct max111x_s { + void (*interrupt)(void *opaque); + void *opaque; + uint8_t tb1, rb2, rb3; + int cycle; + + int input[8]; + int inputs, com; +}; + +/* Control-byte bitfields */ +#define CB_PD0 (1 << 0) +#define CB_PD1 (1 << 1) +#define CB_SGL (1 << 2) +#define CB_UNI (1 << 3) +#define CB_SEL0 (1 << 4) +#define CB_SEL1 (1 << 5) +#define CB_SEL2 (1 << 6) +#define CB_START (1 << 7) + +#define CHANNEL_NUM(v, b0, b1, b2) \ + ((((v) >> (2 + (b0))) & 4) | \ + (((v) >> (3 + (b1))) & 2) | \ + (((v) >> (4 + (b2))) & 1)) + +uint32_t max111x_read(void *opaque) +{ + struct max111x_s *s = (struct max111x_s *) opaque; + + if (!s->tb1) + return 0; + + switch (s->cycle ++) { + case 1: + return s->rb2; + case 2: + return s->rb3; + } + + return 0; +} + +/* Interpret a control-byte */ +void max111x_write(void *opaque, uint32_t value) +{ + struct max111x_s *s = (struct max111x_s *) opaque; + int measure, chan; + + /* Ignore the value if START bit is zero */ + if (!(value & CB_START)) + return; + + s->cycle = 0; + + if (!(value & CB_PD1)) { + s->tb1 = 0; + return; + } + + s->tb1 = value; + + if (s->inputs == 8) + chan = CHANNEL_NUM(value, 1, 0, 2); + else + chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2); + + if (value & CB_SGL) + measure = s->input[chan] - s->com; + else + measure = s->input[chan] - s->input[chan ^ 1]; + + if (!(value & CB_UNI)) + measure ^= 0x80; + + s->rb2 = (measure >> 2) & 0x3f; + s->rb3 = (measure << 6) & 0xc0; + + if (s->interrupt) + s->interrupt(s->opaque); +} + +struct max111x_s *max111x_init(void (*cb)(void *opaque), void *opaque) +{ + struct max111x_s *s; + s = (struct max111x_s *) + qemu_mallocz(sizeof(struct max111x_s)); + memset(s, 0, sizeof(struct max111x_s)); + + s->interrupt = cb; + s->opaque = opaque; + + /* TODO: add a user interface for setting these */ + s->input[0] = 0xf0; + s->input[1] = 0xe0; + s->input[2] = 0xd0; + s->input[3] = 0xc0; + s->input[4] = 0xb0; + s->input[5] = 0xa0; + s->input[6] = 0x90; + s->input[7] = 0x80; + s->com = 0; + return s; +} + +struct max111x_s *max1110_init(void (*cb)(void *opaque), void *opaque) +{ + struct max111x_s *s = max111x_init(cb, opaque); + s->inputs = 8; + return s; +} + +struct max111x_s *max1111_init(void (*cb)(void *opaque), void *opaque) +{ + struct max111x_s *s = max111x_init(cb, opaque); + s->inputs = 4; + return s; +} + +void max111x_set_input(struct max111x_s *s, int line, uint8_t value) +{ + if (line >= s->inputs) { + printf("%s: There's no input %i\n", __FUNCTION__, line); + return; + } + + s->input[line] = value; +} Index: qemu/hw/max7310.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/max7310.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,176 @@ +/* + * MAX7310 8-port GPIO expansion chip. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This file is licensed under GNU GPL. + */ + +#include "vl.h" + +struct max7310_s { + uint8_t level; + uint8_t direction; + uint8_t polarity; + uint8_t status; + uint8_t command; + int i2c_dir; + struct i2c_slave_s i2c; + struct { + gpio_handler_t fn; + void *opaque; + } handler[8]; +}; + +void max7310_reset(struct i2c_slave_s *i2c) +{ + struct max7310_s *s = (struct max7310_s *) i2c->opaque; + s->level &= s->direction; + s->direction = 0xff; + s->polarity = 0xf0; + s->status = 0x01; + s->command = 0x00; +} + +static void max7310_start(void *opaque, int dir) +{ + struct max7310_s *s = (struct max7310_s *) opaque; + s->i2c_dir = dir; +} + +static int max7310_read(void *opaque, uint8_t *data, int len) +{ + struct max7310_s *s = (struct max7310_s *) opaque; + + switch (s->command) { + case 0x00: /* Input port */ + memset(data, s->level ^ s->polarity, len); + break; + + case 0x01: /* Output port */ + memset(data, s->level & ~s->direction, len); + break; + + case 0x02: /* Polarity inversion */ + memset(data, s->polarity, len); + break; + + case 0x03: /* Configuration */ + memset(data, s->direction, len); + break; + + case 0x04: /* Timeout */ + memset(data, s->status, len); + break; + + case 0xff: /* Reserved */ + memset(data, 0xff, len); + break; + + default: +#ifdef VERBOSE + printf("%s: unknown register %02x\n", __FUNCTION__, s->command); +#endif + return 1; + } + return 0; +} + +static int max7310_write(void *opaque, uint8_t *data, int len) +{ + struct max7310_s *s = (struct max7310_s *) opaque; + uint8_t diff; + int line; + + if (len >= 1) + s->command = data[0]; + + if (len < 2) { +#ifdef VERBOSE + printf("%s: message too short (%i bytes)\n", __FUNCTION__, len); +#endif + return 0; + } + + switch (s->command) { + case 0x01: /* Output port */ + for (diff = (data[1] ^ s->level) & ~s->direction; diff; + diff &= ~(1 << line)) { + line = ffs(diff) - 1; + if (s->handler[line].fn) + s->handler[line].fn(line, (data[1] >> line) & 1, + s->handler[line].opaque); + } + s->level = (s->level & s->direction) | (data[1] & ~s->direction); + break; + + case 0x02: /* Polarity inversion */ + s->polarity = data[1]; + break; + + case 0x03: /* Configuration */ + s->level &= ~(s->direction ^ data[1]); + s->direction = data[1]; + break; + + case 0x04: /* Timeout */ + s->status = data[1]; + break; + + default: +#ifdef VERBOSE + printf("%s: unknown register %02x\n", __FUNCTION__, s->command); +#endif + return 1; + } + + return 0; +} + +static int max7310_tx(void *opaque, uint8_t *data, int len) +{ + struct max7310_s *s = (struct max7310_s *) opaque; + if (len) { + if (s->i2c_dir) + return max7310_write(opaque, data, len); + else + return max7310_read(opaque, data, len); + } + + return 0; +} + +struct i2c_slave_s *max7310_init(void) +{ + struct max7310_s *s = qemu_mallocz(sizeof(struct max7310_s)); + s->i2c.opaque = s; + s->i2c.tx = max7310_tx; + s->i2c.start = max7310_start; + + max7310_reset(&s->i2c); + return &s->i2c; +} + +void max7310_gpio_set(struct i2c_slave_s *i2c, int line, int level) +{ + struct max7310_s *s = (struct max7310_s *) i2c->opaque; + if (line >= sizeof(s->handler) / sizeof(*s->handler) || line < 0) + cpu_abort(cpu_single_env, "bad GPIO line"); + + if (level) + s->level |= s->direction & (1 << line); + else + s->level &= ~(s->direction & (1 << line)); +} + +void max7310_gpio_handler_set(struct i2c_slave_s *i2c, int line, + gpio_handler_t handler, void *opaque) +{ + struct max7310_s *s = (struct max7310_s *) i2c->opaque; + if (line >= sizeof(s->handler) / sizeof(*s->handler) || line < 0) + cpu_abort(cpu_single_env, "bad GPIO line"); + + s->handler[line].fn = handler; + s->handler[line].opaque = opaque; +} Index: qemu/hw/md.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/md.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,499 @@ +/* + * DSCM-1XXXX Microdrive hard disk with CF+ II / PCMCIA interface. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + */ + +#include "vl.h" + +#define METADATA_SIZE 0x20 + +struct md_s { + IDEState ide[2]; + struct pcmcia_card_s card; + uint32_t attr_base; + uint32_t io_base; + + /* Card state */ + uint8_t opt; + uint8_t stat; + uint8_t pins; + + uint8_t ctrl; + uint16_t io; + int cycle; +}; + +/* Register bitfields */ +enum md_opt { + OPT_MODE_MMAP = 0, + OPT_MODE_IOMAP16 = 1, + OPT_MODE_IOMAP1 = 2, + OPT_MODE_IOMAP2 = 3, + OPT_MODE = 0x3f, + OPT_LEVIREQ = 0x40, + OPT_SRESET = 0x80, +}; +enum md_cstat { + STAT_INT = 0x02, + STAT_PWRDWN = 0x04, + STAT_XE = 0x10, + STAT_IOIS8 = 0x20, + STAT_SIGCHG = 0x40, + STAT_CHANGED = 0x80, +}; +enum md_pins { + PINS_MRDY = 0x02, + PINS_CRDY = 0x20, +}; +enum md_ctrl { + CTRL_IEN = 0x02, + CTRL_SRST = 0x04, +}; + +static inline void md_interrupt_update(struct md_s *s) +{ + if (!s->card.slot) + return; + + s->card.slot->set_irq(s->card.slot->opaque, 0, + !(s->stat & STAT_INT) && /* Inverted */ + !(s->ctrl & (CTRL_IEN | CTRL_SRST)) && + !(s->opt & OPT_SRESET)); +} + +static void md_set_irq(void *opaque, int irq, int level) +{ + struct md_s *s = (struct md_s *) opaque; + if (level) + s->stat |= STAT_INT; + else + s->stat &= ~STAT_INT; + + md_interrupt_update(s); +} + +static void md_reset(struct md_s *s) +{ + s->opt = OPT_MODE_MMAP; + s->stat = 0; + s->pins = 0; + s->cycle = 0; + s->ctrl = 0; + ide_reset(s->ide); +} + +static uint8_t md_attr_read(void *opaque, uint16_t at) +{ + struct md_s *s = (struct md_s *) opaque; + if (at < s->attr_base) { + if (at < s->card.cis_len) + return s->card.cis[at]; + else + return 0x00; + } + + at -= s->attr_base; + + switch (at) { + case 0x00: /* Configuration Option Register */ + return s->opt; + case 0x02: /* Card Configuration Status Register */ + if (s->ctrl & CTRL_IEN) + return s->stat & ~STAT_INT; + else + return s->stat; + case 0x04: /* Pin Replacement Register */ + return (s->pins & PINS_CRDY) | 0x0c; + case 0x06: /* Socket and Copy Register */ + return 0x00; +#ifdef VERBOSE + default: + printf("%s: Bad attribute space register %02x\n", __FUNCTION__, at); +#endif + } + + return 0; +} + +static void md_attr_write(void *opaque, uint16_t at, uint8_t value) +{ + struct md_s *s = (struct md_s *) opaque; + at -= s->attr_base; + + switch (at) { + case 0x00: /* Configuration Option Register */ + s->opt = value & 0xcf; + if (value & OPT_SRESET) + md_reset(s); + md_interrupt_update(s); + break; + case 0x02: /* Card Configuration Status Register */ + if ((s->stat ^ value) & STAT_PWRDWN) + s->pins |= PINS_CRDY; + s->stat &= 0x82; + s->stat |= value & 0x74; + md_interrupt_update(s); + /* Word 170 in Identify Device must be equal to STAT_XE */ + break; + case 0x04: /* Pin Replacement Register */ + s->pins &= PINS_CRDY; + s->pins |= value & PINS_MRDY; + break; + case 0x06: /* Socket and Copy Register */ + break; + default: + printf("%s: Bad attribute space register %02x\n", __FUNCTION__, at); + } +} + +static uint16_t md_common_read(void *opaque, uint16_t at) +{ + struct md_s *s = (struct md_s *) opaque; + uint16_t ret; + at -= s->io_base; + + switch (s->opt & OPT_MODE) { + case OPT_MODE_MMAP: + if ((at & ~0x3ff) == 0x400) + at = 0; + break; + case OPT_MODE_IOMAP16: + at &= 0xf; + break; + case OPT_MODE_IOMAP1: + if ((at & ~0xf) == 0x3f0) + at -= 0x3e8; + else if ((at & ~0xf) == 0x1f0) + at -= 0x1f0; + break; + case OPT_MODE_IOMAP2: + if ((at & ~0xf) == 0x370) + at -= 0x368; + else if ((at & ~0xf) == 0x170) + at -= 0x170; + } + + switch (at) { + case 0x0: /* Even RD Data */ + case 0x8: + return ide_data_readw(s->ide, 0); + + /* TODO: 8-bit accesses */ + if (s->cycle) + ret = s->io >> 8; + else { + s->io = ide_data_readw(s->ide, 0); + ret = s->io & 0xff; + } + s->cycle = !s->cycle; + return ret; + case 0x9: /* Odd RD Data */ + return s->io >> 8; + case 0xd: /* Error */ + return ide_ioport_read(s->ide, 0x1); + case 0xe: /* Alternate Status */ + if (s->ide->cur_drive->bs) + return s->ide->cur_drive->status; + else + return 0; + case 0xf: /* Device Address */ + return 0xc2 | ((~s->ide->select << 2) & 0x3c); + default: + return ide_ioport_read(s->ide, at); + } + + return 0; +} + +static void md_common_write(void *opaque, uint16_t at, uint16_t value) +{ + struct md_s *s = (struct md_s *) opaque; + at -= s->io_base; + + switch (s->opt & OPT_MODE) { + case OPT_MODE_MMAP: + if ((at & ~0x3ff) == 0x400) + at = 0; + break; + case OPT_MODE_IOMAP16: + at &= 0xf; + break; + case OPT_MODE_IOMAP1: + if ((at & ~0xf) == 0x3f0) + at -= 0x3e8; + else if ((at & ~0xf) == 0x1f0) + at -= 0x1f0; + break; + case OPT_MODE_IOMAP2: + if ((at & ~0xf) == 0x370) + at -= 0x368; + else if ((at & ~0xf) == 0x170) + at -= 0x170; + } + + switch (at) { + case 0x0: /* Even WR Data */ + case 0x8: + ide_data_writew(s->ide, 0, value); + break; + + /* TODO: 8-bit accesses */ + if (s->cycle) + ide_data_writew(s->ide, 0, s->io | (value << 8)); + else + s->io = value & 0xff; + s->cycle = !s->cycle; + break; + case 0x9: + s->io = value & 0xff; + s->cycle = !s->cycle; + break; + case 0xd: /* Features */ + ide_ioport_write(s->ide, 0x1, value); + break; + case 0xe: /* Device Control */ + s->ctrl = value; + if (value & CTRL_SRST) + md_reset(s); + md_interrupt_update(s); + break; + default: + if (s->stat & STAT_PWRDWN) { + s->pins |= PINS_CRDY; + s->stat &= ~STAT_PWRDWN; + } + ide_ioport_write(s->ide, at, value); + } +} + +static const uint8_t dscm1xxxx_cis[0x14a] = { + [0x000] = CISTPL_DEVICE, /* 5V Device Information */ + [0x002] = 0x03, /* Tuple length = 4 bytes */ + [0x004] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */ + [0x006] = 0x01, /* Size = 2K bytes */ + [0x008] = CISTPL_ENDMARK, + + [0x00a] = CISTPL_DEVICE_OC, /* Additional Device Information */ + [0x00c] = 0x04, /* Tuple length = 4 byest */ + [0x00e] = 0x03, /* Conditions: Ext = 0, Vcc 3.3V, MWAIT = 1 */ + [0x010] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */ + [0x012] = 0x01, /* Size = 2K bytes */ + [0x014] = CISTPL_ENDMARK, + + [0x016] = CISTPL_JEDEC_C, /* JEDEC ID */ + [0x018] = 0x02, /* Tuple length = 2 bytes */ + [0x01a] = 0xdf, /* PC Card ATA with no Vpp required */ + [0x01c] = 0x01, + + [0x01e] = CISTPL_MANFID, /* Manufacture ID */ + [0x020] = 0x04, /* Tuple length = 4 bytes */ + [0x022] = 0xa4, /* TPLMID_MANF = 00a4 (IBM) */ + [0x024] = 0x00, + [0x026] = 0x00, /* PLMID_CARD = 0000 */ + [0x028] = 0x00, + + [0x02a] = CISTPL_VERS_1, /* Level 1 Version */ + [0x02c] = 0x12, /* Tuple length = 23 bytes */ + [0x02e] = 0x04, /* Major Version = JEIDA 4.2 / PCMCIA 2.1 */ + [0x030] = 0x01, /* Minor Version = 1 */ + [0x032] = 'I', + [0x034] = 'B', + [0x036] = 'M', + [0x038] = 0x00, + [0x03a] = 'm', + [0x03c] = 'i', + [0x03e] = 'c', + [0x040] = 'r', + [0x042] = 'o', + [0x044] = 'd', + [0x046] = 'r', + [0x048] = 'i', + [0x04a] = 'v', + [0x04c] = 'e', + [0x04e] = 0x00, + [0x050] = CISTPL_ENDMARK, + + [0x052] = CISTPL_FUNCID, /* Function ID */ + [0x054] = 0x02, /* Tuple length = 2 bytes */ + [0x056] = 0x04, /* TPLFID_FUNCTION = Fixed Disk */ + [0x058] = 0x01, /* TPLFID_SYSINIT: POST = 1, ROM = 0 */ + + [0x05a] = CISTPL_FUNCE, /* Function Extension */ + [0x05c] = 0x02, /* Tuple length = 2 bytes */ + [0x05e] = 0x01, /* TPLFE_TYPE = Disk Device Interface */ + [0x060] = 0x01, /* TPLFE_DATA = PC Card ATA Interface */ + + [0x062] = CISTPL_FUNCE, /* Function Extension */ + [0x064] = 0x03, /* Tuple length = 3 bytes */ + [0x066] = 0x02, /* TPLFE_TYPE = Basic PC Card ATA Interface */ + [0x068] = 0x08, /* TPLFE_DATA: Rotating, Unique, Single */ + [0x06a] = 0x0f, /* TPLFE_DATA: Sleep, Standby, Idle, Auto */ + + [0x06c] = CISTPL_CONFIG, /* Configuration */ + [0x06e] = 0x05, /* Tuple length = 5 bytes */ + [0x070] = 0x01, /* TPCC_RASZ = 2 bytes, TPCC_RMSZ = 1 byte */ + [0x072] = 0x07, /* TPCC_LAST = 7 */ + [0x074] = 0x00, /* TPCC_RADR = 0200 */ + [0x076] = 0x02, + [0x078] = 0x0f, /* TPCC_RMSK = 200, 202, 204, 206 */ + + [0x07a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x07c] = 0x0b, /* Tuple length = 11 bytes */ + [0x07e] = 0xc0, /* TPCE_INDX = Memory Mode, Default, Iface */ + [0x080] = 0xc0, /* TPCE_IF = Memory, no BVDs, no WP, READY */ + [0x082] = 0xa1, /* TPCE_FS = Vcc only, no I/O, Memory, Misc */ + [0x084] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ + [0x086] = 0x55, /* NomV: 5.0 V */ + [0x088] = 0x4d, /* MinV: 4.5 V */ + [0x08a] = 0x5d, /* MaxV: 5.5 V */ + [0x08c] = 0x4e, /* Peakl: 450 mA */ + [0x08e] = 0x08, /* TPCE_MS = 1 window, 1 byte, Host address */ + [0x090] = 0x00, /* Window descriptor: Window length = 0 */ + [0x092] = 0x20, /* TPCE_MI: support power down mode, RW */ + + [0x094] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x096] = 0x06, /* Tuple length = 6 bytes */ + [0x098] = 0x00, /* TPCE_INDX = Memory Mode, no Default */ + [0x09a] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ + [0x09c] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ + [0x09e] = 0xb5, /* NomV: 3.3 V */ + [0x0a0] = 0x1e, + [0x0a2] = 0x3e, /* Peakl: 350 mA */ + + [0x0a4] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x0a6] = 0x0d, /* Tuple length = 13 bytes */ + [0x0a8] = 0xc1, /* TPCE_INDX = I/O and Memory Mode, Default */ + [0x0aa] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ + [0x0ac] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ + [0x0ae] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ + [0x0b0] = 0x55, /* NomV: 5.0 V */ + [0x0b2] = 0x4d, /* MinV: 4.5 V */ + [0x0b4] = 0x5d, /* MaxV: 5.5 V */ + [0x0b6] = 0x4e, /* Peakl: 450 mA */ + [0x0b8] = 0x64, /* TPCE_IO = 16-byte boundary, 16/8 accesses */ + [0x0ba] = 0xf0, /* TPCE_IR = MASK, Level, Pulse, Share */ + [0x0bc] = 0xff, /* IRQ0..IRQ7 supported */ + [0x0be] = 0xff, /* IRQ8..IRQ15 supported */ + [0x0c0] = 0x20, /* TPCE_MI = support power down mode */ + + [0x0c2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x0c4] = 0x06, /* Tuple length = 6 bytes */ + [0x0c6] = 0x01, /* TPCE_INDX = I/O and Memory Mode */ + [0x0c8] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ + [0x0ca] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ + [0x0cc] = 0xb5, /* NomV: 3.3 V */ + [0x0ce] = 0x1e, + [0x0d0] = 0x3e, /* Peakl: 350 mA */ + + [0x0d2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x0d4] = 0x12, /* Tuple length = 18 bytes */ + [0x0d6] = 0xc2, /* TPCE_INDX = I/O Primary Mode */ + [0x0d8] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ + [0x0da] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ + [0x0dc] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ + [0x0de] = 0x55, /* NomV: 5.0 V */ + [0x0e0] = 0x4d, /* MinV: 4.5 V */ + [0x0e2] = 0x5d, /* MaxV: 5.5 V */ + [0x0e4] = 0x4e, /* Peakl: 450 mA */ + [0x0e6] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */ + [0x0e8] = 0x61, /* Range: 2 fields, 2 bytes addr, 1 byte len */ + [0x0ea] = 0xf0, /* Field 1 address = 0x01f0 */ + [0x0ec] = 0x01, + [0x0ee] = 0x07, /* Address block length = 8 */ + [0x0f0] = 0xf6, /* Field 2 address = 0x03f6 */ + [0x0f2] = 0x03, + [0x0f4] = 0x01, /* Address block length = 2 */ + [0x0f6] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */ + [0x0f8] = 0x20, /* TPCE_MI = support power down mode */ + + [0x0fa] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x0fc] = 0x06, /* Tuple length = 6 bytes */ + [0x0fe] = 0x02, /* TPCE_INDX = I/O Primary Mode, no Default */ + [0x100] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ + [0x102] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ + [0x104] = 0xb5, /* NomV: 3.3 V */ + [0x106] = 0x1e, + [0x108] = 0x3e, /* Peakl: 350 mA */ + + [0x10a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x10c] = 0x12, /* Tuple length = 18 bytes */ + [0x10e] = 0xc3, /* TPCE_INDX = I/O Secondary Mode, Default */ + [0x110] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ + [0x112] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ + [0x114] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ + [0x116] = 0x55, /* NomV: 5.0 V */ + [0x118] = 0x4d, /* MinV: 4.5 V */ + [0x11a] = 0x5d, /* MaxV: 5.5 V */ + [0x11c] = 0x4e, /* Peakl: 450 mA */ + [0x11e] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */ + [0x120] = 0x61, /* Range: 2 fields, 2 byte addr, 1 byte len */ + [0x122] = 0x70, /* Field 1 address = 0x0170 */ + [0x124] = 0x01, + [0x126] = 0x07, /* Address block length = 8 */ + [0x128] = 0x76, /* Field 2 address = 0x0376 */ + [0x12a] = 0x03, + [0x12c] = 0x01, /* Address block length = 2 */ + [0x12e] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */ + [0x130] = 0x20, /* TPCE_MI = support power down mode */ + + [0x132] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x134] = 0x06, /* Tuple length = 6 bytes */ + [0x136] = 0x03, /* TPCE_INDX = I/O Secondary Mode */ + [0x138] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ + [0x13a] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ + [0x13c] = 0xb5, /* NomV: 3.3 V */ + [0x13e] = 0x1e, + [0x140] = 0x3e, /* Peakl: 350 mA */ + + [0x142] = CISTPL_NO_LINK, /* No Link */ + [0x144] = 0x00, /* Tuple length = 0 bytes */ + + [0x146] = CISTPL_END, /* Tuple End */ +}; + +static int dscm1xxxx_attach(void *opaque) +{ + struct md_s *md = (struct md_s *) opaque; + md->card.attr_read = md_attr_read; + md->card.attr_write = md_attr_write; + md->card.common_read = md_common_read; + md->card.common_write = md_common_write; + md->card.io_read = md_common_read; + md->card.io_write = md_common_write; + + md->attr_base = md->card.cis[0x74] | (md->card.cis[0x76] << 8); + md->io_base = 0x0; + + md_reset(md); + md_interrupt_update(md); + + md->card.slot->card_string = "DSCM-1xxxx Hitachi Microdrive"; + return 0; +} + +static int dscm1xxxx_detach(void *opaque) +{ + struct md_s *md = (struct md_s *) opaque; + md_reset(md); + return 0; +} + +struct pcmcia_card_s *dscm1xxxx_init(BlockDriverState *bdrv) +{ + struct md_s *md = (struct md_s *) qemu_mallocz(sizeof(struct md_s)); + md->card.state = md; + md->card.attach = dscm1xxxx_attach; + md->card.detach = dscm1xxxx_detach; + md->card.cis = dscm1xxxx_cis; + md->card.cis_len = sizeof(dscm1xxxx_cis); + + ide_init2(md->ide, bdrv, 0, md_set_irq, md, 0); + md->ide->is_cf = 1; + md->ide->mdata_size = METADATA_SIZE; + md->ide->mdata_storage = (uint8_t *) qemu_mallocz(METADATA_SIZE); + return &md->card; +} Index: qemu/hw/nand.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/nand.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,612 @@ +/* + * Flash NAND memory emulation. Based on "16M x 8 Bit NAND Flash + * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from + * Samsung Electronic. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + */ + +#ifndef NAND_IO + +# include "vl.h" + +# define NAND_CMD_READ0 0x00 +# define NAND_CMD_READ1 0x01 +# define NAND_CMD_READ2 0x50 +# define NAND_CMD_LPREAD2 0x30 +# define NAND_CMD_NOSERIALREAD2 0x35 +# define NAND_CMD_RANDOMREAD1 0x05 +# define NAND_CMD_RANDOMREAD2 0xe0 +# define NAND_CMD_READID 0x90 +# define NAND_CMD_RESET 0xff +# define NAND_CMD_PAGEPROGRAM1 0x80 +# define NAND_CMD_PAGEPROGRAM2 0x10 +# define NAND_CMD_CACHEPROGRAM2 0x15 +# define NAND_CMD_BLOCKERASE1 0x60 +# define NAND_CMD_BLOCKERASE2 0xd0 +# define NAND_CMD_READSTATUS 0x70 +# define NAND_CMD_COPYBACKPRG1 0x85 + +# define NAND_IOSTATUS_ERROR (1 << 0) +# define NAND_IOSTATUS_PLANE0 (1 << 1) +# define NAND_IOSTATUS_PLANE1 (1 << 2) +# define NAND_IOSTATUS_PLANE2 (1 << 3) +# define NAND_IOSTATUS_PLANE3 (1 << 4) +# define NAND_IOSTATUS_BUSY (1 << 6) +# define NAND_IOSTATUS_UNPROTCT (1 << 7) + +# define MAX_PAGE 0x800 +# define MAX_OOB 0x40 + +struct nand_flash_s { + uint8_t manf_id, chip_id; + int size, pages; + int page_shift, oob_shift, erase_shift, addr_shift; + uint8_t *storage; + BlockDriverState *bdrv; + int mem_oob; + + int cle, ale, ce, wp, gnd; + + uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; + uint8_t *ioaddr; + int iolen; + + uint32_t cmd, addr; + int addrlen; + int status; + + void (*blk_write)(struct nand_flash_s *s); + void (*blk_erase)(struct nand_flash_s *s); + void (*blk_load)(struct nand_flash_s *s, uint32_t addr, int offset); +}; + +# define NAND_NO_AUTOINCR 0x00000001 +# define NAND_BUSWIDTH_16 0x00000002 +# define NAND_NO_PADDING 0x00000004 +# define NAND_CACHEPRG 0x00000008 +# define NAND_COPYBACK 0x00000010 +# define NAND_IS_AND 0x00000020 +# define NAND_4PAGE_ARRAY 0x00000040 +# define NAND_NO_READRDY 0x00000100 +# define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) + +# define NAND_IO + +# define PAGE(addr) ((addr) >> ADDR_SHIFT) +# define PAGE_START(page) (PAGE(page) * (PAGE_SIZE + OOB_SIZE)) +# define PAGE_MASK (PAGE_SIZE - 1) +# define OOB_SHIFT (PAGE_SHIFT - 5) +# define OOB_SIZE (1 << OOB_SHIFT) +# define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) +# define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) + +# define PAGE_SIZE 256 +# define PAGE_SHIFT 8 +# define PAGE_SECTORS 1 +# define ADDR_SHIFT 8 +# include "nand.c" +# define PAGE_SIZE 512 +# define PAGE_SHIFT 9 +# define PAGE_SECTORS 1 +# define ADDR_SHIFT 9 +# include "nand.c" +# define PAGE_SIZE 2048 +# define PAGE_SHIFT 11 +# define PAGE_SECTORS 4 +# define ADDR_SHIFT 16 +# include "nand.c" + +/* Information based on Linux drivers/mtd/nand/nand_ids.c */ +struct nand_info_s { + int size; + int width; + int page_shift; + int erase_shift; + uint32_t options; +} nand_flash_ids[0x100] = { + [0 ... 0xff] = { 0 }, + + [0x6e] = { 1, 8, 8, 4, 0 }, + [0x64] = { 2, 8, 8, 4, 0 }, + [0x6b] = { 4, 8, 9, 4, 0 }, + [0xe8] = { 1, 8, 8, 4, 0 }, + [0xec] = { 1, 8, 8, 4, 0 }, + [0xea] = { 2, 8, 8, 4, 0 }, + [0xd5] = { 4, 8, 9, 4, 0 }, + [0xe3] = { 4, 8, 9, 4, 0 }, + [0xe5] = { 4, 8, 9, 4, 0 }, + [0xd6] = { 8, 8, 9, 4, 0 }, + + [0x39] = { 8, 8, 9, 4, 0 }, + [0xe6] = { 8, 8, 9, 4, 0 }, + [0x49] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, + [0x59] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, + + [0x33] = { 16, 8, 9, 5, 0 }, + [0x73] = { 16, 8, 9, 5, 0 }, + [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, + + [0x35] = { 32, 8, 9, 5, 0 }, + [0x75] = { 32, 8, 9, 5, 0 }, + [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, + + [0x36] = { 64, 8, 9, 5, 0 }, + [0x76] = { 64, 8, 9, 5, 0 }, + [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, + + [0x78] = { 128, 8, 9, 5, 0 }, + [0x39] = { 128, 8, 9, 5, 0 }, + [0x79] = { 128, 8, 9, 5, 0 }, + [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, + + [0x71] = { 256, 8, 9, 5, 0 }, + + /* + * These are the new chips with large page size. The pagesize and the + * erasesize is determined from the extended id bytes + */ +# define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) +# define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + + /* 512 Megabit */ + [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, + [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, + [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, + [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, + + /* 1 Gigabit */ + [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, + [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, + [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, + [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, + + /* 2 Gigabit */ + [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, + [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, + [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, + [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, + + /* 4 Gigabit */ + [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, + [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, + [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, + [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, + + /* 8 Gigabit */ + [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, + [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, + [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, + [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, + + /* 16 Gigabit */ + [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, + [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, + [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, + [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, +}; + +static void nand_reset(struct nand_flash_s *s) +{ + s->cmd = NAND_CMD_READ0; + s->addr = 0; + s->addrlen = 0; + s->iolen = 0; + s->status &= NAND_IOSTATUS_UNPROTCT; +} + +static void nand_command(struct nand_flash_s *s) +{ + switch (s->cmd) { + case NAND_CMD_READ0: + case NAND_CMD_READ1: + case NAND_CMD_READ2: + s->iolen = 0; + break; + + case NAND_CMD_READID: + s->io[0] = s->manf_id; + s->io[1] = s->chip_id; + s->io[2] = 'Q'; /* Don't-care byte (often 0xa5) */ + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) + s->io[3] = 0x15; /* Page Size, Block Size, Spare Size.. */ + else + s->io[3] = 0xc0; /* Multi-plane */ + s->ioaddr = s->io; + s->iolen = 4; + break; + + case NAND_CMD_RANDOMREAD2: + case NAND_CMD_NOSERIALREAD2: + if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) + break; + + s->blk_load(s, s->addr, s->addr & ((1 << s->addr_shift) - 1)); + break; + + case NAND_CMD_RESET: + nand_reset(s); + break; + + case NAND_CMD_PAGEPROGRAM1: + s->ioaddr = s->io; + s->iolen = 0; + break; + + case NAND_CMD_PAGEPROGRAM2: + if (s->wp) { + s->blk_write(s); + } + break; + + case NAND_CMD_BLOCKERASE1: + break; + + case NAND_CMD_BLOCKERASE2: + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) + s->addr <<= 16; + else + s->addr <<= 8; + + if (s->wp) { + s->blk_erase(s); + } + break; + + case NAND_CMD_READSTATUS: + s->io[0] = s->status; + s->ioaddr = s->io; + s->iolen = 1; + break; + + default: + printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd); + } +} + +/* + * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip + * outputs are R/B and eight I/O pins. + * + * CE, WP and R/B are active low. + */ +void nand_setpins(struct nand_flash_s *s, + int cle, int ale, int ce, int wp, int gnd) +{ + s->cle = cle; + s->ale = ale; + s->ce = ce; + s->wp = wp; + s->gnd = gnd; + if (wp) + s->status |= NAND_IOSTATUS_UNPROTCT; + else + s->status &= ~NAND_IOSTATUS_UNPROTCT; +} + +void nand_getpins(struct nand_flash_s *s, int *rb) +{ + *rb = 1; +} + +void nand_setio(struct nand_flash_s *s, uint8_t value) +{ + if (!s->ce && s->cle) { + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { + if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) + return; + if (value == NAND_CMD_RANDOMREAD1) { + s->addr &= ~((1 << s->addr_shift) - 1); + s->addrlen = 0; + return; + } + } + + s->cmd = value; + + if (s->cmd == NAND_CMD_READSTATUS || + s->cmd == NAND_CMD_PAGEPROGRAM2 || + s->cmd == NAND_CMD_BLOCKERASE1 || + s->cmd == NAND_CMD_BLOCKERASE2 || + s->cmd == NAND_CMD_NOSERIALREAD2 || + s->cmd == NAND_CMD_RANDOMREAD2 || + s->cmd == NAND_CMD_RESET) + nand_command(s); + + if (s->cmd != NAND_CMD_RANDOMREAD2) { + s->addrlen = 0; + s->addr = 0; + } + } + + if (s->ale) { + s->addr |= value << (s->addrlen * 8); + s->addrlen ++; + + if (s->addrlen == 1 && s->cmd == NAND_CMD_READID) + nand_command(s); + + if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && + s->addrlen == 3 && ( + s->cmd == NAND_CMD_READ0 || + s->cmd == NAND_CMD_READ1 || + s->cmd == NAND_CMD_READ2 || + s->cmd == NAND_CMD_PAGEPROGRAM1)) + nand_command(s); + if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && + s->addrlen == 4 && ( + s->cmd == NAND_CMD_READ0 || + s->cmd == NAND_CMD_PAGEPROGRAM1)) + nand_command(s); + } + + if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { + if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) + s->io[s->iolen ++] = value; + } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { + if ((s->addr & ((1 << s->addr_shift) - 1)) < + (1 << s->page_shift) + (1 << s->oob_shift)) { + s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value; + s->addr ++; + } + } +} + +uint8_t nand_getio(struct nand_flash_s *s) +{ + int offset; + + /* Allow sequential reading */ + if (!s->iolen && ( + s->cmd == NAND_CMD_READ0 || + s->cmd == NAND_CMD_READ1 || + s->cmd == NAND_CMD_READ2)) { + if (s->cmd == NAND_CMD_READ0) + offset = (s->addr & ((1 << s->addr_shift) - 1)) | 0x0; + else if (s->cmd == NAND_CMD_READ1) { + offset = (s->addr & ((1 << s->addr_shift) - 1)) | 0x100; + s->cmd = NAND_CMD_READ0; + } else + offset = ((s->addr & 0xf0) >> 4) + (1 << s->page_shift); + + s->blk_load(s, s->addr, offset); + if (s->gnd) + s->iolen = (1 << s->page_shift) - offset; + else + s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; + } + + if (s->ce || s->iolen <= 0) + return 0; + + s->iolen --; + return *(s->ioaddr ++); +} + +struct nand_flash_s *nand_init(int manf_id, int chip_id) +{ + int pagesize; + struct nand_flash_s *s; + BlockDriverState *bs = 0; + + if (nand_flash_ids[chip_id].size == 0) { + cpu_abort(cpu_single_env, "%s: Unsupported NAND chip ID.\n", + __FUNCTION__); + } + + if (mtd_filename) { + bs = bdrv_new("mtd"); + if (bdrv_open(bs, mtd_filename, snapshot ? BDRV_O_SNAPSHOT : 0) < 0 || + qemu_key_check(bs, mtd_filename)) { + bdrv_delete(bs); + bs = 0; + } + } + + s = (struct nand_flash_s *) qemu_mallocz(sizeof(struct nand_flash_s)); + s->bdrv = bs; + s->manf_id = manf_id; + s->chip_id = chip_id; + s->size = nand_flash_ids[s->chip_id].size << 20; + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { + s->page_shift = 11; + s->erase_shift = 6; + } else { + s->page_shift = nand_flash_ids[s->chip_id].page_shift; + s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; + } + + switch (1 << s->page_shift) { + case 256: + nand_init_256(s); + break; + case 512: + nand_init_512(s); + break; + case 2048: + nand_init_2048(s); + break; + default: + cpu_abort(cpu_single_env, "%s: Unsupported NAND block size.\n", + __FUNCTION__); + } + + pagesize = 1 << s->oob_shift; + s->mem_oob = 1; + if (bs && bdrv_getlength(bs) >= + (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { + pagesize = 0; + s->mem_oob = 0; + } + + if (!bs) + pagesize += 1 << s->page_shift; + if (pagesize) + s->storage = (uint8_t *) memset(qemu_mallocz(s->pages * pagesize), + 0xff, s->pages * pagesize); + return s; +} + +void nand_done(struct nand_flash_s *s) +{ + if (s->bdrv) { + bdrv_close(s->bdrv); + bdrv_delete(s->bdrv); + } + + if (!s->bdrv || s->mem_oob) + free(s->storage); + + free(s); +} + +#else + +/* Program a single page */ +static void glue(nand_blk_write_, PAGE_SIZE)(struct nand_flash_s *s) +{ + uint32_t off, page, sector, soff; + uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; + if (PAGE(s->addr) >= s->pages) + return; + + if (!s->bdrv) { + memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK), + s->io, s->iolen); + } else if (s->mem_oob) { + sector = SECTOR(s->addr); + off = s->addr & PAGE_MASK; + soff = SECTOR_OFFSET(s->addr); + if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) { + printf("%s: read error in sector %i\n", __FUNCTION__, sector); + return; + } + + memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); + if (off + s->iolen > PAGE_SIZE) { + page = PAGE(s->addr); + memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, + MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE)); + } + + if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) + printf("%s: write error in sector %i\n", __FUNCTION__, sector); + } else { + off = PAGE_START(s->addr) + (s->addr & PAGE_MASK); + sector = off >> 9; + soff = off & 0x1ff; + if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) { + printf("%s: read error in sector %i\n", __FUNCTION__, sector); + return; + } + + memcpy(iobuf + soff, s->io, s->iolen); + + if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) + printf("%s: write error in sector %i\n", __FUNCTION__, sector); + } +} + +/* Erase a single block */ +static void glue(nand_blk_erase_, PAGE_SIZE)(struct nand_flash_s *s) +{ + uint32_t i, page, addr; + uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; + addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); + + if (PAGE(addr) >= s->pages) + return; + + if (!s->bdrv) { + memset(s->storage + PAGE_START(addr), + 0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift); + } else if (s->mem_oob) { + memset(s->storage + (PAGE(addr) << OOB_SHIFT), + 0xff, OOB_SIZE << s->erase_shift); + i = SECTOR(addr); + page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift)); + for (; i < page; i ++) + if (bdrv_write(s->bdrv, i, iobuf, 1) == -1) + printf("%s: write error in sector %i\n", __FUNCTION__, i); + } else { + addr = PAGE_START(addr); + page = addr >> 9; + if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) + printf("%s: read error in sector %i\n", __FUNCTION__, page); + memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); + if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) + printf("%s: write error in sector %i\n", __FUNCTION__, page); + + memset(iobuf, 0xff, 0x200); + i = (addr & ~0x1ff) + 0x200; + for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; + i < addr; i += 0x200) + if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1) + printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9); + + page = i >> 9; + if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) + printf("%s: read error in sector %i\n", __FUNCTION__, page); + memset(iobuf, 0xff, addr & 0x1ff); + if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) + printf("%s: write error in sector %i\n", __FUNCTION__, page); + } +} + +static void glue(nand_blk_load_, PAGE_SIZE)(struct nand_flash_s *s, + uint32_t addr, int offset) +{ + if (PAGE(addr) >= s->pages) + return; + + if (s->bdrv) { + if (s->mem_oob) { + if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1) + printf("%s: read error in sector %i\n", + __FUNCTION__, SECTOR(addr)); + memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE, + s->storage + (PAGE(s->addr) << OOB_SHIFT), + OOB_SIZE); + s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; + } else { + if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9, + s->io, (PAGE_SECTORS + 2)) == -1) + printf("%s: read error in sector %i\n", + __FUNCTION__, PAGE_START(addr) >> 9); + s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; + } + } else { + memcpy(s->io, s->storage + PAGE_START(s->addr) + + offset, PAGE_SIZE + OOB_SIZE - offset); + s->ioaddr = s->io; + } + + s->addr &= ~PAGE_MASK; + s->addr += PAGE_SIZE; +} + +static void glue(nand_init_, PAGE_SIZE)(struct nand_flash_s *s) +{ + s->oob_shift = PAGE_SHIFT - 5; + s->pages = s->size >> PAGE_SHIFT; + s->addr_shift = ADDR_SHIFT; + + s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE); + s->blk_write = glue(nand_blk_write_, PAGE_SIZE); + s->blk_load = glue(nand_blk_load_, PAGE_SIZE); +} + +# undef PAGE_SIZE +# undef PAGE_SHIFT +# undef PAGE_SECTORS +# undef ADDR_SHIFT +#endif /* NAND_IO */ Index: qemu/hw/ndis.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/ndis.h 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,217 @@ +/* + * ndis.h + * + * ntddndis.h modified by Benedikt Spranger + * + * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _LINUX_NDIS_H +#define _LINUX_NDIS_H + + +#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 +#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A +#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B + +enum NDIS_DEVICE_POWER_STATE { + NdisDeviceStateUnspecified = 0, + NdisDeviceStateD0, + NdisDeviceStateD1, + NdisDeviceStateD2, + NdisDeviceStateD3, + NdisDeviceStateMaximum +}; + +struct NDIS_PM_WAKE_UP_CAPABILITIES { + enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp; + enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp; + enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp; +}; + +/* NDIS_PNP_CAPABILITIES.Flags constants */ +#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 +#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 +#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 + +struct NDIS_PNP_CAPABILITIES { + __le32 Flags; + struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities; +}; + +struct NDIS_PM_PACKET_PATTERN { + __le32 Priority; + __le32 Reserved; + __le32 MaskSize; + __le32 PatternOffset; + __le32 PatternSize; + __le32 PatternFlags; +}; + + +/* Required Object IDs (OIDs) */ +#define OID_GEN_SUPPORTED_LIST 0x00010101 +#define OID_GEN_HARDWARE_STATUS 0x00010102 +#define OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define OID_GEN_MEDIA_IN_USE 0x00010104 +#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define OID_GEN_LINK_SPEED 0x00010107 +#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define OID_GEN_VENDOR_ID 0x0001010C +#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define OID_GEN_DRIVER_VERSION 0x00010110 +#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define OID_GEN_MAC_OPTIONS 0x00010113 +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define OID_GEN_SUPPORTED_GUIDS 0x00010117 +#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define OID_GEN_MACHINE_NAME 0x0001021A +#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B +#define OID_GEN_VLAN_ID 0x0001021C + +/* Optional OIDs */ +#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 +#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 + +/* Required statistics OIDs */ +#define OID_GEN_XMIT_OK 0x00020101 +#define OID_GEN_RCV_OK 0x00020102 +#define OID_GEN_XMIT_ERROR 0x00020103 +#define OID_GEN_RCV_ERROR 0x00020104 +#define OID_GEN_RCV_NO_BUFFER 0x00020105 + +/* Optional statistics OIDs */ +#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +#define OID_GEN_RCV_CRC_ERROR 0x0002020D +#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E +#define OID_GEN_GET_TIME_CAPS 0x0002020F +#define OID_GEN_GET_NETCARD_TIME 0x00020210 +#define OID_GEN_NETCARD_LOAD 0x00020211 +#define OID_GEN_DEVICE_PROFILE 0x00020212 +#define OID_GEN_INIT_TIME_MS 0x00020213 +#define OID_GEN_RESET_COUNTS 0x00020214 +#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 +#define OID_GEN_FRIENDLY_NAME 0x00020216 +#define OID_GEN_MINIPORT_INFO 0x00020217 +#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 + +/* IEEE 802.3 (Ethernet) OIDs */ +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 + +#define OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +#define OID_802_3_MULTICAST_LIST 0x01010103 +#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define OID_802_3_MAC_OPTIONS 0x01010105 +#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +#define OID_802_3_XMIT_DEFERRED 0x01020201 +#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define OID_802_3_RCV_OVERRUN 0x01020203 +#define OID_802_3_XMIT_UNDERRUN 0x01020204 +#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + +/* OID_GEN_MINIPORT_INFO constants */ +#define NDIS_MINIPORT_BUS_MASTER 0x00000001 +#define NDIS_MINIPORT_WDM_DRIVER 0x00000002 +#define NDIS_MINIPORT_SG_LIST 0x00000004 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 +#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010 +#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 +#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 +#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 +#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 +#define NDIS_MINIPORT_IS_NDIS_5 0x00000200 +#define NDIS_MINIPORT_IS_CO 0x00000400 +#define NDIS_MINIPORT_DESERIALIZE 0x00000800 +#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 +#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000 +#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000 +#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 +#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 +#define NDIS_MINIPORT_HIDDEN 0x00040000 +#define NDIS_MINIPORT_SWENUM 0x00080000 +#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 +#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 +#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 +#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 +#define NDIS_MINIPORT_64BITS_DMA 0x01000000 + +#define NDIS_MEDIUM_802_3 0x00000000 +#define NDIS_MEDIUM_802_5 0x00000001 +#define NDIS_MEDIUM_FDDI 0x00000002 +#define NDIS_MEDIUM_WAN 0x00000003 +#define NDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define NDIS_MEDIUM_DIX 0x00000005 +#define NDIS_MEDIUM_ARCENT_RAW 0x00000006 +#define NDIS_MEDIUM_ARCENT_878_2 0x00000007 +#define NDIS_MEDIUM_ATM 0x00000008 +#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009 +#define NDIS_MEDIUM_IRDA 0x0000000A +#define NDIS_MEDIUM_BPC 0x0000000B +#define NDIS_MEDIUM_CO_WAN 0x0000000C +#define NDIS_MEDIUM_1394 0x0000000D + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + +#define NDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001 + +#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 +#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 +#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 +#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 +#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 +#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 +#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 +#define NDIS_MAC_OPTION_RESERVED 0x80000000 + +#endif /* _LINUX_NDIS_H */ Index: qemu/hw/pc.c =================================================================== --- qemu.orig/hw/pc.c 2007-02-09 07:09:59.000000000 +0800 +++ qemu/hw/pc.c 2007-03-06 10:25:41.000000000 +0800 @@ -690,6 +690,8 @@ } if (strcmp(nd->model, "ne2k_isa") == 0) { pc_init_ne2k_isa(nd); + } else if (strcmp(nd->model, "usb") == 0) { + /* ignore */ } else if (pci_enabled) { pci_nic_init(pci_bus, nd, -1); } else { @@ -758,7 +760,8 @@ int snapshot, const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename) + const char *initrd_filename, + const char *cpu_model) { pc_init1(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, @@ -771,7 +774,8 @@ int snapshot, const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename) + const char *initrd_filename, + const char *cpu_model) { pc_init1(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, Index: qemu/hw/ppc_chrp.c =================================================================== --- qemu.orig/hw/ppc_chrp.c 2007-01-11 00:17:21.000000000 +0800 +++ qemu/hw/ppc_chrp.c 2007-03-06 10:25:41.000000000 +0800 @@ -292,13 +292,14 @@ } /* PowerPC CHRP hardware initialisation */ -static void ppc_chrp_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, - int is_heathrow) +static void ppc_chrp_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, + const char *cpu_model, + int is_heathrow) { CPUState *env; char buf[1024]; @@ -320,22 +321,16 @@ env = cpu_init(); register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); - /* Register CPU as a 74x/75x */ + /* Default CPU is a generic 74x/75x */ + if (cpu_model == NULL) + cpu_model = "750"; /* XXX: CPU model (or PVR) should be provided on command line */ // ppc_find_by_name("750gx", &def); // Linux boot OK // ppc_find_by_name("750fx", &def); // Linux boot OK /* Linux does not boot on 750cxe (and probably other 750cx based) * because it assumes it has 8 IBAT & DBAT pairs as it only have 4. */ - // ppc_find_by_name("750cxe", &def); - // ppc_find_by_name("750p", &def); - // ppc_find_by_name("740p", &def); - ppc_find_by_name("750", &def); - // ppc_find_by_name("740", &def); - // ppc_find_by_name("G3", &def); - // ppc_find_by_name("604r", &def); - // ppc_find_by_name("604e", &def); - // ppc_find_by_name("604", &def); + ppc_find_by_name(cpu_model, &def); if (def == NULL) { cpu_abort(env, "Unable to find PowerPC CPU definition\n"); } @@ -506,7 +501,7 @@ } if (usb_enabled) { - usb_ohci_init(pci_bus, 3, -1); + usb_ohci_init_pci(pci_bus, 3, -1); } if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) @@ -525,30 +520,32 @@ register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL); } -static void ppc_core99_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 ppc_core99_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, + const char *cpu_model) { ppc_chrp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, kernel_filename, kernel_cmdline, - initrd_filename, 0); + initrd_filename, cpu_model, 0); } -static void ppc_heathrow_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 ppc_heathrow_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, + const char *cpu_model) { ppc_chrp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, kernel_filename, kernel_cmdline, - initrd_filename, 1); + initrd_filename, cpu_model, 1); } QEMUMachine core99_machine = { Index: qemu/hw/ppc_prep.c =================================================================== --- qemu.orig/hw/ppc_prep.c 2006-12-22 00:50:54.000000000 +0800 +++ qemu/hw/ppc_prep.c 2007-03-06 10:25:41.000000000 +0800 @@ -518,10 +518,12 @@ #define NVRAM_SIZE 0x2000 /* PowerPC PREP hardware initialisation */ -static void ppc_prep_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 ppc_prep_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, + const char *cpu_model) { CPUState *env; char buf[1024]; @@ -543,12 +545,11 @@ env = cpu_init(); register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); - - /* Register CPU as a 604 */ - /* XXX: CPU model (or PVR) should be provided on command line */ - // ppc_find_by_name("604r", &def); - // ppc_find_by_name("604e", &def); - ppc_find_by_name("604", &def); + + /* Default CPU is a 604 */ + if (cpu_model == NULL) + cpu_model = "604"; + ppc_find_by_name(cpu_model, &def); if (def == NULL) { cpu_abort(env, "Unable to find PowerPC CPU definition\n"); } @@ -665,7 +666,7 @@ #endif if (usb_enabled) { - usb_ohci_init(pci_bus, 3, -1); + usb_ohci_init_pci(pci_bus, 3, -1); } nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE, 59); Index: qemu/hw/pxa.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa.h 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,1765 @@ +/* + * Intel XScale PXA255/270 processor support. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licenced under the GPL. + */ +#ifndef PXA_H +# define PXA_H 1 + +# include "arm_pic.h" + +/* Interrupt numbers */ +# define PXA2XX_PIC_SSP3 0 +# define PXA2XX_PIC_USBH2 2 +# define PXA2XX_PIC_USBH1 3 +# 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 PXA25X_PIC_NSSP 16 +# define PXA27X_PIC_SSP2 16 +# define PXA2XX_PIC_LCD 17 +# define PXA2XX_PIC_I2C 18 +# 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_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_RAM_BASE 0xa0000000 + +/* pxa2xx_pic.c */ +struct pxa2xx_pic_state_s; +struct pxa2xx_pic_state_s *pxa2xx_pic_init(target_phys_addr_t base, + CPUState *env, int parent_irq, int parent_fiq); + +/* pxa2xx_timer.c */ +void pxa25x_timer_init(target_phys_addr_t base, + void *pic, int irq, CPUState *cpustate); +void pxa27x_timer_init(target_phys_addr_t base, + void *pic, int irq, CPUState *cpustate); + +/* pxa2xx_gpio.c */ +struct pxa2xx_gpio_info_s; +struct pxa2xx_gpio_info_s *pxa2xx_gpio_init(target_phys_addr_t base, + CPUState *env, void *pic, int lines); +void pxa2xx_gpio_set(struct pxa2xx_gpio_info_s *s, int line, int level); +void pxa2xx_gpio_handler_set(struct pxa2xx_gpio_info_s *s, int line, + gpio_handler_t handler, void *opaque); +void pxa2xx_gpio_read_notifier(struct pxa2xx_gpio_info_s *s, + void (*handler)(void *opaque), void *opaque); + +/* pxa2xx_dma.c */ +struct pxa2xx_dma_state_s; +struct pxa2xx_dma_state_s *pxa255_dma_init(target_phys_addr_t base, + void *pic, int irq); +struct pxa2xx_dma_state_s *pxa27x_dma_init(target_phys_addr_t base, + void *pic, int 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, + void *pic, DisplayState *ds); +void pxa2xx_lcd_vsync_cb(struct pxa2xx_lcdc_s *s, + void (*cb)(void *opaque), void *opaque); +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, + void *pic, void *dma); +void pxa2xx_mmci_handlers(struct pxa2xx_mmci_s *s, void *opaque, + void (*readonly_cb)(void *, int), + void (*coverswitch_cb)(void *, int)); + +/* 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, void (*set_irq)(void *opaque, + int line, int level), int irq, int cd_irq, void *pic); + +static struct { + target_phys_addr_t io_base; + int irq; +} pxa255_serial[] = { + { 0x40100000, PXA2XX_PIC_FFUART }, + { 0x40200000, PXA2XX_PIC_BTUART }, + { 0x40700000, PXA2XX_PIC_STUART }, + { 0x41600000, PXA25X_PIC_HWUART }, + { 0, 0 } +}, pxa270_serial[] = { + { 0x40100000, PXA2XX_PIC_FFUART }, + { 0x40200000, PXA2XX_PIC_BTUART }, + { 0x40700000, PXA2XX_PIC_STUART }, + { 0, 0 } +}; + +static struct { + target_phys_addr_t io_base; + int irq; +} pxa27x_ssp[] = { + { 0x41000000, PXA2XX_PIC_SSP }, + { 0x41700000, PXA27X_PIC_SSP2 }, + { 0x41900000, PXA2XX_PIC_SSP3 }, + { 0, 0 } +}; + +/* The CPU is also modeled as an interrupt controller. */ +# define PXA2XX_PIC_CPU_IRQ 0 +# define PXA2XX_PIC_CPU_FIQ 1 + +struct pxa2xx_ssp_s; +struct pxa2xx_i2c_s; +struct pxa2xx_i2s_s; + +struct pxa2xx_state_s { + CPUState *env; + struct pxa2xx_pic_state_s *pic; + struct pxa2xx_dma_state_s *dma; + struct pxa2xx_gpio_info_s *gpio; + struct pxa2xx_lcdc_s *lcd; + struct pxa2xx_ssp_s **ssp; + struct pxa2xx_mmci_s *mmc; + struct pxa2xx_pcmcia_s *pcmcia[2]; + struct pxa2xx_i2c_s *i2c[2]; + struct pxa2xx_i2s_s *i2s; + + /* 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; +}; + +# define PMCR 0x00 /* Power Manager Control Register */ +# define PSSR 0x04 /* Power Manager Sleep Status Register */ +# define PSPR 0x08 /* Power Manager Scratch-Pad Register */ +# define PWER 0x0c /* Power Manager Wake-Up Enable Register */ +# define PRER 0x10 /* Power Manager Rising-Edge Detect Enable Register */ +# define PFER 0x14 /* Power Manager Falling-Edge Detect Enable Register */ +# define PEDR 0x18 /* Power Manager Edge-Detect Status Register */ +# define PCFR 0x1c /* Power Manager General Configuration Register */ +# define PGSR0 0x20 /* Power Manager GPIO Sleep-State Register 0 */ +# define PGSR1 0x24 /* Power Manager GPIO Sleep-State Register 1 */ +# define PGSR2 0x28 /* Power Manager GPIO Sleep-State Register 2 */ +# define PGSR3 0x2c /* Power Manager GPIO Sleep-State Register 3 */ +# define RCSR 0x30 /* Reset Controller Status Register */ +# define PSLR 0x34 /* Power Manager Sleep Configuration Register */ +# define PTSR 0x38 /* Power Manager Standby Configuration Register */ +# define PVCR 0x40 /* Power Manager Voltage Change Control Register */ +# define PUCR 0x4c /* Power Manager USIM Card Control/Status Register */ +# define PKWR 0x50 /* Power Manager Keyboard Wake-Up Enable Register */ +# define PKSR 0x54 /* Power Manager Keyboard Level-Detect Status */ +# define PCMD0 0x80 /* Power Manager I2C Command Register File 0 */ +# define PCMD31 0xfc /* Power Manager I2C Command Register File 31 */ + +static uint32_t pxa2xx_i2c_read(void *, target_phys_addr_t); +static void pxa2xx_i2c_write(void *, target_phys_addr_t, uint32_t); + +static uint32_t pxa2xx_pm_read(void *opaque, target_phys_addr_t addr) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + if (addr > s->pm_base + PCMD31) { + /* Special case: PWRI2C registers appear in the same range. */ + return pxa2xx_i2c_read(s->i2c[1], addr); + } + addr -= s->pm_base; + + switch (addr) { + case PMCR ... PCMD31: + if (addr & 3) + goto fail; + + return s->pm_regs[addr >> 2]; + default: + fail: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } + return 0; +} + +static void pxa2xx_pm_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + if (addr > s->pm_base + PCMD31) { + /* Special case: PWRI2C registers appear in the same range. */ + pxa2xx_i2c_write(s->i2c[1], addr, value); + return; + } + addr -= s->pm_base; + + switch (addr) { + case PMCR: + s->pm_regs[addr >> 2] &= 0x15 & ~(value & 0x2a); + s->pm_regs[addr >> 2] |= value & 0x15; + break; + + case PSSR: /* Read-clean registers */ + case RCSR: + case PKSR: + s->pm_regs[addr >> 2] &= ~value; + break; + + default: /* Read-write registers */ + if (addr >= PMCR && addr <= PCMD31 && !(addr & 3)) { + s->pm_regs[addr >> 2] = value; + break; + } + + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } +} + +static CPUReadMemoryFunc *pxa2xx_pm_readfn[] = { + pxa2xx_pm_read, + pxa2xx_pm_read, + pxa2xx_pm_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_pm_writefn[] = { + pxa2xx_pm_write, + pxa2xx_pm_write, + pxa2xx_pm_write, +}; + +# define CCCR 0x00 /* Core Clock Configuration Register */ +# define CKEN 0x04 /* Clock Enable Register */ +# define OSCC 0x08 /* Oscillator Configuration Register */ +# define CCSR 0x0c /* Core Clock Status Register */ + +static uint32_t pxa2xx_cm_read(void *opaque, target_phys_addr_t addr) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + addr -= s->cm_base; + + switch (addr) { + case CCCR: + case CKEN: + case OSCC: + return s->cm_regs[addr >> 2]; + + case CCSR: + return s->cm_regs[CCCR >> 2] | (3 << 28); + + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } + return 0; +} + +static void pxa2xx_cm_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + addr -= s->cm_base; + + switch (addr) { + case CCCR: + case CKEN: + s->cm_regs[addr >> 2] = value; + break; + + case OSCC: + s->cm_regs[addr >> 2] &= ~0x6e; + s->cm_regs[addr >> 2] |= value & 0x6e; + break; + + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } +} + +static CPUReadMemoryFunc *pxa2xx_cm_readfn[] = { + pxa2xx_cm_read, + pxa2xx_cm_read, + pxa2xx_cm_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_cm_writefn[] = { + pxa2xx_cm_write, + pxa2xx_cm_write, + pxa2xx_cm_write, +}; + +static uint32_t pxa2xx_clkpwr_read(void *opaque, int op2, int reg, int crm) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + + switch (reg) { + case 6: /* Clock Configuration Register */ + return s->clkcfg; + + case 7: /* Power Mode Register */ + return 0; + + default: + printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); + break; + } + return 0; +} + +static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm, + uint32_t value) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + static const char *pwrmode[8] = { + "Normal", "Idle", "Deep-idle", "Standby", + "Sleep", "reserved (!)", "reserved (!)", "Deep-sleep", + }; + + switch (reg) { + case 6: /* Clock Configuration Register */ + s->clkcfg = value & 0xf; + if (value & 2) + printf("%s: CPU frequency change attempt\n", __FUNCTION__); + break; + + case 7: /* Power Mode Register */ + if (value & 8) + printf("%s: CPU voltage change attempt\n", __FUNCTION__); + switch (value & 7) { + case 0: + /* Do nothing */ + break; + + case 1: + /* Idle */ + if (!(s->cm_regs[CCCR] & (1 << 31))) { /* CPDIS */ + cpu_interrupt(s->env, CPU_INTERRUPT_HALT); + break; + } + /* Fall through. */ + + case 2: + /* Deep-Idle */ + cpu_interrupt(s->env, CPU_INTERRUPT_HALT); + s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ + goto message; + + case 3: + cpu_reset(s->env); + s->env->cp15.c1_sys = 0; + s->env->cp15.c1_coproc = 0; + s->env->cp15.c2 = 0; + s->env->cp15.c3 = 0; + s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ + s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ + + /* + * The scratch-pad register is almost universally used + * for storing the return address on suspend. For the + * lack of a resuming bootloader, perform a jump + * directly to that address. + */ + memset(s->env->regs, 0, 4 * 15); + s->env->regs[15] = s->pm_regs[PSPR >> 2]; + +#if 0 + buffer = 0xe59ff000; /* ldr pc, [pc, #0] */ + cpu_physical_memory_write(0, &buffer, 4); + buffer = s->pm_regs[PSPR >> 2]; + cpu_physical_memory_write(8, &buffer, 4); +#endif + + /* Suspend */ + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); + + goto message; + + default: + message: + printf("%s: machine entered %s mode\n", __FUNCTION__, + pwrmode[value & 7]); + } + break; + + default: + printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); + break; + } +} + +/* Performace Monitoring registers */ +# define CPPMNC 0 /* Performance Monitor Control Register */ +# define CPCCNT 1 /* Clock Counter Register */ +# define CPINTEN 4 /* Interrupt Enable Register */ +# define CPFLAG 5 /* Overflow Flag Register */ +# define CPEVTSEL 8 /* Event Selection Register */ + +# define CPPMN0 0 /* Performance Count Register 0 */ +# define CPPMN1 1 /* Performance Count Register 1 */ +# define CPPMN2 2 /* Performance Count Register 2 */ +# define CPPMN3 3 /* Performance Count Register 3 */ + +static uint32_t pxa2xx_perf_read(void *opaque, int op2, int reg, int crm) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + + switch (reg) { + case CPPMNC: + return s->pmnc; + case CPCCNT: + if (s->pmnc & 1) + return qemu_get_clock(vm_clock); + else + return 0; + case CPINTEN: + case CPFLAG: + case CPEVTSEL: + return 0; + + default: + printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); + break; + } + return 0; +} + +static void pxa2xx_perf_write(void *opaque, int op2, int reg, int crm, + uint32_t value) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + + switch (reg) { + case CPPMNC: + s->pmnc = value; + break; + + case CPCCNT: + case CPINTEN: + case CPFLAG: + case CPEVTSEL: + break; + + default: + printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); + break; + } +} + +static uint32_t pxa2xx_cp14_read(void *opaque, int op2, int reg, int crm) +{ + switch (crm) { + case 0: + return pxa2xx_clkpwr_read(opaque, op2, reg, crm); + case 1: + return pxa2xx_perf_read(opaque, op2, reg, crm); + case 2: + switch (reg) { + case CPPMN0: + case CPPMN1: + case CPPMN2: + case CPPMN3: + return 0; + } + /* Fall through */ + default: + printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); + break; + } + return 0; +} + +static void pxa2xx_cp14_write(void *opaque, int op2, int reg, int crm, + uint32_t value) +{ + switch (crm) { + case 0: + pxa2xx_clkpwr_write(opaque, op2, reg, crm, value); + break; + case 1: + pxa2xx_perf_write(opaque, op2, reg, crm, value); + break; + case 2: + switch (reg) { + case CPPMN0: + case CPPMN1: + case CPPMN2: + case CPPMN3: + return; + } + /* Fall through */ + default: + printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); + break; + } +} + +# define MDCNFG 0x00 /* SDRAM Configuration Register */ +# define MDREFR 0x04 /* SDRAM Refresh Control Register */ +# define MSC0 0x08 /* Static Memory Control Register 0 */ +# define MSC1 0x0c /* Static Memory Control Register 1 */ +# define MSC2 0x10 /* Static Memory Control Register 2 */ +# define MECR 0x14 /* Expansion Memory Bus Config Register */ +# define SXCNFG 0x1c /* Synchronous Static Memory Config Register */ +# define MCMEM0 0x28 /* PC Card Memory Socket 0 Timing Register */ +# define MCMEM1 0x2c /* PC Card Memory Socket 1 Timing Register */ +# define MCATT0 0x30 /* PC Card Attribute Socket 0 Register */ +# define MCATT1 0x34 /* PC Card Attribute Socket 1 Register */ +# define MCIO0 0x38 /* PC Card I/O Socket 0 Timing Register */ +# define MCIO1 0x3c /* PC Card I/O Socket 1 Timing Register */ +# define MDMRS 0x40 /* SDRAM Mode Register Set Config Register */ +# define BOOT_DEF 0x44 /* Boot-time Default Configuration Register */ +# define ARB_CNTL 0x48 /* Arbiter Control Register */ +# define BSCNTR0 0x4c /* Memory Buffer Strength Control Register 0 */ +# define BSCNTR1 0x50 /* Memory Buffer Strength Control Register 1 */ +# define LCDBSCNTR 0x54 /* LCD Buffer Strength Control Register */ +# define MDMRSLP 0x58 /* Low Power SDRAM Mode Set Config Register */ +# define BSCNTR2 0x5c /* Memory Buffer Strength Control Register 2 */ +# define BSCNTR3 0x60 /* Memory Buffer Strength Control Register 3 */ +# define SA1110 0x64 /* SA-1110 Memory Compatibility Register */ + +static uint32_t pxa2xx_mm_read(void *opaque, target_phys_addr_t addr) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + addr -= s->mm_base; + + switch (addr) { + case MDCNFG ... SA1110: + if ((addr & 3) == 0) + return s->mm_regs[addr >> 2]; + + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } + return 0; +} + +static void pxa2xx_mm_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + addr -= s->mm_base; + + switch (addr) { + case MDCNFG ... SA1110: + if ((addr & 3) == 0) { + s->mm_regs[addr >> 2] = value; + break; + } + + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } +} + +static CPUReadMemoryFunc *pxa2xx_mm_readfn[] = { + pxa2xx_mm_read, + pxa2xx_mm_read, + pxa2xx_mm_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_mm_writefn[] = { + pxa2xx_mm_write, + pxa2xx_mm_write, + pxa2xx_mm_write, +}; + +/* Synchronous Serial Ports */ +struct pxa2xx_ssp_s { + target_phys_addr_t base; + int irq; + struct pxa2xx_pic_state_s *pic; + int enable; + + uint32_t sscr[2]; + uint32_t sspsp; + uint32_t ssto; + uint32_t ssitr; + uint32_t sssr; + uint8_t sstsa; + uint8_t ssrsa; + uint8_t ssacd; + + uint32_t rx_fifo[16]; + int rx_level; + int rx_start; + + uint32_t (*readfn)(void *opaque); + void (*writefn)(void *opaque, uint32_t value); + void *opaque; +}; + +# define SSCR0 0x00 /* SSP Control Register 0 */ +# define SSCR1 0x04 /* SSP Control Register 1 */ +# define SSSR 0x08 /* SSP Status Register */ +# define SSITR 0x0c /* SSP Interrupt Test Register */ +# define SSDR 0x10 /* SSP Data Register */ +# define SSTO 0x28 /* SSP Time-Out Register */ +# define SSPSP 0x2c /* SSP Programmable Serial Protocol Register */ +# define SSTSA 0x30 /* SSP TX Time Slot Active Register */ +# define SSRSA 0x34 /* SSP RX Time Slot Active Register */ +# define SSTSS 0x38 /* SSP Time Slot Status Register */ +# define SSACD 0x3c /* SSP Audio Clock Divider Register */ + +/* Bitfields for above registers */ +# define SSCR0_SPI(x) (((x) & 0x30) == 0x00) +# define SSCR0_SSP(x) (((x) & 0x30) == 0x10) +# define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20) +# define SSCR0_PSP(x) (((x) & 0x30) == 0x30) +# define SSCR0_SSE (1 << 7) +# define SSCR0_RIM (1 << 22) +# define SSCR0_TIM (1 << 23) +# define SSCR0_MOD (1 << 31) +# define SSCR0_DSS(x) (((((x) >> 16) & 0x10) | ((x) & 0xf)) + 1) +# define SSCR1_RIE (1 << 0) +# define SSCR1_TIE (1 << 1) +# define SSCR1_LBM (1 << 2) +# define SSCR1_MWDS (1 << 5) +# define SSCR1_TFT(x) ((((x) >> 6) & 0xf) + 1) +# define SSCR1_RFT(x) ((((x) >> 10) & 0xf) + 1) +# define SSCR1_EFWR (1 << 14) +# define SSCR1_PINTE (1 << 18) +# define SSCR1_TINTE (1 << 19) +# define SSCR1_RSRE (1 << 20) +# define SSCR1_TSRE (1 << 21) +# define SSCR1_EBCEI (1 << 29) +# define SSITR_INT (7 << 5) +# define SSSR_TNF (1 << 2) +# define SSSR_RNE (1 << 3) +# define SSSR_TFS (1 << 5) +# define SSSR_RFS (1 << 6) +# define SSSR_ROR (1 << 7) +# define SSSR_PINT (1 << 18) +# define SSSR_TINT (1 << 19) +# define SSSR_EOC (1 << 20) +# define SSSR_TUR (1 << 21) +# define SSSR_BCE (1 << 23) +# define SSSR_RW 0x00bc0080 + +static void pxa2xx_ssp_int_update(struct pxa2xx_ssp_s *s) +{ + int level = 0; + + level |= s->ssitr & SSITR_INT; + level |= (s->sssr & SSSR_BCE) && (s->sscr[1] & SSCR1_EBCEI); + level |= (s->sssr & SSSR_TUR) && !(s->sscr[0] & SSCR0_TIM); + level |= (s->sssr & SSSR_EOC) && (s->sssr & (SSSR_TINT | SSSR_PINT)); + level |= (s->sssr & SSSR_TINT) && (s->sscr[1] & SSCR1_TINTE); + level |= (s->sssr & SSSR_PINT) && (s->sscr[1] & SSCR1_PINTE); + level |= (s->sssr & SSSR_ROR) && !(s->sscr[0] & SSCR0_RIM); + level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE); + level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE); + pic_set_irq_new(s->pic, s->irq, !!level); +} + +static void pxa2xx_ssp_fifo_update(struct pxa2xx_ssp_s *s) +{ + s->sssr &= ~(0xf << 12); /* Clear RFL */ + s->sssr &= ~(0xf << 8); /* Clear TFL */ + s->sssr &= ~SSSR_TNF; + if (s->enable) { + s->sssr |= ((s->rx_level - 1) & 0xf) << 12; + if (s->rx_level >= SSCR1_RFT(s->sscr[1])) + s->sssr |= SSSR_RFS; + else + s->sssr &= ~SSSR_RFS; + if (0 <= SSCR1_TFT(s->sscr[1])) + s->sssr |= SSSR_TFS; + else + s->sssr &= ~SSSR_TFS; + if (s->rx_level) + s->sssr |= SSSR_RNE; + else + s->sssr &= ~SSSR_RNE; + s->sssr |= SSSR_TNF; + } + + pxa2xx_ssp_int_update(s); +} + +static uint32_t pxa2xx_ssp_read(void *opaque, target_phys_addr_t addr) +{ + struct pxa2xx_ssp_s *s = (struct pxa2xx_ssp_s *) opaque; + uint32_t retval; + addr -= s->base; + + switch (addr) { + case SSCR0: + return s->sscr[0]; + case SSCR1: + return s->sscr[1]; + case SSPSP: + return s->sspsp; + case SSTO: + return s->ssto; + case SSITR: + return s->ssitr; + case SSSR: + return s->sssr | s->ssitr; + case SSDR: + if (!s->enable) + return 0xffffffff; + if (s->rx_level < 1) { + printf("%s: SSP Rx Underrun\n", __FUNCTION__); + return 0xffffffff; + } + s->rx_level --; + retval = s->rx_fifo[s->rx_start ++]; + s->rx_start &= 0xf; + pxa2xx_ssp_fifo_update(s); + return retval; + case SSTSA: + return s->sstsa; + case SSRSA: + return s->ssrsa; + case SSTSS: + return 0; + case SSACD: + return s->ssacd; + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } + return 0; +} + +static void pxa2xx_ssp_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct pxa2xx_ssp_s *s = (struct pxa2xx_ssp_s *) opaque; + addr -= s->base; + + switch (addr) { + case SSCR0: + s->sscr[0] = value & 0xc7ffffff; + s->enable = value & SSCR0_SSE; + if (value & SSCR0_MOD) + printf("%s: Attempt to use network mode\n", __FUNCTION__); + if (s->enable && SSCR0_DSS(value) < 4) + printf("%s: Wrong data size: %i bits\n", __FUNCTION__, + SSCR0_DSS(value)); + if (!(value & SSCR0_SSE)) { + s->sssr = 0; + s->ssitr = 0; + s->rx_level = 0; + } + pxa2xx_ssp_fifo_update(s); + break; + + case SSCR1: + s->sscr[1] = value; + if (value & (SSCR1_LBM | SSCR1_EFWR)) + printf("%s: Attempt to use SSP test mode\n", __FUNCTION__); + pxa2xx_ssp_fifo_update(s); + break; + + case SSPSP: + s->sspsp = value; + break; + + case SSTO: + s->ssto = value; + break; + + case SSITR: + s->ssitr = value & SSITR_INT; + pxa2xx_ssp_int_update(s); + break; + + case SSSR: + s->sssr &= ~(value & SSSR_RW); + pxa2xx_ssp_int_update(s); + break; + + case SSDR: + if (SSCR0_UWIRE(s->sscr[0])) { + if (s->sscr[1] & SSCR1_MWDS) + value &= 0xffff; + else + value &= 0xff; + } else + /* Note how 32bits overflow does no harm here */ + value &= (1 << SSCR0_DSS(s->sscr[0])) - 1; + + /* Data goes from here to the Tx FIFO and is shifted out from + * there directly to the slave, no need to buffer it. + */ + if (s->enable) { + if (s->writefn) + s->writefn(s->opaque, value); + + if (s->rx_level < 0x10) { + if (s->readfn) + s->rx_fifo[(s->rx_start + s->rx_level ++) & 0xf] = + s->readfn(s->opaque); + else + s->rx_fifo[(s->rx_start + s->rx_level ++) & 0xf] = 0x0; + } else + s->sssr |= SSSR_ROR; + } + pxa2xx_ssp_fifo_update(s); + break; + + case SSTSA: + s->sstsa = value; + break; + + case SSRSA: + s->ssrsa = value; + break; + + case SSACD: + s->ssacd = value; + break; + + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } +} + +static inline void pxa2xx_ssp_attach(struct pxa2xx_ssp_s *port, + uint32_t (*readfn)(void *opaque), + void (*writefn)(void *opaque, uint32_t value), void *opaque) +{ + if (!port) { + printf("%s: no such SSP\n", __FUNCTION__); + exit(-1); + } + + port->opaque = opaque; + port->readfn = readfn; + port->writefn = writefn; +} + +static CPUReadMemoryFunc *pxa2xx_ssp_readfn[] = { + pxa2xx_ssp_read, + pxa2xx_ssp_read, + pxa2xx_ssp_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_ssp_writefn[] = { + pxa2xx_ssp_write, + pxa2xx_ssp_write, + pxa2xx_ssp_write, +}; + +# define RCNR 0x00 /* RTC Counter Register */ +# define RTAR 0x04 /* RTC Alarm Register */ +# define RTSR 0x08 /* RTC Status Register */ +# define RTTR 0x0c /* RTC Timer Trim Register */ +# define RDCR 0x10 /* RTC Day Counter Register */ +# define RYCR 0x14 /* RTC Year Counter Register */ +# define RDAR1 0x18 /* RTC Wristwatch Day Alarm Register 1 */ +# define RYAR1 0x1c /* RTC Wristwatch Year Alarm Register 1 */ +# define RDAR2 0x20 /* RTC Wristwatch Day Alarm Register 2 */ +# define RYAR2 0x24 /* RTC Wristwatch Year Alarm Register 2 */ +# define SWCR 0x28 /* RTC Stopwatch Counter Register */ +# define SWAR1 0x2c /* RTC Stopwatch Alarm Register 1 */ +# define SWAR2 0x30 /* RTC Stopwatch Alarm Register 2 */ +# define RTCPICR 0x34 /* RTC Periodic Interrupt Counter Register */ +# define PIAR 0x38 /* RTC Periodic Interrupt Alarm Register */ + +static inline void pxa2xx_rtc_int_update(struct pxa2xx_state_s *s) +{ + pic_set_irq_new(s->pic, PXA2XX_PIC_RTCALARM, !!(s->rtsr & 0x2553)); +} + +static void pxa2xx_rtc_hzupdate(struct pxa2xx_state_s *s) +{ + int64_t rt = qemu_get_clock(rt_clock); + s->last_rcnr += ((rt - s->last_hz) << 15) / + (1000 * ((s->rttr & 0xffff) + 1)); + s->last_rdcr += ((rt - s->last_hz) << 15) / + (1000 * ((s->rttr & 0xffff) + 1)); + s->last_hz = rt; +} + +static void pxa2xx_rtc_swupdate(struct pxa2xx_state_s *s) +{ + int64_t rt = qemu_get_clock(rt_clock); + if (s->rtsr & (1 << 12)) + s->last_swcr += (rt - s->last_sw) / 10; + s->last_sw = rt; +} + +static void pxa2xx_rtc_piupdate(struct pxa2xx_state_s *s) +{ + int64_t rt = qemu_get_clock(rt_clock); + if (s->rtsr & (1 << 15)) + s->last_swcr += rt - s->last_pi; + s->last_pi = rt; +} + +static inline void pxa2xx_rtc_alarm_update(struct pxa2xx_state_s *s, + uint32_t rtsr) +{ + if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) + qemu_mod_timer(s->rtc_hz, s->last_hz + + (((s->rtar - s->last_rcnr) * 1000 * + ((s->rttr & 0xffff) + 1)) >> 15)); + else + qemu_del_timer(s->rtc_hz); + + if ((rtsr & (1 << 5)) && !(rtsr & (1 << 4))) + qemu_mod_timer(s->rtc_rdal1, s->last_hz + + (((s->rdar1 - s->last_rdcr) * 1000 * + ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */ + else + qemu_del_timer(s->rtc_rdal1); + + if ((rtsr & (1 << 7)) && !(rtsr & (1 << 6))) + qemu_mod_timer(s->rtc_rdal2, s->last_hz + + (((s->rdar2 - s->last_rdcr) * 1000 * + ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */ + else + qemu_del_timer(s->rtc_rdal2); + + if ((rtsr & 0x1200) == 0x1200 && !(rtsr & (1 << 8))) + qemu_mod_timer(s->rtc_swal1, s->last_sw + + (s->swar1 - s->last_swcr) * 10); /* TODO: fixup */ + else + qemu_del_timer(s->rtc_swal1); + + if ((rtsr & 0x1800) == 0x1800 && !(rtsr & (1 << 10))) + qemu_mod_timer(s->rtc_swal2, s->last_sw + + (s->swar2 - s->last_swcr) * 10); /* TODO: fixup */ + else + qemu_del_timer(s->rtc_swal2); + + if ((rtsr & 0xc000) == 0xc000 && !(rtsr & (1 << 13))) + qemu_mod_timer(s->rtc_pi, s->last_pi + + (s->piar & 0xffff) - s->last_rtcpicr); + else + qemu_del_timer(s->rtc_pi); +} + +static inline void pxa2xx_rtc_hz_tick(void *opaque) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + s->rtsr |= (1 << 0); + pxa2xx_rtc_alarm_update(s, s->rtsr); + pxa2xx_rtc_int_update(s); +} + +static inline void pxa2xx_rtc_rdal1_tick(void *opaque) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + s->rtsr |= (1 << 4); + pxa2xx_rtc_alarm_update(s, s->rtsr); + pxa2xx_rtc_int_update(s); +} + +static inline void pxa2xx_rtc_rdal2_tick(void *opaque) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + s->rtsr |= (1 << 6); + pxa2xx_rtc_alarm_update(s, s->rtsr); + pxa2xx_rtc_int_update(s); +} + +static inline void pxa2xx_rtc_swal1_tick(void *opaque) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + s->rtsr |= (1 << 8); + pxa2xx_rtc_alarm_update(s, s->rtsr); + pxa2xx_rtc_int_update(s); +} + +static inline void pxa2xx_rtc_swal2_tick(void *opaque) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + s->rtsr |= (1 << 10); + pxa2xx_rtc_alarm_update(s, s->rtsr); + pxa2xx_rtc_int_update(s); +} + +static inline void pxa2xx_rtc_pi_tick(void *opaque) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + s->rtsr |= (1 << 13); + pxa2xx_rtc_piupdate(s); + s->last_rtcpicr = 0; + pxa2xx_rtc_alarm_update(s, s->rtsr); + pxa2xx_rtc_int_update(s); +} + +static uint32_t pxa2xx_rtc_read(void *opaque, target_phys_addr_t addr) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + addr -= s->rtc_base; + + switch (addr) { + case RTTR: + return s->rttr; + case RTSR: + return s->rtsr; + case RTAR: + return s->rtar; + case RDAR1: + return s->rdar1; + case RDAR2: + return s->rdar2; + case RYAR1: + return s->ryar1; + case RYAR2: + return s->ryar2; + case SWAR1: + return s->swar1; + case SWAR2: + return s->swar2; + case PIAR: + return s->piar; + case RCNR: + return s->last_rcnr + ((qemu_get_clock(rt_clock) - s->last_hz) << 15) / + (1000 * ((s->rttr & 0xffff) + 1)); + case RDCR: + return s->last_rdcr + ((qemu_get_clock(rt_clock) - s->last_hz) << 15) / + (1000 * ((s->rttr & 0xffff) + 1)); + case RYCR: + return s->last_rycr; + case SWCR: + if (s->rtsr & (1 << 12)) + return s->last_swcr + (qemu_get_clock(rt_clock) - s->last_sw) / 10; + else + return s->last_swcr; + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } + return 0; +} + +static void pxa2xx_rtc_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + addr -= s->rtc_base; + + switch (addr) { + case RTTR: + if (!(s->rttr & (1 << 31))) { + pxa2xx_rtc_hzupdate(s); + s->rttr = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + } + break; + + case RTSR: + if ((s->rtsr ^ value) & (1 << 15)) + pxa2xx_rtc_piupdate(s); + + if ((s->rtsr ^ value) & (1 << 12)) + pxa2xx_rtc_swupdate(s); + + if (((s->rtsr ^ value) & 0x4aac) | (value & ~0xdaac)) + pxa2xx_rtc_alarm_update(s, value); + + s->rtsr = (value & 0xdaac) | (s->rtsr & ~(value & ~0xdaac)); + pxa2xx_rtc_int_update(s); + break; + + case RTAR: + s->rtar = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case RDAR1: + s->rdar1 = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case RDAR2: + s->rdar2 = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case RYAR1: + s->ryar1 = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case RYAR2: + s->ryar2 = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case SWAR1: + pxa2xx_rtc_swupdate(s); + s->swar1 = value; + s->last_swcr = 0; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case SWAR2: + s->swar2 = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case PIAR: + s->piar = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case RCNR: + pxa2xx_rtc_hzupdate(s); + s->last_rcnr = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case RDCR: + pxa2xx_rtc_hzupdate(s); + s->last_rdcr = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case RYCR: + s->last_rycr = value; + break; + + case SWCR: + pxa2xx_rtc_swupdate(s); + s->last_swcr = value; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + case RTCPICR: + pxa2xx_rtc_piupdate(s); + s->last_rtcpicr = value & 0xffff; + pxa2xx_rtc_alarm_update(s, s->rtsr); + break; + + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + } +} + +static void pxa2xx_rtc_reset(struct pxa2xx_state_s *s) +{ + struct tm *tm; + time_t ti; + int wom; + + s->rttr = 0x7fff; + s->rtsr = 0; + + time(&ti); + if (rtc_utc) + tm = gmtime(&ti); + else + tm = localtime(&ti); + wom = ((tm->tm_mday - 1) / 7) + 1; + + s->last_rcnr = (uint32_t) ti; + s->last_rdcr = (wom << 20) | ((tm->tm_wday + 1) << 17) | + (tm->tm_hour << 12) | (tm->tm_min << 6) | tm->tm_sec; + s->last_rycr = ((tm->tm_year + 1900) << 9) | + ((tm->tm_mon + 1) << 5) | tm->tm_mday; + s->last_swcr = (tm->tm_hour << 19) | + (tm->tm_min << 13) | (tm->tm_sec << 7); + s->last_rtcpicr = 0; + s->last_hz = s->last_sw = s->last_pi = qemu_get_clock(rt_clock); + + s->rtc_hz = qemu_new_timer(rt_clock, pxa2xx_rtc_hz_tick, s); + s->rtc_rdal1 = qemu_new_timer(rt_clock, pxa2xx_rtc_rdal1_tick, s); + s->rtc_rdal2 = qemu_new_timer(rt_clock, pxa2xx_rtc_rdal2_tick, s); + s->rtc_swal1 = qemu_new_timer(rt_clock, pxa2xx_rtc_swal1_tick, s); + s->rtc_swal2 = qemu_new_timer(rt_clock, pxa2xx_rtc_swal2_tick, s); + s->rtc_pi = qemu_new_timer(rt_clock, pxa2xx_rtc_pi_tick, s); +} + +static CPUReadMemoryFunc *pxa2xx_rtc_readfn[] = { + pxa2xx_rtc_read, + pxa2xx_rtc_read, + pxa2xx_rtc_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_rtc_writefn[] = { + pxa2xx_rtc_write, + pxa2xx_rtc_write, + pxa2xx_rtc_write, +}; + +/* I2C Interface */ +struct pxa2xx_i2c_s { + target_phys_addr_t base; + int irq; + struct pxa2xx_pic_state_s *pic; + struct i2c_master_s master; + struct i2c_slave_s slave; + + uint16_t control; + uint16_t status; + uint8_t ibmr; +}; + +# define IBMR 0x80 /* I2C Bus Monitor Register */ +# define IDBR 0x88 /* I2C Data Buffer Register */ +# define ICR 0x90 /* I2C Control Register */ +# define ISR 0x98 /* I2C Status Register */ +# define ISAR 0xa0 /* I2C Slave Address Register */ + +static void pxa2xx_i2c_update(struct pxa2xx_i2c_s *s) +{ + uint16_t level = 0; + level |= s->status & s->control & (1 << 10); /* BED */ + level |= (s->status & (1 << 7)) && (s->control & (1 << 9)); /* IRF */ + level |= (s->status & (1 << 6)) && (s->control & (1 << 8)); /* ITE */ + level |= s->status & (1 << 9); /* SAD */ + pic_set_irq_new(s->pic, s->irq, !!level); +} + +static void pxa2xx_i2c_start(void *opaque, int dir) +{ + struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque; + s->status |= (1 << 9); /* set SAD */ + if (dir) + s->status |= 1 << 0; /* set RWM */ + else + s->status &= ~(1 << 0); /* clear RWM */ +} + +static void pxa2xx_i2c_stop(void *opaque) +{ + struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque; + s->status |= (1 << 4); /* set SSD */ +} + +static int pxa2xx_i2c_tx(void *opaque, uint8_t *message, int len) +{ + struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque; + if ((s->control & (1 << 14)) || !(s->control & (1 << 6))) + return 1; + + if (len) { + if (s->status & (1 << 0)) { /* RWM */ + s->status |= 1 << 6; /* set ITE */ + s->master.data = message[0]; /* TODO */ + } else { + s->status |= 1 << 7; /* set IRF */ + message[0] = s->master.data; /* TODO */ + } + } + pxa2xx_i2c_update(s); + + return !s->master.ack; +} + +static uint32_t pxa2xx_i2c_read(void *opaque, target_phys_addr_t addr) +{ + struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque; + addr -= s->base; + + switch (addr) { + case ICR: + return s->control; + case ISR: + return s->status; + case ISAR: + return s->slave.address; + case IDBR: + return s->master.data; + case IBMR: + if (s->status & (1 << 2)) + s->ibmr ^= 3; /* Fake SCL and SDA pin changes */ + else + s->ibmr = 0; + return s->ibmr; + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } + return 0; +} + +static void pxa2xx_i2c_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque; + addr -= s->base; + + switch (addr) { + case ICR: + s->control = value & 0xfff7; + if ((value & (1 << 3)) && (value & (1 << 6))) { /* TB and IUE */ + /* TODO: slave mode */ + if (value & (1 << 0)) { /* START condition */ + if (s->master.data & 1) + s->status |= 1 << 0; /* set RWM */ + else + s->status &= ~(1 << 0); /* clear RWM */ + s->status |= 1 << 2; /* set UB */ + } + + i2c_master_submit(&s->master, + value & (1 << 0), /* START condition */ + value & (1 << 1)); /* STOP condition */ + + if (s->master.ack) { + if (s->status & (1 << 0)) /* RWM */ + s->status |= 1 << 7; /* set IRF */ + else + s->status |= 1 << 6; /* set ITE */ + s->status &= ~(1 << 1); /* clear ACKNAK */ + } else { + s->status |= 1 << 6; /* set ITE */ + s->status |= 1 << 10; /* set BED */ + s->status |= 1 << 1; /* set ACKNAK */ + s->status &= ~(1 << 2); /* clear UB */ + } + if (value & (1 << 1)) /* STOP condition */ + s->status &= ~(1 << 2); /* clear UB */ + } + if (value & (1 << 4)) /* MA */ + s->status &= ~(1 << 2); /* clear UB */ + if (!(value & (1 << 3)) && (value & (1 << 6))) /* !TB and IUE */ + s->status &= ~(1 << 2); /* clear UB */ + pxa2xx_i2c_update(s); + break; + + case ISR: + s->status &= ~(value & 0x07f0); + pxa2xx_i2c_update(s); + break; + + case ISAR: + if (s->master.bus) + i2c_slave_attach(s->master.bus, value & 0x7f, &s->slave); + break; + + case IDBR: + s->master.data = value & 0xff; + break; + + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + } +} + +static CPUReadMemoryFunc *pxa2xx_i2c_readfn[] = { + pxa2xx_i2c_read, + pxa2xx_i2c_read, + pxa2xx_i2c_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_i2c_writefn[] = { + pxa2xx_i2c_write, + pxa2xx_i2c_write, + pxa2xx_i2c_write, +}; + +static struct pxa2xx_i2c_s *pxa2xx_i2c_init(target_phys_addr_t base, int irq, + struct pxa2xx_pic_state_s *pic) +{ + int iomemtype; + struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) + qemu_mallocz(sizeof(struct pxa2xx_i2c_s)); + + s->base = base; + s->irq = irq; + s->pic = pic; + s->slave.tx = pxa2xx_i2c_tx; + s->slave.start = pxa2xx_i2c_start; + s->slave.stop = pxa2xx_i2c_stop; + s->slave.opaque = s; + + iomemtype = cpu_register_io_memory(0, pxa2xx_i2c_readfn, + pxa2xx_i2c_writefn, s); + cpu_register_physical_memory(s->base & 0xfffff000, 0xfff, iomemtype); + + return s; +} + +/* PXA Inter-IC Sound Controller */ +struct pxa2xx_i2s_s { + target_phys_addr_t base; + int irq; + struct pxa2xx_dma_state_s *dma; + struct pxa2xx_pic_state_s *pic; + 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]; +}; + +static void pxa2xx_i2s_reset(struct pxa2xx_i2s_s *i2s) +{ + i2s->rx_len = 0; + i2s->tx_len = 0; + i2s->fifo_len = 0; + i2s->clk = 0x1a; + i2s->control[0] = 0x00; + i2s->control[1] = 0x00; + i2s->status = 0x00; + i2s->mask = 0x00; +} + +# define SACR_TFTH(val) ((val >> 8) & 0xf) +# define SACR_RFTH(val) ((val >> 12) & 0xf) +# define SACR_DREC(val) (val & (1 << 3)) +# define SACR_DPRL(val) (val & (1 << 4)) + +static inline void pxa2xx_i2s_update(struct pxa2xx_i2s_s *i2s) +{ + int rfs, tfs; + rfs = SACR_RFTH(i2s->control[0]) < i2s->rx_len && + !SACR_DREC(i2s->control[1]); + tfs = (i2s->tx_len || i2s->fifo_len < SACR_TFTH(i2s->control[0])) && + i2s->enable && !SACR_DPRL(i2s->control[1]); + + pxa2xx_dma_request(i2s->dma, PXA2XX_RX_RQ_I2S, rfs); + pxa2xx_dma_request(i2s->dma, PXA2XX_TX_RQ_I2S, tfs); + + i2s->status &= 0xe0; + if (i2s->rx_len) + i2s->status |= 1 << 1; /* RNE */ + if (i2s->enable) + i2s->status |= 1 << 2; /* BSY */ + if (tfs) + i2s->status |= 1 << 3; /* TFS */ + if (rfs) + i2s->status |= 1 << 4; /* RFS */ + if (!(i2s->tx_len && i2s->enable)) + i2s->status |= i2s->fifo_len << 8; /* TFL */ + i2s->status |= MAX(i2s->rx_len, 0xf) << 12; /* RFL */ + + pic_set_irq_new(i2s->pic, i2s->irq, i2s->status & i2s->mask); +} + +# define SACR0 0x00 /* Serial Audio Global Control Register */ +# define SACR1 0x04 /* Serial Audio I2S/MSB-Justified Control Register */ +# define SASR0 0x0c /* Serial Audio Interface and FIFO Status Register */ +# define SAIMR 0x14 /* Serial Audio Interrupt Mask Register */ +# define SAICR 0x18 /* Serial Audio Interrupt Clear Register */ +# define SADIV 0x60 /* Serial Audio Clock Divider Register */ +# define SADR 0x80 /* Serial Audio Data Register */ + +static uint32_t pxa2xx_i2s_read(void *opaque, target_phys_addr_t addr) +{ + struct pxa2xx_i2s_s *s = (struct pxa2xx_i2s_s *) opaque; + addr -= s->base; + + switch (addr) { + case SACR0: + return s->control[0]; + case SACR1: + return s->control[1]; + case SASR0: + return s->status; + case SAIMR: + return s->mask; + case SAICR: + return 0; + case SADIV: + return s->clk; + case SADR: + if (s->rx_len > 0) { + s->rx_len --; + pxa2xx_i2s_update(s); + return s->codec_in(s->opaque); + } + return 0; + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + break; + } + return 0; +} + +static void pxa2xx_i2s_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct pxa2xx_i2s_s *s = (struct pxa2xx_i2s_s *) opaque; + uint32_t *sample; + addr -= s->base; + + switch (addr) { + case SACR0: + if (value & (1 << 3)) /* RST */ + pxa2xx_i2s_reset(s); + s->control[0] = value & 0xff3d; + if (!s->enable && (value & 1) && s->tx_len) { /* ENB */ + for (sample = s->fifo; s->fifo_len > 0; s->fifo_len --, sample ++) + s->codec_out(s->opaque, *sample); + s->status &= ~(1 << 7); /* I2SOFF */ + } + if (value & (1 << 4)) /* EFWR */ + printf("%s: Attempt to use special function\n", __FUNCTION__); + s->enable = ((value ^ 4) & 5) == 5; /* ENB && !RST*/ + pxa2xx_i2s_update(s); + break; + case SACR1: + s->control[1] = value & 0x0039; + if (value & (1 << 5)) /* ENLBF */ + printf("%s: Attempt to use loopback function\n", __FUNCTION__); + if (value & (1 << 4)) /* DPRL */ + s->fifo_len = 0; + pxa2xx_i2s_update(s); + break; + case SAIMR: + s->mask = value & 0x0078; + pxa2xx_i2s_update(s); + break; + case SAICR: + s->status &= ~(value & (3 << 5)); + pxa2xx_i2s_update(s); + break; + case SADIV: + s->clk = value & 0x007f; + break; + case SADR: + if (s->tx_len && s->enable) { + s->tx_len --; + pxa2xx_i2s_update(s); + s->codec_out(s->opaque, value); + } else if (s->fifo_len < 16) { + s->fifo[s->fifo_len ++] = value; + pxa2xx_i2s_update(s); + } + break; + default: + printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr); + } +} + +static CPUReadMemoryFunc *pxa2xx_i2s_readfn[] = { + pxa2xx_i2s_read, + pxa2xx_i2s_read, + pxa2xx_i2s_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_i2s_writefn[] = { + pxa2xx_i2s_write, + pxa2xx_i2s_write, + pxa2xx_i2s_write, +}; + +static void pxa2xx_i2s_data_req(void *opaque, int tx, int rx) +{ + struct pxa2xx_i2s_s *s = (struct pxa2xx_i2s_s *) opaque; + uint32_t *sample; + + /* Signal FIFO errors */ + if (s->enable && s->tx_len) + s->status |= 1 << 5; /* TUR */ + if (s->enable && s->rx_len) + s->status |= 1 << 6; /* ROR */ + + /* Should be tx - MIN(tx, s->fifo_len) but we don't really need to + * handle the cases where it makes a difference. */ + s->tx_len = tx - s->fifo_len; + s->rx_len = rx; + /* Note that is s->codec_out wasn't set, we wouldn't get called. */ + if (s->enable) + for (sample = s->fifo; s->fifo_len; s->fifo_len --, sample ++) + s->codec_out(s->opaque, *sample); + pxa2xx_i2s_update(s); +} + +static struct pxa2xx_i2s_s *pxa2xx_i2s_init(target_phys_addr_t base, int irq, + struct pxa2xx_pic_state_s *pic, struct pxa2xx_dma_state_s *dma) +{ + int iomemtype; + struct pxa2xx_i2s_s *s = (struct pxa2xx_i2s_s *) + qemu_mallocz(sizeof(struct pxa2xx_i2s_s)); + + s->base = base; + s->irq = irq; + s->pic = pic; + s->dma = dma; + s->data_req = pxa2xx_i2s_data_req; + + pxa2xx_i2s_reset(s); + + iomemtype = cpu_register_io_memory(0, pxa2xx_i2s_readfn, + pxa2xx_i2s_writefn, s); + cpu_register_physical_memory(s->base & 0xfff00000, 0xfffff, iomemtype); + + return s; +} + +static void pxa2xx_reset(int line, int level, void *opaque) +{ + struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque; + if (level && (s->pm_regs[PCFR >> 2] & 0x10)) { /* GPR_EN */ + cpu_reset(s->env); + /* TODO: reset peripherals */ + } +} + +/* Initialise a PXA270 integrated chip (ARM based core). */ +static inline struct pxa2xx_state_s *pxa270_init(DisplayState *ds, + int revision) +{ + struct pxa2xx_state_s *s; + struct pxa2xx_ssp_s *ssp; + int iomemtype, i; + s = (struct pxa2xx_state_s *) qemu_mallocz(sizeof(struct pxa2xx_state_s)); + + s->env = cpu_init(); + cpu_arm_set_model(s->env, ARM_CPUID_PXA270 | revision); + + s->pic = pxa2xx_pic_init(0x40d00000, s->env, + PXA2XX_PIC_CPU_IRQ, PXA2XX_PIC_CPU_FIQ); + + s->dma = pxa27x_dma_init(0x40000000, s->pic, PXA2XX_PIC_DMA); + + pxa27x_timer_init(0x40a00000, s->pic, PXA2XX_PIC_OST_0, s->env); + + s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 121); + + s->mmc = pxa2xx_mmci_init(0x41100000, s->pic, s->dma); + + for (i = 0; pxa270_serial[i].io_base; i ++) + if (serial_hds[i]) + serial_mm_init(*(arm_pic_handler *) s->pic, s->pic, + pxa270_serial[i].io_base, 2, + pxa270_serial[i].irq, serial_hds[i]); + + if (ds) + s->lcd = pxa2xx_lcdc_init(0x44000000, s->pic, ds); + + s->cm_base = 0x41300000; + s->cm_regs[CCCR >> 4] = 0x02000210; /* 416.0 MHz */ + s->clkcfg = 0x00000009; /* Turbo mode active */ + iomemtype = cpu_register_io_memory(0, pxa2xx_cm_readfn, + pxa2xx_cm_writefn, s); + cpu_register_physical_memory(s->cm_base, 0xfff, iomemtype); + + cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); + + s->mm_base = 0x48000000; + s->mm_regs[MDMRS >> 2] = 0x00020002; + s->mm_regs[MDREFR >> 2] = 0x03ca4000; + s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ + iomemtype = cpu_register_io_memory(0, pxa2xx_mm_readfn, + pxa2xx_mm_writefn, s); + cpu_register_physical_memory(s->mm_base, 0xfff, iomemtype); + + for (i = 0; pxa27x_ssp[i].io_base; i ++); + s->ssp = (struct pxa2xx_ssp_s **) + qemu_mallocz(sizeof(struct pxa2xx_ssp_s *) * i); + ssp = (struct pxa2xx_ssp_s *) + qemu_mallocz(sizeof(struct pxa2xx_ssp_s) * i); + for (i = 0; pxa27x_ssp[i].io_base; i ++) { + s->ssp[i] = &ssp[i]; + ssp[i].base = pxa27x_ssp[i].io_base; + ssp[i].irq = pxa27x_ssp[i].irq; + ssp[i].pic = s->pic; + + iomemtype = cpu_register_io_memory(0, pxa2xx_ssp_readfn, + pxa2xx_ssp_writefn, &ssp[i]); + cpu_register_physical_memory(ssp[i].base, 0xfff, iomemtype); + } + + if (usb_enabled) { + usb_ohci_init_memio(0x4c000000, 3, -1, s->pic, PXA2XX_PIC_USBH1); + } + + s->pcmcia[0] = pxa2xx_pcmcia_init(0x20000000); + s->pcmcia[1] = pxa2xx_pcmcia_init(0x30000000); + + s->rtc_base = 0x40900000; + iomemtype = cpu_register_io_memory(0, pxa2xx_rtc_readfn, + pxa2xx_rtc_writefn, s); + cpu_register_physical_memory(s->rtc_base, 0xfff, iomemtype); + pxa2xx_rtc_reset(s); + + s->i2c[0] = pxa2xx_i2c_init(0x40301600, PXA2XX_PIC_I2C, s->pic); + s->i2c[1] = pxa2xx_i2c_init(0x40f00100, PXA2XX_PIC_PWRI2C, s->pic); + + /* Register PM after PWRI2C to get the register mappings right. */ + s->pm_base = 0x40f00000; + iomemtype = cpu_register_io_memory(0, pxa2xx_pm_readfn, + pxa2xx_pm_writefn, s); + cpu_register_physical_memory(s->pm_base, 0xfff, iomemtype); + + s->i2s = pxa2xx_i2s_init(0x40400000, PXA2XX_PIC_I2S, s->pic, s->dma); + + /* GPIO1 resets the processor */ + /* The handler can be overriden by board-specific code */ + pxa2xx_gpio_handler_set(s->gpio, 1, pxa2xx_reset, s); + return s; +} + +#endif /* PXA_H */ Index: qemu/hw/pxa2xx_dma.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa2xx_dma.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,499 @@ +/* + * Intel XScale PXA255/270 DMA controller. + * + * Copyright (c) 2006 Openedhand Ltd. + * Copyright (c) 2006 Thorsten Zitterell + * Written by Andrzej Zaborowski + * + * This code is licenced under the GPL. + */ + +#include "vl.h" + +struct pxa2xx_dma_channel_s { + target_phys_addr_t descr; + target_phys_addr_t src; + target_phys_addr_t dest; + uint32_t cmd; + uint32_t state; + int request; +}; + +/* The first element of an individual PIC state structures should + * be a pointer to the handler routine. We allow the DMA to be used + * as a PIC. */ +typedef void (*pxa2xx_dma_handler_t)(void *opaque, int irq, int level); + +struct pxa2xx_dma_state_s { + pxa2xx_dma_handler_t handler; + target_phys_addr_t base; + void *pic; + int irq; + + uint32_t stopintr; + uint32_t eorintr; + uint32_t rasintr; + uint32_t startintr; + uint32_t endintr; + + uint32_t align; + uint32_t pio; + + int channels; + struct pxa2xx_dma_channel_s *chan; + + uint8_t *req; + + /* Flag to avoid recursive DMA invocations. */ + int running; +}; + +#define PXA255_DMA_NUM_CHANNELS 16 +#define PXA27X_DMA_NUM_CHANNELS 32 + +#define PXA2XX_DMA_NUM_REQUESTS 75 + +#define DCSR0 0x0000 /* DMA Control / Status Register for Channel 0 */ +#define DCSR31 0x007c /* DMA Control / Status Register for Channel 31 */ +#define DALGN 0x00a0 /* DMA Alignment Register */ +#define DPCSR 0x00a4 /* DMA Programmed I/O Control Status Register */ +#define DRQSR0 0x00e0 /* DMA DREQ<0> Status Register */ +#define DRQSR1 0x00e4 /* DMA DREQ<1> Status Register */ +#define DRQSR2 0x00e8 /* DMA DREQ<2> Status Register */ +#define DINT 0x00f0 /* DMA Interrupt Register */ +#define DRCMR0 0x0100 /* Request to Channel Map Register 0 */ +#define DRCMR63 0x01fc /* Request to Channel Map Register 63 */ +#define D_CH0 0x0200 /* Channel 0 Descriptor start */ +#define DRCMR64 0x1100 /* Request to Channel Map Register 64 */ +#define DRCMR74 0x1128 /* Request to Channel Map Register 74 */ + +/* Per-channel register */ +#define DDADR 0x00 +#define DSADR 0x01 +#define DTADR 0x02 +#define DCMD 0x03 + +/* Bit-field masks */ +#define DRCMR_CHLNUM 0x1f +#define DRCMR_MAPVLD (1 << 7) +#define DDADR_STOP (1 << 0) +#define DDADR_BREN (1 << 1) +#define DCMD_LEN 0x1fff +#define DCMD_WIDTH(x) (1 << ((((x) >> 14) & 3) - 1)) +#define DCMD_SIZE(x) (4 << (((x) >> 16) & 3)) +#define DCMD_FLYBYT (1 << 19) +#define DCMD_FLYBYS (1 << 20) +#define DCMD_ENDIRQEN (1 << 21) +#define DCMD_STARTIRQEN (1 << 22) +#define DCMD_CMPEN (1 << 25) +#define DCMD_FLOWTRG (1 << 28) +#define DCMD_FLOWSRC (1 << 29) +#define DCMD_INCTRGADDR (1 << 30) +#define DCMD_INCSRCADDR (1 << 31) +#define DCSR_BUSERRINTR (1 << 0) +#define DCSR_STARTINTR (1 << 1) +#define DCSR_ENDINTR (1 << 2) +#define DCSR_STOPINTR (1 << 3) +#define DCSR_RASINTR (1 << 4) +#define DCSR_REQPEND (1 << 8) +#define DCSR_EORINT (1 << 9) +#define DCSR_CMPST (1 << 10) +#define DCSR_MASKRUN (1 << 22) +#define DCSR_RASIRQEN (1 << 23) +#define DCSR_CLRCMPST (1 << 24) +#define DCSR_SETCMPST (1 << 25) +#define DCSR_EORSTOPEN (1 << 26) +#define DCSR_EORJMPEN (1 << 27) +#define DCSR_EORIRQEN (1 << 28) +#define DCSR_STOPIRQEN (1 << 29) +#define DCSR_NODESCFETCH (1 << 30) +#define DCSR_RUN (1 << 31) + +static inline void pxa2xx_dma_update(struct pxa2xx_dma_state_s *s, int ch) +{ + if (ch >= 0) { + if ((s->chan[ch].state & DCSR_STOPIRQEN) && + (s->chan[ch].state & DCSR_STOPINTR)) + s->stopintr |= 1 << ch; + else + s->stopintr &= ~(1 << ch); + + if ((s->chan[ch].state & DCSR_EORIRQEN) && + (s->chan[ch].state & DCSR_EORINT)) + s->eorintr |= 1 << ch; + else + s->eorintr &= ~(1 << ch); + + if ((s->chan[ch].state & DCSR_RASIRQEN) && + (s->chan[ch].state & DCSR_RASINTR)) + s->rasintr |= 1 << ch; + else + s->rasintr &= ~(1 << ch); + + if (s->chan[ch].state & DCSR_STARTINTR) + s->startintr |= 1 << ch; + else + s->startintr &= ~(1 << ch); + + if (s->chan[ch].state & DCSR_ENDINTR) + s->endintr |= 1 << ch; + else + s->endintr &= ~(1 << ch); + } + + if (s->stopintr | s->eorintr | s->rasintr | s->startintr | s->endintr) + pic_set_irq_new(s->pic, s->irq, 1); + else + pic_set_irq_new(s->pic, s->irq, 0); +} + +static inline void pxa2xx_dma_descriptor_fetch( + struct pxa2xx_dma_state_s *s, int ch) +{ + uint32_t desc[4]; + target_phys_addr_t daddr = s->chan[ch].descr & ~0xf; + if ((s->chan[ch].descr & DDADR_BREN) && (s->chan[ch].state & DCSR_CMPST)) + daddr += 32; + + cpu_physical_memory_read(daddr, (uint8_t *) desc, 16); + s->chan[ch].descr = desc[DDADR]; + s->chan[ch].src = desc[DSADR]; + s->chan[ch].dest = desc[DTADR]; + s->chan[ch].cmd = desc[DCMD]; + + if (s->chan[ch].cmd & DCMD_FLOWSRC) + s->chan[ch].src &= ~3; + if (s->chan[ch].cmd & DCMD_FLOWTRG) + s->chan[ch].dest &= ~3; + + if (s->chan[ch].cmd & (DCMD_CMPEN | DCMD_FLYBYS | DCMD_FLYBYT)) + printf("%s: unsupported mode in channel %i\n", __FUNCTION__, ch); + + if (s->chan[ch].cmd & DCMD_STARTIRQEN) + s->chan[ch].state |= DCSR_STARTINTR; +} + +static void pxa2xx_dma_run(struct pxa2xx_dma_state_s *s) +{ + int c, srcinc, destinc; + uint32_t n, size; + uint32_t width; + uint32_t length; + char buffer[32]; + struct pxa2xx_dma_channel_s *ch; + + if (s->running ++) + return; + + while (s->running) { + s->running = 1; + for (c = 0; c < s->channels; c ++) { + ch = &s->chan[c]; + + while ((ch->state & DCSR_RUN) && !(ch->state & DCSR_STOPINTR)) { + /* Test for pending requests */ + if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && !ch->request) + break; + + length = ch->cmd & DCMD_LEN; + size = DCMD_SIZE(ch->cmd); + width = DCMD_WIDTH(ch->cmd); + + srcinc = (ch->cmd & DCMD_INCSRCADDR) ? width : 0; + destinc = (ch->cmd & DCMD_INCTRGADDR) ? width : 0; + + while (length) { + size = MIN(length, size); + + for (n = 0; n < size; n += width) { + cpu_physical_memory_read(ch->src, buffer + n, width); + ch->src += srcinc; + } + + for (n = 0; n < size; n += width) { + cpu_physical_memory_write(ch->dest, buffer + n, width); + ch->dest += destinc; + } + + length -= size; + + if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && + !ch->request) { + ch->state |= DCSR_EORINT; + if (ch->state & DCSR_EORSTOPEN) + ch->state |= DCSR_STOPINTR; + if ((ch->state & DCSR_EORJMPEN) && + !(ch->state & DCSR_NODESCFETCH)) + pxa2xx_dma_descriptor_fetch(s, c); + break; + } + } + + ch->cmd = (ch->cmd & ~DCMD_LEN) | length; + + /* Is the transfer complete now? */ + if (!length) { + if (ch->cmd & DCMD_ENDIRQEN) + ch->state |= DCSR_ENDINTR; + + if ((ch->state & DCSR_NODESCFETCH) || + (ch->descr & DDADR_STOP) || + (ch->state & DCSR_EORSTOPEN)) { + ch->state |= DCSR_STOPINTR; + ch->state &= ~DCSR_RUN; + + break; + } + + ch->state |= DCSR_STOPINTR; + break; + } + } + } + + s->running --; + } +} + +static uint32_t pxa2xx_dma_read(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_dma_state_s *s = (struct pxa2xx_dma_state_s *) opaque; + unsigned int channel; + offset -= s->base; + + switch (offset) { + case DRCMR64 ... DRCMR74: + offset -= DRCMR64 - DRCMR0 - (64 << 2); + /* Fall through */ + case DRCMR0 ... DRCMR63: + channel = (offset - DRCMR0) >> 2; + return s->req[channel]; + + case DRQSR0: + case DRQSR1: + case DRQSR2: + return 0; + + case DCSR0 ... DCSR31: + channel = offset >> 2; + if (s->chan[channel].request) + return s->chan[channel].state | DCSR_REQPEND; + return s->chan[channel].state; + + case DINT: + return s->stopintr | s->eorintr | s->rasintr | + s->startintr | s->endintr; + + case DALGN: + return s->align; + + case DPCSR: + return s->pio; + } + + if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) { + channel = (offset - D_CH0) >> 4; + switch ((offset & 0x0f) >> 2) { + case DDADR: + return s->chan[channel].descr; + case DSADR: + return s->chan[channel].src; + case DTADR: + return s->chan[channel].dest; + case DCMD: + return s->chan[channel].cmd; + } + } + + cpu_abort(cpu_single_env, "%s: Bad offset 0x%04x\n", __FUNCTION__, offset); + return 7; +} + +static void pxa2xx_dma_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_dma_state_s *s = (struct pxa2xx_dma_state_s *) opaque; + unsigned int channel; + offset -= s->base; + + switch (offset) { + case DRCMR64 ... DRCMR74: + offset -= DRCMR64 - DRCMR0 - (64 << 2); + /* Fall through */ + case DRCMR0 ... DRCMR63: + channel = (offset - DRCMR0) >> 2; + + if (value & DRCMR_MAPVLD) + if ((value & DRCMR_CHLNUM) > s->channels) + cpu_abort(cpu_single_env, "%s: Bad DMA channel %i\n", + __FUNCTION__, value & DRCMR_CHLNUM); + + s->req[channel] = value; + break; + + case DRQSR0: + case DRQSR1: + case DRQSR2: + /* Nothing to do */ + break; + + case DCSR0 ... DCSR31: + channel = offset >> 2; + s->chan[channel].state &= 0x0000071f & ~(value & + (DCSR_EORINT | DCSR_ENDINTR | + DCSR_STARTINTR | DCSR_BUSERRINTR)); + s->chan[channel].state |= value & 0xfc800000; + + if (s->chan[channel].state & DCSR_STOPIRQEN) + s->chan[channel].state &= ~DCSR_STOPINTR; + + if (value & DCSR_NODESCFETCH) { + /* No-descriptor-fetch mode */ + if (value & DCSR_RUN) + pxa2xx_dma_run(s); + } else { + /* Descriptor-fetch mode */ + if (value & DCSR_RUN) { + s->chan[channel].state &= ~DCSR_STOPINTR; + pxa2xx_dma_descriptor_fetch(s, channel); + pxa2xx_dma_run(s); + } + } + + /* Shouldn't matter as our DMA is synchronous. */ + if (!(value & (DCSR_RUN | DCSR_MASKRUN))) + s->chan[channel].state |= DCSR_STOPINTR; + + if (value & DCSR_CLRCMPST) + s->chan[channel].state &= ~DCSR_CMPST; + if (value & DCSR_SETCMPST) + s->chan[channel].state |= DCSR_CMPST; + + pxa2xx_dma_update(s, channel); + break; + + case DALGN: + s->align = value; + break; + + case DPCSR: + s->pio = value & 0x80000001; + break; + + default: + if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) { + channel = (offset - D_CH0) >> 4; + switch ((offset & 0x0f) >> 2) { + case DDADR: + s->chan[channel].descr = value; + break; + case DSADR: + s->chan[channel].src = value; + break; + case DTADR: + s->chan[channel].dest = value; + break; + case DCMD: + s->chan[channel].cmd = value; + break; + default: + goto fail; + } + + break; + } + fail: + cpu_abort(cpu_single_env, "%s: Bad offset 0x%04x\n", + __FUNCTION__, offset); + } +} + +static uint32_t pxa2xx_dma_readbad(void *opaque, target_phys_addr_t offset) +{ + cpu_abort(cpu_single_env, "%s: Bad access width\n", __FUNCTION__); + return 5; +} + +static void pxa2xx_dma_writebad(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + cpu_abort(cpu_single_env, "%s: Bad access width\n", __FUNCTION__); +} + +static CPUReadMemoryFunc *pxa2xx_dma_readfn[] = { + pxa2xx_dma_readbad, + pxa2xx_dma_readbad, + pxa2xx_dma_read +}; + +static CPUWriteMemoryFunc *pxa2xx_dma_writefn[] = { + pxa2xx_dma_writebad, + pxa2xx_dma_writebad, + pxa2xx_dma_write +}; + +static struct pxa2xx_dma_state_s *pxa2xx_dma_init(target_phys_addr_t base, + void *pic, int irq, int channels) +{ + int i, iomemtype; + struct pxa2xx_dma_state_s *s; + s = (struct pxa2xx_dma_state_s *) + qemu_mallocz(sizeof(struct pxa2xx_dma_state_s)); + + s->channels = channels; + s->chan = qemu_mallocz(sizeof(struct pxa2xx_dma_channel_s) * s->channels); + s->base = base; + s->pic = pic; + s->irq = irq; + s->handler = (pxa2xx_dma_handler_t) pxa2xx_dma_request; + s->req = qemu_mallocz(sizeof(int) * PXA2XX_DMA_NUM_REQUESTS); + + memset(s->chan, 0, sizeof(struct pxa2xx_dma_channel_s) * s->channels); + for (i = 0; i < s->channels; i ++) + s->chan[i].state = DCSR_STOPINTR; + + memset(s->req, 0, sizeof(int) * PXA2XX_DMA_NUM_REQUESTS); + + iomemtype = cpu_register_io_memory(0, pxa2xx_dma_readfn, + pxa2xx_dma_writefn, s); + cpu_register_physical_memory(base, 0x0000ffff, iomemtype); + + return s; +} + +struct pxa2xx_dma_state_s *pxa27x_dma_init(target_phys_addr_t base, + void *pic, int irq) +{ + return pxa2xx_dma_init(base, pic, irq, PXA27X_DMA_NUM_CHANNELS); +} + +struct pxa2xx_dma_state_s *pxa255_dma_init(target_phys_addr_t base, + void *pic, int irq) +{ + return pxa2xx_dma_init(base, pic, irq, PXA255_DMA_NUM_CHANNELS); +} + +void pxa2xx_dma_request(struct pxa2xx_dma_state_s *s, int req_num, int on) +{ + int ch; + if (req_num < 0 || req_num >= PXA2XX_DMA_NUM_REQUESTS) + cpu_abort(cpu_single_env, + "%s: Bad DMA request %i\n", __FUNCTION__, req_num); + + if (!(s->req[req_num] & DRCMR_MAPVLD)) + return; + ch = s->req[req_num] & DRCMR_CHLNUM; + + if (!s->chan[ch].request && on) + s->chan[ch].state |= DCSR_RASINTR; + else + s->chan[ch].state &= ~DCSR_RASINTR; + if (s->chan[ch].request && !on) + s->chan[ch].state |= DCSR_EORINT; + + s->chan[ch].request = on; + if (on) { + pxa2xx_dma_run(s); + pxa2xx_dma_update(s, ch); + } +} Index: qemu/hw/pxa2xx_gpio.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa2xx_gpio.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,290 @@ +/* + * Intel XScale PXA255/270 GPIO controller emulation. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPL. + */ + +#include "vl.h" + +#define PXA2XX_GPIO_BANKS 4 + +struct pxa2xx_gpio_info_s { + target_phys_addr_t base; + void *pic; + int lines; + CPUState *cpu_env; + + /* XXX: GNU C vectors are more suitable */ + uint32_t ilevel[PXA2XX_GPIO_BANKS]; + uint32_t olevel[PXA2XX_GPIO_BANKS]; + uint32_t dir[PXA2XX_GPIO_BANKS]; + uint32_t rising[PXA2XX_GPIO_BANKS]; + uint32_t falling[PXA2XX_GPIO_BANKS]; + uint32_t status[PXA2XX_GPIO_BANKS]; + uint32_t gafr[PXA2XX_GPIO_BANKS * 2]; + + uint32_t prev_level[PXA2XX_GPIO_BANKS]; + struct { + gpio_handler_t fn; + void *opaque; + } handler[PXA2XX_GPIO_BANKS * 32]; + + void (*read_notify)(void *opaque); + void *opaque; +}; + +static struct { + enum { + GPIO_NONE, + GPLR, + GPSR, + GPCR, + GPDR, + GRER, + GFER, + GEDR, + GAFR_L, + GAFR_U, + } reg; + int bank; +} pxa2xx_gpio_regs[0x200] = { + [0 ... 0x1ff] = { GPIO_NONE, 0 }, +#define PXA2XX_REG(reg, a0, a1, a2, a3) \ + [a0] = { reg, 0 }, [a1] = { reg, 1 }, [a2] = { reg, 2 }, [a3] = { reg, 3 }, + + PXA2XX_REG(GPLR, 0x000, 0x004, 0x008, 0x100) + PXA2XX_REG(GPSR, 0x018, 0x01c, 0x020, 0x118) + PXA2XX_REG(GPCR, 0x024, 0x028, 0x02c, 0x124) + PXA2XX_REG(GPDR, 0x00c, 0x010, 0x014, 0x10c) + PXA2XX_REG(GRER, 0x030, 0x034, 0x038, 0x130) + PXA2XX_REG(GFER, 0x03c, 0x040, 0x044, 0x13c) + PXA2XX_REG(GEDR, 0x048, 0x04c, 0x050, 0x148) + PXA2XX_REG(GAFR_L, 0x054, 0x05c, 0x064, 0x06c) + PXA2XX_REG(GAFR_U, 0x058, 0x060, 0x068, 0x070) +}; + +static void pxa2xx_gpio_irq_update(struct pxa2xx_gpio_info_s *s) +{ + if (s->status[0] & (1 << 0)) + pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_0, 1); + else + pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_0, 0); + + if (s->status[0] & (1 << 1)) + pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_1, 1); + else + pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_1, 0); + + if ((s->status[0] & ~3) | s->status[1] | s->status[2] | s->status[3]) + pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_X, 1); + else + pic_set_irq_new(s->pic, PXA2XX_PIC_GPIO_X, 0); +} + +/* Bitmap of pins used as standby and sleep wake-up sources. */ +const int pxa2xx_gpio_wake[PXA2XX_GPIO_BANKS] = { + 0x8003fe1b, 0x002001fc, 0xec080000, 0x0012007f, +}; + +void pxa2xx_gpio_set(struct pxa2xx_gpio_info_s *s, int line, int level) +{ + int bank; + uint32_t mask; + + if (line >= s->lines) { + printf("%s: No GPIO pin %i\n", __FUNCTION__, line); + return; + } + + bank = line >> 5; + mask = 1 << (line & 31); + + if (level) { + s->status[bank] |= s->rising[bank] & mask & + ~s->ilevel[bank] & ~s->dir[bank]; + s->ilevel[bank] |= mask; + } else { + s->status[bank] |= s->falling[bank] & mask & + s->ilevel[bank] & ~s->dir[bank]; + s->ilevel[bank] &= ~mask; + } + + if (s->status[bank] & mask) + pxa2xx_gpio_irq_update(s); + + /* Wake-up GPIOs */ + if (s->cpu_env->halted && (mask & ~s->dir[bank] & pxa2xx_gpio_wake[bank])) + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_EXITTB); +} + +static void pxa2xx_gpio_handler_update(struct pxa2xx_gpio_info_s *s) { + uint32_t level, diff; + int i, bit, line; + for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) { + level = s->olevel[i] & s->dir[i]; + + for (diff = s->prev_level[i] ^ level; diff; diff ^= 1 << bit) { + bit = ffs(diff) - 1; + line = bit + 32 * i; + if (s->handler[line].fn) + s->handler[line].fn(line, (level >> bit) & 1, + s->handler[line].opaque); + } + + s->prev_level[i] = level; + } +} + +static uint32_t pxa2xx_gpio_read(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_gpio_info_s *s = (struct pxa2xx_gpio_info_s *) opaque; + uint32_t ret; + int bank; + offset -= s->base; + if (offset >= 0x200) + return 0; + + bank = pxa2xx_gpio_regs[offset].bank; + switch (pxa2xx_gpio_regs[offset].reg) { + case GPDR: /* GPIO Pin-Direction Registers */ + return s->dir[bank]; + + case GRER: /* GPIO Rising-Edge Detect Enable Registers */ + return s->rising[bank]; + + case GFER: /* GPIO Falling-Edge Detect Enable Registers */ + return s->falling[bank]; + + case GAFR_L: /* GPIO Alternate Function Registers */ + return s->gafr[bank * 2]; + + case GAFR_U: /* GPIO Alternate Function Registers */ + return s->gafr[bank * 2 + 1]; + + case GPLR: /* GPIO Pin-Level Registers */ + ret = (s->olevel[bank] & s->dir[bank]) | + (s->ilevel[bank] & ~s->dir[bank]); + if (s->read_notify) + s->read_notify(s->opaque); + return ret; + + case GEDR: /* GPIO Edge Detect Status Registers */ + return s->status[bank]; + + default: + cpu_abort(cpu_single_env, + "%s: Bad offset %x\n", __FUNCTION__, offset); + } + + return 0; +} + +static void pxa2xx_gpio_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_gpio_info_s *s = (struct pxa2xx_gpio_info_s *) opaque; + int bank; + offset -= s->base; + if (offset >= 0x200) + return; + + bank = pxa2xx_gpio_regs[offset].bank; + switch (pxa2xx_gpio_regs[offset].reg) { + case GPDR: /* GPIO Pin-Direction Registers */ + s->dir[bank] = value; + pxa2xx_gpio_handler_update(s); + break; + + case GPSR: /* GPIO Pin-Output Set Registers */ + s->olevel[bank] |= value; + pxa2xx_gpio_handler_update(s); + break; + + case GPCR: /* GPIO Pin-Output Clear Registers */ + s->olevel[bank] &= ~value; + pxa2xx_gpio_handler_update(s); + break; + + case GRER: /* GPIO Rising-Edge Detect Enable Registers */ + s->rising[bank] = value; + break; + + case GFER: /* GPIO Falling-Edge Detect Enable Registers */ + s->falling[bank] = value; + break; + + case GAFR_L: /* GPIO Alternate Function Registers */ + s->gafr[bank * 2] = value; + break; + + case GAFR_U: /* GPIO Alternate Function Registers */ + s->gafr[bank * 2 + 1] = value; + break; + + case GEDR: /* GPIO Edge Detect Status Registers */ + s->status[bank] &= ~value; + pxa2xx_gpio_irq_update(s); + break; + + default: + cpu_abort(cpu_single_env, + "%s: Bad offset %x\n", __FUNCTION__, offset); + } +} + +static CPUReadMemoryFunc *pxa2xx_gpio_readfn[] = { + pxa2xx_gpio_read, + pxa2xx_gpio_read, + pxa2xx_gpio_read +}; + +static CPUWriteMemoryFunc *pxa2xx_gpio_writefn[] = { + pxa2xx_gpio_write, + pxa2xx_gpio_write, + pxa2xx_gpio_write +}; + +struct pxa2xx_gpio_info_s *pxa2xx_gpio_init(target_phys_addr_t base, + CPUState *env, void *pic, int lines) +{ + int iomemtype; + struct pxa2xx_gpio_info_s *s; + + s = (struct pxa2xx_gpio_info_s *) + qemu_mallocz(sizeof(struct pxa2xx_gpio_info_s)); + memset(s, 0, sizeof(struct pxa2xx_gpio_info_s)); + s->base = base; + s->pic = pic; + s->lines = lines; + s->cpu_env = env; + + iomemtype = cpu_register_io_memory(0, pxa2xx_gpio_readfn, + pxa2xx_gpio_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + + return s; +} + +void pxa2xx_gpio_handler_set(struct pxa2xx_gpio_info_s *s, int line, + gpio_handler_t handler, void *opaque) { + if (line >= s->lines) { + printf("%s: No GPIO pin %i\n", __FUNCTION__, line); + return; + } + + s->handler[line].fn = handler; + s->handler[line].opaque = opaque; +} + +/* + * Registers a callback to notify on GPLR reads. This normally + * shouldn't be needed but it is used for the hack on Spitz machines. + */ +void pxa2xx_gpio_read_notifier(struct pxa2xx_gpio_info_s *s, + void (*handler)(void *opaque), void *opaque) { + s->read_notify = handler; + s->opaque = opaque; +} Index: qemu/hw/pxa2xx_lcd.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa2xx_lcd.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,999 @@ +/* + * Intel XScale PXA255/270 LCDC emulation. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + */ + +#include "vl.h" + +typedef void (*drawfn)(uint32_t *, uint8_t *, const uint8_t *, int, int); + +struct pxa2xx_lcdc_s { + target_phys_addr_t base; + void *pic; + int irqlevel; + + int invalidated; + DisplayState *ds; + drawfn *line_fn[2]; + int dest_width; + int xres, yres; + int pal_for; + int transp; + enum { + pxa_lcdc_2bpp = 1, + pxa_lcdc_4bpp = 2, + pxa_lcdc_8bpp = 3, + pxa_lcdc_16bpp = 4, + pxa_lcdc_18bpp = 5, + pxa_lcdc_18pbpp = 6, + pxa_lcdc_19bpp = 7, + pxa_lcdc_19pbpp = 8, + pxa_lcdc_24bpp = 9, + pxa_lcdc_25bpp = 10, + } bpp; + + uint32_t control[6]; + uint32_t status[2]; + uint32_t ovl1c[2]; + uint32_t ovl2c[2]; + uint32_t ccr; + uint32_t cmdcr; + uint32_t trgbr; + uint32_t tcr; + uint32_t liidr; + uint8_t bscntr; + + struct { + target_phys_addr_t branch; + int up; + uint8_t palette[1024]; + uint8_t pbuffer[1024]; + void (*redraw)(struct pxa2xx_lcdc_s *s, uint8_t *fb, + int *miny, int *maxy); + + target_phys_addr_t descriptor; + target_phys_addr_t source; + uint32_t id; + uint32_t command; + } dma_ch[7]; + + void (*vsync_cb)(void *opaque); + void *opaque; + int orientation; +}; + +struct __attribute__ ((__packed__)) pxa_frame_descriptor_s { + uint32_t fdaddr; + uint32_t fsaddr; + uint32_t fidr; + uint32_t ldcmd; +}; + +#define LCCR0 0x000 /* LCD Controller Control Register 0 */ +#define LCCR1 0x004 /* LCD Controller Control Register 1 */ +#define LCCR2 0x008 /* LCD Controller Control Register 2 */ +#define LCCR3 0x00c /* LCD Controller Control Register 3 */ +#define LCCR4 0x010 /* LCD Controller Control Register 4 */ +#define LCCR5 0x014 /* LCD Controller Control Register 5 */ + +#define FBR0 0x020 /* DMA Channel 0 Frame Branch Register */ +#define FBR1 0x024 /* DMA Channel 1 Frame Branch Register */ +#define FBR2 0x028 /* DMA Channel 2 Frame Branch Register */ +#define FBR3 0x02c /* DMA Channel 3 Frame Branch Register */ +#define FBR4 0x030 /* DMA Channel 4 Frame Branch Register */ +#define FBR5 0x110 /* DMA Channel 5 Frame Branch Register */ +#define FBR6 0x114 /* DMA Channel 6 Frame Branch Register */ + +#define LCSR1 0x034 /* LCD Controller Status Register 1 */ +#define LCSR0 0x038 /* LCD Controller Status Register 0 */ +#define LIIDR 0x03c /* LCD Controller Interrupt ID Register */ + +#define TRGBR 0x040 /* TMED RGB Seed Register */ +#define TCR 0x044 /* TMED Control Register */ + +#define OVL1C1 0x050 /* Overlay 1 Control Register 1 */ +#define OVL1C2 0x060 /* Overlay 1 Control Register 2 */ +#define OVL2C1 0x070 /* Overlay 2 Control Register 1 */ +#define OVL2C2 0x080 /* Overlay 2 Control Register 2 */ +#define CCR 0x090 /* Cursor Control Register */ + +#define CMDCR 0x100 /* Command Control Register */ +#define PRSR 0x104 /* Panel Read Status Register */ + +#define PXA_LCDDMA_CHANS 7 +#define DMA_FDADR 0x00 /* Frame Descriptor Address Register */ +#define DMA_FSADR 0x04 /* Frame Source Address Register */ +#define DMA_FIDR 0x08 /* Frame ID Register */ +#define DMA_LDCMD 0x0c /* Command Register */ + +/* LCD Buffer Strength Control Register */ +#define BSCNTR 0x04000054 + +/* Bitfield masks */ +#define LCCR0_ENB (1 << 0) +#define LCCR0_CMS (1 << 1) +#define LCCR0_SDS (1 << 2) +#define LCCR0_LDM (1 << 3) +#define LCCR0_SOFM0 (1 << 4) +#define LCCR0_IUM (1 << 5) +#define LCCR0_EOFM0 (1 << 6) +#define LCCR0_PAS (1 << 7) +#define LCCR0_DPD (1 << 9) +#define LCCR0_DIS (1 << 10) +#define LCCR0_QDM (1 << 11) +#define LCCR0_PDD (0xff << 12) +#define LCCR0_BSM0 (1 << 20) +#define LCCR0_OUM (1 << 21) +#define LCCR0_LCDT (1 << 22) +#define LCCR0_RDSTM (1 << 23) +#define LCCR0_CMDIM (1 << 24) +#define LCCR0_OUC (1 << 25) +#define LCCR0_LDDALT (1 << 26) +#define LCCR1_PPL(x) ((x) & 0x3ff) +#define LCCR2_LPP(x) ((x) & 0x3ff) +#define LCCR3_API (15 << 16) +#define LCCR3_BPP(x) ((((x) >> 24) & 7) | (((x) >> 26) & 8)) +#define LCCR3_PDFOR(x) (((x) >> 30) & 3) +#define LCCR4_K1(x) (((x) >> 0) & 7) +#define LCCR4_K2(x) (((x) >> 3) & 7) +#define LCCR4_K3(x) (((x) >> 6) & 7) +#define LCCR4_PALFOR(x) (((x) >> 15) & 3) +#define LCCR5_SOFM(ch) (1 << (ch - 1)) +#define LCCR5_EOFM(ch) (1 << (ch + 7)) +#define LCCR5_BSM(ch) (1 << (ch + 15)) +#define LCCR5_IUM(ch) (1 << (ch + 23)) +#define OVLC1_EN (1 << 31) +#define CCR_CEN (1 << 31) +#define FBR_BRA (1 << 0) +#define FBR_BINT (1 << 1) +#define FBR_SRCADDR (0xfffffff << 4) +#define LCSR0_LDD (1 << 0) +#define LCSR0_SOF0 (1 << 1) +#define LCSR0_BER (1 << 2) +#define LCSR0_ABC (1 << 3) +#define LCSR0_IU0 (1 << 4) +#define LCSR0_IU1 (1 << 5) +#define LCSR0_OU (1 << 6) +#define LCSR0_QD (1 << 7) +#define LCSR0_EOF0 (1 << 8) +#define LCSR0_BS0 (1 << 9) +#define LCSR0_SINT (1 << 10) +#define LCSR0_RDST (1 << 11) +#define LCSR0_CMDINT (1 << 12) +#define LCSR0_BERCH(x) (((x) & 7) << 28) +#define LCSR1_SOF(ch) (1 << (ch - 1)) +#define LCSR1_EOF(ch) (1 << (ch + 7)) +#define LCSR1_BS(ch) (1 << (ch + 15)) +#define LCSR1_IU(ch) (1 << (ch + 23)) +#define LDCMD_LENGTH(x) ((x) & 0x001ffffc) +#define LDCMD_EOFINT (1 << 21) +#define LDCMD_SOFINT (1 << 22) +#define LDCMD_PAL (1 << 26) + +/* Route internal interrupt lines to the global IC */ +static void pxa2xx_lcdc_int_update(struct pxa2xx_lcdc_s *s) +{ + int level = 0; + level |= (s->status[0] & LCSR0_LDD) && !(s->control[0] & LCCR0_LDM); + level |= (s->status[0] & LCSR0_SOF0) && !(s->control[0] & LCCR0_SOFM0); + level |= (s->status[0] & LCSR0_IU0) && !(s->control[0] & LCCR0_IUM); + level |= (s->status[0] & LCSR0_IU1) && !(s->control[5] & LCCR5_IUM(1)); + level |= (s->status[0] & LCSR0_OU) && !(s->control[0] & LCCR0_OUM); + level |= (s->status[0] & LCSR0_QD) && !(s->control[0] & LCCR0_QDM); + level |= (s->status[0] & LCSR0_EOF0) && !(s->control[0] & LCCR0_EOFM0); + level |= (s->status[0] & LCSR0_BS0) && !(s->control[0] & LCCR0_BSM0); + level |= (s->status[0] & LCSR0_RDST) && !(s->control[0] & LCCR0_RDSTM); + level |= (s->status[0] & LCSR0_CMDINT) && !(s->control[0] & LCCR0_CMDIM); + level |= (s->status[1] & ~s->control[5]); + + pic_set_irq_new(s->pic, PXA2XX_PIC_LCD, !!level); + s->irqlevel = level; +} + +/* Set Branch Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_bs_set(struct pxa2xx_lcdc_s *s, int ch) +{ + int unmasked; + if (ch == 0) { + s->status[0] |= LCSR0_BS0; + unmasked = !(s->control[0] & LCCR0_BSM0); + } else { + s->status[1] |= LCSR1_BS(ch); + unmasked = !(s->control[5] & LCCR5_BSM(ch)); + } + + if (unmasked) { + if (s->irqlevel) + s->status[0] |= LCSR0_SINT; + else + s->liidr = s->dma_ch[ch].id; + } +} + +/* Set Start Of Frame Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_sof_set(struct pxa2xx_lcdc_s *s, int ch) +{ + int unmasked; + if (!(s->dma_ch[ch].command & LDCMD_SOFINT)) + return; + + if (ch == 0) { + s->status[0] |= LCSR0_SOF0; + unmasked = !(s->control[0] & LCCR0_SOFM0); + } else { + s->status[1] |= LCSR1_SOF(ch); + unmasked = !(s->control[5] & LCCR5_SOFM(ch)); + } + + if (unmasked) { + if (s->irqlevel) + s->status[0] |= LCSR0_SINT; + else + s->liidr = s->dma_ch[ch].id; + } +} + +/* Set End Of Frame Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_eof_set(struct pxa2xx_lcdc_s *s, int ch) +{ + int unmasked; + if (!(s->dma_ch[ch].command & LDCMD_EOFINT)) + return; + + if (ch == 0) { + s->status[0] |= LCSR0_EOF0; + unmasked = !(s->control[0] & LCCR0_EOFM0); + } else { + s->status[1] |= LCSR1_EOF(ch); + unmasked = !(s->control[5] & LCCR5_EOFM(ch)); + } + + if (unmasked) { + if (s->irqlevel) + s->status[0] |= LCSR0_SINT; + else + s->liidr = s->dma_ch[ch].id; + } +} + +/* Set Bus Error Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_ber_set(struct pxa2xx_lcdc_s *s, int ch) +{ + s->status[0] |= LCSR0_BERCH(ch) | LCSR0_BER; + if (s->irqlevel) + s->status[0] |= LCSR0_SINT; + else + s->liidr = s->dma_ch[ch].id; +} + +/* Set Read Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_rdst_set(struct pxa2xx_lcdc_s *s) +{ + s->status[0] |= LCSR0_RDST; + if (s->irqlevel && !(s->control[0] & LCCR0_RDSTM)) + s->status[0] |= LCSR0_SINT; +} + +/* Load new Frame Descriptors from DMA */ +static void pxa2xx_descriptor_load(struct pxa2xx_lcdc_s *s) +{ + struct pxa_frame_descriptor_s *desc[PXA_LCDDMA_CHANS]; + target_phys_addr_t descptr; + int i; + + for (i = 0; i < PXA_LCDDMA_CHANS; i ++) { + desc[i] = 0; + s->dma_ch[i].source = 0; + + if (!s->dma_ch[i].up) + continue; + + if (s->dma_ch[i].branch & FBR_BRA) { + descptr = s->dma_ch[i].branch & FBR_SRCADDR; + if (s->dma_ch[i].branch & FBR_BINT) + pxa2xx_dma_bs_set(s, i); + s->dma_ch[i].branch &= ~FBR_BRA; + } else + descptr = s->dma_ch[i].descriptor; + + if (!(descptr >= PXA2XX_RAM_BASE && descptr + + sizeof(*desc[i]) <= PXA2XX_RAM_BASE + phys_ram_size)) + continue; + + descptr -= PXA2XX_RAM_BASE; + desc[i] = (struct pxa_frame_descriptor_s *) (phys_ram_base + descptr); + s->dma_ch[i].descriptor = desc[i]->fdaddr; + s->dma_ch[i].source = desc[i]->fsaddr; + s->dma_ch[i].id = desc[i]->fidr; + s->dma_ch[i].command = desc[i]->ldcmd; + } +} + +static uint32_t pxa2xx_lcdc_read(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_lcdc_s *s = (struct pxa2xx_lcdc_s *) opaque; + int ch; + offset -= s->base; + + switch (offset) { + case LCCR0: + return s->control[0]; + case LCCR1: + return s->control[1]; + case LCCR2: + return s->control[2]; + case LCCR3: + return s->control[3]; + case LCCR4: + return s->control[4]; + case LCCR5: + return s->control[5]; + + case OVL1C1: + return s->ovl1c[0]; + case OVL1C2: + return s->ovl1c[1]; + case OVL2C1: + return s->ovl2c[0]; + case OVL2C2: + return s->ovl2c[1]; + + case CCR: + return s->ccr; + + case CMDCR: + return s->cmdcr; + + case TRGBR: + return s->trgbr; + case TCR: + return s->tcr; + + case 0x200 ... 0x1000: /* DMA per-channel registers */ + ch = (offset - 0x200) >> 4; + if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) + goto fail; + + switch (offset & 0xf) { + case DMA_FDADR: + return s->dma_ch[ch].descriptor; + case DMA_FSADR: + return s->dma_ch[ch].source; + case DMA_FIDR: + return s->dma_ch[ch].id; + case DMA_LDCMD: + return s->dma_ch[ch].command; + default: + goto fail; + } + + case FBR0: + return s->dma_ch[0].branch; + case FBR1: + return s->dma_ch[1].branch; + case FBR2: + return s->dma_ch[2].branch; + case FBR3: + return s->dma_ch[3].branch; + case FBR4: + return s->dma_ch[4].branch; + case FBR5: + return s->dma_ch[5].branch; + case FBR6: + return s->dma_ch[6].branch; + + case BSCNTR: + return s->bscntr; + + case PRSR: + return 0; + + case LCSR0: + return s->status[0]; + case LCSR1: + return s->status[1]; + case LIIDR: + return s->liidr; + + default: + fail: + cpu_abort(cpu_single_env, + "%s: Bad offset %x\n", __FUNCTION__, offset); + } + + return 0; +} + +static void pxa2xx_lcdc_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_lcdc_s *s = (struct pxa2xx_lcdc_s *) opaque; + int ch; + offset -= s->base; + + switch (offset) { + case LCCR0: + /* ACK Quick Disable done */ + if ((s->control[0] & LCCR0_ENB) && !(value & LCCR0_ENB)) + s->status[0] |= LCSR0_QD; + + if (!(s->control[0] & LCCR0_LCDT) && (value & LCCR0_LCDT)) + printf("%s: internal frame buffer unsupported\n", __FUNCTION__); + + if ((s->control[3] & LCCR3_API) && + (value & LCCR0_ENB) && !(value & LCCR0_LCDT)) + s->status[0] |= LCSR0_ABC; + + s->control[0] = value & 0x07ffffff; + pxa2xx_lcdc_int_update(s); + + s->dma_ch[0].up = !!(value & LCCR0_ENB); + s->dma_ch[1].up = (s->ovl1c[0] & OVLC1_EN) || (value & LCCR0_SDS); + break; + + case LCCR1: + s->control[1] = value; + break; + + case LCCR2: + s->control[2] = value; + break; + + case LCCR3: + s->control[3] = value & 0xefffffff; + s->bpp = LCCR3_BPP(value); + break; + + case LCCR4: + s->control[4] = value & 0x83ff81ff; + break; + + case LCCR5: + s->control[5] = value & 0x3f3f3f3f; + break; + + case OVL1C1: + if (!(s->ovl1c[0] & OVLC1_EN) && (value & OVLC1_EN)) + printf("%s: Overlay 1 not supported\n", __FUNCTION__); + + s->ovl1c[0] = value & 0x80ffffff; + s->dma_ch[1].up = (value & OVLC1_EN) || (s->control[0] & LCCR0_SDS); + break; + + case OVL1C2: + s->ovl1c[1] = value & 0x000fffff; + break; + + case OVL2C1: + if (!(s->ovl2c[0] & OVLC1_EN) && (value & OVLC1_EN)) + printf("%s: Overlay 2 not supported\n", __FUNCTION__); + + s->ovl2c[0] = value & 0x80ffffff; + s->dma_ch[2].up = !!(value & OVLC1_EN); + s->dma_ch[3].up = !!(value & OVLC1_EN); + s->dma_ch[4].up = !!(value & OVLC1_EN); + break; + + case OVL2C2: + s->ovl2c[1] = value & 0x007fffff; + break; + + case CCR: + if (!(s->ccr & CCR_CEN) && (value & CCR_CEN)) + printf("%s: Hardware cursor unimplemented\n", __FUNCTION__); + + s->ccr = value & 0x81ffffe7; + s->dma_ch[5].up = !!(value & CCR_CEN); + break; + + case CMDCR: + s->cmdcr = value & 0xff; + break; + + case TRGBR: + s->trgbr = value & 0x00ffffff; + break; + + case TCR: + s->tcr = value & 0x7fff; + break; + + case 0x200 ... 0x1000: /* DMA per-channel registers */ + ch = (offset - 0x200) >> 4; + if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) + goto fail; + + switch (offset & 0xf) { + case DMA_FDADR: + s->dma_ch[ch].descriptor = value & 0xfffffff0; + break; + + default: + goto fail; + } + break; + + case FBR0: + s->dma_ch[0].branch = value & 0xfffffff3; + break; + case FBR1: + s->dma_ch[1].branch = value & 0xfffffff3; + break; + case FBR2: + s->dma_ch[2].branch = value & 0xfffffff3; + break; + case FBR3: + s->dma_ch[3].branch = value & 0xfffffff3; + break; + case FBR4: + s->dma_ch[4].branch = value & 0xfffffff3; + break; + case FBR5: + s->dma_ch[5].branch = value & 0xfffffff3; + break; + case FBR6: + s->dma_ch[6].branch = value & 0xfffffff3; + break; + + case BSCNTR: + s->bscntr = value & 0xf; + break; + + case PRSR: + break; + + case LCSR0: + s->status[0] &= ~(value & 0xfff); + if (value & LCSR0_BER) + s->status[0] &= ~LCSR0_BERCH(7); + break; + + case LCSR1: + s->status[1] &= ~(value & 0x3e3f3f); + break; + + default: + fail: + cpu_abort(cpu_single_env, + "%s: Bad offset %x\n", __FUNCTION__, offset); + } +} + +static CPUReadMemoryFunc *pxa2xx_lcdc_readfn[] = { + pxa2xx_lcdc_read, + pxa2xx_lcdc_read, + pxa2xx_lcdc_read +}; + +static CPUWriteMemoryFunc *pxa2xx_lcdc_writefn[] = { + pxa2xx_lcdc_write, + pxa2xx_lcdc_write, + pxa2xx_lcdc_write +}; + +static inline +uint32_t rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); +} + +static inline +uint32_t rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); +} + +static inline +uint32_t rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); +} + +static inline +uint32_t rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b) +{ + return (r << 16) | (g << 8) | b; +} + +static inline +uint32_t rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b) +{ + return (r << 16) | (g << 8) | b; +} + +/* Load new palette for a given DMA channel, convert to internal format */ +static void pxa2xx_palette_parse(struct pxa2xx_lcdc_s *s, int ch, int bpp) +{ + int i, n, format, r, g, b, alpha; + uint32_t *dest, *src; + s->pal_for = LCCR4_PALFOR(s->control[4]); + format = s->pal_for; + + switch (bpp) { + case pxa_lcdc_2bpp: + n = 4; + break; + case pxa_lcdc_4bpp: + n = 16; + break; + case pxa_lcdc_8bpp: + n = 256; + break; + default: + format = 0; + return; + } + + src = (uint32_t *) s->dma_ch[ch].pbuffer; + dest = (uint32_t *) s->dma_ch[ch].palette; + alpha = r = g = b = 0; + + for (i = 0; i < n; i ++) { + switch (format) { + case 0: /* 16 bpp, no transparency */ + alpha = 0; + if (s->control[0] & LCCR0_CMS) + r = g = b = *src & 0xff; + else { + r = (*src & 0xf800) >> 8; + g = (*src & 0x07e0) >> 3; + b = (*src & 0x001f) << 3; + } + break; + case 1: /* 16 bpp plus transparency */ + alpha = *src & (1 << 24); + if (s->control[0] & LCCR0_CMS) + r = g = b = *src & 0xff; + else { + r = (*src & 0xf800) >> 8; + g = (*src & 0x07e0) >> 3; + b = (*src & 0x001f) << 3; + } + break; + case 2: /* 18 bpp plus transparency */ + alpha = *src & (1 << 24); + if (s->control[0] & LCCR0_CMS) + r = g = b = *src & 0xff; + else { + r = (*src & 0xf80000) >> 16; + g = (*src & 0x00fc00) >> 8; + b = (*src & 0x0000f8); + } + break; + case 3: /* 24 bpp plus transparency */ + alpha = *src & (1 << 24); + if (s->control[0] & LCCR0_CMS) + r = g = b = *src & 0xff; + else { + r = (*src & 0xff0000) >> 16; + g = (*src & 0x00ff00) >> 8; + b = (*src & 0x0000ff); + } + break; + } + switch (s->ds->depth) { + case 8: + *dest = rgb_to_pixel8(r, g, b) | alpha; + break; + case 15: + *dest = rgb_to_pixel15(r, g, b) | alpha; + break; + case 16: + *dest = rgb_to_pixel16(r, g, b) | alpha; + break; + case 24: + *dest = rgb_to_pixel24(r, g, b) | alpha; + break; + case 32: + *dest = rgb_to_pixel32(r, g, b) | alpha; + break; + } + src ++; + dest ++; + } +} + +static void pxa2xx_lcdc_dma0_redraw_horiz(struct pxa2xx_lcdc_s *s, + uint8_t *fb, int *miny, int *maxy) +{ + int y, src_width, dest_width, dirty[2]; + uint8_t *src, *dest; + ram_addr_t x, addr, new_addr, start, end; + drawfn fn = 0; + if (s->dest_width) + fn = s->line_fn[s->transp][s->bpp]; + if (!fn) + return; + + src = fb; + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) + src_width *= 3; + else if (s->bpp > pxa_lcdc_16bpp) + src_width *= 4; + else if (s->bpp > pxa_lcdc_8bpp) + src_width *= 2; + + dest = s->ds->data; + dest_width = s->xres * s->dest_width; + + addr = (ram_addr_t) (fb - phys_ram_base); + start = addr + (s->yres + 1) * src_width; + end = addr; + dirty[0] = dirty[1] = cpu_physical_memory_get_dirty(start, VGA_DIRTY_FLAG); + for (y = 0; y < s->yres; y ++) { + new_addr = addr + src_width; + for (x = addr + TARGET_PAGE_SIZE; x < new_addr; + x += TARGET_PAGE_SIZE) { + dirty[1] = cpu_physical_memory_get_dirty(x, VGA_DIRTY_FLAG); + dirty[0] |= dirty[1]; + } + if (dirty[0] || s->invalidated) { + fn((uint32_t *) s->dma_ch[0].palette, + dest, src, s->xres, s->dest_width); + if (addr < start) + start = addr; + if (new_addr > end) + end = new_addr; + if (y < *miny) + *miny = y; + if (y >= *maxy) + *maxy = y + 1; + } + addr = new_addr; + dirty[0] = dirty[1]; + src += src_width; + dest += dest_width; + } + + cpu_physical_memory_reset_dirty(start, end, VGA_DIRTY_FLAG); +} + +static void pxa2xx_lcdc_dma0_redraw_vert(struct pxa2xx_lcdc_s *s, + uint8_t *fb, int *miny, int *maxy) +{ + int y, src_width, dest_width, dirty[2]; + uint8_t *src, *dest; + ram_addr_t x, addr, new_addr, start, end; + drawfn fn = 0; + if (s->dest_width) + fn = s->line_fn[s->transp][s->bpp]; + if (!fn) + return; + + src = fb; + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) + src_width *= 3; + else if (s->bpp > pxa_lcdc_16bpp) + src_width *= 4; + else if (s->bpp > pxa_lcdc_8bpp) + src_width *= 2; + + dest_width = s->yres * s->dest_width; + dest = s->ds->data + dest_width * (s->xres - 1); + + addr = (ram_addr_t) (fb - phys_ram_base); + start = addr + (s->yres + 1) * src_width; + end = addr; + dirty[0] = dirty[1] = cpu_physical_memory_get_dirty(start, VGA_DIRTY_FLAG); + for (y = 0; y < s->yres; y ++) { + new_addr = addr + src_width; + for (x = addr + TARGET_PAGE_SIZE; x < new_addr; + x += TARGET_PAGE_SIZE) { + dirty[1] = cpu_physical_memory_get_dirty(x, VGA_DIRTY_FLAG); + dirty[0] |= dirty[1]; + } + if (dirty[0] || s->invalidated) { + fn((uint32_t *) s->dma_ch[0].palette, + dest, src, s->xres, -dest_width); + if (addr < start) + start = addr; + if (new_addr > end) + end = new_addr; + if (y < *miny) + *miny = y; + if (y >= *maxy) + *maxy = y + 1; + } + addr = new_addr; + dirty[0] = dirty[1]; + src += src_width; + dest += s->dest_width; + } + + cpu_physical_memory_reset_dirty(start, end, VGA_DIRTY_FLAG); +} + +static void pxa2xx_lcdc_resize(struct pxa2xx_lcdc_s *s) +{ + int width, height; + if (!(s->control[0] & LCCR0_ENB)) + return; + + width = LCCR1_PPL(s->control[1]) + 1; + height = LCCR2_LPP(s->control[2]) + 1; + + if (width != s->xres || height != s->yres) { + if (s->orientation) + dpy_resize(s->ds, height, width); + else + dpy_resize(s->ds, width, height); + s->invalidated = 1; + s->xres = width; + s->yres = height; + } +} + +static void pxa2xx_update_display(void *opaque) +{ + struct pxa2xx_lcdc_s *s = (struct pxa2xx_lcdc_s *) opaque; + uint8_t *fb; + target_phys_addr_t fbptr; + int miny, maxy; + int ch; + if (!(s->control[0] & LCCR0_ENB)) + return; + + pxa2xx_descriptor_load(s); + + pxa2xx_lcdc_resize(s); + miny = s->yres; + maxy = 0; + s->transp = s->dma_ch[2].up || s->dma_ch[3].up; + /* Note: With overlay planes the order depends on LCCR0 bit 25. */ + for (ch = 0; ch < PXA_LCDDMA_CHANS; ch ++) + if (s->dma_ch[ch].up) { + if (!s->dma_ch[ch].source) { + pxa2xx_dma_ber_set(s, ch); + continue; + } + fbptr = s->dma_ch[ch].source; + if (!(fbptr >= PXA2XX_RAM_BASE && + fbptr <= PXA2XX_RAM_BASE + phys_ram_size)) { + pxa2xx_dma_ber_set(s, ch); + continue; + } + fbptr -= PXA2XX_RAM_BASE; + fb = phys_ram_base + fbptr; + + if (s->dma_ch[ch].command & LDCMD_PAL) { + memcpy(s->dma_ch[ch].pbuffer, fb, + MAX(LDCMD_LENGTH(s->dma_ch[ch].command), + sizeof(s->dma_ch[ch].pbuffer))); + pxa2xx_palette_parse(s, ch, s->bpp); + } else { + /* Do we need to reparse palette */ + if (LCCR4_PALFOR(s->control[4]) != s->pal_for) + pxa2xx_palette_parse(s, ch, s->bpp); + + /* ACK frame start */ + pxa2xx_dma_sof_set(s, ch); + + s->dma_ch[ch].redraw(s, fb, &miny, &maxy); + s->invalidated = 0; + + /* ACK frame completed */ + pxa2xx_dma_eof_set(s, ch); + } + } + + if (s->control[0] & LCCR0_DIS) { + /* ACK last frame completed */ + s->control[0] &= ~LCCR0_ENB; + s->status[0] |= LCSR0_LDD; + } + + if (s->orientation) + dpy_update(s->ds, miny, 0, maxy, s->xres); + else + dpy_update(s->ds, 0, miny, s->xres, maxy); + pxa2xx_lcdc_int_update(s); + + if (s->vsync_cb) + s->vsync_cb(s->opaque); +} + +static void pxa2xx_invalidate_display(void *opaque) +{ + struct pxa2xx_lcdc_s *s = (struct pxa2xx_lcdc_s *) opaque; + s->invalidated = 1; +} + +static void pxa2xx_screen_dump(void *opaque, const char *filename) +{ + /* TODO */ +} + +void pxa2xx_lcdc_orientation(void *opaque, int angle) +{ + struct pxa2xx_lcdc_s *s = (struct pxa2xx_lcdc_s *) opaque; + + if (angle) { + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_vert; + } else { + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_horiz; + } + + s->orientation = angle; + s->xres = s->yres = -1; + pxa2xx_lcdc_resize(s); +} + +#define BITS 8 +#include "pxa2xx_template.h" +#define BITS 15 +#include "pxa2xx_template.h" +#define BITS 16 +#include "pxa2xx_template.h" +#define BITS 24 +#include "pxa2xx_template.h" +#define BITS 32 +#include "pxa2xx_template.h" + +struct pxa2xx_lcdc_s *pxa2xx_lcdc_init(target_phys_addr_t base, void *pic, + DisplayState *ds) +{ + int iomemtype; + struct pxa2xx_lcdc_s *s; + + s = (struct pxa2xx_lcdc_s *) qemu_mallocz(sizeof(struct pxa2xx_lcdc_s)); + s->base = base; + s->invalidated = 1; + s->pic = pic; + s->ds = ds; + + pxa2xx_lcdc_orientation(s, graphic_rotate); + + iomemtype = cpu_register_io_memory(0, pxa2xx_lcdc_readfn, + pxa2xx_lcdc_writefn, s); + cpu_register_physical_memory(base, 0x000fffff, iomemtype); + + graphic_console_init(ds, pxa2xx_update_display, + pxa2xx_invalidate_display, pxa2xx_screen_dump, s); + + switch (s->ds->depth) { + case 0: + s->dest_width = 0; + break; + case 8: + s->line_fn[0] = pxa2xx_draw_fn_8; + s->line_fn[1] = pxa2xx_draw_fn_8t; + s->dest_width = 1; + break; + case 15: + s->line_fn[0] = pxa2xx_draw_fn_15; + s->line_fn[1] = pxa2xx_draw_fn_15t; + s->dest_width = 2; + break; + case 16: + s->line_fn[0] = pxa2xx_draw_fn_16; + s->line_fn[1] = pxa2xx_draw_fn_16t; + s->dest_width = 2; + break; + case 24: + s->line_fn[0] = pxa2xx_draw_fn_24; + s->line_fn[1] = pxa2xx_draw_fn_24t; + s->dest_width = 3; + break; + case 32: + s->line_fn[0] = pxa2xx_draw_fn_32; + s->line_fn[1] = pxa2xx_draw_fn_32t; + s->dest_width = 4; + break; + default: + fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); + exit(1); + } + return s; +} + +void pxa2xx_lcd_vsync_cb(struct pxa2xx_lcdc_s *s, + void (*cb)(void *opaque), void *opaque) { + s->vsync_cb = cb; + s->opaque = opaque; +} Index: qemu/hw/pxa2xx_mmci.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa2xx_mmci.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,487 @@ +/* + * Intel XScale PXA255/270 MultiMediaCard/SD/SDIO Controller emulation. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + */ + +#include "vl.h" +#include "sd.h" + +struct pxa2xx_mmci_s { + target_phys_addr_t base; + void *pic; + void *dma; + + struct sd_state_s *card; + + uint32_t status; + uint32_t clkrt; + uint32_t spi; + uint32_t cmdat; + uint32_t resp_tout; + uint32_t read_tout; + int blklen; + int numblk; + uint32_t intmask; + uint32_t intreq; + int cmd; + uint32_t arg; + + int active; + int bytesleft; + uint8_t tx_fifo[64]; + int tx_start; + int tx_len; + uint8_t rx_fifo[32]; + int rx_start; + int rx_len; + uint16_t resp_fifo[9]; + int resp_len; + + int cmdreq; + int ac_width; +}; + +#define MMC_STRPCL 0x00 /* MMC Clock Start/Stop Register */ +#define MMC_STAT 0x04 /* MMC Status Register */ +#define MMC_CLKRT 0x08 /* MMC Clock Rate Register */ +#define MMC_SPI 0x0c /* MMC SPI Mode Register */ +#define MMC_CMDAT 0x10 /* MMC Command/Data Register */ +#define MMC_RESTO 0x14 /* MMC Response Time-Out Register */ +#define MMC_RDTO 0x18 /* MMC Read Time-Out Register */ +#define MMC_BLKLEN 0x1c /* MMC Block Length Register */ +#define MMC_NUMBLK 0x20 /* MMC Number of Blocks Register */ +#define MMC_PRTBUF 0x24 /* MMC Buffer Partly Full Register */ +#define MMC_I_MASK 0x28 /* MMC Interrupt Mask Register */ +#define MMC_I_REG 0x2c /* MMC Interrupt Request Register */ +#define MMC_CMD 0x30 /* MMC Command Register */ +#define MMC_ARGH 0x34 /* MMC Argument High Register */ +#define MMC_ARGL 0x38 /* MMC Argument Low Register */ +#define MMC_RES 0x3c /* MMC Response FIFO */ +#define MMC_RXFIFO 0x40 /* MMC Receive FIFO */ +#define MMC_TXFIFO 0x44 /* MMC Transmit FIFO */ +#define MMC_RDWAIT 0x48 /* MMC RD_WAIT Register */ +#define MMC_BLKS_REM 0x4c /* MMC Blocks Remaining Register */ + +/* Bitfield masks */ +#define STRPCL_STOP_CLK (1 << 0) +#define STRPCL_STRT_CLK (1 << 1) +#define STAT_TOUT_RES (1 << 1) +#define STAT_CLK_EN (1 << 8) +#define STAT_DATA_DONE (1 << 11) +#define STAT_PRG_DONE (1 << 12) +#define STAT_END_CMDRES (1 << 13) +#define SPI_SPI_MODE (1 << 0) +#define CMDAT_RES_TYPE (3 << 0) +#define CMDAT_DATA_EN (1 << 2) +#define CMDAT_WR_RD (1 << 3) +#define CMDAT_DMA_EN (1 << 7) +#define CMDAT_STOP_TRAN (1 << 10) +#define INT_DATA_DONE (1 << 0) +#define INT_PRG_DONE (1 << 1) +#define INT_END_CMD (1 << 2) +#define INT_STOP_CMD (1 << 3) +#define INT_CLK_OFF (1 << 4) +#define INT_RXFIFO_REQ (1 << 5) +#define INT_TXFIFO_REQ (1 << 6) +#define INT_TINT (1 << 7) +#define INT_DAT_ERR (1 << 8) +#define INT_RES_ERR (1 << 9) +#define INT_RD_STALLED (1 << 10) +#define INT_SDIO_INT (1 << 11) +#define INT_SDIO_SACK (1 << 12) +#define PRTBUF_PRT_BUF (1 << 0) + +/* Route internal interrupt lines to the global IC and DMA */ +static void pxa2xx_mmci_int_update(struct pxa2xx_mmci_s *s) +{ + uint32_t mask = s->intmask; + if (s->cmdat & CMDAT_DMA_EN) { + mask |= INT_RXFIFO_REQ | INT_TXFIFO_REQ; + + pic_set_irq_new(s->dma, PXA2XX_RX_RQ_MMCI, + !!(s->intreq & INT_RXFIFO_REQ)); + pic_set_irq_new(s->dma, PXA2XX_TX_RQ_MMCI, + !!(s->intreq & INT_TXFIFO_REQ)); + } + + pic_set_irq_new(s->pic, PXA2XX_PIC_MMC, !!(s->intreq & ~mask)); +} + +static void pxa2xx_mmci_fifo_update(struct pxa2xx_mmci_s *s) +{ + if (!s->active) + return; + + if (s->cmdat & CMDAT_WR_RD) { + while (s->bytesleft && s->tx_len) { + sd_write_datline(s->card, s->tx_fifo[s->tx_start ++]); + s->tx_start &= 0x1f; + s->tx_len --; + s->bytesleft --; + } + if (s->bytesleft) + s->intreq |= INT_TXFIFO_REQ; + } else + while (s->bytesleft && s->rx_len < 32) { + s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] = + sd_read_datline(s->card); + s->bytesleft --; + s->intreq |= INT_RXFIFO_REQ; + } + + if (!s->bytesleft) { + s->active = 0; + s->intreq |= INT_DATA_DONE; + s->status |= STAT_DATA_DONE; + + if (s->cmdat & CMDAT_WR_RD) { + s->intreq |= INT_PRG_DONE; + s->status |= STAT_PRG_DONE; + } + } + + pxa2xx_mmci_int_update(s); +} + +static void pxa2xx_mmci_wakequeues(struct pxa2xx_mmci_s *s) +{ + int rsplen; + struct sd_request_s request; + union sd_response_u response; + + s->active = 1; + s->rx_len = 0; + s->tx_len = 0; + s->cmdreq = 0; + + request.cmd = s->cmd; + request.arg = s->arg; + request.crc = 0; /* FIXME */ + + response = sd_write_cmdline(s->card, request, &rsplen); + s->intreq |= INT_END_CMD; + + memset(s->resp_fifo, 0, sizeof(s->resp_fifo)); + switch (s->cmdat & CMDAT_RES_TYPE) { +#define PXAMMCI_RESP(wd, value) \ + s->resp_fifo[(wd) + 0] |= (value) >> 8; \ + s->resp_fifo[(wd) + 1] |= (value) << 8; + case 0: /* No response */ + goto complete; + + case 1: /* R1, R4, R5 or R6 */ + if (rsplen < 48) + goto timeout; + + if (request.cmd == 3) { /* R6 */ + PXAMMCI_RESP(0, response.r6.arg); + PXAMMCI_RESP(1, response.r6.status); + } else { + PXAMMCI_RESP(0, response.r1.status >> 16); + PXAMMCI_RESP(1, response.r1.status & 0x0000ffff); + } + goto complete; + + case 2: /* R2 */ + if (rsplen < 128) + goto timeout; + + PXAMMCI_RESP(0, bswap16(response.r2.reg[0])); + PXAMMCI_RESP(1, bswap16(response.r2.reg[1])); + PXAMMCI_RESP(2, bswap16(response.r2.reg[2])); + PXAMMCI_RESP(3, bswap16(response.r2.reg[3])); + PXAMMCI_RESP(4, bswap16(response.r2.reg[4])); + PXAMMCI_RESP(5, bswap16(response.r2.reg[5])); + PXAMMCI_RESP(6, bswap16(response.r2.reg[6])); + PXAMMCI_RESP(7, bswap16(response.r2.reg[7])); + goto complete; + + case 3: /* R3 */ + if (rsplen < 32) + goto timeout; + + PXAMMCI_RESP(0, response.r3.ocr_reg >> 16); + PXAMMCI_RESP(1, response.r3.ocr_reg & 0x0000ffff); + goto complete; + + complete: + s->status |= STAT_END_CMDRES; + + if (!(s->cmdat & CMDAT_DATA_EN)) + s->active = 0; + else + s->bytesleft = s->numblk * s->blklen; + + s->resp_len = 0; + break; + + timeout: + s->active = 0; + s->status |= STAT_TOUT_RES; + break; + } + + pxa2xx_mmci_fifo_update(s); +} + +static uint32_t pxa2xx_mmci_read(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_mmci_s *s = (struct pxa2xx_mmci_s *) opaque; + uint32_t ret; + offset -= s->base; + + switch (offset) { + case MMC_STRPCL: + return 0; + case MMC_STAT: + return s->status; + case MMC_CLKRT: + return s->clkrt; + case MMC_SPI: + return s->spi; + case MMC_CMDAT: + return s->cmdat; + case MMC_RESTO: + return s->resp_tout; + case MMC_RDTO: + return s->read_tout; + case MMC_BLKLEN: + return s->blklen; + case MMC_NUMBLK: + return s->numblk; + case MMC_PRTBUF: + return 0; + case MMC_I_MASK: + return s->intmask; + case MMC_I_REG: + return s->intreq; + case MMC_CMD: + return s->cmd | 0x40; + case MMC_ARGH: + return s->arg >> 16; + case MMC_ARGL: + return s->arg & 0xffff; + case MMC_RES: + if (s->resp_len < 9) + return s->resp_fifo[s->resp_len ++]; + return 0; + case MMC_RXFIFO: + ret = 0; + while (s->ac_width -- && s->rx_len) { + ret |= s->rx_fifo[s->rx_start ++] << (s->ac_width << 3); + s->rx_start &= 0x1f; + s->rx_len --; + } + s->intreq &= ~INT_RXFIFO_REQ; + pxa2xx_mmci_fifo_update(s); + return ret; + case MMC_RDWAIT: + return 0; + case MMC_BLKS_REM: + return s->numblk; + default: + cpu_abort(cpu_single_env, "%s: Bad offset %x\n", __FUNCTION__, offset); + } + + return 0; +} + +static void pxa2xx_mmci_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_mmci_s *s = (struct pxa2xx_mmci_s *) opaque; + offset -= s->base; + + switch (offset) { + case MMC_STRPCL: + if (value & STRPCL_STRT_CLK) { + s->status |= STAT_CLK_EN; + s->intreq &= ~INT_CLK_OFF; + + if (s->cmdreq && !(s->cmdat & CMDAT_STOP_TRAN)) { + s->status &= STAT_CLK_EN; + pxa2xx_mmci_wakequeues(s); + } + } + + if (value & STRPCL_STOP_CLK) { + s->status &= ~STAT_CLK_EN; + s->intreq |= INT_CLK_OFF; + s->active = 0; + } + + pxa2xx_mmci_int_update(s); + break; + + case MMC_CLKRT: + s->clkrt = value & 7; + break; + + case MMC_SPI: + s->spi = value & 0xf; + if (value & SPI_SPI_MODE) + printf("%s: attempted to use card in SPI mode\n", __FUNCTION__); + break; + + case MMC_CMDAT: + s->cmdat = value & 0x3dff; + s->active = 0; + s->cmdreq = 1; + if (!(value & CMDAT_STOP_TRAN)) { + s->status &= STAT_CLK_EN; + + if (s->status & STAT_CLK_EN) + pxa2xx_mmci_wakequeues(s); + } + + pxa2xx_mmci_int_update(s); + break; + + case MMC_RESTO: + s->resp_tout = value & 0x7f; + break; + + case MMC_RDTO: + s->read_tout = value & 0xffff; + break; + + case MMC_BLKLEN: + s->blklen = value & 0xfff; + break; + + case MMC_NUMBLK: + s->numblk = value & 0xffff; + break; + + case MMC_PRTBUF: + if (value & PRTBUF_PRT_BUF) { + s->tx_start ^= 32; + s->tx_len = 0; + } + pxa2xx_mmci_fifo_update(s); + break; + + case MMC_I_MASK: + s->intmask = value & 0x1fff; + pxa2xx_mmci_int_update(s); + break; + + case MMC_CMD: + s->cmd = value & 0x3f; + break; + + case MMC_ARGH: + s->arg &= 0x0000ffff; + s->arg |= value << 16; + break; + + case MMC_ARGL: + s->arg &= 0xffff0000; + s->arg |= value & 0x0000ffff; + break; + + case MMC_TXFIFO: + while (s->ac_width -- && s->tx_len < 0x20) + s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] = + (value >> (s->ac_width << 3)) & 0xff; + s->intreq &= ~INT_TXFIFO_REQ; + pxa2xx_mmci_fifo_update(s); + break; + + case MMC_RDWAIT: + case MMC_BLKS_REM: + break; + + default: + cpu_abort(cpu_single_env, "%s: Bad offset %x\n", __FUNCTION__, offset); + } +} + +static uint32_t pxa2xx_mmci_readb(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_mmci_s *s = (struct pxa2xx_mmci_s *) opaque; + s->ac_width = 1; + return pxa2xx_mmci_read(opaque, offset); +} + +static uint32_t pxa2xx_mmci_readh(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_mmci_s *s = (struct pxa2xx_mmci_s *) opaque; + s->ac_width = 2; + return pxa2xx_mmci_read(opaque, offset); +} + +static uint32_t pxa2xx_mmci_readw(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_mmci_s *s = (struct pxa2xx_mmci_s *) opaque; + s->ac_width = 4; + return pxa2xx_mmci_read(opaque, offset); +} + +static CPUReadMemoryFunc *pxa2xx_mmci_readfn[] = { + pxa2xx_mmci_readb, + pxa2xx_mmci_readh, + pxa2xx_mmci_readw +}; + +static void pxa2xx_mmci_writeb(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_mmci_s *s = (struct pxa2xx_mmci_s *) opaque; + s->ac_width = 1; + pxa2xx_mmci_write(opaque, offset, value); +} + +static void pxa2xx_mmci_writeh(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_mmci_s *s = (struct pxa2xx_mmci_s *) opaque; + s->ac_width = 2; + pxa2xx_mmci_write(opaque, offset, value); +} + +static void pxa2xx_mmci_writew(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_mmci_s *s = (struct pxa2xx_mmci_s *) opaque; + s->ac_width = 4; + pxa2xx_mmci_write(opaque, offset, value); +} + +static CPUWriteMemoryFunc *pxa2xx_mmci_writefn[] = { + pxa2xx_mmci_writeb, + pxa2xx_mmci_writeh, + pxa2xx_mmci_writew +}; + +struct pxa2xx_mmci_s *pxa2xx_mmci_init(target_phys_addr_t base, + void *pic, void *dma) +{ + int iomemtype; + struct pxa2xx_mmci_s *s; + + s = (struct pxa2xx_mmci_s *) qemu_mallocz(sizeof(struct pxa2xx_mmci_s)); + s->base = base; + s->pic = pic; + s->dma = dma; + + iomemtype = cpu_register_io_memory(0, pxa2xx_mmci_readfn, + pxa2xx_mmci_writefn, s); + cpu_register_physical_memory(base, 0x000fffff, iomemtype); + + /* Instantiate the actual storage */ + s->card = sd_init(); + + return s; +} + +void pxa2xx_mmci_handlers(struct pxa2xx_mmci_s *s, void *opaque, + void (*readonly_cb)(void *, int), + void (*coverswitch_cb)(void *, int)) +{ + sd_set_cb(s->card, opaque, readonly_cb, coverswitch_cb); +} Index: qemu/hw/pxa2xx_pcmcia.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa2xx_pcmcia.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,230 @@ +/* + * Intel XScale PXA255/270 PC Card and CompactFlash Interface. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + */ + +#include "vl.h" + +struct pxa2xx_pcmcia_s { + struct pcmcia_socket_s slot; + struct pcmcia_card_s *card; + target_phys_addr_t common_base; + target_phys_addr_t attr_base; + target_phys_addr_t io_base; + + void *pic; + int irq; + int cd_irq; + void (*set_irq)(void *opaque, int line, int level); +}; + +static uint32_t pxa2xx_pcmcia_common_read(void *opaque, + target_phys_addr_t offset) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + + if (s->slot.attached) { + offset -= s->common_base; + return s->card->common_read(s->card->state, offset); + } + + return 0; +} + +static void pxa2xx_pcmcia_common_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + + if (s->slot.attached) { + offset -= s->common_base; + s->card->common_write(s->card->state, offset, value); + } +} + +static uint32_t pxa2xx_pcmcia_attr_read(void *opaque, + target_phys_addr_t offset) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + + if (s->slot.attached) { + offset -= s->attr_base; + return s->card->attr_read(s->card->state, offset); + } + + return 0; +} + +static void pxa2xx_pcmcia_attr_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + + if (s->slot.attached) { + offset -= s->attr_base; + s->card->attr_write(s->card->state, offset, value); + } +} + +static uint32_t pxa2xx_pcmcia_io_read(void *opaque, + target_phys_addr_t offset) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + + if (s->slot.attached) { + offset -= s->io_base; + return s->card->io_read(s->card->state, offset); + } + + return 0; +} + +static void pxa2xx_pcmcia_io_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + + if (s->slot.attached) { + offset -= s->io_base; + s->card->io_write(s->card->state, offset, value); + } +} + +static CPUReadMemoryFunc *pxa2xx_pcmcia_common_readfn[] = { + pxa2xx_pcmcia_common_read, + pxa2xx_pcmcia_common_read, + pxa2xx_pcmcia_common_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_pcmcia_common_writefn[] = { + pxa2xx_pcmcia_common_write, + pxa2xx_pcmcia_common_write, + pxa2xx_pcmcia_common_write, +}; + +static CPUReadMemoryFunc *pxa2xx_pcmcia_attr_readfn[] = { + pxa2xx_pcmcia_attr_read, + pxa2xx_pcmcia_attr_read, + pxa2xx_pcmcia_attr_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_pcmcia_attr_writefn[] = { + pxa2xx_pcmcia_attr_write, + pxa2xx_pcmcia_attr_write, + pxa2xx_pcmcia_attr_write, +}; + +static CPUReadMemoryFunc *pxa2xx_pcmcia_io_readfn[] = { + pxa2xx_pcmcia_io_read, + pxa2xx_pcmcia_io_read, + pxa2xx_pcmcia_io_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_pcmcia_io_writefn[] = { + pxa2xx_pcmcia_io_write, + pxa2xx_pcmcia_io_write, + pxa2xx_pcmcia_io_write, +}; + +static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + if (!s->set_irq) + return; + + s->set_irq(s->pic, s->irq, level); +} + +struct pxa2xx_pcmcia_s *pxa2xx_pcmcia_init(target_phys_addr_t base) +{ + int iomemtype; + struct pxa2xx_pcmcia_s *s; + + s = (struct pxa2xx_pcmcia_s *) + qemu_mallocz(sizeof(struct pxa2xx_pcmcia_s)); + + /* Socket I/O Memory Space */ + s->io_base = base | 0x00000000; + iomemtype = cpu_register_io_memory(0, pxa2xx_pcmcia_io_readfn, + pxa2xx_pcmcia_io_writefn, s); + cpu_register_physical_memory(s->io_base, 0x03ffffff, iomemtype); + + /* Then next 64 MB is reserved */ + + /* Socket Attribute Memory Space */ + s->attr_base = base | 0x08000000; + iomemtype = cpu_register_io_memory(0, pxa2xx_pcmcia_attr_readfn, + pxa2xx_pcmcia_attr_writefn, s); + cpu_register_physical_memory(s->attr_base, 0x03ffffff, iomemtype); + + /* Socket Common Memory Space */ + s->common_base = base | 0x0c000000; + iomemtype = cpu_register_io_memory(0, pxa2xx_pcmcia_common_readfn, + pxa2xx_pcmcia_common_writefn, s); + cpu_register_physical_memory(s->common_base, 0x03ffffff, iomemtype); + + if (base == 0x30000000) + s->slot.slot_string = "PXA PC Card Socket 1"; + else + s->slot.slot_string = "PXA PC Card Socket 0"; + s->slot.opaque = s; + s->slot.set_irq = pxa2xx_pcmcia_set_irq; + pcmcia_socket_register(&s->slot); + return s; +} + +/* Insert a new card into a slot */ +int pxa2xx_pcmcia_attach(void *opaque, struct pcmcia_card_s *card) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + if (s->slot.attached) + return -EEXIST; + + if (s->set_irq) { + s->set_irq(s->pic, s->cd_irq, 1); + } + + s->card = card; + + s->slot.attached = 1; + s->card->slot = &s->slot; + s->card->attach(s->card->state); + + return 0; +} + +/* Eject card from the slot */ +int pxa2xx_pcmcia_dettach(void *opaque) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + if (!s->slot.attached) + return -ENOENT; + + s->card->detach(s->card->state); + s->card->slot = 0; + s->card = 0; + + s->slot.attached = 0; + + if (s->set_irq) { + s->set_irq(s->pic, s->irq, 0); + s->set_irq(s->pic, s->cd_irq, 0); + } + + return 0; +} + +/* Who to notify on card events */ +void pxa2xx_pcmcia_set_irq_cb(void *opaque, void (*set_irq)(void *opaque, + int line, int level), int irq, int cd_irq, void *pic) +{ + struct pxa2xx_pcmcia_s *s = (struct pxa2xx_pcmcia_s *) opaque; + s->set_irq = set_irq; + s->pic = pic; + s->irq = irq; + s->cd_irq = cd_irq; +} Index: qemu/hw/pxa2xx_pic.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa2xx_pic.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,287 @@ +/* + * Intel XScale PXA Programmable Interrupt Controller. + * + * Copyright (c) 2006 Openedhand Ltd. + * Copyright (c) 2006 Thorsten Zitterell + * Written by Andrzej Zaborowski + * + * This code is licenced under the GPL. + */ + +#include "vl.h" + +#define ICIP 0x00 /* Interrupt Controller IRQ Pending register */ +#define ICMR 0x04 /* Interrupt Controller Mask register */ +#define ICLR 0x08 /* Interrupt Controller Level register */ +#define ICFP 0x0c /* Interrupt Controller FIQ Pending register */ +#define ICPR 0x10 /* Interrupt Controller Pending register */ +#define ICCR 0x14 /* Interrupt Controller Control register */ +#define ICHP 0x18 /* Interrupt Controller Highest Priority register */ +#define IPR0 0x1c /* Interrupt Controller Priority register 0 */ +#define IPR31 0x98 /* Interrupt Controller Priority register 31 */ +#define ICIP2 0x9c /* Interrupt Controller IRQ Pending register 2 */ +#define ICMR2 0xa0 /* Interrupt Controller Mask register 2 */ +#define ICLR2 0xa4 /* Interrupt Controller Level register 2 */ +#define ICFP2 0xa8 /* Interrupt Controller FIQ Pending register 2 */ +#define ICPR2 0xac /* Interrupt Controller Pending register 2 */ +#define IPR32 0xb0 /* Interrupt Controller Priority register 32 */ +#define IPR39 0xcc /* Interrupt Controller Priority register 39 */ + +/* The first element of an individual PIC state structures should + * be a pointer to the handler routine. */ +typedef void (*pxa2xx_pic_handler_t)(void *opaque, int irq, int level); + +#define PXA2XX_PIC_SRCS 40 + +struct pxa2xx_pic_state_s { + pxa2xx_pic_handler_t handler; + CPUState *cpu_env; + target_phys_addr_t base; + uint32_t int_enabled[2]; + uint32_t int_pending[2]; + uint32_t is_fiq[2]; + uint32_t int_idle; + uint32_t priority[PXA2XX_PIC_SRCS]; + void *parent; + int parent_irq; + int parent_fiq; +}; + +static void pxa2xx_pic_update(void *opaque) +{ + uint32_t mask[2]; + struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque; + + if (s->cpu_env->halted) { + mask[0] = s->int_pending[0] & (s->int_enabled[0] | s->int_idle); + mask[1] = s->int_pending[1] & (s->int_enabled[1] | s->int_idle); + if (mask[0] || mask[1]) + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_EXITTB); + } + + mask[0] = s->int_pending[0] & s->int_enabled[0]; + mask[1] = s->int_pending[1] & s->int_enabled[1]; + + if ((mask[0] & s->is_fiq[0]) || (mask[1] & s->is_fiq[1])) + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ); + else + cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ); + + if ((mask[0] & ~s->is_fiq[0]) || (mask[1] & ~s->is_fiq[1])) + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); + else + cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); +} + +/* Note: Here level means state of the signal on a pin, not + * IRQ/FIQ distinction as in PXA Developer Manual. */ +static void pxa2xx_pic_set_irq(void *opaque, int irq, int level) +{ + struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque; + int int_set = (irq >= 32); + irq &= 31; + + if (level) + s->int_pending[int_set] |= 1 << irq; + else + s->int_pending[int_set] &= ~(1 << irq); + + pxa2xx_pic_update(opaque); +} + +static inline uint32_t pxa2xx_pic_highest(struct pxa2xx_pic_state_s *s) { + int i, int_set, irq; + uint32_t bit, mask[2]; + uint32_t ichp = 0x003f003f; /* Both IDs invalid */ + + mask[0] = s->int_pending[0] & s->int_enabled[0]; + mask[1] = s->int_pending[1] & s->int_enabled[1]; + + for (i = PXA2XX_PIC_SRCS - 1; i >= 0; i --) { + irq = s->priority[i] & 0x3f; + if ((s->priority[i] & (1 << 31)) && irq < PXA2XX_PIC_SRCS) { + /* Source peripheral ID is valid. */ + bit = 1 << (irq & 31); + int_set = (irq >= 32); + + if (mask[int_set] & bit & s->is_fiq[int_set]) { + /* FIQ asserted */ + ichp &= 0xffff0000; + ichp |= (1 << 15) | irq; + } + + if (mask[int_set] & bit & ~s->is_fiq[int_set]) { + /* IRQ asserted */ + ichp &= 0x0000ffff; + ichp |= (1 << 31) | (irq << 16); + } + } + } + + return ichp; +} + +static uint32_t pxa2xx_pic_mem_read(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque; + offset -= s->base; + + switch (offset) { + case ICIP: /* IRQ Pending register */ + return s->int_pending[0] & ~s->is_fiq[0] & s->int_enabled[0]; + case ICIP2: /* IRQ Pending register 2 */ + return s->int_pending[1] & ~s->is_fiq[1] & s->int_enabled[1]; + case ICMR: /* Mask register */ + return s->int_enabled[0]; + case ICMR2: /* Mask register 2 */ + return s->int_enabled[1]; + case ICLR: /* Level register */ + return s->is_fiq[0]; + case ICLR2: /* Level register 2 */ + return s->is_fiq[1]; + case ICCR: /* Idle mask */ + return (s->int_idle == 0); + case ICFP: /* FIQ Pending register */ + return s->int_pending[0] & s->is_fiq[0] & s->int_enabled[0]; + case ICFP2: /* FIQ Pending register 2 */ + return s->int_pending[1] & s->is_fiq[1] & s->int_enabled[1]; + case ICPR: /* Pending register */ + return s->int_pending[0]; + case ICPR2: /* Pending register 2 */ + return s->int_pending[1]; + case IPR0 ... IPR31: + return s->priority[0 + ((offset - IPR0 ) >> 2)]; + case IPR32 ... IPR39: + return s->priority[32 + ((offset - IPR32) >> 2)]; + case ICHP: /* Highest Priority register */ + return pxa2xx_pic_highest(s); + default: + printf("%s: Bad register offset 0x%lx\n", __FUNCTION__, offset); + return 0; + } +} + +static void pxa2xx_pic_mem_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque; + offset -= s->base; + + switch (offset) { + case ICMR: /* Mask register */ + s->int_enabled[0] = value; + break; + case ICMR2: /* Mask register 2 */ + s->int_enabled[1] = value; + break; + case ICLR: /* Level register */ + s->is_fiq[0] = value; + break; + case ICLR2: /* Level register 2 */ + s->is_fiq[1] = value; + break; + case ICCR: /* Idle mask */ + s->int_idle = (value & 1) ? 0 : ~0; + break; + case IPR0 ... IPR31: + s->priority[0 + ((offset - IPR0 ) >> 2)] = value & 0x8000003f; + break; + case IPR32 ... IPR39: + s->priority[32 + ((offset - IPR32) >> 2)] = value & 0x8000003f; + break; + default: + printf("%s: Bad register offset 0x%lx\n", __FUNCTION__, offset); + return; + } + pxa2xx_pic_update(opaque); +} + +/* Interrupt Controller Coprocessor Space Register Mapping */ +static const int pxa2xx_cp_reg_map[0x10] = { + [0x0 ... 0xf] = -1, + [0x0] = ICIP, + [0x1] = ICMR, + [0x2] = ICLR, + [0x3] = ICFP, + [0x4] = ICPR, + [0x5] = ICHP, + [0x6] = ICIP2, + [0x7] = ICMR2, + [0x8] = ICLR2, + [0x9] = ICFP2, + [0xa] = ICPR2, +}; + +static uint32_t pxa2xx_pic_cp_read(void *opaque, int op2, int reg, int crm) +{ + struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque; + target_phys_addr_t offset; + + if (pxa2xx_cp_reg_map[reg] == -1) { + printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); + return 0; + } + + offset = s->base + pxa2xx_cp_reg_map[reg]; + return pxa2xx_pic_mem_read(opaque, offset); +} + +static void pxa2xx_pic_cp_write(void *opaque, int op2, int reg, int crm, + uint32_t value) +{ + struct pxa2xx_pic_state_s *s = (struct pxa2xx_pic_state_s *) opaque; + target_phys_addr_t offset; + + if (pxa2xx_cp_reg_map[reg] == -1) { + printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); + return; + } + + offset = s->base + pxa2xx_cp_reg_map[reg]; + pxa2xx_pic_mem_write(opaque, offset, value); +} + +static CPUReadMemoryFunc *pxa2xx_pic_readfn[] = { + pxa2xx_pic_mem_read, + pxa2xx_pic_mem_read, + pxa2xx_pic_mem_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_pic_writefn[] = { + pxa2xx_pic_mem_write, + pxa2xx_pic_mem_write, + pxa2xx_pic_mem_write, +}; + +struct pxa2xx_pic_state_s *pxa2xx_pic_init(target_phys_addr_t base, + CPUState *env, int parent_irq, int parent_fiq) +{ + struct pxa2xx_pic_state_s *s; + int iomemtype; + + s = (struct pxa2xx_pic_state_s *) + qemu_mallocz(sizeof(struct pxa2xx_pic_state_s)); + if (!s) + return NULL; + + s->cpu_env = env; + s->base = base; + + s->int_pending[0] = 0; + s->int_pending[1] = 0; + s->int_enabled[0] = 0; + s->int_enabled[1] = 0; + s->is_fiq[0] = 0; + s->is_fiq[1] = 0; + s->handler = pxa2xx_pic_set_irq; + + /* Enable IC memory-mapped registers access. */ + iomemtype = cpu_register_io_memory(0, pxa2xx_pic_readfn, + pxa2xx_pic_writefn, s); + cpu_register_physical_memory(base, 0x000fffff, iomemtype); + + /* Enable IC coprocessor access. */ + cpu_arm_set_cp_io(env, 6, pxa2xx_pic_cp_read, pxa2xx_pic_cp_write, s); + + return s; +} Index: qemu/hw/pxa2xx_template.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa2xx_template.h 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,431 @@ +/* + * Intel XScale PXA255/270 LCDC emulation. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + * + * Framebuffer format conversion routines. + */ + +# define SKIP_PIXEL(to) to += deststep +#if BITS == 8 +# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to) +#elif BITS == 15 || BITS == 16 +# define COPY_PIXEL(to, from) *(uint16_t *) to = from; SKIP_PIXEL(to) +#elif BITS == 24 +# define COPY_PIXEL(to, from) \ + *(uint16_t *) to = from; *(to + 2) = (from) >> 16; SKIP_PIXEL(to) +#elif BITS == 32 +# define COPY_PIXEL(to, from) *(uint32_t *) to = from; SKIP_PIXEL(to) +#else +# error unknown bit depth +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP_WORDS 1 +#endif + +#define FN_2(x) FN(x + 1) FN(x) +#define FN_4(x) FN_2(x + 2) FN_2(x) + +static void glue(pxa2xx_draw_line2_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + while (width > 0) { + data = *(uint32_t *) src; +#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 2)) & 3]); +#ifdef SWAP_WORDS + FN_4(12) + FN_4(8) + FN_4(4) + FN_4(0) +#else + FN_4(0) + FN_4(4) + FN_4(8) + FN_4(12) +#endif +#undef FN + width -= 16; + src += 4; + } +} + +static void glue(pxa2xx_draw_line4_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + while (width > 0) { + data = *(uint32_t *) src; +#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 4)) & 0xf]); +#ifdef SWAP_WORDS + FN_2(6) + FN_2(4) + FN_2(2) + FN_2(0) +#else + FN_2(0) + FN_2(2) + FN_2(4) + FN_2(6) +#endif +#undef FN + width -= 8; + src += 4; + } +} + +static void glue(pxa2xx_draw_line8_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + while (width > 0) { + data = *(uint32_t *) src; +#define FN(x) COPY_PIXEL(dest, palette[(data >> (x)) & 0xff]); +#ifdef SWAP_WORDS + FN(24) + FN(16) + FN(8) + FN(0) +#else + FN(0) + FN(8) + FN(16) + FN(24) +#endif +#undef FN + width -= 4; + src += 4; + } +} + +static void glue(pxa2xx_draw_line16_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *) src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif + b = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x3f) << 2; + data >>= 6; + r = (data & 0x1f) << 3; + data >>= 5; + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + b = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x3f) << 2; + data >>= 6; + r = (data & 0x1f) << 3; + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 2; + src += 4; + } +} + +static void glue(pxa2xx_draw_line16t_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *) src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif + b = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x1f) << 3; + data >>= 5; + r = (data & 0x1f) << 3; + data >>= 5; + if (data & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + data >>= 1; + b = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x1f) << 3; + data >>= 5; + r = (data & 0x1f) << 3; + if (data & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 2; + src += 4; + } +} + +static void glue(pxa2xx_draw_line18_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *) src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif + b = (data & 0x3f) << 2; + data >>= 6; + g = (data & 0x3f) << 2; + data >>= 6; + r = (data & 0x3f) << 2; + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 1; + src += 4; + } +} + +/* The wicked packed format */ +static void glue(pxa2xx_draw_line18p_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data[3]; + unsigned int r, g, b; + while (width > 0) { + data[0] = *(uint32_t *) src; + src += 4; + data[1] = *(uint32_t *) src; + src += 4; + data[2] = *(uint32_t *) src; + src += 4; +#ifdef SWAP_WORDS + data[0] = bswap32(data[0]); + data[1] = bswap32(data[1]); + data[2] = bswap32(data[2]); +#endif + b = (data[0] & 0x3f) << 2; + data[0] >>= 6; + g = (data[0] & 0x3f) << 2; + data[0] >>= 6; + r = (data[0] & 0x3f) << 2; + data[0] >>= 12; + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + b = (data[0] & 0x3f) << 2; + data[0] >>= 6; + g = ((data[1] & 0xf) << 4) | (data[0] << 2); + data[1] >>= 4; + r = (data[1] & 0x3f) << 2; + data[1] >>= 12; + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + b = (data[1] & 0x3f) << 2; + data[1] >>= 6; + g = (data[1] & 0x3f) << 2; + data[1] >>= 6; + r = ((data[2] & 0x3) << 6) | (data[1] << 2); + data[2] >>= 8; + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + b = (data[2] & 0x3f) << 2; + data[2] >>= 6; + g = (data[2] & 0x3f) << 2; + data[2] >>= 6; + r = data[2] << 2; + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 4; + } +} + +static void glue(pxa2xx_draw_line19_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *) src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif + b = (data & 0x3f) << 2; + data >>= 6; + g = (data & 0x3f) << 2; + data >>= 6; + r = (data & 0x3f) << 2; + data >>= 6; + if (data & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 1; + src += 4; + } +} + +/* The wicked packed format */ +static void glue(pxa2xx_draw_line19p_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data[3]; + unsigned int r, g, b; + while (width > 0) { + data[0] = *(uint32_t *) src; + src += 4; + data[1] = *(uint32_t *) src; + src += 4; + data[2] = *(uint32_t *) src; + src += 4; +# ifdef SWAP_WORDS + data[0] = bswap32(data[0]); + data[1] = bswap32(data[1]); + data[2] = bswap32(data[2]); +# endif + b = (data[0] & 0x3f) << 2; + data[0] >>= 6; + g = (data[0] & 0x3f) << 2; + data[0] >>= 6; + r = (data[0] & 0x3f) << 2; + data[0] >>= 6; + if (data[0] & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + data[0] >>= 6; + b = (data[0] & 0x3f) << 2; + data[0] >>= 6; + g = ((data[1] & 0xf) << 4) | (data[0] << 2); + data[1] >>= 4; + r = (data[1] & 0x3f) << 2; + data[1] >>= 6; + if (data[1] & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + data[1] >>= 6; + b = (data[1] & 0x3f) << 2; + data[1] >>= 6; + g = (data[1] & 0x3f) << 2; + data[1] >>= 6; + r = ((data[2] & 0x3) << 6) | (data[1] << 2); + data[2] >>= 2; + if (data[2] & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + data[2] >>= 6; + b = (data[2] & 0x3f) << 2; + data[2] >>= 6; + g = (data[2] & 0x3f) << 2; + data[2] >>= 6; + r = data[2] << 2; + data[2] >>= 6; + if (data[2] & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 4; + } +} + +static void glue(pxa2xx_draw_line24_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *) src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif + b = data & 0xff; + data >>= 8; + g = data & 0xff; + data >>= 8; + r = data & 0xff; + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 1; + src += 4; + } +} + +static void glue(pxa2xx_draw_line24t_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *) src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif + b = (data & 0x7f) << 1; + data >>= 7; + g = data & 0xff; + data >>= 8; + r = data & 0xff; + data >>= 8; + if (data & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 1; + src += 4; + } +} + +static void glue(pxa2xx_draw_line25_, BITS)(uint32_t *palette, + uint8_t *dest, const uint8_t *src, int width, int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *) src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif + b = data & 0xff; + data >>= 8; + g = data & 0xff; + data >>= 8; + r = data & 0xff; + data >>= 8; + if (data & 1) + SKIP_PIXEL(dest); + else + COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); + width -= 1; + src += 4; + } +} + +/* Overlay planes disabled, no transparency */ +static drawfn glue(pxa2xx_draw_fn_, BITS)[16] = +{ + [0 ... 0xf] = 0, + [pxa_lcdc_2bpp] = glue(pxa2xx_draw_line2_, BITS), + [pxa_lcdc_4bpp] = glue(pxa2xx_draw_line4_, BITS), + [pxa_lcdc_8bpp] = glue(pxa2xx_draw_line8_, BITS), + [pxa_lcdc_16bpp] = glue(pxa2xx_draw_line16_, BITS), + [pxa_lcdc_18bpp] = glue(pxa2xx_draw_line18_, BITS), + [pxa_lcdc_18pbpp] = glue(pxa2xx_draw_line18p_, BITS), + [pxa_lcdc_24bpp] = glue(pxa2xx_draw_line24_, BITS), +}; + +/* Overlay planes enabled, transparency used */ +static drawfn glue(glue(pxa2xx_draw_fn_, BITS), t)[16] = +{ + [0 ... 0xf] = 0, + [pxa_lcdc_4bpp] = glue(pxa2xx_draw_line4_, BITS), + [pxa_lcdc_8bpp] = glue(pxa2xx_draw_line8_, BITS), + [pxa_lcdc_16bpp] = glue(pxa2xx_draw_line16t_, BITS), + [pxa_lcdc_19bpp] = glue(pxa2xx_draw_line19_, BITS), + [pxa_lcdc_19pbpp] = glue(pxa2xx_draw_line19p_, BITS), + [pxa_lcdc_24bpp] = glue(pxa2xx_draw_line24t_, BITS), + [pxa_lcdc_25bpp] = glue(pxa2xx_draw_line25_, BITS), +}; + +#undef BITS +#undef COPY_PIXEL +#undef SKIP_PIXEL + +#ifdef SWAP_WORDS +# undef SWAP_WORDS +#endif Index: qemu/hw/pxa2xx_timer.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/pxa2xx_timer.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,212 @@ +/* + * Intel XScale PXA255/270 OS Timers. + * + * Copyright (c) 2006 Openedhand Ltd. + * Copyright (c) 2006 Thorsten Zitterell + * + * This code is licenced under the GPL. + */ + +#include "vl.h" + +#define OSMR0 0x00 +#define OSMR1 0x04 +#define OSMR2 0x08 +#define OSMR3 0x0c +#define OSMR4 0x80 +#define OSCR 0x10 /* OS Timer Count */ +#define OSCR4 0x40 +#define OMCR4 0xc0 +#define OSSR 0x14 /* Timer status register */ +#define OWER 0x18 +#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */ + +#define PXA25X_FREQ 3686400 /* 3.6864 MHz */ +#define PXA27X_FREQ 3250000 /* 3.25 MHz */ + +typedef struct { + uint32_t value; + int level; + int irq; + void *pic; + QEMUTimer *qtimer; + int num; + void *info; +} pxa2xx_timer; + +typedef struct { + uint32_t base; + int32_t clock; + int32_t oldclock; + uint64_t lastload; + uint32_t freq; + pxa2xx_timer timer[4]; + uint32_t events; + uint32_t irq_enabled; + uint32_t reset3; + CPUState *cpustate; + int64_t qemu_ticks; +} pxa2xx_timer_info; + +static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu) +{ + pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; + int i; + uint32_t now_vm; + uint64_t new_qemu; + + now_vm = s->clock + + muldiv64(now_qemu - s->lastload, s->freq, ticks_per_sec); + + for (i = 0; i < 4; i ++) { + new_qemu = now_qemu + muldiv64((uint32_t) (s->timer[i].value - now_vm), + ticks_per_sec, s->freq); + qemu_mod_timer(s->timer[i].qtimer, new_qemu); + } +} + +static uint32_t pxa2xx_timer_read(void *opaque, target_phys_addr_t offset) +{ + pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; + + offset -= s->base; + + switch (offset) { + case OSMR0: + case OSMR1: + case OSMR2: + case OSMR3: + return s->timer[offset >> 2].value; + case OSCR: + return s->clock + muldiv64(qemu_get_clock(vm_clock) - + s->lastload, s->freq, ticks_per_sec); + case OIER: + return s->irq_enabled; + case OWER: + return s->reset3; + default: + cpu_abort(cpu_single_env, + "pxa2xx_timer_read: Bad offset %x\n", offset); + } + + return 0; +} + +static void pxa2xx_timer_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + int i; + pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; + + offset -= s->base; + + switch (offset) { + case OSMR0: + case OSMR1: + case OSMR2: + case OSMR3: + s->timer[offset >> 2].value = value; + pxa2xx_timer_update(s, qemu_get_clock(vm_clock)); + break; + case OSCR: + s->oldclock = s->clock; + s->lastload = qemu_get_clock(vm_clock); + s->clock = value; + pxa2xx_timer_update(s, s->lastload); + break; + case OIER: + s->irq_enabled = value; + break; + case OSSR: /* Status register */ + s->events &= ~value; + for (i = 0; i < 4; i++) { + if (s->timer[i].level && (value & (1 << i))) { + s->timer[i].level = 0; + pic_set_irq_new(s->timer[i].pic, s->timer[i].irq, 0); + } + } + break; + case OWER: /* XXX: Reset on OSMR3 match? */ + s->reset3 = value; + break; + default: + cpu_abort(cpu_single_env, + "pxa2xx_timer_write: Bad offset %x\n", offset); + } +} + +static CPUReadMemoryFunc *pxa2xx_timer_readfn[] = { + pxa2xx_timer_read, + pxa2xx_timer_read, + pxa2xx_timer_read, +}; + +static CPUWriteMemoryFunc *pxa2xx_timer_writefn[] = { + pxa2xx_timer_write, + pxa2xx_timer_write, + pxa2xx_timer_write, +}; + +static void pxa2xx_timer_tick(void *opaque) +{ + pxa2xx_timer *t = (pxa2xx_timer *) opaque; + pxa2xx_timer_info *i = (pxa2xx_timer_info *) t->info; + + if (i->irq_enabled & (1 << t->num)) { + t->level = 1; + pic_set_irq_new(t->pic, t->irq, 1); + } + + if (t->num == 3) + if (i->reset3 & 1) { + i->reset3 = 0; + cpu_reset(i->cpustate); + } +} + +static pxa2xx_timer_info *pxa2xx_timer_init(target_phys_addr_t base, + void *pic, int irq, CPUState *cpustate) +{ + int i; + int iomemtype; + pxa2xx_timer_info *s; + + s = (pxa2xx_timer_info *) qemu_mallocz(sizeof(pxa2xx_timer_info)); + s->base = base; + s->irq_enabled = 0; + s->oldclock = 0; + s->clock = 0; + s->lastload = qemu_get_clock(vm_clock); + s->reset3 = 0; + s->cpustate = cpustate; + + for (i = 0; i < 4; i ++) { + s->timer[i].value = 0; + s->timer[i].irq = irq + i; + s->timer[i].pic = pic; + s->timer[i].info = s; + s->timer[i].num = i; + s->timer[i].level = 0; + s->timer[i].qtimer = qemu_new_timer(vm_clock, + pxa2xx_timer_tick, &s->timer[i]); + } + + iomemtype = cpu_register_io_memory(0, pxa2xx_timer_readfn, + pxa2xx_timer_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + return s; +} + +void pxa25x_timer_init(target_phys_addr_t base, + void *pic, int irq, CPUState *cpustate) +{ + pxa2xx_timer_info *s = pxa2xx_timer_init(base, pic, irq, cpustate); + s->freq = PXA25X_FREQ; +} + +void pxa27x_timer_init(target_phys_addr_t base, + void *pic, int irq, CPUState *cpustate) +{ + pxa2xx_timer_info *s = pxa2xx_timer_init(base, pic, irq, cpustate); + s->freq = PXA27X_FREQ; +} Index: qemu/hw/realview.c =================================================================== --- qemu.orig/hw/realview.c 2007-01-17 02:54:31.000000000 +0800 +++ qemu/hw/realview.c 2007-03-06 10:25:41.000000000 +0800 @@ -15,7 +15,7 @@ static void realview_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) + const char *initrd_filename, const char *cpu_model) { CPUState *env; void *pic; @@ -56,7 +56,7 @@ pci_bus = pci_vpb_init(pic, 48, 1); if (usb_enabled) { - usb_ohci_init(pci_bus, 3, -1); + usb_ohci_init_pci(pci_bus, 3, -1); } scsi_hba = lsi_scsi_init(pci_bus, -1); for (n = 0; n < MAX_DISKS; n++) { @@ -128,7 +128,7 @@ /* 0x6c000000 PCI mem 2. */ arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline, - initrd_filename, 0x33b); + initrd_filename, 0x33b, 0x0); } QEMUMachine realview_machine = { Index: qemu/hw/sd.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/sd.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,1494 @@ +/* + * SD Memory Card emulation as defined in the "SD Memory Card Physical + * layer specification, Version 1.10." + * + * Copyright (c) 2006 Andrzej Zaborowski + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "sd.h" + +struct sd_state_s { + enum { + sd_inactive, + sd_card_identification_mode, + sd_data_transfer_mode, + } mode; + enum { + sd_inactive_state = -1, + sd_idle_state = 0, + sd_ready_state, + sd_identification_state, + sd_standby_state, + sd_transfer_state, + sd_sendingdata_state, + sd_receivingdata_state, + sd_programming_state, + sd_disconnect_state, + } state; + uint32_t ocr; + uint8_t scr[8]; + uint8_t cid[16]; + uint8_t csd[16]; + uint16_t rca; + uint32_t card_status; + uint8_t sd_status[64]; + int wp_switch; + int *wp_groups; + uint32_t size; + int blk_len; + uint32_t erase_start; + uint32_t erase_end; + uint8_t pwd[16]; + int pwd_len; + int function_group[6]; + + int current_cmd; + int blk_written; + uint32_t data_start; + uint32_t data_offset; + uint8_t data[512]; + void (*readonly_cb)(void *, int); + void (*inserted_cb)(void *, int); + void *opaque; + BlockDriverState *bdrv; +}; + +static void sd_set_status(struct sd_state_s *sd) { + switch (sd->state) { + case sd_inactive_state: + sd->mode = sd_inactive; + break; + + case sd_idle_state: + case sd_ready_state: + case sd_identification_state: + sd->mode = sd_card_identification_mode; + break; + + case sd_standby_state: + case sd_transfer_state: + case sd_sendingdata_state: + case sd_receivingdata_state: + case sd_programming_state: + case sd_disconnect_state: + sd->mode = sd_data_transfer_mode; + break; + } + + sd->card_status &= ~CURRENT_STATE; + sd->card_status |= sd->state << 9; +} + +const sd_cmd_type_t sd_cmd_type[64] = { + sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac, + sd_none, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac, + sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, + sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none, + sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none, + sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none, + sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac, + sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, +}; + +const sd_cmd_type_t sd_acmd_type[64] = { + sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none, + sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none, + sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_ac, + sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, + sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, + sd_none, sd_bcr, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_none, + sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none, sd_none, sd_none, + sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, +}; + +static const int sd_cmd_class[64] = { + 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, + 5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7, + 7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8, +}; + +static const sd_rsp_type_t sd_cmd_response[64] = { + sd_nore, sd_nore, sd_r2, sd_r6, sd_nore, sd_nore, sd_r1, sd_r1b, + sd_nore, sd_r2, sd_r2, sd_nore, sd_r1b, sd_r1, sd_nore, sd_nore, + sd_r1, sd_r1, sd_r1, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, + sd_r1, sd_r1, sd_r1, sd_r1, sd_r1b, sd_r1b, sd_r1, sd_nore, + sd_r1, sd_r1, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1b, sd_nore, + sd_nore, sd_nore, sd_r1, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, + sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1, + sd_r1, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, +}; + +static const sd_rsp_type_t sd_acmd_response[64] = { + sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1, sd_nore, + sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1, sd_nore, sd_nore, + sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1, sd_nore, + sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, + sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, + sd_nore, sd_r3, sd_r1, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, + sd_nore, sd_nore, sd_nore, sd_r1, sd_nore, sd_nore, sd_nore, sd_nore, + sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, +}; + +static uint8_t sd_crc7(void *message, size_t width) { + int i, bit; + uint8_t shift_reg = 0x00; + uint8_t *msg = (uint8_t *) message; + + for (i = 0; i < width; i ++, msg ++) + for (bit = 7; bit >= 0; bit --) { + shift_reg <<= 1; + if ((shift_reg >> 7) ^ ((*msg >> bit) & 1)) + shift_reg ^= 0x89; + } + + return shift_reg; +} + +static uint16_t sd_crc16(void *message, size_t width) { + int i, bit; + uint16_t shift_reg = 0x0000; + uint16_t *msg = (uint16_t *) message; + width <<= 1; + + for (i = 0; i < width; i ++, msg ++) + for (bit = 15; bit >= 0; bit --) { + shift_reg <<= 1; + if ((shift_reg >> 15) ^ ((*msg >> bit) & 1)) + shift_reg ^= 0x1011; + } + + return shift_reg; +} + +static void sd_set_ocr(struct sd_state_s *sd) { + sd->ocr = 0x80fffff0; +} + +static void sd_set_scr(struct sd_state_s *sd) { + sd->scr[0] = 0x00; /* SCR Structure */ + sd->scr[1] = 0x2f; /* SD Security Support */ + sd->scr[2] = 0x00; + sd->scr[3] = 0x00; + sd->scr[4] = 0x00; + sd->scr[5] = 0x00; + sd->scr[6] = 0x00; + sd->scr[7] = 0x00; +} + +#define MID 0xaa +#define OID "XY" +#define PNM "QEMU!" +#define PRV 0x01 +#define MDT_YR 2006 +#define MDT_MON 2 + +static void sd_set_cid(struct sd_state_s *sd) { + sd->cid[0] = MID; /* Fake card manufacturer ID (MID) */ + sd->cid[1] = OID[0]; /* OEM/Application ID (OID) */ + sd->cid[2] = OID[1]; + sd->cid[3] = PNM[0]; /* Fake product name (PNM) */ + sd->cid[4] = PNM[1]; + sd->cid[5] = PNM[2]; + sd->cid[6] = PNM[3]; + sd->cid[7] = PNM[4]; + sd->cid[8] = PRV; /* Fake product revision (PRV) */ + sd->cid[9] = 0xde; /* Fake serial number (PSN) */ + sd->cid[10] = 0xad; + sd->cid[11] = 0xbe; + sd->cid[12] = 0xef; + sd->cid[13] = 0x00 | /* Manufacture date (MDT) */ + ((MDT_YR - 2000) / 10); + sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON; + sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1; +} + +#define HWBLOCK_SHIFT 9 /* 512 bytes */ +#define SECTOR_SHIFT 5 /* 16 kilobytes */ +#define WPGROUP_SHIFT 7 /* 2 megs */ +#define CMULT_SHIFT 9 /* 512 times HWBLOCK_SIZE */ +#define BLOCK_SIZE (1 << (HWBLOCK_SHIFT)) +#define SECTOR_SIZE (1 << (HWBLOCK_SHIFT + SECTOR_SHIFT)) +#define WPGROUP_SIZE (1 << (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + +static const uint8_t sd_csd_rw_mask[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe, +}; + +static void sd_set_csd(struct sd_state_s *sd, uint32_t size) { + uint32_t csize = (size >> (CMULT_SHIFT + HWBLOCK_SHIFT)) - 1; + uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1; + uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1; + + sd->csd[0] = 0x00; /* CSD structure */ + sd->csd[1] = 0x26; /* Data read access-time-1 */ + sd->csd[2] = 0x00; /* Data read access-time-2 */ + sd->csd[3] = 0x5a; /* Max. data transfer rate */ + sd->csd[4] = 0x5f; /* Card Command Classes */ + sd->csd[5] = 0x50 | /* Max. read data block length */ + HWBLOCK_SHIFT; + sd->csd[6] = 0xe0 | /* Partial block for read allowed */ + ((csize >> 10) & 0x03); + sd->csd[7] = 0x00 | /* Device size */ + ((csize >> 2) & 0xff); + sd->csd[8] = 0x3f | /* Max. read current */ + ((csize << 6) & 0xc0); + sd->csd[9] = 0xfc | /* Max. write current */ + ((CMULT_SHIFT - 2) >> 1); + sd->csd[10] = 0x40 | /* Erase sector size */ + (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1); + sd->csd[11] = 0x00 | /* Write protect group size */ + ((sectsize << 7) & 0x80) | wpsize; + sd->csd[12] = 0x90 | /* Write speed factor */ + (HWBLOCK_SHIFT >> 2); + sd->csd[13] = 0x20 | /* Max. write data block length */ + ((HWBLOCK_SHIFT << 6) & 0xc0); + sd->csd[14] = 0x00; /* File format group */ + sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; +} + +static void sd_set_rca(struct sd_state_s *sd) { + sd->rca += 0x4567; +} + +#define CARD_STATUS_A 0x02004100 +#define CARD_STATUS_B 0x00c01e00 +#define CARD_STATUS_C 0xfd39a028 + +static void sd_set_cardstatus(struct sd_state_s *sd) { + sd->card_status = 0x00000100; +} + +static void sd_set_sdstatus(struct sd_state_s *sd) { + memset(sd->sd_status, 0, 64); +} + +static int sd_req_crc_validate(struct sd_request_s *req) { + uint8_t buffer[5]; + buffer[0] = 0x40 | req->cmd; + buffer[1] = (req->arg >> 24) & 0xff; + buffer[2] = (req->arg >> 16) & 0xff; + buffer[3] = (req->arg >> 8) & 0xff; + buffer[4] = (req->arg >> 0) & 0xff; + return 0; + return sd_crc7(buffer, 5) != req->crc; /* TODO */ +} + +void sd_response_r1_make(struct sd_state_s *sd, + union sd_response_u *response, uint32_t last_status) { + int buffer[5]; + uint32_t mask = CARD_STATUS_B ^ ILLEGAL_COMMAND; + + response->r1.cmd = sd->current_cmd; + response->r1.status = (sd->card_status & ~mask) | (last_status & mask); + sd->card_status &= ~CARD_STATUS_C | APP_CMD; + + buffer[0] = 0x00 | response->r1.cmd; + buffer[1] = (response->r1.status >> 24) & 0xff; + buffer[2] = (response->r1.status >> 16) & 0xff; + buffer[3] = (response->r1.status >> 8) & 0xff; + buffer[4] = (response->r1.status >> 0) & 0xff; + response->r1.crc = sd_crc7(buffer, 5); +} + +void sd_response_r2_make(struct sd_state_s *sd, + union sd_response_u *response) { + response->r2.reg[7] |= 0x01; +} + +void sd_response_r3_make(struct sd_state_s *sd, + union sd_response_u *response) { + response->r3.ocr_reg = sd->ocr; +} + +void sd_response_r6_make(struct sd_state_s *sd, + union sd_response_u *response) { + int buffer[5]; + + response->r6.cmd = sd->current_cmd; + response->r6.arg = sd->rca; + response->r6.status = + ((sd->card_status >> 8) & 0xc000) | + ((sd->card_status >> 6) & 0x2000) | + (sd->card_status & 0x1fff); + + buffer[0] = 0x00 | response->r6.cmd; + buffer[1] = (response->r6.arg >> 8) & 0xff; + buffer[2] = (response->r6.arg >> 0) & 0xff; + buffer[3] = (response->r6.status >> 8) & 0xff; + buffer[4] = (response->r6.status >> 0) & 0xff; + response->r6.crc = sd_crc7(buffer, 5); +} + +static void sd_reset(struct sd_state_s *sd, BlockDriverState *bdrv) { + uint32_t size; + uint64_t sect; + + bdrv_get_geometry(bdrv, §); + sect <<= 9; + + if (sect > 0x40000000) + size = 0x40000000; /* 1 gig */ + else + size = sect + 1; + + sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1; + + sd->state = sd_idle_state; + sd->rca = 0x0000; + sd_set_ocr(sd); + sd_set_scr(sd); + sd_set_cid(sd); + sd_set_csd(sd, size); + sd_set_cardstatus(sd); + sd_set_sdstatus(sd); + + sd->bdrv = bdrv; + + sd->wp_switch = bdrv_is_read_only(bdrv); + sd->wp_groups = (int *) qemu_mallocz(sizeof(int) * sect); + memset(sd->wp_groups, 0, sizeof(int) * sect); + memset(sd->function_group, 0, sizeof(int) * 6); + sd->erase_start = 0; + sd->erase_end = 0; + sd->size = size; + sd->blk_len = 0x200; + sd->pwd_len = 0; +} + +static void sd_cardchange(void *opaque) { + struct sd_state_s *sd = opaque; + if (sd->inserted_cb) + sd->inserted_cb(sd->opaque, bdrv_is_inserted(sd->bdrv)); + if (bdrv_is_inserted(sd->bdrv)) { + sd_reset(sd, sd->bdrv); + if (sd->readonly_cb) + sd->readonly_cb(sd->opaque, sd->wp_switch); + } +} + +struct sd_state_s *sd_init() { + struct sd_state_s *sd; + BlockDriverState *bs = bdrv_new("sd"); + + if (sd_filename) { + if (bdrv_open(bs, sd_filename, + snapshot ? BDRV_O_SNAPSHOT : 0) < 0) + fprintf(stderr, "%s: Couldn't open %s\n", + __FUNCTION__, sd_filename); + else + qemu_key_check(bs, sd_filename); + } + + sd = (struct sd_state_s *) qemu_mallocz(sizeof(struct sd_state_s)); + sd_reset(sd, bs); + return sd; +} + +void sd_set_cb(struct sd_state_s *sd, void *opaque, + void (*readonly_cb)(void *, int), + void (*inserted_cb)(void *, int)) { + sd->opaque = opaque; + sd->readonly_cb = readonly_cb; + sd->inserted_cb = inserted_cb; + if (sd->readonly_cb) + sd->readonly_cb(sd->opaque, bdrv_is_read_only(sd->bdrv)); + if (sd->inserted_cb) + sd->inserted_cb(sd->opaque, bdrv_is_inserted(sd->bdrv)); + bdrv_set_change_cb(sd->bdrv, sd_cardchange, sd); +} + +static void sd_erase(struct sd_state_s *sd) { + int i, start, end; + if (!sd->erase_start || !sd->erase_end) { + sd->card_status |= ERASE_SEQ_ERROR; + return; + } + + start = sd->erase_start >> + (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); + end = sd->erase_end >> + (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); + sd->erase_start = 0; + sd->erase_end = 0; + sd->csd[14] |= 0x40; + + for (i = start; i <= end; i ++) + if (sd->wp_groups[i]) + sd->card_status |= WP_ERASE_SKIP; +} + +static uint32_t sd_wpbits(struct sd_state_s *sd, uint32_t addr) { + uint32_t i, wpnum; + uint32_t ret = 0; + + wpnum = addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); + + for (i = 0; i < 32; i ++, wpnum ++, addr += WPGROUP_SIZE) + if (addr < sd->size && sd->wp_groups[wpnum]) + ret |= (1 << i); + + return ret; +} + +static void sd_function_switch(struct sd_state_s *sd, uint32_t arg) { + int i, mode, new_func, crc; + mode = !!(arg & 0x80000000); + + sd->data[0] = 0x00; /* Maximum current consumption */ + sd->data[1] = 0x01; + sd->data[2] = 0x80; /* Supported group 6 functions */ + sd->data[3] = 0x01; + sd->data[4] = 0x80; /* Supported group 5 functions */ + sd->data[5] = 0x01; + sd->data[6] = 0x80; /* Supported group 4 functions */ + sd->data[7] = 0x01; + sd->data[8] = 0x80; /* Supported group 3 functions */ + sd->data[9] = 0x01; + sd->data[10] = 0x80; /* Supported group 2 functions */ + sd->data[11] = 0x43; + sd->data[12] = 0x80; /* Supported group 1 functions */ + sd->data[13] = 0x03; + for (i = 0; i < 6; i ++) { + new_func = (arg >> (i * 4)) & 0x0f; + if (mode && new_func != 0x0f) + sd->function_group[i] = new_func; + sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4); + } + memset(&sd->data[17], 0, 47); + crc = sd_crc16(sd->data, 64); + sd->data[65] = crc >> 8; + sd->data[66] = crc & 0xff; +} + +static inline int sd_wp_addr(struct sd_state_s *sd, uint32_t addr) { + return sd->wp_groups[addr >> + (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)]; +} + +static void sd_lock_command(struct sd_state_s *sd) { + int erase, lock, clr_pwd, set_pwd, pwd_len; + erase = !!(sd->data[0] & 0x08); + lock = sd->data[0] & 0x04; + clr_pwd = sd->data[0] & 0x02; + set_pwd = sd->data[0] & 0x01; + + if (sd->blk_len > 1) + pwd_len = sd->data[1]; + else + pwd_len = 0; + + if (erase) { + if (!(sd->card_status & CARD_IS_LOCKED) || sd->blk_len > 1 || + set_pwd || clr_pwd || lock || sd->wp_switch || + (sd->csd[14] & 0x20)) { + sd->card_status |= LOCK_UNLOCK_FAILED; + return; + } + memset(sd->wp_groups, 0, sizeof(int) * (sd->size >> + (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT))); + sd->csd[14] &= ~0x10; + sd->card_status &= ~CARD_IS_LOCKED; + sd->pwd_len = 0; + /* Erasing the entire card here! */ + printf("SD: Card force-erased by CMD42\n"); + return; + } + + if (sd->blk_len < 2 + pwd_len || + pwd_len <= sd->pwd_len || + pwd_len > sd->pwd_len + 16) { + sd->card_status |= LOCK_UNLOCK_FAILED; + return; + } + + if (sd->pwd_len && memcmp(sd->pwd, sd->data + 2, sd->pwd_len)) { + sd->card_status |= LOCK_UNLOCK_FAILED; + return; + } + + pwd_len -= sd->pwd_len; + if ((pwd_len && !set_pwd) || + (clr_pwd && (set_pwd || lock)) || + (lock && !sd->pwd_len && !set_pwd) || + (!set_pwd && !clr_pwd && + (((sd->card_status & CARD_IS_LOCKED) && lock) || + (!(sd->card_status & CARD_IS_LOCKED) && !lock)))) { + sd->card_status |= LOCK_UNLOCK_FAILED; + return; + } + + if (set_pwd) { + memcpy(sd->pwd, sd->data + 2 + sd->pwd_len, pwd_len); + sd->pwd_len = pwd_len; + } + + if (clr_pwd) { + sd->pwd_len = 0; + } + + if (lock) + sd->card_status |= CARD_IS_LOCKED; + else + sd->card_status &= ~CARD_IS_LOCKED; +} + +static union sd_response_u sd_normal_command(struct sd_state_s *sd, + struct sd_request_s req) { + uint32_t rca = 0x0000; + union sd_response_u response; + + if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc) + rca = req.arg >> 16; + + switch (req.cmd) { + /* Basic commands (Class 0 and Class 1) */ + case 0: /* CMD0: GO_IDLE_STATE */ + switch (sd->state) { + case sd_inactive_state: + return response; + + default: + sd->state = sd_idle_state; + sd_reset(sd, sd->bdrv); + return response; + } + break; + + case 2: /* CMD2: ALL_SEND_CID */ + switch (sd->state) { + case sd_ready_state: + sd->state = sd_identification_state; + memcpy(response.r2.reg, sd->cid, 16); + return response; + + default: + break; + } + break; + + case 3: /* CMD3: SEND_RELATIVE_ADDR */ + switch (sd->state) { + case sd_identification_state: + case sd_standby_state: + sd->state = sd_standby_state; + sd_set_rca(sd); + return response; + + default: + break; + } + break; + + case 4: /* CMD4: SEND_DSR */ + switch (sd->state) { + case sd_standby_state: + break; + + default: + break; + } + break; + + case 6: /* CMD6: SWITCH_FUNCTION */ + switch (sd->mode) { + case sd_data_transfer_mode: + sd_function_switch(sd, req.arg); + sd->state = sd_sendingdata_state; + sd->data_start = 0; + sd->data_offset = 0; + return response; + + default: + break; + } + break; + + case 7: /* CMD7: SELECT/DESELECT_CARD */ + switch (sd->state) { + case sd_standby_state: + if (sd->rca != rca) + return response; + + sd->state = sd_transfer_state; + return response; + + case sd_transfer_state: + case sd_sendingdata_state: + if (sd->rca == rca) + break; + + sd->state = sd_standby_state; + return response; + + case sd_disconnect_state: + if (sd->rca != rca) + return response; + + sd->state = sd_programming_state; + return response; + + case sd_programming_state: + if (sd->rca == rca) + break; + + sd->state = sd_disconnect_state; + return response; + + default: + break; + } + break; + + case 9: /* CMD9: SEND_CSD */ + switch (sd->state) { + case sd_standby_state: + if (sd->rca != rca) + return response; + + memcpy(response.r2.reg, sd->csd, sizeof(sd->csd)); + return response; + + default: + break; + } + break; + + case 10: /* CMD10: SEND_CID */ + switch (sd->state) { + case sd_standby_state: + if (sd->rca != rca) + return response; + + memcpy(response.r2.reg, sd->cid, sizeof(sd->cid)); + return response; + + default: + break; + } + break; + + case 11: /* CMD11: READ_DAT_UNTIL_STOP */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_sendingdata_state; + sd->data_start = req.arg; + sd->data_offset = 0; + + if (sd->data_start + sd->blk_len > sd->size) + sd->card_status |= ADDRESS_ERROR; + return response; + + default: + break; + } + break; + + case 12: /* CMD12: STOP_TRANSMISSION */ + switch (sd->state) { + case sd_sendingdata_state: + sd->state = sd_transfer_state; + return response; + + case sd_receivingdata_state: + sd->state = sd_programming_state; + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return response; + + default: + break; + } + break; + + case 13: /* CMD13: SEND_STATUS */ + switch (sd->mode) { + case sd_data_transfer_mode: + if (sd->rca != rca) + return response; + + return response; + + default: + break; + } + break; + + case 15: /* CMD15: GO_INACTIVE_STATE */ + switch (sd->mode) { + case sd_data_transfer_mode: + if (sd->rca != rca) + return response; + + sd->state = sd_inactive_state; + return response; + + default: + break; + } + break; + + /* Block read commands (Classs 2) */ + case 16: /* CMD16: SET_BLOCKLEN */ + switch (sd->state) { + case sd_transfer_state: + if (req.arg > (1 << HWBLOCK_SHIFT)) + sd->card_status |= BLOCK_LEN_ERROR; + else + sd->blk_len = req.arg; + + return response; + + default: + break; + } + break; + + case 17: /* CMD17: READ_SINGLE_BLOCK */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_sendingdata_state; + sd->data_start = req.arg; + sd->data_offset = 0; + + if (sd->data_start + sd->blk_len > sd->size) + sd->card_status |= ADDRESS_ERROR; + return response; + + default: + break; + } + break; + + case 18: /* CMD18: READ_MULTIPLE_BLOCK */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_sendingdata_state; + sd->data_start = req.arg; + sd->data_offset = 0; + + if (sd->data_start + sd->blk_len > sd->size) + sd->card_status |= ADDRESS_ERROR; + return response; + + default: + break; + } + break; + + /* Block write commands (Class 4) */ + case 24: /* CMD24: WRITE_SINGLE_BLOCK */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_receivingdata_state; + sd->data_start = req.arg; + sd->data_offset = 0; + sd->blk_written = 0; + + if (sd->data_start + sd->blk_len > sd->size) + sd->card_status |= ADDRESS_ERROR; + if (sd_wp_addr(sd, sd->data_start)) + sd->card_status |= WP_VIOLATION; + if (sd->csd[14] & 0x30) + sd->card_status |= WP_VIOLATION; + return response; + + default: + break; + } + break; + + case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_receivingdata_state; + sd->data_start = req.arg; + sd->data_offset = 0; + sd->blk_written = 0; + + if (sd->data_start + sd->blk_len > sd->size) + sd->card_status |= ADDRESS_ERROR; + if (sd_wp_addr(sd, sd->data_start)) + sd->card_status |= WP_VIOLATION; + if (sd->csd[14] & 0x30) + sd->card_status |= WP_VIOLATION; + return response; + + default: + break; + } + break; + + case 26: /* CMD26: PROGRAM_CID */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_receivingdata_state; + sd->data_start = 0; + sd->data_offset = 0; + return response; + + default: + break; + } + break; + + case 27: /* CMD27: PROGRAM_CSD */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_receivingdata_state; + sd->data_start = 0; + sd->data_offset = 0; + return response; + + default: + break; + } + break; + + /* Write protection (Class 6) */ + case 28: /* CMD28: SET_WRITE_PROT */ + switch (sd->state) { + case sd_transfer_state: + if (req.arg >= sd->size) { + sd->card_status = ADDRESS_ERROR; + return response; + } + + sd->state = sd_programming_state; + sd->wp_groups[req.arg >> (HWBLOCK_SHIFT + + SECTOR_SHIFT + WPGROUP_SHIFT)] = 1; + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return response; + + default: + break; + } + break; + + case 29: /* CMD29: CLR_WRITE_PROT */ + switch (sd->state) { + case sd_transfer_state: + if (req.arg >= sd->size) { + sd->card_status = ADDRESS_ERROR; + return response; + } + + sd->state = sd_programming_state; + sd->wp_groups[req.arg >> (HWBLOCK_SHIFT + + SECTOR_SHIFT + WPGROUP_SHIFT)] = 0; + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return response; + + default: + break; + } + break; + + case 30: /* CMD30: SEND_WRITE_PROT */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_sendingdata_state; + *(uint32_t *) sd->data = sd_wpbits(sd, req.arg); + sd->data_start = req.arg; + sd->data_offset = 0; + return response; + + default: + break; + } + break; + + /* Erase commands (Class 5) */ + case 32: /* CMD32: ERASE_WR_BLK_START */ + switch (sd->state) { + case sd_transfer_state: + sd->erase_start = req.arg; + return response; + + default: + break; + } + break; + + case 33: /* CMD33: ERASE_WR_BLK_END */ + switch (sd->state) { + case sd_transfer_state: + sd->erase_end = req.arg; + return response; + + default: + break; + } + break; + + case 38: /* CMD38: ERASE */ + switch (sd->state) { + case sd_transfer_state: + if (sd->csd[14] & 0x30) { + sd->card_status |= WP_VIOLATION; + return response; + } + + sd->state = sd_programming_state; + sd_erase(sd); + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return response; + + default: + break; + } + break; + + /* Lock card commands (Class 7) */ + case 42: /* CMD42: LOCK_UNLOCK */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_receivingdata_state; + sd->data_start = 0; + sd->data_offset = 0; + return response; + + default: + break; + } + break; + + /* Application specific commands (Class 8) */ + case 55: /* CMD55: APP_CMD */ + if (sd->rca != rca) + return response; + + sd->card_status |= APP_CMD; + return response; + + case 56: /* CMD56: GEN_CMD */ + printf("SD: GEN_CMD 0x%08x\n", req.arg); + + switch (sd->state) { + case sd_transfer_state: + sd->data_offset = 0; + if (req.arg & 1) + sd->state = sd_sendingdata_state; + else + sd->state = sd_receivingdata_state; + return response; + + default: + break; + } + break; + + default: + sd->card_status |= ILLEGAL_COMMAND; + + printf("SD: Unknown CMD%i\n", req.cmd); + return response; + } + + sd->card_status |= ILLEGAL_COMMAND; + printf("SD: CMD%i in a wrong state\n", req.cmd); + return response; +} + +static union sd_response_u sd_app_command(struct sd_state_s *sd, + struct sd_request_s req) { + uint32_t rca; + union sd_response_u response; + + if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc) + rca = req.arg >> 16; + + switch (req.cmd) { + case 6: /* ACMD6: SET_BUS_WIDTH */ + switch (sd->state) { + case sd_transfer_state: + sd->sd_status[0] &= 0x3f; + sd->sd_status[0] |= (req.arg & 0x03) << 6; + return response; + + default: + break; + } + break; + + case 13: /* ACMD13: SD_STATUS */ + switch (sd->state) { + case sd_transfer_state: + sd->data_start = 0; + sd->data_offset = 0; + return response; + + default: + break; + } + break; + + case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ + switch (sd->state) { + case sd_transfer_state: + *(uint32_t *) sd->data = sd->blk_written; + + sd->data_start = 0; + sd->data_offset = 0; + return response; + + default: + break; + } + break; + + case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */ + switch (sd->state) { + case sd_transfer_state: + return response; + + default: + break; + } + break; + + case 41: /* ACMD41: SD_APP_OP_COND */ + switch (sd->state) { + case sd_idle_state: + /* We accept any voltage. 10000 V is nothing. */ + if (req.arg) + sd->state = sd_ready_state; + + return response; + + default: + break; + } + break; + + case 42: /* ACMD42: SET_CLR_CARD_DETECT */ + switch (sd->state) { + case sd_transfer_state: + /* Bringing in the 50KOhm pull-up resistor... Done. */ + return response; + + default: + break; + } + break; + + case 51: /* ACMD51: SEND_SCR */ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_sendingdata_state; + sd->data_start = 0; + sd->data_offset = 0; + return response; + + default: + break; + } + break; + + default: + /* Fall back to standard commands. */ + sd->card_status &= ~APP_CMD; + return sd_normal_command(sd, req); + } + + printf("SD: ACMD%i in a wrong state\n", req.cmd); + return response; +} + +union sd_response_u sd_write_cmdline(struct sd_state_s *sd, + struct sd_request_s req, int *rsplen) { + uint32_t last_status = sd->card_status; + union sd_response_u response; + sd_rsp_type_t rtype; + + if (!bdrv_is_inserted(sd->bdrv)) { + *rsplen = 0; + return response; + } + + if (sd_req_crc_validate(&req)) { + sd->card_status &= ~COM_CRC_ERROR; + *rsplen = 0; + return response; + } + + sd->card_status &= ~CARD_STATUS_B; + sd_set_status(sd); + + if (last_status & CARD_IS_LOCKED) + if (((last_status & APP_CMD) && + (sd_acmd_type[req.cmd] == 0 || + sd_acmd_type[req.cmd] == 7 || + req.cmd == 41)) || + (!(last_status & APP_CMD) && + (sd_cmd_type[req.cmd] == 0 || + sd_cmd_type[req.cmd] == 7 || + req.cmd == 16 || req.cmd == 55))) { + sd->card_status |= ILLEGAL_COMMAND; + printf("SD: Card is locked\n"); + return response; + } + + if (last_status & APP_CMD) + response = sd_app_command(sd, req); + else + response = sd_normal_command(sd, req); + + if (last_status & APP_CMD) { + if (sd->card_status & APP_CMD) { + rtype = sd_acmd_response[req.cmd]; + sd->card_status &= ~APP_CMD; + } else + rtype = sd_cmd_response[req.cmd]; + } else + rtype = sd_cmd_response[req.cmd]; + + sd->current_cmd = req.cmd; + + switch (rtype) { + case sd_r1: + case sd_r1b: + sd_response_r1_make(sd, &response, last_status); + *rsplen = 48; + break; + + case sd_r2: + sd_response_r2_make(sd, &response); + *rsplen = 128; + break; + + case sd_r3: + sd_response_r3_make(sd, &response); + *rsplen = 48; + break; + + case sd_r6: + sd_response_r6_make(sd, &response); + *rsplen = 48; + break; + + case sd_nore: + default: + *rsplen = 0; + break; + } + + if (sd->card_status & ILLEGAL_COMMAND) + *rsplen = 0; + + return response; +} + +/* No real need for 64 bit addresses here */ +static void sd_blk_read(BlockDriverState *bdrv, + void *data, uint32_t addr, uint32_t len) { + uint8_t buf[512]; + uint32_t end = addr + len; + + if (!bdrv || bdrv_read(bdrv, addr >> 9, buf, 1) == -1) { + printf("sd_blk_read: read error on host side\n"); + return; + } + + if (end > (addr & ~511) + 512) { + memcpy(data, buf + (addr & 511), 512 - (addr & 511)); + + if (bdrv_read(bdrv, end >> 9, buf, 1) == -1) { + printf("sd_blk_read: read error on host side\n"); + return; + } + memcpy(data + 512 - (addr & 511), buf, end & 511); + } else + memcpy(data, buf + (addr & 511), len); +} + +static void sd_blk_write(BlockDriverState *bdrv, + void *data, uint32_t addr, uint32_t len) { + uint8_t buf[512]; + uint32_t end = addr + len; + + if ((addr & 511) || len < 512) + if (!bdrv || bdrv_read(bdrv, addr >> 9, buf, 1) == -1) { + printf("sd_blk_write: read error on host side\n"); + return; + } + + if (end > (addr & ~511) + 512) { + memcpy(buf + (addr & 511), data, 512 - (addr & 511)); + if (bdrv_write(bdrv, addr >> 9, buf, 1) == -1) { + printf("sd_blk_write: write error on host side\n"); + return; + } + + if (bdrv_read(bdrv, end >> 9, buf, 1) == -1) { + printf("sd_blk_write: read error on host side\n"); + return; + } + memcpy(buf, data + 512 - (addr & 511), end & 511); + if (bdrv_write(bdrv, end >> 9, buf, 1) == -1) + printf("sd_blk_write: write error on host side\n"); + } else { + memcpy(buf + (addr & 511), data, len); + if (!bdrv || bdrv_write(bdrv, addr >> 9, buf, 1) == -1) + printf("sd_blk_write: write error on host side\n"); + } +} + +#define BLK_READ_BLOCK(a, len) sd_blk_read(sd->bdrv, sd->data, a, len) +#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd->bdrv, sd->data, a, len) +#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len) +#define APP_WRITE_BLOCK(a, len) + +void sd_write_datline(struct sd_state_s *sd, uint8_t value) { + int i; + + if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv)) + return; + + if (sd->state != sd_receivingdata_state) { + printf("sd_write_datline: not in Receiving-Data state\n"); + return; + } + + if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) + return; + + switch (sd->current_cmd) { + case 24: /* CMD24: WRITE_SINGLE_BLOCK */ + sd->data[sd->data_offset ++] = value; + if (sd->data_offset >= sd->blk_len) { + /* TODO: Check CRC before committing */ + sd->state = sd_programming_state; + BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); + sd->blk_written ++; + sd->csd[14] |= 0x40; + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + } + break; + + case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ + sd->data[sd->data_offset ++] = value; + if (sd->data_offset >= sd->blk_len) { + /* TODO: Check CRC before committing */ + sd->state = sd_programming_state; + BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); + sd->blk_written ++; + sd->data_start += sd->blk_len; + sd->data_offset = 0; + if (sd->data_start + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + break; + } + if (sd_wp_addr(sd, sd->data_start)) { + sd->card_status |= WP_VIOLATION; + break; + } + sd->csd[14] |= 0x40; + + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_receivingdata_state; + } + break; + + case 26: /* CMD26: PROGRAM_CID */ + sd->data[sd->data_offset ++] = value; + if (sd->data_offset >= sizeof(sd->cid)) { + /* TODO: Check CRC before committing */ + sd->state = sd_programming_state; + for (i = 0; i < sizeof(sd->cid); i ++) + if ((sd->cid[i] | 0x00) != sd->data[i]) + sd->card_status |= CID_CSD_OVERWRITE; + + if (!(sd->card_status & CID_CSD_OVERWRITE)) + for (i = 0; i < sizeof(sd->cid); i ++) { + sd->cid[i] |= 0x00; + sd->cid[i] &= sd->data[i]; + } + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + } + break; + + case 27: /* CMD27: PROGRAM_CSD */ + sd->data[sd->data_offset ++] = value; + if (sd->data_offset >= sizeof(sd->csd)) { + /* TODO: Check CRC before committing */ + sd->state = sd_programming_state; + for (i = 0; i < sizeof(sd->csd); i ++) + if ((sd->csd[i] | sd_csd_rw_mask[i]) != + (sd->data[i] | sd_csd_rw_mask[i])) + sd->card_status |= CID_CSD_OVERWRITE; + + /* Copy flag (OTP) & Permanent write protect */ + if (sd->csd[14] & ~sd->data[14] & 0x60) + sd->card_status |= CID_CSD_OVERWRITE; + + if (!(sd->card_status & CID_CSD_OVERWRITE)) + for (i = 0; i < sizeof(sd->csd); i ++) { + sd->csd[i] |= sd_csd_rw_mask[i]; + sd->csd[i] &= sd->data[i]; + } + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + } + break; + + case 42: /* CMD42: LOCK_UNLOCK */ + sd->data[sd->data_offset ++] = value; + if (sd->data_offset >= sd->blk_len) { + /* TODO: Check CRC before committing */ + sd->state = sd_programming_state; + sd_lock_command(sd); + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + } + break; + + case 56: /* CMD56: GEN_CMD */ + sd->data[sd->data_offset ++] = value; + if (sd->data_offset >= sd->blk_len) { + APP_WRITE_BLOCK(sd->data_start, sd->data_offset); + sd->state = sd_transfer_state; + } + break; + + default: + printf("sd_write_datline: unknown command\n"); + break; + } +} + +uint8_t sd_read_datline(struct sd_state_s *sd) { + /* TODO: Append CRCs */ + uint8_t ret; + + if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv)) + return 0x00; + + if (sd->state != sd_sendingdata_state) { + printf("sd_read_datline: not in Sending-Data state\n"); + return 0x00; + } + + if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) + return 0x00; + + switch (sd->current_cmd) { + case 6: /* CMD6: SWITCH_FUNCTION */ + ret = sd->data[sd->data_offset ++]; + + if (sd->data_offset >= 64) + sd->state = sd_transfer_state; + break; + + case 11: /* CMD11: READ_DAT_UNTIL_STOP */ + if (sd->data_offset == 0) + BLK_READ_BLOCK(sd->data_start, sd->blk_len); + ret = sd->data[sd->data_offset ++]; + + if (sd->data_offset >= sd->blk_len) { + sd->data_start += sd->blk_len; + sd->data_offset = 0; + if (sd->data_start + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + break; + } + } + break; + + case 13: /* ACMD13: SD_STATUS */ + ret = sd->sd_status[sd->data_offset ++]; + + if (sd->data_offset >= sizeof(sd->sd_status)) + sd->state = sd_transfer_state; + break; + + case 17: /* CMD17: READ_SINGLE_BLOCK */ + if (sd->data_offset == 0) + BLK_READ_BLOCK(sd->data_start, sd->blk_len); + ret = sd->data[sd->data_offset ++]; + + if (sd->data_offset >= sd->blk_len) + sd->state = sd_transfer_state; + break; + + case 18: /* CMD18: READ_MULTIPLE_BLOCK */ + if (sd->data_offset == 0) + BLK_READ_BLOCK(sd->data_start, sd->blk_len); + ret = sd->data[sd->data_offset ++]; + + if (sd->data_offset >= sd->blk_len) { + sd->data_start += sd->blk_len; + sd->data_offset = 0; + if (sd->data_start + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + break; + } + } + break; + + case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ + ret = sd->data[sd->data_offset ++]; + + if (sd->data_offset >= 4) + sd->state = sd_transfer_state; + break; + + case 30: /* CMD30: SEND_WRITE_PROT */ + ret = sd->data[sd->data_offset ++]; + + if (sd->data_offset >= 4) + sd->state = sd_transfer_state; + break; + + case 51: /* ACMD51: SEND_SCR */ + ret = sd->scr[sd->data_offset ++]; + + if (sd->data_offset >= sizeof(sd->scr)) + sd->state = sd_transfer_state; + break; + + case 56: /* CMD56: GEN_CMD */ + if (sd->data_offset == 0) + APP_READ_BLOCK(sd->data_start, sd->blk_len); + ret = sd->data[sd->data_offset ++]; + + if (sd->data_offset >= sd->blk_len) + sd->state = sd_transfer_state; + break; + + default: + printf("sd_read_datline: unknown command\n"); + return 0x00; + } + + return ret; +} Index: qemu/hw/sd.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/sd.h 2007-02-12 15:26:44.000000000 +0800 @@ -0,0 +1,129 @@ +/* + * SD Memory Card emulation. Mostly correct for MMC too. + * + * Copyright (c) 2006 Andrzej Zaborowski + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __hw_sd_h +#define __hw_sd_h 1 + +#include + +#define OUT_OF_RANGE (1 << 31) +#define ADDRESS_ERROR (1 << 30) +#define BLOCK_LEN_ERROR (1 << 29) +#define ERASE_SEQ_ERROR (1 << 28) +#define ERASE_PARAM (1 << 27) +#define WP_VIOLATION (1 << 26) +#define CARD_IS_LOCKED (1 << 25) +#define LOCK_UNLOCK_FAILED (1 << 24) +#define COM_CRC_ERROR (1 << 23) +#define ILLEGAL_COMMAND (1 << 22) +#define CARD_ECC_FAILED (1 << 21) +#define CC_ERROR (1 << 20) +#define SD_ERROR (1 << 19) +#define CID_CSD_OVERWRITE (1 << 16) +#define WP_ERASE_SKIP (1 << 15) +#define CARD_ECC_DISABLED (1 << 14) +#define ERASE_RESET (1 << 13) +#define CURRENT_STATE (7 << 9) +#define READY_FOR_DATA (1 << 8) +#define APP_CMD (1 << 5) +#define AKE_SEQ_ERROR (1 << 3) + +typedef enum { + sd_none = -1, + sd_bc = 0, /* broadcast -- no response */ + sd_bcr, /* broadcast with response */ + sd_ac, /* addressed -- no data transfer */ + sd_adtc, /* addressed with data transfer */ +} sd_cmd_type_t; + +typedef enum { + sd_nore = 0, /* no response */ + sd_r1, /* normal response command */ + sd_r2, /* CID, CSD registers */ + sd_r3, /* OCR register */ + sd_r6 = 6, /* Published RCA response */ + sd_r1b = -1, +} sd_rsp_type_t; + +struct sd_request_s { + uint8_t cmd; + uint32_t arg; + uint8_t crc; +}; + +struct sd_response_none_s { +}; + +struct sd_response_r1_s { + uint8_t cmd; + uint32_t status; + uint8_t crc; +}; + +struct sd_response_r1b_s { + uint8_t cmd; + uint32_t status; + uint8_t crc; +}; + +struct sd_response_r2_s { + uint16_t reg[8]; +}; + +struct sd_response_r3_s { + uint32_t ocr_reg; +}; + +struct sd_response_r6_s { + uint8_t cmd; + uint16_t arg; + uint16_t status; + uint8_t crc; +}; + +union sd_response_u { + struct sd_response_none_s none; + struct sd_response_r1_s r1; + struct sd_response_r1b_s r1b; + struct sd_response_r2_s r2; + struct sd_response_r3_s r3; + struct sd_response_r6_s r6; +}; + +struct sd_state_s; + +struct sd_state_s *sd_init(void); +union sd_response_u sd_write_cmdline(struct sd_state_s *sd, + struct sd_request_s req, int *rsplen); +void sd_write_datline(struct sd_state_s *sd, uint8_t value); +uint8_t sd_read_datline(struct sd_state_s *sd); +void sd_set_cb(struct sd_state_s *sd, void *opaque, + void (*readonly_cb)(void *, int), + void (*inserted_cb)(void *, int)); + +#endif /* __hw_sd_h */ Index: qemu/hw/usb-msd.c =================================================================== --- qemu.orig/hw/usb-msd.c 2006-08-29 12:52:16.000000000 +0800 +++ qemu/hw/usb-msd.c 2007-02-12 15:21:33.000000000 +0800 @@ -522,6 +522,8 @@ bdrv = bdrv_new("usb"); if (bdrv_open(bdrv, filename, 0) < 0) goto fail; + if (qemu_key_check(bdrv, filename)) + goto fail; s->bs = bdrv; s->dev.speed = USB_SPEED_FULL; Index: qemu/hw/usb-net.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/usb-net.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,1341 @@ +/* + * QEMU USB Net devices + * + * Copyright (c) 2006 Thomas Sailer + * based on usb-hid.c Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "../audio/sys-queue.h" + +typedef uint32_t __le32; +#include "ndis.h" + +/*#define TRAFFIC_DEBUG*/ +/* Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only CDC Ethernet configurations. + */ +#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ +/* For hardware that can talk RNDIS and either of the above protocols, + * use this ID ... the windows INF files will know it. + */ +#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ +#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ + +#define STRING_MANUFACTURER 1 +#define STRING_PRODUCT 2 +#define STRING_ETHADDR 3 +#define STRING_DATA 4 +#define STRING_CONTROL 5 +#define STRING_RNDIS_CONTROL 6 +#define STRING_CDC 7 +#define STRING_SUBSET 8 +#define STRING_RNDIS 9 +#define STRING_SERIALNUMBER 10 + +#define DEV_CONFIG_VALUE 1 /* cdc or subset */ +#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ + +#define USB_CDC_SUBCLASS_ACM 0x02 +#define USB_CDC_SUBCLASS_ETHERNET 0x06 + +#define USB_CDC_PROTO_NONE 0 +#define USB_CDC_ACM_PROTO_VENDOR 0xff + +#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ +#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ +#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ +#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ +#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ + +#define USB_DT_CS_INTERFACE 0x24 +#define USB_DT_CS_ENDPOINT 0x25 + +#define ClassInterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) +#define ClassInterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) + +#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 +#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 +#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 +#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 +#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 + +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 + +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ + +#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ + +/* + * mostly the same descriptor as the linux gadget rndis driver + */ +static const uint8_t qemu_net_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + USB_DT_DEVICE, /* u8 bDescriptorType; Device */ + 0x00, 0x02, /* u16 bcdUSB; v2.0 */ + USB_CLASS_COMM, /* u8 bDeviceClass; */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x40, /* u8 bMaxPacketSize0 */ + RNDIS_VENDOR_NUM & 0xff, RNDIS_VENDOR_NUM >> 8, /* u16 idVendor; */ + RNDIS_PRODUCT_NUM & 0xff, RNDIS_PRODUCT_NUM >> 8, /* u16 idProduct; */ + 0x00, 0x00, /* u16 bcdDevice */ + STRING_MANUFACTURER, /* u8 iManufacturer; */ + STRING_PRODUCT, /* u8 iProduct; */ + STRING_SERIALNUMBER, /* u8 iSerialNumber; */ + 0x02 /* u8 bNumConfigurations; */ +}; + +static const uint8_t qemu_net_rndis_config_descriptor[] = { + /* Configuration Descriptor */ + 0x09, /* u8 bLength */ + USB_DT_CONFIG, /* u8 bDescriptorType */ + 0x00, 0x00, /* le16 wTotalLength */ + 0x02, /* u8 bNumInterfaces */ + DEV_RNDIS_CONFIG_VALUE, /* u8 bConfigurationValue */ + STRING_RNDIS, /* u8 iConfiguration */ + 0xc0, /* u8 bmAttributes */ + 0x32, /* u8 bMaxPower */ + /* RNDIS Control Interface */ + 0x09, /* u8 bLength */ + USB_DT_INTERFACE, /* u8 bDescriptorType */ + 0x00, /* u8 bInterfaceNumber */ + 0x00, /* u8 bAlternateSetting */ + 0x01, /* u8 bNumEndpoints */ + USB_CLASS_COMM, /* u8 bInterfaceClass */ + USB_CDC_SUBCLASS_ACM, /* u8 bInterfaceSubClass */ + USB_CDC_ACM_PROTO_VENDOR, /* u8 bInterfaceProtocol */ + STRING_RNDIS_CONTROL, /* u8 iInterface */ + /* Header Descriptor */ + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ + 0x10, 0x01, /* le16 bcdCDC */ + /* Call Management Descriptor */ + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bmCapabilities */ + 0x01, /* u8 bDataInterface */ + /* ACM Descriptor */ + 0x04, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bmCapabilities */ + /* Union Descriptor */ + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bMasterInterface0 */ + 0x01, /* u8 bSlaveInterface0 */ + /* Status Descriptor */ + 0x07, /* u8 bLength */ + USB_DT_ENDPOINT, /* u8 bDescriptorType */ + USB_DIR_IN | 1, /* u8 bEndpointAddress */ + USB_ENDPOINT_XFER_INT, /* u8 bmAttributes */ + STATUS_BYTECOUNT & 0xff, STATUS_BYTECOUNT >> 8, /* le16 wMaxPacketSize */ + 1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */ + /* RNDIS Data Interface */ + 0x09, /* u8 bLength */ + USB_DT_INTERFACE, /* u8 bDescriptorType */ + 0x01, /* u8 bInterfaceNumber */ + 0x00, /* u8 bAlternateSetting */ + 0x02, /* u8 bNumEndpoints */ + USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ + 0x00, /* u8 bInterfaceSubClass */ + 0x00, /* u8 bInterfaceProtocol */ + STRING_DATA, /* u8 iInterface */ + /* Source Endpoint */ + 0x07, /* u8 bLength */ + USB_DT_ENDPOINT, /* u8 bDescriptorType */ + USB_DIR_IN | 2, /* u8 bEndpointAddress */ + USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ + 0x40, 0x00, /* le16 wMaxPacketSize */ + 0x00, /* u8 bInterval */ + /* Sink Endpoint */ + 0x07, /* u8 bLength */ + USB_DT_ENDPOINT, /* u8 bDescriptorType */ + USB_DIR_OUT | 2, /* u8 bEndpointAddress */ + USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ + 0x40, 0x00, /* le16 wMaxPacketSize */ + 0x00 /* u8 bInterval */ +}; + +static const uint8_t qemu_net_cdc_config_descriptor[] = { + /* Configuration Descriptor */ + 0x09, /* u8 bLength */ + USB_DT_CONFIG, /* u8 bDescriptorType */ + 0x00, 0x00, /* le16 wTotalLength */ + 0x02, /* u8 bNumInterfaces */ + DEV_RNDIS_CONFIG_VALUE, /* u8 bConfigurationValue */ + STRING_RNDIS, /* u8 iConfiguration */ + 0xc0, /* u8 bmAttributes */ + 0x32, /* u8 bMaxPower */ + /* CDC Control Interface */ + 0x09, /* u8 bLength */ + USB_DT_INTERFACE, /* u8 bDescriptorType */ + 0x00, /* u8 bInterfaceNumber */ + 0x00, /* u8 bAlternateSetting */ + 0x01, /* u8 bNumEndpoints */ + USB_CLASS_COMM, /* u8 bInterfaceClass */ + USB_CDC_SUBCLASS_ETHERNET, /* u8 bInterfaceSubClass */ + USB_CDC_PROTO_NONE, /* u8 bInterfaceProtocol */ + STRING_CONTROL, /* u8 iInterface */ + /* Header Descriptor */ + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ + 0x10, 0x01, /* le16 bcdCDC */ + /* Union Descriptor */ + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bMasterInterface0 */ + 0x01, /* u8 bSlaveInterface0 */ + /* Ethernet Descriptor */ + 0x0d, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */ + STRING_ETHADDR, /* u8 iMACAddress */ + 0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */ + ETH_FRAME_LEN & 0xff, ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */ + 0x00, 0x00, /* le16 wNumberMCFilters */ + 0x00, /* u8 bNumberPowerFilters */ + /* Status Descriptor */ + 0x07, /* u8 bLength */ + USB_DT_ENDPOINT, /* u8 bDescriptorType */ + USB_DIR_IN | 1, /* u8 bEndpointAddress */ + USB_ENDPOINT_XFER_INT, /* u8 bmAttributes */ + STATUS_BYTECOUNT & 0xff, STATUS_BYTECOUNT >> 8, /* le16 wMaxPacketSize */ + 1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */ + /* CDC Data (nop) Interface */ + 0x09, /* u8 bLength */ + USB_DT_INTERFACE, /* u8 bDescriptorType */ + 0x01, /* u8 bInterfaceNumber */ + 0x00, /* u8 bAlternateSetting */ + 0x00, /* u8 bNumEndpoints */ + USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ + 0x00, /* u8 bInterfaceSubClass */ + 0x00, /* u8 bInterfaceProtocol */ + 0x00, /* u8 iInterface */ + /* CDC Data Interface */ + 0x09, /* u8 bLength */ + USB_DT_INTERFACE, /* u8 bDescriptorType */ + 0x01, /* u8 bInterfaceNumber */ + 0x01, /* u8 bAlternateSetting */ + 0x02, /* u8 bNumEndpoints */ + USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ + 0x00, /* u8 bInterfaceSubClass */ + 0x00, /* u8 bInterfaceProtocol */ + STRING_DATA, /* u8 iInterface */ + /* Source Endpoint */ + 0x07, /* u8 bLength */ + USB_DT_ENDPOINT, /* u8 bDescriptorType */ + USB_DIR_IN | 2, /* u8 bEndpointAddress */ + USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ + 0x40, 0x00, /* le16 wMaxPacketSize */ + 0x00, /* u8 bInterval */ + /* Sink Endpoint */ + 0x07, /* u8 bLength */ + USB_DT_ENDPOINT, /* u8 bDescriptorType */ + USB_DIR_OUT | 2, /* u8 bEndpointAddress */ + USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ + 0x40, 0x00, /* le16 wMaxPacketSize */ + 0x00 /* u8 bInterval */ +}; + +/* + * RNDIS Status + */ + +#define RNDIS_MAXIMUM_FRAME_SIZE 1518 +#define RNDIS_MAX_TOTAL_SIZE 1558 + +/* Remote NDIS Versions */ +#define RNDIS_MAJOR_VERSION 1 +#define RNDIS_MINOR_VERSION 0 + +/* Status Values */ +#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ +#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */ +#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */ +#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */ +#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */ +#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */ + +/* Message Set for Connectionless (802.3) Devices */ +#define REMOTE_NDIS_PACKET_MSG 0x00000001U +#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */ +#define REMOTE_NDIS_HALT_MSG 0x00000003U +#define REMOTE_NDIS_QUERY_MSG 0x00000004U +#define REMOTE_NDIS_SET_MSG 0x00000005U +#define REMOTE_NDIS_RESET_MSG 0x00000006U +#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U +#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U + +/* Message completion */ +#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U +#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U +#define REMOTE_NDIS_SET_CMPLT 0x80000005U +#define REMOTE_NDIS_RESET_CMPLT 0x80000006U +#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U + +/* Device Flags */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001U +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U + +#define RNDIS_MEDIUM_802_3 0x00000000U + +/* from drivers/net/sk98lin/h/skgepnmi.h */ +#define OID_PNP_CAPABILITIES 0xFD010100 +#define OID_PNP_SET_POWER 0xFD010101 +#define OID_PNP_QUERY_POWER 0xFD010102 +#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 +#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 +#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 + +typedef struct rndis_init_msg_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 MajorVersion; + __le32 MinorVersion; + __le32 MaxTransferSize; +} rndis_init_msg_type; + +typedef struct rndis_init_cmplt_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 Status; + __le32 MajorVersion; + __le32 MinorVersion; + __le32 DeviceFlags; + __le32 Medium; + __le32 MaxPacketsPerTransfer; + __le32 MaxTransferSize; + __le32 PacketAlignmentFactor; + __le32 AFListOffset; + __le32 AFListSize; +} rndis_init_cmplt_type; + +typedef struct rndis_halt_msg_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; +} rndis_halt_msg_type; + +typedef struct rndis_query_msg_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 OID; + __le32 InformationBufferLength; + __le32 InformationBufferOffset; + __le32 DeviceVcHandle; +} rndis_query_msg_type; + +typedef struct rndis_query_cmplt_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 Status; + __le32 InformationBufferLength; + __le32 InformationBufferOffset; +} rndis_query_cmplt_type; + +typedef struct rndis_set_msg_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 OID; + __le32 InformationBufferLength; + __le32 InformationBufferOffset; + __le32 DeviceVcHandle; +} rndis_set_msg_type; + +typedef struct rndis_set_cmplt_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 Status; +} rndis_set_cmplt_type; + +typedef struct rndis_reset_msg_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 Reserved; +} rndis_reset_msg_type; + +typedef struct rndis_reset_cmplt_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 Status; + __le32 AddressingReset; +} rndis_reset_cmplt_type; + +typedef struct rndis_indicate_status_msg_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 Status; + __le32 StatusBufferLength; + __le32 StatusBufferOffset; +} rndis_indicate_status_msg_type; + +typedef struct rndis_keepalive_msg_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; +} rndis_keepalive_msg_type; + +typedef struct rndis_keepalive_cmplt_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 Status; +} rndis_keepalive_cmplt_type; + +struct rndis_packet_msg_type +{ + __le32 MessageType; + __le32 MessageLength; + __le32 DataOffset; + __le32 DataLength; + __le32 OOBDataOffset; + __le32 OOBDataLength; + __le32 NumOOBDataElements; + __le32 PerPacketInfoOffset; + __le32 PerPacketInfoLength; + __le32 VcHandle; + __le32 Reserved; +}; + +struct rndis_config_parameter +{ + __le32 ParameterNameOffset; + __le32 ParameterNameLength; + __le32 ParameterType; + __le32 ParameterValueOffset; + __le32 ParameterValueLength; +}; + +/* implementation specific */ +enum rndis_state +{ + RNDIS_UNINITIALIZED, + RNDIS_INITIALIZED, + RNDIS_DATA_INITIALIZED, +}; + +static const uint32_t oid_supported_list[] = +{ + /* the general stuff */ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_PHYSICAL_MEDIUM, + /* the statistical stuff */ + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + /* mandatory 802.3 */ + /* the general stuff */ + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAC_OPTIONS, + OID_802_3_MAXIMUM_LIST_SIZE, + + /* the statistical stuff */ + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS +}; + +struct rndis_response { + TAILQ_ENTRY(rndis_response) entries; + uint32_t length; + uint8_t buf[0]; +}; + + +typedef struct USBNetState { + USBDevice dev; + + unsigned int rndis; + enum rndis_state rndis_state; + uint32_t medium; + uint32_t speed; + uint32_t media_state; + uint16_t filter; + uint32_t vendorid; + uint8_t mac[6]; + + unsigned int out_ptr; + uint8_t out_buf[2048]; + + USBPacket *inpkt; + unsigned int in_ptr, in_len; + uint8_t in_buf[2048]; + + VLANClientState *vc; + TAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp; +} USBNetState; + + +static int ndis_query(USBNetState *s, uint32_t oid, uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf) +{ + switch (oid) { + /* general oids (table 4-1) */ + /* mandatory */ + case OID_GEN_SUPPORTED_LIST: + { + unsigned int i, count = sizeof(oid_supported_list) / sizeof(uint32_t); + for (i = 0; i < count; i++) + ((__le32 *)outbuf)[i] = cpu_to_le32(oid_supported_list[i]); + return sizeof(oid_supported_list); + } + + /* mandatory */ + case OID_GEN_HARDWARE_STATUS: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_MEDIA_SUPPORTED: + *((__le32 *)outbuf) = cpu_to_le32(s->medium); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_MEDIA_IN_USE: + *((__le32 *)outbuf) = cpu_to_le32(s->medium); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_MAXIMUM_FRAME_SIZE: + *((__le32 *)outbuf) = cpu_to_le32(ETH_FRAME_LEN); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_LINK_SPEED: + *((__le32 *)outbuf) = cpu_to_le32(s->speed); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_TRANSMIT_BLOCK_SIZE: + *((__le32 *)outbuf) = cpu_to_le32(ETH_FRAME_LEN); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_RECEIVE_BLOCK_SIZE: + *((__le32 *)outbuf) = cpu_to_le32(ETH_FRAME_LEN); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_VENDOR_ID: + *((__le32 *)outbuf) = cpu_to_le32(0x1234); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_VENDOR_DESCRIPTION: + strcpy(outbuf, "QEMU USB RNDIS Net"); + return strlen(outbuf) + 1; + + case OID_GEN_VENDOR_DRIVER_VERSION: + *((__le32 *)outbuf) = cpu_to_le32(1); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_CURRENT_PACKET_FILTER: + *((__le32 *)outbuf) = cpu_to_le32(s->filter); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_MAXIMUM_TOTAL_SIZE: + *((__le32 *)outbuf) = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_MEDIA_CONNECT_STATUS: + *((__le32 *)outbuf) = cpu_to_le32(s->media_state); + return sizeof(__le32); + + case OID_GEN_PHYSICAL_MEDIUM: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + case OID_GEN_MAC_OPTIONS: + *((__le32 *)outbuf) = cpu_to_le32(NDIS_MAC_OPTION_RECEIVE_SERIALIZED | NDIS_MAC_OPTION_FULL_DUPLEX); + return sizeof(__le32); + + /* statistics OIDs (table 4-2) */ + /* mandatory */ + case OID_GEN_XMIT_OK: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_RCV_OK: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_XMIT_ERROR: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_RCV_ERROR: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + /* mandatory */ + case OID_GEN_RCV_NO_BUFFER: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + /* ieee802.3 OIDs (table 4-3) */ + /* mandatory */ + case OID_802_3_PERMANENT_ADDRESS: + memcpy(outbuf, s->mac, 6); + return 6; + + /* mandatory */ + case OID_802_3_CURRENT_ADDRESS: + memcpy(outbuf, s->mac, 6); + return 6; + + /* mandatory */ + case OID_802_3_MULTICAST_LIST: + *((__le32 *)outbuf) = cpu_to_le32(0xE0000000); + return sizeof(__le32); + + /* mandatory */ + case OID_802_3_MAXIMUM_LIST_SIZE: + *((__le32 *)outbuf) = cpu_to_le32(1); + return sizeof(__le32); + + case OID_802_3_MAC_OPTIONS: + return 0; + + /* ieee802.3 statistics OIDs (table 4-4) */ + /* mandatory */ + case OID_802_3_RCV_ERROR_ALIGNMENT: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + /* mandatory */ + case OID_802_3_XMIT_ONE_COLLISION: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + /* mandatory */ + case OID_802_3_XMIT_MORE_COLLISIONS: + *((__le32 *)outbuf) = cpu_to_le32(0); + return sizeof(__le32); + + default: + fprintf(stderr, "usbnet: unknown OID 0x%08x\n", oid); + return 0; + } + return -1; +} + +static int ndis_set(USBNetState *s, uint32_t oid, uint8_t *inbuf, unsigned int inlen) +{ + switch (oid) { + case OID_GEN_CURRENT_PACKET_FILTER: + s->filter = le32_to_cpup((__le32 *)inbuf); + if (s->filter) { + s->rndis_state = RNDIS_DATA_INITIALIZED; + } else { + s->rndis_state = RNDIS_INITIALIZED; + } + return 0; + + case OID_802_3_MULTICAST_LIST: + return 0; + + } + return -1; +} + +static int rndis_get_response(USBNetState *s, uint8_t *buf) +{ + int ret = 0; + struct rndis_response *r = s->rndis_resp.tqh_first; + if (!r) + return ret; + TAILQ_REMOVE(&s->rndis_resp, r, entries); + ret = r->length; + memcpy(buf, r->buf, r->length); + qemu_free(r); + return ret; +} + +static void *rndis_queue_response(USBNetState *s, unsigned int length) +{ + struct rndis_response *r = qemu_mallocz(sizeof(struct rndis_response) + length); + if (!r) + return NULL; + TAILQ_INSERT_TAIL(&s->rndis_resp, r, entries); + r->length = length; + return &r->buf[0]; +} + +static void rndis_clear_responsequeue(USBNetState *s) +{ + struct rndis_response *r; + + while ((r = s->rndis_resp.tqh_first)) { + TAILQ_REMOVE(&s->rndis_resp, r, entries); + qemu_free(r); + } +} + +static int rndis_init_response(USBNetState *s, rndis_init_msg_type *buf) +{ + rndis_init_cmplt_type *resp = rndis_queue_response(s, sizeof(rndis_init_cmplt_type)); + if (!resp) + return USB_RET_STALL; + resp->MessageType = cpu_to_le32(REMOTE_NDIS_INITIALIZE_CMPLT); + resp->MessageLength = cpu_to_le32(sizeof(rndis_init_cmplt_type)); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); + resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION); + resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION); + resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS); + resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3); + resp->MaxPacketsPerTransfer = cpu_to_le32(1); + resp->MaxTransferSize = cpu_to_le32(ETH_FRAME_LEN + sizeof(struct rndis_packet_msg_type) + 22); + resp->PacketAlignmentFactor = cpu_to_le32(0); + resp->AFListOffset = cpu_to_le32(0); + resp->AFListSize = cpu_to_le32(0); + return 0; +} + +static int rndis_query_response(USBNetState *s, rndis_query_msg_type *buf, unsigned int length) +{ + rndis_query_cmplt_type *resp; + uint8_t infobuf[sizeof(oid_supported_list)]; /* oid_supported_list is the largest data reply */ + uint32_t bufoffs, buflen; + int infobuflen; + unsigned int resplen; + bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; + buflen = le32_to_cpu(buf->InformationBufferLength); + if (bufoffs + buflen > length) + return USB_RET_STALL; + infobuflen = ndis_query(s, le32_to_cpu(buf->OID), bufoffs + (uint8_t *)buf, buflen, infobuf); + resplen = sizeof(rndis_query_cmplt_type) + ((infobuflen < 0) ? 0 : infobuflen); + resp = rndis_queue_response(s, resplen); + if (!resp) + return USB_RET_STALL; + resp->MessageType = cpu_to_le32(REMOTE_NDIS_QUERY_CMPLT); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->MessageLength = cpu_to_le32(resplen); + if (infobuflen < 0) { + /* OID not supported */ + resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); + resp->InformationBufferLength = cpu_to_le32(0); + resp->InformationBufferOffset = cpu_to_le32(0); + return 0; + } + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); + resp->InformationBufferOffset = cpu_to_le32(infobuflen ? sizeof(rndis_query_cmplt_type) - 8 : 0); + resp->InformationBufferLength = cpu_to_le32(infobuflen); + memcpy(resp + 1, infobuf, infobuflen); + return 0; +} + +static int rndis_set_response(USBNetState *s, rndis_set_msg_type *buf, unsigned int length) +{ + rndis_set_cmplt_type *resp = rndis_queue_response(s, sizeof(rndis_set_cmplt_type)); + uint32_t bufoffs, buflen; + if (!resp) + return USB_RET_STALL; + bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; + buflen = le32_to_cpu(buf->InformationBufferLength); + if (bufoffs + buflen > length) + return USB_RET_STALL; + int ret = ndis_set(s, le32_to_cpu(buf->OID), bufoffs + (uint8_t *)buf, buflen); + resp->MessageType = cpu_to_le32(REMOTE_NDIS_SET_CMPLT); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->MessageLength = cpu_to_le32(sizeof(rndis_set_cmplt_type)); + if (ret < 0) { + /* OID not supported */ + resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); + return 0; + } + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); + return 0; +} + +static int rndis_reset_response(USBNetState *s, rndis_reset_msg_type *buf) +{ + rndis_reset_cmplt_type *resp = rndis_queue_response(s, sizeof(rndis_reset_cmplt_type)); + if (!resp) + return USB_RET_STALL; + resp->MessageType = cpu_to_le32(REMOTE_NDIS_RESET_CMPLT); + resp->MessageLength = cpu_to_le32(sizeof(rndis_reset_cmplt_type)); + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); + /* resent information */ + resp->AddressingReset = cpu_to_le32(1); + return 0; +} + +static int rndis_keepalive_response(USBNetState *s, rndis_keepalive_msg_type *buf) +{ + rndis_keepalive_cmplt_type *resp = rndis_queue_response(s, sizeof(rndis_keepalive_cmplt_type)); + if (!resp) + return USB_RET_STALL; + resp->MessageType = cpu_to_le32(REMOTE_NDIS_KEEPALIVE_CMPLT); + resp->MessageLength = cpu_to_le32(sizeof(rndis_keepalive_cmplt_type)); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); + return 0; +} + +static int rndis_parse(USBNetState *s, uint8_t *data, int length) +{ + uint32_t MsgType, MsgLength; + __le32 *tmp = (__le32 *)data; + MsgType = le32_to_cpup(tmp++); + MsgLength = le32_to_cpup(tmp++); + + switch (MsgType) { + case REMOTE_NDIS_INITIALIZE_MSG: + s->rndis_state = RNDIS_INITIALIZED; + return rndis_init_response(s, (rndis_init_msg_type *)data); + + case REMOTE_NDIS_HALT_MSG: + s->rndis_state = RNDIS_UNINITIALIZED; + return 0; + + case REMOTE_NDIS_QUERY_MSG: + return rndis_query_response(s, (rndis_query_msg_type *)data, length); + + case REMOTE_NDIS_SET_MSG: + return rndis_set_response(s, (rndis_set_msg_type *)data, length); + + case REMOTE_NDIS_RESET_MSG: + rndis_clear_responsequeue(s); + s->out_ptr = s->in_ptr = s->in_len = 0; + return rndis_reset_response(s, (rndis_reset_msg_type *)data); + + case REMOTE_NDIS_KEEPALIVE_MSG: + /* For USB: host does this every 5 seconds */ + return rndis_keepalive_response(s, (rndis_keepalive_msg_type *)data); + } + return USB_RET_STALL; +} + +static void usb_net_handle_reset(USBDevice *dev) +{ +} + +static int usb_net_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBNetState *s = (USBNetState *)dev; + int ret = 0; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + + case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND: + if (!s->rndis || value || index != 0) + goto fail; +#if TRAFFIC_DEBUG + { + unsigned int i; + fprintf(stderr, "SEND_ENCAPSULATED_COMMAND:"); + for (i = 0; i < length; i++) { + if (!(i & 15)) + fprintf(stderr, "\n%04X:", i); + fprintf(stderr, " %02X", data[i]); + } + fprintf(stderr, "\n\n"); + } +#endif + ret = rndis_parse(s, data, length); + break; + + case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE: + if (!s->rndis || value || index != 0) + goto fail; + ret = rndis_get_response(s, data); + if (!ret) { + data[0] = 0; + ret = 1; + } +#if TRAFFIC_DEBUG + { + unsigned int i; + fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:"); + for (i = 0; i < ret; i++) { + if (!(i & 15)) + fprintf(stderr, "\n%04X:", i); + fprintf(stderr, " %02X", data[i]); + } + fprintf(stderr, "\n\n"); + } +#endif + break; + + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + ret = sizeof(qemu_net_dev_descriptor); + memcpy(data, qemu_net_dev_descriptor, ret); + break; + + case USB_DT_CONFIG: + switch (value & 0xff) { + case 0: + ret = sizeof(qemu_net_rndis_config_descriptor); + memcpy(data, qemu_net_rndis_config_descriptor, ret); + break; + + case 1: + ret = sizeof(qemu_net_cdc_config_descriptor); + memcpy(data, qemu_net_cdc_config_descriptor, ret); + break; + + default: + goto fail; + } + data[2] = ret & 0xff; + data[3] = ret >> 8; + break; + + case USB_DT_STRING: + switch (value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + + case STRING_MANUFACTURER: + ret = set_usb_string(data, "QEMU"); + break; + + case STRING_PRODUCT: + ret = set_usb_string(data, "RNDIS/QEMU USB Network Device"); + break; + + case STRING_ETHADDR: + ret = set_usb_string(data, "400102030405"); + break; + + case STRING_DATA: + ret = set_usb_string(data, "QEMU USB Net Data Interface"); + break; + + case STRING_CONTROL: + ret = set_usb_string(data, "QEMU USB Net Control Interface"); + break; + + case STRING_RNDIS_CONTROL: + ret = set_usb_string(data, "QEMU USB Net RNDIS Control Interface"); + break; + + case STRING_CDC: + ret = set_usb_string(data, "QEMU USB Net CDC"); + break; + + case STRING_SUBSET: + ret = set_usb_string(data, "QEMU USB Net Subset"); + break; + + case STRING_RNDIS: + ret = set_usb_string(data, "QEMU USB Net RNDIS"); + break; + + case STRING_SERIALNUMBER: + ret = set_usb_string(data, "1"); + break; + + default: + goto fail; + } + break; + + default: + goto fail; + } + break; + + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = s->rndis ? DEV_RNDIS_CONFIG_VALUE : DEV_CONFIG_VALUE; + ret = 1; + break; + + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + switch (value & 0xff) { + case DEV_CONFIG_VALUE: + s->rndis = 0; + break; + + case DEV_RNDIS_CONFIG_VALUE: + s->rndis = 1; + break; + + default: + goto fail; + } + ret = 0; + break; + + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + + default: + fail: + fprintf(stderr, "usbnet: failed control transaction: request 0x%x value 0x%x index 0x%x length 0x%x\n", + request, value, index, length); + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_net_handle_statusin(USBNetState *s, USBPacket *p) +{ + int ret = 8; + if (p->len < 8) + return USB_RET_STALL; + ((__le32 *)p->data)[0] = cpu_to_le32(1); + ((__le32 *)p->data)[1] = cpu_to_le32(0); + if (!s->rndis_resp.tqh_first) + ret = USB_RET_NAK; +#if DEBUG + fprintf(stderr, "usbnet: interrupt poll len %u return %d", p->len, ret); + { + int i; + fprintf(stderr, ":"); + for (i = 0; i < ret; i++) { + if (!(i & 15)) + fprintf(stderr, "\n%04X:", i); + fprintf(stderr, " %02X", p->data[i]); + } + fprintf(stderr, "\n\n"); + } +#endif + return ret; +} + +static int usb_net_handle_datain(USBNetState *s, USBPacket *p) +{ + int ret = USB_RET_NAK; + + if (s->in_ptr > s->in_len) { + s->in_ptr = s->in_len = 0; + ret = USB_RET_NAK; + return ret; + } + if (!s->in_len) { + ret = USB_RET_NAK; + return ret; + } + ret = s->in_len - s->in_ptr; + if (ret > p->len) + ret = p->len; + memcpy(p->data, &s->in_buf[s->in_ptr], ret); + s->in_ptr += ret; + if (s->in_ptr >= s->in_len && (s->rndis || (s->in_len & (64-1)) || !ret)) { + /* no short packet necessary */ + s->in_ptr = s->in_len = 0; + } +#if TRAFFIC_DEBUG + fprintf(stderr, "usbnet: data in len %u return %d", p->len, ret); + { + int i; + fprintf(stderr, ":"); + for (i = 0; i < ret; i++) { + if (!(i & 15)) + fprintf(stderr, "\n%04X:", i); + fprintf(stderr, " %02X", p->data[i]); + } + fprintf(stderr, "\n\n"); + } +#endif + return ret; +} + +static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) +{ + int ret = p->len; + int sz = sizeof(s->out_buf) - s->out_ptr; + struct rndis_packet_msg_type *msg = (struct rndis_packet_msg_type *)s->out_buf; + uint32_t len; + +#if TRAFFIC_DEBUG + fprintf(stderr, "usbnet: data out len %u\n", p->len); + { + int i; + fprintf(stderr, ":"); + for (i = 0; i < p->len; i++) { + if (!(i & 15)) + fprintf(stderr, "\n%04X:", i); + fprintf(stderr, " %02X", p->data[i]); + } + fprintf(stderr, "\n\n"); + } +#endif + if (sz > ret) + sz = ret; + memcpy(&s->out_buf[s->out_ptr], p->data, sz); + s->out_ptr += sz; + if (!s->rndis) { + if (ret < 64) { + qemu_send_packet(s->vc, s->out_buf, s->out_ptr); + s->out_ptr = 0; + } + return ret; + } + len = le32_to_cpu(msg->MessageLength); + if (s->out_ptr < 8 || s->out_ptr < len) + return ret; + if (le32_to_cpu(msg->MessageType) == REMOTE_NDIS_PACKET_MSG) { + uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); + uint32_t size = le32_to_cpu(msg->DataLength); + if (offs + size <= len) + qemu_send_packet(s->vc, s->out_buf + offs, size); + } + s->out_ptr -= len; + memmove(s->out_buf, &s->out_buf[len], s->out_ptr); + return ret; +} + +static int usb_net_handle_data(USBDevice *dev, USBPacket *p) +{ + USBNetState *s = (USBNetState *)dev; + int ret = 0; + + switch(p->pid) { + case USB_TOKEN_IN: + switch (p->devep) { + case 1: + ret = usb_net_handle_statusin(s, p); + break; + + case 2: + ret = usb_net_handle_datain(s, p); + break; + + default: + goto fail; + } + break; + + case USB_TOKEN_OUT: + switch (p->devep) { + case 2: + ret = usb_net_handle_dataout(s, p); + break; + + default: + goto fail; + } + break; + + +#if 0 + case USB_TOKEN_IN: + if (p->devep == 1) { + 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 { + goto fail; + } + break; + case USB_TOKEN_OUT: +#endif + default: + fail: + ret = USB_RET_STALL; + break; + } + if (ret == USB_RET_STALL) + fprintf(stderr, "usbnet: failed data transaction: pid 0x%x ep 0x%x len 0x%x\n", p->pid, p->devep, p->len); + return ret; +} + +static void usbnet_receive(void *opaque, const uint8_t *buf, int size) +{ + USBNetState *s = opaque; + + if (s->rndis) { + struct rndis_packet_msg_type *msg = (struct rndis_packet_msg_type *)s->in_buf; + if (!s->rndis_state == RNDIS_DATA_INITIALIZED) + return; + if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf)) + return; + memset(msg, 0, sizeof(struct rndis_packet_msg_type)); + msg->MessageType = cpu_to_le32(REMOTE_NDIS_PACKET_MSG); + msg->MessageLength = cpu_to_le32(size + sizeof(struct rndis_packet_msg_type)); + msg->DataOffset = cpu_to_le32(sizeof(struct rndis_packet_msg_type) - 8); + msg->DataLength = cpu_to_le32(size); + //msg->OOBDataOffset; + //msg->OOBDataLength; + //msg->NumOOBDataElements; + //msg->PerPacketInfoOffset; + //msg->PerPacketInfoLength; + //msg->VcHandle; + //msg->Reserved; + memcpy(msg + 1, buf, size); + s->in_len = size + sizeof(struct rndis_packet_msg_type); + } else { + if (size > sizeof(s->in_buf)) + return; + memcpy(s->in_buf, buf, size); + s->in_len = size; + } + s->in_ptr = 0; +} + +static int usbnet_can_receive(void *opaque) +{ + USBNetState *s = opaque; + + if (s->rndis && !s->rndis_state == RNDIS_DATA_INITIALIZED) + return 1; + return !s->in_len; +} + +static void usb_net_handle_destroy(USBDevice *dev) +{ + USBNetState *s = (USBNetState *)dev; + rndis_clear_responsequeue(s); + qemu_free(s); +} + +USBDevice *usb_net_init(NICInfo *nd) +{ + USBNetState *s; + + s = qemu_mallocz(sizeof(USBNetState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_net_handle_reset; + s->dev.handle_control = usb_net_handle_control; + s->dev.handle_data = usb_net_handle_data; + s->dev.handle_destroy = usb_net_handle_destroy; + + s->rndis = 1; + s->rndis_state = RNDIS_UNINITIALIZED; + s->medium = NDIS_MEDIUM_802_3; + s->speed = 1000000; /* 100MBps, in 100Bps units */ + s->media_state = NDIS_MEDIA_STATE_CONNECTED; + s->filter = 0; + s->vendorid = 0x1234; + memcpy(s->mac, nd->macaddr, 6); + TAILQ_INIT(&s->rndis_resp); + + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Network Interface"); + s->vc = qemu_new_vlan_client(nd->vlan, usbnet_receive, usbnet_can_receive, s); + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "usbnet macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + s->mac[0], s->mac[1], s->mac[2], + s->mac[3], s->mac[4], s->mac[5]); + fprintf(stderr, "usbnet: initialized mac %02x:%02x:%02x:%02x:%02x:%02x\n", + s->mac[0], s->mac[1], s->mac[2], + s->mac[3], s->mac[4], s->mac[5]); + return (USBDevice *)s; +} Index: qemu/hw/usb-ohci.c =================================================================== --- qemu.orig/hw/usb-ohci.c 2006-08-12 09:04:27.000000000 +0800 +++ qemu/hw/usb-ohci.c 2007-02-12 15:21:33.000000000 +0800 @@ -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 @@ -52,11 +53,16 @@ uint32_t ctrl; } OHCIPort; +typedef void (*ohci_irq_service_t)(void *opaque, int irq, int level); + typedef struct { - struct PCIDevice pci_dev; + void *pic; + int irq; + ohci_irq_service_t set_irq; target_phys_addr_t mem_base; int mem; int num_ports; + const char *name; QEMUTimer *eof_timer; int64_t sof_time; @@ -90,6 +96,12 @@ 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; @@ -256,6 +268,8 @@ #define OHCI_CC_BUFFEROVERRUN 0xc #define OHCI_CC_BUFFERUNDERRUN 0xd +#define OHCI_HRESET_FSBIR (1 << 0) + /* Update IRQ levels */ static inline void ohci_intr_update(OHCIState *ohci) { @@ -265,7 +279,7 @@ (ohci->intr_status & ohci->intr)) level = 1; - pci_set_irq(&ohci->pci_dev, 0, level); + ohci->set_irq(ohci->pic, ohci->irq, level); } /* Set an interrupt */ @@ -295,6 +309,11 @@ else port->ctrl &= ~OHCI_PORT_LSDA; port->port.dev = dev; + + /* notify of remote-wakeup */ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) + ohci_set_interrupt(s, OHCI_INTR_RD); + /* send the attach message */ usb_send_msg(dev, USB_MSG_ATTACH); dprintf("usb-ohci: Attached port %d\n", port1->index); @@ -367,7 +386,7 @@ usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } - dprintf("usb-ohci: Reset %s\n", ohci->pci_dev.name); + dprintf("usb-ohci: Reset %s\n", ohci->name); } /* Get an array of dwords from main memory */ @@ -795,13 +814,12 @@ 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); @@ -854,7 +872,7 @@ 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; @@ -892,13 +910,13 @@ 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); + dprintf("usb-ohci: %s: USB Reset\n", ohci->name); break; } } @@ -1086,6 +1104,19 @@ 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; @@ -1187,6 +1218,24 @@ 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; @@ -1207,22 +1256,11 @@ ohci_mem_write }; -static void ohci_mapfunc(PCIDevice *pci_dev, int i, - uint32_t addr, uint32_t size, int type) +static void usb_ohci_init(OHCIState *ohci, int num_ports, int devfn, + void *pic, int irq, ohci_irq_service_t set_irq, const char *name) { - 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) -{ - OHCIState *ohci; - int vid = 0x106b; - int did = 0x003f; int i; - if (usb_frame_time == 0) { #if OHCI_TIME_WARP usb_frame_time = ticks_per_sec; @@ -1239,8 +1277,43 @@ 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->pic = pic; + ohci->irq = irq; + ohci->set_irq = set_irq; + + 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; + 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; @@ -1255,16 +1328,21 @@ 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, + 0, (ohci_irq_service_t) pci_set_irq, 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_memio(target_phys_addr_t base, int num_ports, int devfn, + void *pic, int irq) +{ + OHCIState *ohci = (OHCIState *)qemu_mallocz(sizeof(OHCIState)); - ohci->async_td = 0; - ohci_reset(ohci); + usb_ohci_init(ohci, num_ports, devfn, pic, irq, + pic_set_irq_new, "OHCI USB"); + ohci->mem_base = base; + + cpu_register_physical_memory(ohci->mem_base, 0xfff, ohci->mem); } Index: qemu/hw/usb.h =================================================================== --- qemu.orig/hw/usb.h 2007-02-12 13:06:50.000000000 +0800 +++ qemu/hw/usb.h 2007-02-12 15:21:33.000000000 +0800 @@ -206,7 +206,9 @@ void usb_uhci_init(PCIBus *bus, int devfn); /* usb-ohci.c */ -void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn); +void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn); +void usb_ohci_init_memio(target_phys_addr_t base, int num_ports, int devfn, + void *pic, int irq); /* usb-linux.c */ USBDevice *usb_host_device_open(const char *devname); @@ -218,3 +220,6 @@ /* usb-msd.c */ USBDevice *usb_msd_init(const char *filename); + +/* usb-net.c */ +USBDevice *usb_net_init(NICInfo *nd); Index: qemu/hw/versatilepb.c =================================================================== --- qemu.orig/hw/versatilepb.c 2007-01-17 02:54:31.000000000 +0800 +++ qemu/hw/versatilepb.c 2007-03-06 10:25:41.000000000 +0800 @@ -192,7 +192,7 @@ } } if (usb_enabled) { - usb_ohci_init(pci_bus, 3, -1); + usb_ohci_init_pci(pci_bus, 3, -1); } scsi_hba = lsi_scsi_init(pci_bus, -1); for (n = 0; n < MAX_DISKS; n++) { @@ -251,13 +251,13 @@ /* 0x101f4000 SSPI. */ arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline, - initrd_filename, board_id); + initrd_filename, board_id, 0x0); } static void vpb_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) + const char *initrd_filename, const char *cpu_model) { versatile_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, @@ -268,7 +268,7 @@ static void vab_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) + const char *initrd_filename, const char *cpu_model) { versatile_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, Index: qemu/hw/wm8750.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/wm8750.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,524 @@ +/* + * WM8750 audio CODEC. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This file is licensed under GNU GPL. + */ + +#include "vl.h" + +#define IN_PORT_N 3 +#define OUT_PORT_N 3 + +#define CODEC "wm8750" + +struct wm_rate_s; +struct wm8750_s { + int i2c_dir; + struct i2c_slave_s i2c; + QEMUSoundCard card; + SWVoiceIn *adc_voice[IN_PORT_N]; + SWVoiceOut *dac_voice[OUT_PORT_N]; + int enable; + void (*data_req)(void *, int, int); + void *opaque; + uint8_t data_in[4096]; + uint8_t data_out[4096]; + int idx_in, req_in; + int idx_out, req_out; + + SWVoiceOut **out[2]; + uint8_t outvol[7], outmute[2]; + SWVoiceIn **in[2]; + uint8_t invol[4], inmute[2]; + + uint8_t diff[2], pol, ds, monomix[2], alc, mute; + uint8_t path[4], mpath[2], power, format; + uint32_t inmask, outmask; + const struct wm_rate_s *rate; +}; + +static inline void wm8750_in_load(struct wm8750_s *s) +{ + int acquired; + if (s->idx_in + s->req_in <= sizeof(s->data_in)) + return; + s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in); + acquired = AUD_read(*s->in[0], s->data_in + s->idx_in, + sizeof(s->data_in) - s->idx_in); +} + +static inline void wm8750_out_flush(struct wm8750_s *s) +{ + int sent; + if (!s->idx_out) + return; + sent = AUD_write(*s->out[0], s->data_out, s->idx_out); + s->idx_out = 0; +} + +static void wm8750_audio_in_cb(void *opaque, int avail_b) +{ + struct wm8750_s *s = (struct wm8750_s *) opaque; + s->req_in = avail_b; + s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2); + +#if 0 + wm8750_in_load(s); +#endif +} + +static void wm8750_audio_out_cb(void *opaque, int free_b) +{ + struct wm8750_s *s = (struct wm8750_s *) opaque; + wm8750_out_flush(s); + + s->req_out = free_b; + s->data_req(s->opaque, free_b >> 2, s->req_in >> 2); +} + +struct wm_rate_s { + int adc; + int adc_hz; + int dac; + int dac_hz; +}; + +static const struct wm_rate_s wm_rate_table[] = { + { 256, 48000, 256, 48000 }, /* SR: 00000 */ + { 384, 48000, 384, 48000 }, /* SR: 00001 */ + { 256, 48000, 1536, 8000 }, /* SR: 00010 */ + { 384, 48000, 2304, 8000 }, /* SR: 00011 */ + { 1536, 8000, 256, 48000 }, /* SR: 00100 */ + { 2304, 8000, 384, 48000 }, /* SR: 00101 */ + { 1536, 8000, 1536, 8000 }, /* SR: 00110 */ + { 2304, 8000, 2304, 8000 }, /* SR: 00111 */ + { 1024, 12000, 1024, 12000 }, /* SR: 01000 */ + { 1526, 12000, 1536, 12000 }, /* SR: 01001 */ + { 768, 16000, 768, 16000 }, /* SR: 01010 */ + { 1152, 16000, 1152, 16000 }, /* SR: 01011 */ + { 384, 32000, 384, 32000 }, /* SR: 01100 */ + { 576, 32000, 576, 32000 }, /* SR: 01101 */ + { 128, 96000, 128, 96000 }, /* SR: 01110 */ + { 192, 96000, 192, 96000 }, /* SR: 01111 */ + { 256, 44100, 256, 44100 }, /* SR: 10000 */ + { 384, 44100, 384, 44100 }, /* SR: 10001 */ + { 256, 44100, 1408, 8018 }, /* SR: 10010 */ + { 384, 44100, 2112, 8018 }, /* SR: 10011 */ + { 1408, 8018, 256, 44100 }, /* SR: 10100 */ + { 2112, 8018, 384, 44100 }, /* SR: 10101 */ + { 1408, 8018, 1408, 8018 }, /* SR: 10110 */ + { 2112, 8018, 2112, 8018 }, /* SR: 10111 */ + { 1024, 11025, 1024, 11025 }, /* SR: 11000 */ + { 1536, 11025, 1536, 11025 }, /* SR: 11001 */ + { 512, 22050, 512, 22050 }, /* SR: 11010 */ + { 768, 22050, 768, 22050 }, /* SR: 11011 */ + { 512, 24000, 512, 24000 }, /* SR: 11100 */ + { 768, 24000, 768, 24000 }, /* SR: 11101 */ + { 128, 88200, 128, 88200 }, /* SR: 11110 */ + { 192, 88200, 128, 88200 }, /* SR: 11111 */ +}; + +void wm8750_set_format(struct wm8750_s *s) +{ + int i; + audsettings_t in_fmt; + audsettings_t out_fmt; + audsettings_t monoout_fmt; + + wm8750_out_flush(s); + + if (s->in[0] && *s->in[0]) + AUD_set_active_in(*s->in[0], 0); + if (s->out[0] && *s->out[0]) + AUD_set_active_out(*s->out[0], 0); + + for (i = 0; i < IN_PORT_N; i ++) + if (s->adc_voice[i]) { + AUD_close_in(&s->card, s->adc_voice[i]); + s->adc_voice[i] = 0; + } + for (i = 0; i < OUT_PORT_N; i ++) + if (s->dac_voice[i]) { + AUD_close_out(&s->card, s->dac_voice[i]); + s->dac_voice[i] = 0; + } + + if (!s->enable) + return; + + /* Setup input */ + in_fmt.endianness = 0; + in_fmt.nchannels = 2; + in_fmt.freq = s->rate->adc_hz; + in_fmt.fmt = AUD_FMT_S16; + + s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0], + CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt); + s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1], + CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt); + s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2], + CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt); + + /* Setup output */ + out_fmt.endianness = 0; + out_fmt.nchannels = 2; + out_fmt.freq = s->rate->dac_hz; + out_fmt.fmt = AUD_FMT_S16; + monoout_fmt.endianness = 0; + monoout_fmt.nchannels = 1; + monoout_fmt.freq = s->rate->dac_hz; + monoout_fmt.fmt = AUD_FMT_S16; + + s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], + CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt); + s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1], + CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt); + /* MONOMIX is also in stereo for simplicity */ + s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2], + CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt); + /* no sense emulating OUT3 which is a mix of other outputs */ + + /* We should connect the left and right channels to their + * respective inputs/outputs but we have completely no need + * for mixing or combining paths to different ports, so we + * connect both channels to where the left channel is routed. */ + if (s->in[0] && *s->in[0]) + AUD_set_active_in(*s->in[0], 1); + if (s->out[0] && *s->out[0]) + AUD_set_active_out(*s->out[0], 1); +} + +void inline wm8750_mask_update(struct wm8750_s *s) +{ +#define R_ONLY 0x0000ffff +#define L_ONLY 0xffff0000 +#define BOTH (R_ONLY | L_ONLY) +#define NONE (R_ONLY & L_ONLY) + s->inmask = + (s->inmute[0] ? R_ONLY : BOTH) & + (s->inmute[1] ? L_ONLY : BOTH) & + (s->mute ? NONE : BOTH); + s->outmask = + (s->outmute[0] ? R_ONLY : BOTH) & + (s->outmute[1] ? L_ONLY : BOTH) & + (s->mute ? NONE : BOTH); +} + +void wm8750_reset(struct i2c_slave_s *i2c) +{ + struct wm8750_s *s = (struct wm8750_s *) i2c->opaque; + s->enable = 0; + wm8750_set_format(s); + s->diff[0] = 0; + s->diff[1] = 0; + s->ds = 0; + s->alc = 0; + s->in[0] = &s->adc_voice[0]; + s->invol[0] = 0x17; + s->invol[1] = 0x17; + s->invol[2] = 0xc3; + s->invol[3] = 0xc3; + s->out[0] = &s->dac_voice[0]; + s->outvol[0] = 0xff; + s->outvol[1] = 0xff; + s->outvol[2] = 0x79; + s->outvol[3] = 0x79; + s->outvol[4] = 0x79; + s->outvol[5] = 0x79; + s->inmute[0] = 0; + s->inmute[1] = 0; + s->outmute[0] = 0; + s->outmute[1] = 0; + s->mute = 1; + s->path[0] = 0; + s->path[1] = 0; + s->path[2] = 0; + s->path[3] = 0; + s->mpath[0] = 0; + s->mpath[1] = 0; + s->format = 0x0a; + s->idx_in = sizeof(s->data_in); + s->req_in = 0; + s->idx_out = 0; + s->req_out = 0; + wm8750_mask_update(s); +} + +static void wm8750_start(void *opaque, int dir) +{ + struct wm8750_s *s = (struct wm8750_s *) opaque; + s->i2c_dir = dir; +} + +#define WM8750_LINVOL 0x00 +#define WM8750_RINVOL 0x01 +#define WM8750_LOUT1V 0x02 +#define WM8750_ROUT1V 0x03 +#define WM8750_ADCDAC 0x05 +#define WM8750_IFACE 0x07 +#define WM8750_SRATE 0x08 +#define WM8750_LDAC 0x0a +#define WM8750_RDAC 0x0b +#define WM8750_BASS 0x0c +#define WM8750_TREBLE 0x0d +#define WM8750_RESET 0x0f +#define WM8750_3D 0x10 +#define WM8750_ALC1 0x11 +#define WM8750_ALC2 0x12 +#define WM8750_ALC3 0x13 +#define WM8750_NGATE 0x14 +#define WM8750_LADC 0x15 +#define WM8750_RADC 0x16 +#define WM8750_ADCTL1 0x17 +#define WM8750_ADCTL2 0x18 +#define WM8750_PWR1 0x19 +#define WM8750_PWR2 0x1a +#define WM8750_ADCTL3 0x1b +#define WM8750_ADCIN 0x1f +#define WM8750_LADCIN 0x20 +#define WM8750_RADCIN 0x21 +#define WM8750_LOUTM1 0x22 +#define WM8750_LOUTM2 0x23 +#define WM8750_ROUTM1 0x24 +#define WM8750_ROUTM2 0x25 +#define WM8750_MOUTM1 0x26 +#define WM8750_MOUTM2 0x27 +#define WM8750_LOUT2V 0x28 +#define WM8750_ROUT2V 0x29 +#define WM8750_MOUTV 0x2a + +static int wm8750_tx(void *opaque, uint8_t *data, int len) +{ + struct wm8750_s *s = (struct wm8750_s *) opaque; + uint8_t cmd; + uint16_t value; + + if (s->i2c_dir) + return 1; + if (len < 2) { +#ifdef VERBOSE + printf("%s: message too short (%i bytes)\n", __FUNCTION__, len); +#endif + return 0; + } + cmd = data[0] >> 1; + value = ((data[0] << 8) | data[1]) & 0x1ff; +#ifdef VERBOSE + if (len > 2) + printf("%s: long message (%i bytes)\n", __FUNCTION__, len); +#endif + + switch (cmd) { + case WM8750_LADCIN: /* ADC Signal Path Control (Left) */ + s->diff[0] = (((value >> 6) & 3) == 3); /* LINSEL */ + if (s->diff[0]) + s->in[0] = &s->adc_voice[0 + s->ds * 1]; + else + s->in[0] = &s->adc_voice[((value >> 6) & 3) * 1 + 0]; + break; + + case WM8750_RADCIN: /* ADC Signal Path Control (Right) */ + s->diff[1] = (((value >> 6) & 3) == 3); /* RINSEL */ + if (s->diff[1]) + s->in[1] = &s->adc_voice[0 + s->ds * 1]; + else + s->in[1] = &s->adc_voice[((value >> 6) & 3) * 1 + 0]; + break; + + case WM8750_ADCIN: /* ADC Input Mode */ + s->ds = (value >> 8) & 1; /* DS */ + if (s->diff[0]) + s->in[0] = &s->adc_voice[0 + s->ds * 1]; + if (s->diff[1]) + s->in[1] = &s->adc_voice[0 + s->ds * 1]; + s->monomix[0] = (value >> 6) & 3; /* MONOMIX */ + break; + + case WM8750_ADCTL1: /* Additional Control (1) */ + s->monomix[1] = (value >> 1) & 1; /* DMONOMIX */ + break; + + case WM8750_PWR1: /* Power Management (1) */ + s->enable = ((value >> 6) & 7) == 3; /* VMIDSEL, VREF */ + wm8750_set_format(s); + break; + + case WM8750_LINVOL: /* Left Channel PGA */ + s->invol[0] = value & 0x3f; /* LINVOL */ + s->inmute[0] = (value >> 7) & 1; /* LINMUTE */ + wm8750_mask_update(s); + break; + + case WM8750_RINVOL: /* Right Channel PGA */ + s->invol[1] = value & 0x3f; /* RINVOL */ + s->inmute[1] = (value >> 7) & 1; /* RINMUTE */ + wm8750_mask_update(s); + break; + + case WM8750_ADCDAC: /* ADC and DAC Control */ + s->pol = (value >> 5) & 3; /* ADCPOL */ + s->mute = (value >> 3) & 1; /* DACMU */ + wm8750_mask_update(s); + break; + + case WM8750_ADCTL3: /* Additional Control (3) */ + break; + + case WM8750_LADC: /* Left ADC Digital Volume */ + s->invol[2] = value & 0xff; /* LADCVOL */ + break; + + case WM8750_RADC: /* Right ADC Digital Volume */ + s->invol[3] = value & 0xff; /* RADCVOL */ + break; + + case WM8750_ALC1: /* ALC Control (1) */ + s->alc = (value >> 7) & 3; /* ALCSEL */ + break; + + case WM8750_NGATE: /* Noise Gate Control */ + case WM8750_3D: /* 3D enhance */ + break; + + case WM8750_LDAC: /* Left Channel Digital Volume */ + s->outvol[0] = value & 0xff; /* LDACVOL */ + break; + + case WM8750_RDAC: /* Right Channel Digital Volume */ + s->outvol[1] = value & 0xff; /* RDACVOL */ + break; + + case WM8750_BASS: /* Bass Control */ + break; + + case WM8750_LOUTM1: /* Left Mixer Control (1) */ + s->path[0] = (value >> 8) & 1; /* LD2LO */ + break; + + case WM8750_LOUTM2: /* Left Mixer Control (2) */ + s->path[1] = (value >> 8) & 1; /* RD2LO */ + break; + + case WM8750_ROUTM1: /* Right Mixer Control (1) */ + s->path[2] = (value >> 8) & 1; /* LD2RO */ + break; + + case WM8750_ROUTM2: /* Right Mixer Control (2) */ + s->path[3] = (value >> 8) & 1; /* RD2RO */ + break; + + case WM8750_MOUTM1: /* Mono Mixer Control (1) */ + s->mpath[0] = (value >> 8) & 1; /* LD2MO */ + break; + + case WM8750_MOUTM2: /* Mono Mixer Control (2) */ + s->mpath[1] = (value >> 8) & 1; /* RD2MO */ + break; + + case WM8750_LOUT1V: /* LOUT1 Volume */ + s->outvol[2] = value & 0x7f; /* LOUT2VOL */ + break; + + case WM8750_LOUT2V: /* LOUT2 Volume */ + s->outvol[4] = value & 0x7f; /* LOUT2VOL */ + break; + + case WM8750_ROUT1V: /* ROUT1 Volume */ + s->outvol[3] = value & 0x7f; /* ROUT2VOL */ + break; + + case WM8750_ROUT2V: /* ROUT2 Volume */ + s->outvol[5] = value & 0x7f; /* ROUT2VOL */ + break; + + case WM8750_MOUTV: /* MONOOUT Volume */ + s->outvol[6] = value & 0x7f; /* MONOOUTVOL */ + break; + + case WM8750_ADCTL2: /* Additional Control (2) */ + break; + + case WM8750_PWR2: /* Power Management (2) */ + s->power = value & 0x7e; + break; + + case WM8750_IFACE: /* Digital Audio Interface Format */ +#ifdef VERBOSE + if (value & 0x40)/* MS */ + printf("%s: attempt to enable Master Mode\n", __FUNCTION__); +#endif + s->format = value; + wm8750_set_format(s); + break; + + case WM8750_SRATE: /* Clocking and Sample Rate Control */ + s->rate = &wm_rate_table[(value >> 1) & 0x1f]; + wm8750_set_format(s); + break; + + case WM8750_RESET: /* Reset */ + wm8750_reset(&s->i2c); + break; + +#ifdef VERBOSE + default: + printf("%s: unknown register %02x\n", __FUNCTION__, cmd); +#endif + } + + return 0; +} + +struct i2c_slave_s *wm8750_init(AudioState *audio) +{ + struct wm8750_s *s = qemu_mallocz(sizeof(struct wm8750_s)); + s->i2c.opaque = s; + s->i2c.tx = wm8750_tx; + s->i2c.start = wm8750_start; + + AUD_register_card(audio, CODEC, &s->card); + wm8750_reset(&s->i2c); + return &s->i2c; +} + +void wm8750_fini(struct i2c_slave_s *i2c) +{ + struct wm8750_s *s = (struct wm8750_s *) i2c->opaque; + wm8750_reset(&s->i2c); + AUD_remove_card(&s->card); + qemu_free(s); +} + +void wm8750_data_req_set(struct i2c_slave_s *i2c, + void (*data_req)(void *, int, int), void *opaque) +{ + struct wm8750_s *s = (struct wm8750_s *) i2c->opaque; + s->data_req = data_req; + s->opaque = opaque; +} + +void wm8750_dac_dat(void *opaque, uint32_t sample) +{ + struct wm8750_s *s = (struct wm8750_s *) opaque; + uint32_t *data = (uint32_t *) &s->data_out[s->idx_out]; + *data = sample & s->outmask; + s->req_out -= 4; + s->idx_out += 4; + if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0) + wm8750_out_flush(s); +} + +uint32_t wm8750_adc_dat(void *opaque) +{ + struct wm8750_s *s = (struct wm8750_s *) opaque; + uint32_t *data; + if (s->idx_in >= sizeof(s->data_in)) + wm8750_in_load(s); + data = (uint32_t *) &s->data_in[s->idx_in]; + s->req_in -= 4; + s->idx_in += 4; + return *data & s->inmask; +} Index: qemu/keymaps.c =================================================================== --- qemu.orig/keymaps.c 2006-05-01 05:28:35.000000000 +0800 +++ qemu/keymaps.c 2007-02-12 15:21:33.000000000 +0800 @@ -84,7 +84,7 @@ *end_of_keysym = 0; keysym = get_keysym(line); if (keysym == 0) { - // fprintf(stderr, "Warning: unknown keysym %s\n", line); + fprintf(stderr, "Warning: unknown keysym %s\n", line); } else { const char *rest = end_of_keysym + 1; int keycode = strtol(rest, NULL, 0); Index: qemu/monitor.c =================================================================== --- qemu.orig/monitor.c 2007-02-06 04:46:05.000000000 +0800 +++ qemu/monitor.c 2007-02-24 21:23:04.000000000 +0800 @@ -54,7 +54,8 @@ const char *help; } term_cmd_t; -static CharDriverState *monitor_hd; +#define MAX_MON 4 +static CharDriverState *monitor_hd[MAX_MON]; static int hide_banner; static term_cmd_t term_cmds[]; @@ -69,8 +70,11 @@ void term_flush(void) { + int i; if (term_outbuf_index > 0) { - qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index); + for (i = 0; i < MAX_MON; i++) + if (monitor_hd[i] && monitor_hd[i]->focus == 0) + qemu_chr_write(monitor_hd[i], term_outbuf, term_outbuf_index); term_outbuf_index = 0; } } @@ -365,8 +369,6 @@ static void do_change(const char *device, const char *filename) { BlockDriverState *bs; - int i; - char password[256]; bs = bdrv_find(device); if (!bs) { @@ -376,15 +378,7 @@ if (eject_device(bs, 0) < 0) return; bdrv_open(bs, filename, 0); - if (bdrv_is_encrypted(bs)) { - term_printf("%s is encrypted.\n", device); - for(i = 0; i < 3; i++) { - monitor_readline("Password: ", 1, password, sizeof(password)); - if (bdrv_set_key(bs, password) == 0) - break; - term_printf("invalid password\n"); - } - } + qemu_key_check(bs, filename); } static void do_screen_dump(const char *filename) @@ -419,14 +413,14 @@ } #ifdef CONFIG_GDBSTUB -static void do_gdbserver(int has_port, int port) +static void do_gdbserver(const char *port) { - if (!has_port) + if (!port) port = DEFAULT_GDBSTUB_PORT; - if (gdbserver_start_port(port) < 0) { - qemu_printf("Could not open gdbserver socket on port %d\n", port); + if (gdbserver_start(port) < 0) { + qemu_printf("Could not open gdbserver socket on port '%s'\n", port); } else { - qemu_printf("Waiting gdb connection on port %d\n", port); + qemu_printf("Waiting gdb connection on port '%s'\n", port); } } #endif @@ -1212,7 +1206,7 @@ { "c|cont", "", do_cont, "", "resume emulation", }, #ifdef CONFIG_GDBSTUB - { "gdbserver", "i?", do_gdbserver, + { "gdbserver", "s?", do_gdbserver, "[port]", "start gdbserver session (default port=1234)", }, #endif { "x", "/l", do_memory_dump, @@ -1295,6 +1289,8 @@ "", "show capture information" }, { "snapshots", "", do_info_snapshots, "", "show the currently saved VM snapshots" }, + { "pcmcia", "", pcmcia_info, + "", "show guest PCMCIA status" }, { "mice", "", do_info_mice, "", "show which guest mouse is receiving events" }, { "vnc", "", do_info_vnc, @@ -2452,9 +2448,25 @@ monitor_start_input(); } +static int is_first_init = 1; + void monitor_init(CharDriverState *hd, int show_banner) { - monitor_hd = hd; + int i; + + if (is_first_init) { + for (i = 0; i < MAX_MON; i++) { + monitor_hd[i] = NULL; + } + is_first_init = 0; + } + for (i = 0; i < MAX_MON; i++) { + if (monitor_hd[i] == NULL) { + monitor_hd[i] = hd; + break; + } + } + hide_banner = !show_banner; qemu_chr_add_handlers(hd, term_can_read, term_read, term_event, NULL); @@ -2475,8 +2487,12 @@ void monitor_readline(const char *prompt, int is_password, char *buf, int buf_size) { + int i; + if (is_password) { - qemu_chr_send_event(monitor_hd, CHR_EVENT_FOCUS); + for (i = 0; i < MAX_MON; i++) + if (monitor_hd[i] && monitor_hd[i]->focus == 0) + qemu_chr_send_event(monitor_hd[i], CHR_EVENT_FOCUS); } readline_start(prompt, is_password, monitor_readline_cb, NULL); monitor_readline_buf = buf; Index: qemu/raw2flash.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/raw2flash.c 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2006 OpenedHand Ltd. + * + * This file is licensed under GNU GPL v2. + */ +#include +#include +#include +#include +#include +#include + +#define TFR(_) _ +#define VERBOSE +#define PBAR_LEN 40 + +#define PARTITION_START 0x00700000 + +static const int ecc_pos8[] = { + 0x0, 0x1, 0x2, +}; + +static const int ecc_pos16[] = { + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, +}; + +static const int ecc_pos64[] = { + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, +}; + +static const int ecc_akita[] = { + 0x05, 0x01, 0x02, 0x03, 0x06, 0x07, 0x15, 0x11, + 0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23, + 0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37, +}; + +struct jffs_marker_s { + int pos; + uint8_t value; +}; + +static const struct jffs_marker_s free_pos8[] = { + { 0x03, 0xff }, { 0x04, 0xff }, { 0x06, 0x85 }, { 0x07, 0x19 }, + { -1 }, +}; + +static const struct jffs_marker_s free_pos16[] = { + { 0x08, 0x85 }, { 0x09, 0x19 }, { 0x0a, 0x03 }, { 0x0b, 0x20 }, + { 0x0c, 0x08 }, { 0x0d, 0x00 }, { 0x0e, 0x00 }, { 0x0f, 0x00 }, + { -1 }, +}; + +static const struct jffs_marker_s free_pos64[] = { + { 0x02, 0xff }, { 0x03, 0xff }, { 0x04, 0xff }, { 0x05, 0xff }, + { 0x06, 0xff }, { 0x07, 0xff }, { 0x08, 0xff }, { 0x09, 0xff }, + { 0x0a, 0xff }, { 0x0b, 0xff }, { 0x0c, 0xff }, { 0x0d, 0xff }, + { 0x0e, 0xff }, { 0x0f, 0xff }, { 0x10, 0x85 }, { 0x11, 0x19 }, + { 0x12, 0x03 }, { 0x13, 0x20 }, { 0x14, 0x08 }, { 0x15, 0x00 }, + { 0x16, 0x00 }, { 0x17, 0x00 }, { 0x18, 0xff }, { 0x19, 0xff }, + { 0x1a, 0xff }, { 0x1b, 0xff }, { 0x1c, 0xff }, { 0x1d, 0xff }, + { 0x1e, 0xff }, { 0x1f, 0xff }, { 0x20, 0xff }, { 0x21, 0xff }, + { 0x22, 0xff }, { 0x23, 0xff }, { 0x24, 0xff }, { 0x25, 0xff }, + { 0x26, 0xff }, { 0x27, 0xff }, + { -1 }, +}; + +static const struct jffs_marker_s free_akita[] = { + { 0x08, 0x85 }, { 0x09, 0x19 }, { 0x0a, 0x03 }, { 0x0b, 0x20 }, + { 0x0c, 0x08 }, { 0x0d, 0x00 }, { 0x0e, 0x00 }, { 0x0f, 0x00 }, + { 0x10, 0xff }, + { -1 }, +}; + +#define LEN(array) (sizeof(array) / sizeof(*array)) + +static const struct ecc_style_s { + int page_size; + int oob_size; + int eccbytes; + int eccsize; + const int *eccpos; + int romsize; + const struct jffs_marker_s *freepos; +} spitz = { + 0x200, 0x10, 0x100, LEN(ecc_pos16), ecc_pos16, 0x01000000, free_pos16 +}, akita = { + 0x800, 0x40, 0x100, LEN(ecc_akita), ecc_akita, 0x08000000, free_akita +}, borzoi = { + 0x800, 0x40, 0x100, LEN(ecc_akita), ecc_akita, 0x08000000, free_akita +}, terrier = { + 0x800, 0x40, 0x100, LEN(ecc_akita), ecc_akita, 0x08000000, free_akita +}; + +struct ecc_state_s { + int count; + uint8_t cp; + uint8_t lp[2]; + const struct ecc_style_s *style; +}; + +#ifndef flash2raw +/* + * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux. + */ +static const uint8_t ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, + 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, + 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, + 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, + 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, + 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, + 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, + 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, + 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, + 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, + 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, + 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, + 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, + 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, + 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, + 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, + 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, +}; + +/* Update ECC parity count */ +static inline uint8_t ecc_digest(struct ecc_state_s *s, uint8_t sample) { + uint8_t idx = ecc_precalc_table[sample]; + + s->cp ^= idx & 0x3f; + if (idx & 0x40) { + s->lp[0] ^= ~(s->count & 0xff); + s->lp[1] ^= s->count & 0xff; + } + s->count ++; + + return sample; +} + +static void buffer_digest(struct ecc_state_s *ecc, + const uint8_t *buf, uint8_t *out) { + int i, lp_a[2]; + + ecc->lp[0] = 0x00; + ecc->lp[1] = 0x00; + ecc->cp = 0x00; + ecc->count = 0; + for (i = 0; i < ecc->style->eccbytes; i ++) + ecc_digest(ecc, buf[i]); + +# define BSHR(byte, from, to) ((ecc->lp[byte] >> (from - to)) & (1 << to)) + lp_a[0] = + BSHR(0, 4, 0) | BSHR(0, 5, 2) | + BSHR(0, 6, 4) | BSHR(0, 7, 6) | + BSHR(1, 4, 1) | BSHR(1, 5, 3) | + BSHR(1, 6, 5) | BSHR(1, 7, 7); + +# define BSHL(byte, from, to) ((ecc->lp[byte] << (to - from)) & (1 << to)) + lp_a[1] = + BSHL(0, 0, 0) | BSHL(0, 1, 2) | + BSHL(0, 2, 4) | BSHL(0, 3, 6) | + BSHL(1, 0, 1) | BSHL(1, 1, 3) | + BSHL(1, 2, 5) | BSHL(1, 3, 7); + + out[0] = ~lp_a[1]; + out[1] = ~lp_a[0]; + out[2] = (~ecc->cp << 2) | 0x03; +} + +static void jffs2_format(const struct ecc_state_s *ecc, uint8_t oob[]) { + const struct jffs_marker_s *byte; + for (byte = ecc->style->freepos; byte->pos >= 0; byte ++) + oob[byte->pos] = byte->value; +} + +static void buffer_fill(const struct ecc_state_s *ecc, uint8_t buffer[], + int *len, int *partition, int count, uint8_t jffs_buffer[]) { + int ret; + + switch (*partition) { + case 0: + if (count < PARTITION_START) { + memcpy(buffer, jffs_buffer + count, + ecc->style->eccbytes); + *len = ecc->style->eccbytes; + break; + } + *partition = 1; + case 1: + if (count - PARTITION_START < PARTITION_START) { + memcpy(buffer, jffs_buffer + count - PARTITION_START, + ecc->style->eccbytes); + *len = ecc->style->eccbytes; + break; + } + + while (*len < ecc->style->eccbytes) { + ret = TFR(read(0, buffer + *len, 0x800 - *len)); + if (ret <= 0) + break; + *len += ret; + } + + if (*len == 0) + *partition = 2; + else if (*len < ecc->style->eccbytes) { + fprintf(stderr, "\nWarning: %i stray bytes\n", *len); + memset(buffer + *len, 0xff, + ecc->style->eccbytes - *len); + *len = ecc->style->eccbytes; + break; + } else + break; + case 2: + memset(buffer, 0xff, ecc->style->eccbytes); + *len = ecc->style->eccbytes; + break; + } +} + +int main(int argc, char *argv[], char *envp[]) { + struct ecc_state_s ecc; + uint8_t buffer[0x1000], ecc_payload[0x40], regs[3], *jffs; + int ret, len, eccbyte, count, partition; + + /* Check if we're called by "raw2flash.spitz" or similar */ + len = strlen(argv[0]); + if (!strcasecmp(argv[0] + len - 5, "akita")) + ecc.style = &akita; + else if (!strcasecmp(argv[0] + len - 6, "borzoi")) + ecc.style = &borzoi; + else if (!strcasecmp(argv[0] + len - 7, "terrier")) + ecc.style = &terrier; + else + ecc.style = &spitz; + +# ifdef VERBOSE + fprintf(stderr, "["); +# endif + + /* Skip first 10 bytes */ + TFR(read(0, buffer, 0x10)); + + len = 0; + jffs = (uint8_t *) malloc(PARTITION_START); + while (len < PARTITION_START) { + ret = TFR(read(0, jffs + len, PARTITION_START - len)); + if (ret <= 0) + break; + len += ret; + } + + /* Convert data from stdin */ + partition = len = eccbyte = count = 0; + memset(ecc_payload, 0xff, ecc.style->oob_size); + jffs2_format(&ecc, ecc_payload); + while (count < ecc.style->romsize) { + buffer_fill(&ecc, buffer, &len, &partition, count, jffs); + buffer_digest(&ecc, buffer, regs); + + ecc_payload[ecc.style->eccpos[eccbyte ++]] = regs[0]; + ecc_payload[ecc.style->eccpos[eccbyte ++]] = regs[1]; + ecc_payload[ecc.style->eccpos[eccbyte ++]] = regs[2]; + + TFR(write(1, buffer, ecc.style->eccbytes)); + count += ecc.style->eccbytes; + len -= ecc.style->eccbytes; + memmove(buffer, buffer + ecc.style->eccbytes, len); + + if (eccbyte >= ecc.style->eccsize) { + TFR(write(1, ecc_payload, ecc.style->oob_size)); + eccbyte = 0; + memset(ecc_payload, 0xff, ecc.style->oob_size); + if (partition < 2) + jffs2_format(&ecc, ecc_payload); + } + +# ifdef VERBOSE + if (count * PBAR_LEN / ecc.style->romsize > + (count - ecc.style->eccbytes) * + PBAR_LEN / ecc.style->romsize) + fprintf(stderr, "#"); +# endif + } + +# ifdef VERBOSE + fprintf(stderr, "]\n"); +# endif + free(jffs); + return 0; +} +#else +int main(int argc, char *argv[], char *envp[]) { + struct ecc_state_s ecc; + uint8_t buffer[0x1000]; + int ret, len, count; + + /* Check if we're called by "flash2raw.spitz" or similar */ + len = strlen(argv[0]); + if (!strcasecmp(argv[0] + len - 5, "akita")) + ecc.style = &akita; + else if (!strcasecmp(argv[0] + len - 6, "borzoi")) + ecc.style = &borzoi; + else if (!strcasecmp(argv[0] + len - 7, "terrier")) + ecc.style = &terrier; + else + ecc.style = &spitz; + +# ifdef VERBOSE + fprintf(stderr, "["); +# endif + + /* Convert data from stdin */ + count = 0; + while (count < ecc.style->romsize) { + len = 0; + while (len < ecc.style->page_size) { + ret = TFR(read(0, buffer + len, + ecc.style->page_size - len)); + if (ret <= 0) + break; + len += ret; + } + if (len == 0) + break; + if (len < ecc.style->page_size) { + fprintf(stderr, "\nWarning: %i stray bytes\n", len); + } + + TFR(write(1, buffer, ecc.style->page_size)); + + count += len; + len = 0; + while (len < ecc.style->oob_size) { + ret = TFR(read(0, buffer, ecc.style->oob_size - len)); + if (ret <= 0) + break; + len += ret; + } + +# ifdef VERBOSE + if (count * PBAR_LEN / ecc.style->romsize > + (count - ecc.style->page_size) * + PBAR_LEN / ecc.style->romsize) + fprintf(stderr, "#"); +# endif + } + +# ifdef VERBOSE + fprintf(stderr, "]\n"); +# endif + return 0; +} +#endif Index: qemu/sdl.c =================================================================== --- qemu.orig/sdl.c 2007-01-25 05:40:21.000000000 +0800 +++ qemu/sdl.c 2007-02-24 21:23:04.000000000 +0800 @@ -34,6 +34,7 @@ static int last_vm_running; static int gui_saved_grab; static int gui_fullscreen; +static int gui_noframe; static int gui_key_modifier_pressed; static int gui_keysym; static int gui_fullscreen_initial_grab; @@ -59,6 +60,8 @@ flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL; if (gui_fullscreen) flags |= SDL_FULLSCREEN; + if (gui_noframe) + flags |= SDL_NOFRAME; width = w; height = h; @@ -225,6 +228,9 @@ static void sdl_hide_cursor(void) { + if (!cursor_hide) + return; + if (kbd_mouse_is_absolute()) { SDL_ShowCursor(1); SDL_SetCursor(sdl_cursor_hidden); @@ -235,6 +241,9 @@ static void sdl_show_cursor(void) { + if (!cursor_hide) + return; + if (!kbd_mouse_is_absolute()) { SDL_ShowCursor(1); SDL_SetCursor(sdl_cursor_normal); @@ -469,7 +478,7 @@ SDL_Quit(); } -void sdl_display_init(DisplayState *ds, int full_screen) +void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) { int flags; uint8_t data = 0; @@ -485,6 +494,9 @@ exit(1); } + if (no_frame) + gui_noframe = 1; + flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; if (SDL_Init (flags)) { fprintf(stderr, "Could not initialize SDL - exiting\n"); Index: qemu/target-arm/cpu.h =================================================================== --- qemu.orig/target-arm/cpu.h 2007-01-31 20:16:50.000000000 +0800 +++ qemu/target-arm/cpu.h 2007-02-12 15:21:33.000000000 +0800 @@ -38,6 +38,10 @@ #define EXCP_FIQ 6 #define EXCP_BKPT 7 +typedef void ARMWriteCPFunc(void *opaque, + int op2, int reg, int crm, uint32_t value); +typedef uint32_t ARMReadCPFunc(void *opaque, int op2, int reg, int crm); + /* We currently assume float and double are IEEE single and double precision respectively. Doing runtime conversions is tricky because VFP registers may contain @@ -75,6 +79,7 @@ /* System control coprocessor (cp15) */ struct { uint32_t c0_cpuid; + uint32_t c0_cachetype; uint32_t c1_sys; /* System control register. */ uint32_t c1_coproc; /* Coprocessor access register. */ uint32_t c2; /* MMU translation table base. */ @@ -87,8 +92,16 @@ uint32_t c9_data; uint32_t c13_fcse; /* FCSE PID. */ uint32_t c13_context; /* Context ID. */ + uint32_t c15_cpar; /* XScale Coprocessor Access Register */ } cp15; + /* Coprocessor IO used by peripherals */ + struct { + ARMReadCPFunc *cp_read; + ARMWriteCPFunc *cp_write; + void *opaque; + } cp[15]; + /* Internal CPU feature flags. */ uint32_t features; @@ -115,6 +128,14 @@ float_status fp_status; } vfp; + /* iwMMXt coprocessor state. */ + struct { + uint64_t regs[16]; + uint64_t val; + + uint32_t cregs[16]; + } iwmmxt; + #if defined(CONFIG_USER_ONLY) /* For usermode syscall translation. */ int eabi; @@ -198,10 +219,21 @@ #define ARM_VFP_FPINST 9 #define ARM_VFP_FPINST2 10 +/* iwMMXt coprocessor control registers. */ +#define ARM_IWMMXT_wCID 0 +#define ARM_IWMMXT_wCon 1 +#define ARM_IWMMXT_wCSSF 2 +#define ARM_IWMMXT_wCASF 3 +#define ARM_IWMMXT_wCGR0 8 +#define ARM_IWMMXT_wCGR1 9 +#define ARM_IWMMXT_wCGR2 10 +#define ARM_IWMMXT_wCGR3 11 enum arm_features { ARM_FEATURE_VFP, - ARM_FEATURE_AUXCR /* ARM1026 Auxiliary control register. */ + ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ + ARM_FEATURE_XSCALE, /* Intel XScale extensions. */ + ARM_FEATURE_IWMMXT /* Intel iwMMXt extension. */ }; static inline int arm_feature(CPUARMState *env, int feature) @@ -211,8 +243,25 @@ void cpu_arm_set_model(CPUARMState *env, uint32_t id); -#define ARM_CPUID_ARM1026 0x4106a262 -#define ARM_CPUID_ARM926 0x41069265 +void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, + ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, + void *opaque); + +#define ARM_CPUID_ARM1026 0x4106a262 +#define ARM_CPUID_ARM926 0x41069265 +#define ARM_CPUID_ARM920T 0x41129200 +#define ARM_CPUID_PXA250 0x69052100 +#define ARM_CPUID_PXA255 0x69052d00 +#define ARM_CPUID_PXA260 0x69052903 +#define ARM_CPUID_PXA261 0x69052d05 +#define ARM_CPUID_PXA262 0x69052d06 +#define ARM_CPUID_PXA270 0x69054110 +#define ARM_CPUID_PXA270_A0 0x69054110 +#define ARM_CPUID_PXA270_A1 0x69054111 +#define ARM_CPUID_PXA270_B0 0x69054112 +#define ARM_CPUID_PXA270_B1 0x69054113 +#define ARM_CPUID_PXA270_C0 0x69054114 +#define ARM_CPUID_PXA270_C5 0x69054117 #if defined(CONFIG_USER_ONLY) #define TARGET_PAGE_BITS 12 Index: qemu/target-arm/exec.h =================================================================== --- qemu.orig/target-arm/exec.h 2007-02-02 09:03:34.000000000 +0800 +++ qemu/target-arm/exec.h 2007-02-12 15:21:33.000000000 +0800 @@ -38,6 +38,8 @@ #define FT0d env->vfp.tmp0d #define FT1d env->vfp.tmp1d +#define M0 env->iwmmxt.val + #include "cpu.h" #include "exec-all.h" @@ -60,6 +62,8 @@ void cpu_lock(void); void cpu_unlock(void); +void helper_set_cp(CPUState *, uint32_t, uint32_t); +uint32_t helper_get_cp(CPUState *, uint32_t); void helper_set_cp15(CPUState *, uint32_t, uint32_t); uint32_t helper_get_cp15(CPUState *, uint32_t); Index: qemu/target-arm/helper.c =================================================================== --- qemu.orig/target-arm/helper.c 2007-01-21 01:12:09.000000000 +0800 +++ qemu/target-arm/helper.c 2007-02-12 15:21:33.000000000 +0800 @@ -43,11 +43,34 @@ case ARM_CPUID_ARM926: set_feature(env, ARM_FEATURE_VFP); env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090; + env->cp15.c0_cachetype = 0x1dd20d2; break; case ARM_CPUID_ARM1026: set_feature(env, ARM_FEATURE_VFP); set_feature(env, ARM_FEATURE_AUXCR); env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0; + env->cp15.c0_cachetype = 0x1dd20d2; + break; + case ARM_CPUID_PXA250: + case ARM_CPUID_PXA255: + case ARM_CPUID_PXA260: + case ARM_CPUID_PXA261: + case ARM_CPUID_PXA262: + set_feature(env, ARM_FEATURE_XSCALE); + /* JTAG_ID is ((id << 28) | 0x09265013) */ + env->cp15.c0_cachetype = 0xd172172; + break; + case ARM_CPUID_PXA270_A0: + case ARM_CPUID_PXA270_A1: + case ARM_CPUID_PXA270_B0: + case ARM_CPUID_PXA270_B1: + case ARM_CPUID_PXA270_C0: + case ARM_CPUID_PXA270_C5: + set_feature(env, ARM_FEATURE_XSCALE); + /* JTAG_ID is ((id << 28) | 0x09265013) */ + set_feature(env, ARM_FEATURE_IWMMXT); + env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; + env->cp15.c0_cachetype = 0xd172172; break; default: cpu_abort(env, "Bad CPU ID: %x\n", id); @@ -324,7 +347,7 @@ phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); ap = (desc >> 10) & 3; code = 13; - } else { + } else if (type == 1) { /* Lookup l2 entry. */ table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); desc = ldl_phys(table); @@ -341,12 +364,16 @@ ap = (desc >> (4 + ((address >> 13) & 6))) & 3; break; case 3: /* 1k page. */ - if (type == 1) { - /* Page translation fault. */ - code = 7; - goto do_fault; + if (arm_feature(env, ARM_FEATURE_XSCALE)) + phys_addr = (desc & 0xfffff000) | (address & 0xfff); + else { + if (type == 1) { + /* Page translation fault. */ + code = 7; + goto do_fault; + } + phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); } - phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); ap = (desc >> 4) & 3; break; default: @@ -354,7 +381,11 @@ abort(); } code = 15; + } else { + code = 15; + goto do_fault; } + *prot = check_ap(env, ap, domain, access_type, is_user); if (!*prot) { /* Access permission fault. */ @@ -409,6 +440,41 @@ return phys_addr; } +void helper_set_cp(CPUState *env, uint32_t insn, uint32_t val) +{ + int op1 = (insn >> 8) & 0xf; + int op2 = (insn >> 5) & 7; + int reg = (insn >> 16) & 0xf; + int crm = insn & 0xf; + + /* On XScale coprocessors [0, 13] have to be enabled in CPAR. */ + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + if (op1 <= 13 && (env->cp15.c15_cpar & (1 << op1)) == 0) + return; /* Permission Denied */ + } + + if (env->cp[op1].cp_write) + env->cp[op1].cp_write(env->cp[op1].opaque, op2, reg, crm, val); +} + +uint32_t helper_get_cp(CPUState *env, uint32_t insn) +{ + int op1 = (insn >> 8) & 0xf; + int op2 = (insn >> 5) & 7; + int reg = (insn >> 16) & 0xf; + int crm = insn & 0xf; + + /* On XScale coprocessors [0, 13] have to be enabled in CPAR. */ + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + if (op1 <= 13 && (env->cp15.c15_cpar & (1 << op1)) == 0) + return 0; /* Permission Denied */ + } + + if (env->cp[op1].cp_read) + return env->cp[op1].cp_read(env->cp[op1].opaque, op2, reg, crm); + return 0; +} + void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) { uint32_t op2; @@ -420,15 +486,23 @@ case 1: /* System configuration. */ switch (op2) { case 0: - env->cp15.c1_sys = val; + if (!arm_feature(env, ARM_FEATURE_XSCALE) || (insn & 0xf) == 0) + env->cp15.c1_sys = val; /* ??? Lots of these bits are not implemented. */ /* This may enable/disable the MMU, so do a TLB flush. */ tlb_flush(env, 1); break; + case 1: + /* XScale doesn't implement AUX CR (P-Bit) but allows + * writing with zero and reading. */ + if (arm_feature(env, ARM_FEATURE_XSCALE)) + break; + goto bad_reg; case 2: env->cp15.c1_coproc = val; /* ??? Is this safe when called from within a TB? */ tb_flush(env); + break; default: goto bad_reg; } @@ -532,13 +606,19 @@ case 14: /* Reserved. */ goto bad_reg; case 15: /* Implementation specific. */ - /* ??? Internal registers not implemented. */ + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + if (op2 == 0 && (insn & 0xf) == 1) { + env->cp15.c15_cpar = val & 0x3fff; + break; + } + goto bad_reg; + } break; } return; bad_reg: /* ??? For debugging only. Should raise illegal instruction exception. */ - cpu_abort(env, "Unimplemented cp15 register read\n"); + cpu_abort(env, "Unimplemented cp15 register write\n"); } uint32_t helper_get_cp15(CPUState *env, uint32_t insn) @@ -552,7 +632,7 @@ default: /* Device ID. */ return env->cp15.c0_cpuid; case 1: /* Cache Type. */ - return 0x1dd20d2; + return env->cp15.c0_cachetype; case 2: /* TCM status. */ return 0; } @@ -563,6 +643,8 @@ case 1: /* Auxiliary control register. */ if (arm_feature(env, ARM_FEATURE_AUXCR)) return 1; + if (arm_feature(env, ARM_FEATURE_XSCALE)) + return 0; goto bad_reg; case 2: /* Coprocessor access register. */ return env->cp15.c1_coproc; @@ -597,7 +679,7 @@ } case 7: /* Cache control. */ /* ??? This is for test, clean and invaidate operations that set the - Z flag. We can't represent N = Z = 1, so it also clears clears + Z flag. We can't represent N = Z = 1, so it also clears the N flag. Oh well. */ env->NZF = 0; return 0; @@ -630,7 +712,12 @@ case 14: /* Reserved. */ goto bad_reg; case 15: /* Implementation specific. */ - /* ??? Internal registers not implemented. */ + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + if (op2 == 0 && (insn & 0xf) == 1) + return env->cp15.c15_cpar; + + goto bad_reg; + } return 0; } bad_reg: @@ -639,4 +726,18 @@ return 0; } +void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, + ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, + void *opaque) +{ + if (cpnum < 0 || cpnum > 14) { + cpu_abort(env, "Bad coprocessor number: %i\n", cpnum); + return; + } + + env->cp[cpnum].cp_read = cp_read; + env->cp[cpnum].cp_write = cp_write; + env->cp[cpnum].opaque = opaque; +} + #endif Index: qemu/target-arm/op.c =================================================================== --- qemu.orig/target-arm/op.c 2006-06-27 03:55:19.000000000 +0800 +++ qemu/target-arm/op.c 2007-02-12 15:25:56.000000000 +0800 @@ -3,6 +3,7 @@ * * Copyright (c) 2003 Fabrice Bellard * Copyright (c) 2005 CodeSourcery, LLC + * Copyright (c) 2007 OpenedHand, Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1142,12 +1143,24 @@ FT0d = u.d; } -/* Copy the most significant bit to T0 to all bits of T1. */ +/* Copy the most significant bit of T0 to all bits of T1. */ void OPPROTO op_signbit_T1_T0(void) { T1 = (int32_t)T0 >> 31; } +void OPPROTO op_movl_cp_T0(void) +{ + helper_set_cp(env, PARAM1, T0); + FORCE_RET(); +} + +void OPPROTO op_movl_T0_cp(void) +{ + T0 = helper_get_cp(env, PARAM1); + FORCE_RET(); +} + void OPPROTO op_movl_cp15_T0(void) { helper_set_cp15(env, PARAM1, T0); @@ -1201,3 +1214,692 @@ { T0 = T2; } + +/* iwMMXt support. */ + +#define M1 env->iwmmxt.regs[PARAM1] + +/* iwMMXt macros extracted from GNU gdb. */ + +/* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations. */ +#define SIMD8_SET( v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) +#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) +#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) +#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) +/* Flags to pass as "n" above. */ +#define SIMD_NBIT -1 +#define SIMD_ZBIT -2 +#define SIMD_CBIT -3 +#define SIMD_VBIT -4 +/* Various status bit macros. */ +#define NBIT8(x) ((x) & 0x80) +#define NBIT16(x) ((x) & 0x8000) +#define NBIT32(x) ((x) & 0x80000000) +#define NBIT64(x) ((x) & 0x8000000000000000ULL) +#define ZBIT8(x) (((x) & 0xff) == 0) +#define ZBIT16(x) (((x) & 0xffff) == 0) +#define ZBIT32(x) (((x) & 0xffffffff) == 0) +#define ZBIT64(x) (x == 0) +/* Sign extension macros. */ +#define EXTEND8H(a) ((a) & 0x80 ? ((a) | 0xff00) : (a)) +#define EXTEND8(a) ((a) & 0x80 ? ((a) | 0xffffff00) : (a)) +#define EXTEND16(a) ((a) & 0x8000 ? ((a) | 0xffff0000) : (a)) +#define EXTEND32(a) ((a) & 0x80000000ULL ? \ + ((a) | 0xffffffff00000000ULL) : (a)) + +void OPPROTO op_iwmmxt_movl_T0_T1_wRn(void) +{ + T0 = M1 & ~(uint32_t) 0; + T1 = M1 >> 32; +} + +void OPPROTO op_iwmmxt_movl_wRn_T0_T1(void) +{ + M1 = ((uint64_t) T1 << 32) | T0; +} + +void OPPROTO op_iwmmxt_movq_M0_wRn(void) +{ + M0 = M1; +} + +void OPPROTO op_iwmmxt_orq_M0_wRn(void) +{ + M0 |= M1; +} + +void OPPROTO op_iwmmxt_andq_M0_wRn(void) +{ + M0 &= M1; +} + +void OPPROTO op_iwmmxt_xorq_M0_wRn(void) +{ + M0 ^= M1; +} + +void OPPROTO op_iwmmxt_maddsq_M0_wRn(void) +{ + M0 = (( + EXTEND16((M0 >> 0) & 0xffff) * EXTEND16((M1 >> 0) & 0xffff) + + EXTEND16((M0 >> 16) & 0xffff) * EXTEND16((M1 >> 16) & 0xffff) + ) & 0xffffffff) | (( + EXTEND16((M0 >> 32) & 0xffff) * EXTEND16((M1 >> 32) & 0xffff) + + EXTEND16((M0 >> 48) & 0xffff) * EXTEND16((M1 >> 48) & 0xffff) + ) << 32); +} + +void OPPROTO op_iwmmxt_madduq_M0_wRn(void) +{ + M0 = (( + ((M0 >> 0) & 0xffff) * ((M1 >> 0) & 0xffff) + + ((M0 >> 16) & 0xffff) * ((M1 >> 16) & 0xffff) + ) & 0xffffffff) | (( + ((M0 >> 32) & 0xffff) * ((M1 >> 32) & 0xffff) + + ((M0 >> 48) & 0xffff) * ((M1 >> 48) & 0xffff) + ) << 32); +} + +void OPPROTO op_iwmmxt_sadb_M0_wRn(void) +{ +#define abs(x) (((x) >= 0) ? x : -x) +#define SADB(SHR) abs((int) ((M0 >> SHR) & 0xff) - (int) ((M1 >> SHR) & 0xff)) + M0 = + SADB(0) + SADB(8) + SADB(16) + SADB(24) + + SADB(32) + SADB(40) + SADB(48) + SADB(56); +#undef SADB +} + +void OPPROTO op_iwmmxt_sadw_M0_wRn(void) +{ +#define SADW(SHR) \ + abs((int) ((M0 >> SHR) & 0xffff) - (int) ((M1 >> SHR) & 0xffff)) + M0 = SADW(0) + SADW(16) + SADW(32) + SADW(48); +#undef SADW +} + +void OPPROTO op_iwmmxt_addl_M0_wRn(void) +{ + M0 += env->iwmmxt.regs[PARAM1] & 0xffffffff; +} + +void OPPROTO op_iwmmxt_mulsw_M0_wRn(void) +{ +#define MULS(SHR) (((( \ + EXTEND16((M0 >> SHR) & 0xffff) * EXTEND16((M1 >> SHR) & 0xffff) \ + ) >> PARAM2) & 0xffff) << SHR) + M0 = MULS(0) | MULS(16) | MULS(32) | MULS(48); +#undef MULS +} + +void OPPROTO op_iwmmxt_muluw_M0_wRn(void) +{ +#define MULU(SHR) (((( \ + ((M0 >> SHR) & 0xffff) * ((M1 >> SHR) & 0xffff) \ + ) >> PARAM2) & 0xffff) << SHR) + M0 = MULU(0) | MULU(16) | MULU(32) | MULU(48); +#undef MULU +} + +void OPPROTO op_iwmmxt_macsw_M0_wRn(void) +{ +#define MACS(SHR) ( \ + (int32_t) EXTEND16((M0 >> SHR) & 0xffff) * \ + (int32_t) EXTEND16((M1 >> SHR) & 0xffff)) + M0 = (int64_t) (MACS(0) + MACS(16) + MACS(32) + MACS(48)); +#undef MACS +} + +void OPPROTO op_iwmmxt_macuw_M0_wRn(void) +{ +#define MACU(SHR) ( \ + (uint32_t) ((M0 >> SHR) & 0xffff) * \ + (uint32_t) ((M1 >> SHR) & 0xffff)) + M0 = MACU(0) + MACU(16) + MACU(32) + MACU(48); +#undef MACU +} + +void OPPROTO op_iwmmxt_addsq_M0_wRn(void) +{ + M0 = (int64_t) M0 + (int64_t) M1; +} + +void OPPROTO op_iwmmxt_adduq_M0_wRn(void) +{ + M0 += M1; +} + +void OPPROTO op_iwmmxt_movq_wRn_M0(void) +{ + M1 = M0; +} + +void OPPROTO op_iwmmxt_movl_wCx_T0(void) +{ + env->iwmmxt.cregs[PARAM1] = T0; +} + +void OPPROTO op_iwmmxt_movl_T0_wCx(void) +{ + T0 = env->iwmmxt.cregs[PARAM1]; +} + +void OPPROTO op_iwmmxt_movl_T1_wCx(void) +{ + T1 = env->iwmmxt.cregs[PARAM1]; +} + +void OPPROTO op_iwmmxt_set_mup(void) +{ + env->iwmmxt.cregs[ARM_IWMMXT_wCon] |= 2; +} + +void OPPROTO op_iwmmxt_set_cup(void) +{ + env->iwmmxt.cregs[ARM_IWMMXT_wCon] |= 1; +} + +void OPPROTO op_iwmmxt_setpsr_nz(void) +{ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + SIMD64_SET((M0 == 0), SIMD_ZBIT) | + SIMD64_SET((M0 & (1ULL << 63)), SIMD_NBIT); +} + +void OPPROTO op_iwmmxt_negq_M0(void) +{ + M0 = ~M0; +} + +#define NZBIT8(x, i) \ + SIMD8_SET(NBIT8((x) & 0xff), SIMD_NBIT, i) | \ + SIMD8_SET(ZBIT8((x) & 0xff), SIMD_ZBIT, i) +#define NZBIT16(x, i) \ + SIMD16_SET(NBIT16((x) & 0xffff), SIMD_NBIT, i) | \ + SIMD16_SET(ZBIT16((x) & 0xffff), SIMD_ZBIT, i) +#define NZBIT32(x, i) \ + SIMD32_SET(NBIT32((x) & 0xffffffff), SIMD_NBIT, i) | \ + SIMD32_SET(ZBIT32((x) & 0xffffffff), SIMD_ZBIT, i) +#define NZBIT64(x) \ + SIMD64_SET(NBIT64(x), SIMD_NBIT) | \ + SIMD64_SET(ZBIT64(x), SIMD_ZBIT) +#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, b_M0_wRn))(void) \ +{ \ + M0 = \ + (((M0 >> SH0) & 0xff) << 0) | (((M1 >> SH0) & 0xff) << 8) | \ + (((M0 >> SH1) & 0xff) << 16) | (((M1 >> SH1) & 0xff) << 24) | \ + (((M0 >> SH2) & 0xff) << 32) | (((M1 >> SH2) & 0xff) << 40) | \ + (((M0 >> SH3) & 0xff) << 48) | (((M1 >> SH3) & 0xff) << 56); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(M0 >> 0, 0) | NZBIT8(M0 >> 8, 1) | \ + NZBIT8(M0 >> 16, 2) | NZBIT8(M0 >> 24, 3) | \ + NZBIT8(M0 >> 32, 4) | NZBIT8(M0 >> 40, 5) | \ + NZBIT8(M0 >> 48, 6) | NZBIT8(M0 >> 56, 7); \ +} \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, w_M0_wRn))(void) \ +{ \ + M0 = \ + (((M0 >> SH0) & 0xffff) << 0) | \ + (((M1 >> SH0) & 0xffff) << 16) | \ + (((M0 >> SH2) & 0xffff) << 32) | \ + (((M1 >> SH2) & 0xffff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(M0 >> 0, 0) | NZBIT8(M0 >> 16, 1) | \ + NZBIT8(M0 >> 32, 2) | NZBIT8(M0 >> 48, 3); \ +} \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, l_M0_wRn))(void) \ +{ \ + M0 = \ + (((M0 >> SH0) & 0xffffffff) << 0) | \ + (((M1 >> SH0) & 0xffffffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); \ +} \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, ub_M0))(void) \ +{ \ + M0 = \ + (((M0 >> SH0) & 0xff) << 0) | \ + (((M0 >> SH1) & 0xff) << 16) | \ + (((M0 >> SH2) & 0xff) << 32) | \ + (((M0 >> SH3) & 0xff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | \ + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); \ +} \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, uw_M0))(void) \ +{ \ + M0 = \ + (((M0 >> SH0) & 0xffff) << 0) | \ + (((M0 >> SH2) & 0xffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); \ +} \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, ul_M0))(void) \ +{ \ + M0 = (((M0 >> SH0) & 0xffffffff) << 0); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(M0 >> 0); \ +} \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, sb_M0))(void) \ +{ \ + M0 = \ + (EXTEND8H((M0 >> SH0) & 0xff) << 0) | \ + (EXTEND8H((M0 >> SH1) & 0xff) << 16) | \ + (EXTEND8H((M0 >> SH2) & 0xff) << 32) | \ + (EXTEND8H((M0 >> SH3) & 0xff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | \ + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); \ +} \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, sw_M0))(void) \ +{ \ + M0 = \ + (EXTEND16((M0 >> SH0) & 0xffff) << 0) | \ + (EXTEND16((M0 >> SH2) & 0xffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); \ +} \ +void OPPROTO glue(op_iwmmxt_unpack, glue(S, sl_M0))(void) \ +{ \ + M0 = EXTEND32((M0 >> SH0) & 0xffffffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(M0 >> 0); \ +} +IWMMXT_OP_UNPACK(l, 0, 8, 16, 24) +IWMMXT_OP_UNPACK(h, 32, 40, 48, 56) + +#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ +void OPPROTO glue(op_iwmmxt_, glue(SUFF, b_M0_wRn))(void) \ +{ \ + M0 = \ + CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ + CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ + CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ + CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(M0 >> 0, 0) | NZBIT8(M0 >> 8, 1) | \ + NZBIT8(M0 >> 16, 2) | NZBIT8(M0 >> 24, 3) | \ + NZBIT8(M0 >> 32, 4) | NZBIT8(M0 >> 40, 5) | \ + NZBIT8(M0 >> 48, 6) | NZBIT8(M0 >> 56, 7); \ +} \ +void OPPROTO glue(op_iwmmxt_, glue(SUFF, w_M0_wRn))(void) \ +{ \ + M0 = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ + CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | \ + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); \ +} \ +void OPPROTO glue(op_iwmmxt_, glue(SUFF, l_M0_wRn))(void) \ +{ \ + M0 = CMP(0, Tl, O, 0xffffffff) | \ + CMP(32, Tl, O, 0xffffffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); \ +} +#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((M0 >> SHR) & MASK) OPER \ + (TYPE) ((M1 >> SHR) & MASK)) ? (uint64_t) MASK : 0) << SHR) +IWMMXT_OP_CMP(cmpeq, uint8_t, uint16_t, uint32_t, ==) +IWMMXT_OP_CMP(cmpgts, int8_t, int16_t, int32_t, >) +IWMMXT_OP_CMP(cmpgtu, uint8_t, uint16_t, uint32_t, >) +#undef CMP +#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((M0 >> SHR) & MASK) OPER \ + (TYPE) ((M1 >> SHR) & MASK)) ? M0 : M1) & ((uint64_t) MASK << SHR)) +IWMMXT_OP_CMP(mins, int8_t, int16_t, int32_t, <) +IWMMXT_OP_CMP(minu, uint8_t, uint16_t, uint32_t, <) +IWMMXT_OP_CMP(maxs, int8_t, int16_t, int32_t, >) +IWMMXT_OP_CMP(maxu, uint8_t, uint16_t, uint32_t, >) +#undef CMP +#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((M0 >> SHR) & MASK) \ + OPER (TYPE) ((M1 >> SHR) & MASK)) & MASK) << SHR) +IWMMXT_OP_CMP(subn, uint8_t, uint16_t, uint32_t, -) +IWMMXT_OP_CMP(addn, uint8_t, uint16_t, uint32_t, +) +#undef CMP +/* TODO Signed- and Unsigned-Saturation */ +#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((M0 >> SHR) & MASK) \ + OPER (TYPE) ((M1 >> SHR) & MASK)) & MASK) << SHR) +IWMMXT_OP_CMP(subu, uint8_t, uint16_t, uint32_t, -) +IWMMXT_OP_CMP(addu, uint8_t, uint16_t, uint32_t, +) +IWMMXT_OP_CMP(subs, int8_t, int16_t, int32_t, -) +IWMMXT_OP_CMP(adds, int8_t, int16_t, int32_t, +) +#undef CMP +#undef IWMMXT_OP_CMP + +void OPPROTO op_iwmmxt_avgb_M0_wRn(void) +{ +#define AVGB(SHR) ((( \ + ((M0 >> SHR) & 0xff) + ((M1 >> SHR) & 0xff) + PARAM2) >> 1) << SHR) + M0 = + AVGB(0) | AVGB(8) | AVGB(16) | AVGB(24) | + AVGB(32) | AVGB(40) | AVGB(48) | AVGB(56); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + SIMD8_SET(ZBIT8((M0 >> 0) & 0xff), SIMD_ZBIT, 0) | + SIMD8_SET(ZBIT8((M0 >> 8) & 0xff), SIMD_ZBIT, 1) | + SIMD8_SET(ZBIT8((M0 >> 16) & 0xff), SIMD_ZBIT, 2) | + SIMD8_SET(ZBIT8((M0 >> 24) & 0xff), SIMD_ZBIT, 3) | + SIMD8_SET(ZBIT8((M0 >> 32) & 0xff), SIMD_ZBIT, 4) | + SIMD8_SET(ZBIT8((M0 >> 40) & 0xff), SIMD_ZBIT, 5) | + SIMD8_SET(ZBIT8((M0 >> 48) & 0xff), SIMD_ZBIT, 6) | + SIMD8_SET(ZBIT8((M0 >> 56) & 0xff), SIMD_ZBIT, 7); +#undef AVGB +} + +void OPPROTO op_iwmmxt_avgw_M0_wRn(void) +{ +#define AVGW(SHR) ((( \ + ((M0 >> SHR) & 0xffff) + ((M1 >> SHR) & 0xffff) + PARAM2) >> 1) << SHR) + M0 = AVGW(0) | AVGW(16) | AVGW(32) | AVGW(48); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + SIMD16_SET(ZBIT16((M0 >> 0) & 0xffff), SIMD_ZBIT, 0) | + SIMD16_SET(ZBIT16((M0 >> 16) & 0xffff), SIMD_ZBIT, 1) | + SIMD16_SET(ZBIT16((M0 >> 32) & 0xffff), SIMD_ZBIT, 2) | + SIMD16_SET(ZBIT16((M0 >> 48) & 0xffff), SIMD_ZBIT, 3); +#undef AVGW +} + +void OPPROTO op_iwmmxt_msadb_M0_wRn(void) +{ + M0 = ((((M0 >> 0) & 0xffff) * ((M1 >> 0) & 0xffff) + + ((M0 >> 16) & 0xffff) * ((M1 >> 16) & 0xffff)) & 0xffffffff) | + ((((M0 >> 32) & 0xffff) * ((M1 >> 32) & 0xffff) + + ((M0 >> 48) & 0xffff) * ((M1 >> 48) & 0xffff)) << 32); +} + +void OPPROTO op_iwmmxt_align_M0_T0_wRn(void) +{ + M0 >>= T0 << 3; + M0 |= M1 << (64 - (T0 << 3)); +} + +void OPPROTO op_iwmmxt_insr_M0_T0_T1(void) +{ + M0 &= ~((uint64_t) T1 << PARAM1); + M0 |= (uint64_t) (T0 & T1) << PARAM1; +} + +void OPPROTO op_iwmmxt_extrsb_T0_M0(void) +{ + T0 = EXTEND8((M0 >> PARAM1) & 0xff); +} + +void OPPROTO op_iwmmxt_extrsw_T0_M0(void) +{ + T0 = EXTEND16((M0 >> PARAM1) & 0xffff); +} + +void OPPROTO op_iwmmxt_extru_T0_M0_T1(void) +{ + T0 = (M0 >> PARAM1) & T1; +} + +void OPPROTO op_iwmmxt_bcstb_M0_T0(void) +{ + T0 &= 0xff; + M0 = + ((uint64_t) T0 << 0) | ((uint64_t) T0 << 8) | + ((uint64_t) T0 << 16) | ((uint64_t) T0 << 24) | + ((uint64_t) T0 << 32) | ((uint64_t) T0 << 40) | + ((uint64_t) T0 << 48) | ((uint64_t) T0 << 56); +} + +void OPPROTO op_iwmmxt_bcstw_M0_T0(void) +{ + T0 &= 0xffff; + M0 = + ((uint64_t) T0 << 0) | ((uint64_t) T0 << 16) | + ((uint64_t) T0 << 32) | ((uint64_t) T0 << 48); +} + +void OPPROTO op_iwmmxt_bcstl_M0_T0(void) +{ + M0 = ((uint64_t) T0 << 0) | ((uint64_t) T0 << 32); +} + +void OPPROTO op_iwmmxt_addcb_M0(void) +{ + M0 = + ((M0 >> 0) & 0xff) + ((M0 >> 8) & 0xff) + + ((M0 >> 16) & 0xff) + ((M0 >> 24) & 0xff) + + ((M0 >> 32) & 0xff) + ((M0 >> 40) & 0xff) + + ((M0 >> 48) & 0xff) + ((M0 >> 56) & 0xff); +} + +void OPPROTO op_iwmmxt_addcw_M0(void) +{ + M0 = + ((M0 >> 0) & 0xffff) + ((M0 >> 16) & 0xffff) + + ((M0 >> 32) & 0xffff) + ((M0 >> 48) & 0xffff); +} + +void OPPROTO op_iwmmxt_addcl_M0(void) +{ + M0 = (M0 & 0xffffffff) + (M0 >> 32); +} + +void OPPROTO op_iwmmxt_msbb_T0_M0(void) +{ + T0 = + ((M0 >> 7) & 0x01) | ((M0 >> 14) & 0x02) | + ((M0 >> 21) & 0x04) | ((M0 >> 28) & 0x08) | + ((M0 >> 35) & 0x10) | ((M0 >> 42) & 0x20) | + ((M0 >> 49) & 0x40) | ((M0 >> 56) & 0x80); +} + +void OPPROTO op_iwmmxt_msbw_T0_M0(void) +{ + T0 = + ((M0 >> 15) & 0x01) | ((M0 >> 30) & 0x02) | + ((M0 >> 45) & 0x04) | ((M0 >> 52) & 0x08); +} + +void OPPROTO op_iwmmxt_msbl_T0_M0(void) +{ + T0 = ((M0 >> 31) & 0x01) | ((M0 >> 62) & 0x02); +} + +void OPPROTO op_iwmmxt_srlw_M0_T0(void) +{ + M0 = + (((M0 & (0xffffll << 0)) >> T0) & (0xffffll << 0)) | + (((M0 & (0xffffll << 16)) >> T0) & (0xffffll << 16)) | + (((M0 & (0xffffll << 32)) >> T0) & (0xffffll << 32)) | + (((M0 & (0xffffll << 48)) >> T0) & (0xffffll << 48)); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); +} + +void OPPROTO op_iwmmxt_srll_M0_T0(void) +{ + M0 = + ((M0 & (0xffffffffll << 0)) >> T0) | + ((M0 >> T0) & (0xffffffffll << 32)); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); +} + +void OPPROTO op_iwmmxt_srlq_M0_T0(void) +{ + M0 >>= T0; + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(M0); +} + +void OPPROTO op_iwmmxt_sllw_M0_T0(void) +{ + M0 = + (((M0 & (0xffffll << 0)) << T0) & (0xffffll << 0)) | + (((M0 & (0xffffll << 16)) << T0) & (0xffffll << 16)) | + (((M0 & (0xffffll << 32)) << T0) & (0xffffll << 32)) | + (((M0 & (0xffffll << 48)) << T0) & (0xffffll << 48)); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); +} + +void OPPROTO op_iwmmxt_slll_M0_T0(void) +{ + M0 = + ((M0 << T0) & (0xffffffffll << 0)) | + ((M0 & (0xffffffffll << 32)) << T0); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); +} + +void OPPROTO op_iwmmxt_sllq_M0_T0(void) +{ + M0 <<= T0; + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(M0); +} + +void OPPROTO op_iwmmxt_sraw_M0_T0(void) +{ + M0 = + (((EXTEND16(M0 >> 0) >> T0) & 0xffff) << 0) | + (((EXTEND16(M0 >> 16) >> T0) & 0xffff) << 16) | + (((EXTEND16(M0 >> 32) >> T0) & 0xffff) << 32) | + (((EXTEND16(M0 >> 48) >> T0) & 0xffff) << 48); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); +} + +void OPPROTO op_iwmmxt_sral_M0_T0(void) +{ + M0 = + (((EXTEND32(M0 >> 0) >> T0) & 0xffffffff) << 0) | + (((EXTEND32(M0 >> 32) >> T0) & 0xffffffff) << 32); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); +} + +void OPPROTO op_iwmmxt_sraq_M0_T0(void) +{ + M0 = (int64_t) M0 >> T0; + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(M0); +} + +void OPPROTO op_iwmmxt_rorw_M0_T0(void) +{ + M0 = + ((((M0 & (0xffffll << 0)) >> T0) | + ((M0 & (0xffffll << 0)) << (16 - T0))) & (0xffffll << 0)) | + ((((M0 & (0xffffll << 16)) >> T0) | + ((M0 & (0xffffll << 16)) << (16 - T0))) & (0xffffll << 16)) | + ((((M0 & (0xffffll << 32)) >> T0) | + ((M0 & (0xffffll << 32)) << (16 - T0))) & (0xffffll << 32)) | + ((((M0 & (0xffffll << 48)) >> T0) | + ((M0 & (0xffffll << 48)) << (16 - T0))) & (0xffffll << 48)); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); +} + +void OPPROTO op_iwmmxt_rorl_M0_T0(void) +{ + M0 = + ((M0 & (0xffffffffll << 0)) >> T0) | + ((M0 >> T0) & (0xffffffffll << 32)) | + ((M0 << (32 - T0)) & (0xffffffffll << 0)) | + ((M0 & (0xffffffffll << 32)) << (32 - T0)); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); +} + +void OPPROTO op_iwmmxt_rorq_M0_T0(void) +{ + M0 = (M0 >> T0) | (M0 << (64 - T0)); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(M0); +} + +void OPPROTO op_iwmmxt_shufh_M0_T0(void) +{ + M0 = + (((M0 >> ((T0 << 4) & 0x30)) & 0xffff) << 0) | + (((M0 >> ((T0 << 2) & 0x30)) & 0xffff) << 16) | + (((M0 >> ((T0 << 0) & 0x30)) & 0xffff) << 32) | + (((M0 >> ((T0 >> 2) & 0x30)) & 0xffff) << 48); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); +} + +/* TODO: Unsigned-Saturation */ +void OPPROTO op_iwmmxt_packuw_M0_wRn(void) +{ + M0 = + (((M0 >> 0) & 0xff) << 0) | (((M0 >> 16) & 0xff) << 8) | + (((M0 >> 32) & 0xff) << 16) | (((M0 >> 48) & 0xff) << 24) | + (((M1 >> 0) & 0xff) << 32) | (((M1 >> 16) & 0xff) << 40) | + (((M1 >> 32) & 0xff) << 48) | (((M1 >> 48) & 0xff) << 56); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT8(M0 >> 0, 0) | NZBIT8(M0 >> 8, 1) | + NZBIT8(M0 >> 16, 2) | NZBIT8(M0 >> 24, 3) | + NZBIT8(M0 >> 32, 4) | NZBIT8(M0 >> 40, 5) | + NZBIT8(M0 >> 48, 6) | NZBIT8(M0 >> 56, 7); +} + +void OPPROTO op_iwmmxt_packul_M0_wRn(void) +{ + M0 = + (((M0 >> 0) & 0xffff) << 0) | (((M0 >> 32) & 0xffff) << 16) | + (((M1 >> 0) & 0xffff) << 32) | (((M1 >> 32) & 0xffff) << 48); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); +} + +void OPPROTO op_iwmmxt_packuq_M0_wRn(void) +{ + M0 = (M0 & 0xffffffff) | ((M1 & 0xffffffff) << 32); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); +} + +/* TODO: Signed-Saturation */ +void OPPROTO op_iwmmxt_packsw_M0_wRn(void) +{ + M0 = + (((M0 >> 0) & 0xff) << 0) | (((M0 >> 16) & 0xff) << 8) | + (((M0 >> 32) & 0xff) << 16) | (((M0 >> 48) & 0xff) << 24) | + (((M1 >> 0) & 0xff) << 32) | (((M1 >> 16) & 0xff) << 40) | + (((M1 >> 32) & 0xff) << 48) | (((M1 >> 48) & 0xff) << 56); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT8(M0 >> 0, 0) | NZBIT8(M0 >> 8, 1) | + NZBIT8(M0 >> 16, 2) | NZBIT8(M0 >> 24, 3) | + NZBIT8(M0 >> 32, 4) | NZBIT8(M0 >> 40, 5) | + NZBIT8(M0 >> 48, 6) | NZBIT8(M0 >> 56, 7); +} + +void OPPROTO op_iwmmxt_packsl_M0_wRn(void) +{ + M0 = + (((M0 >> 0) & 0xffff) << 0) | (((M0 >> 32) & 0xffff) << 16) | + (((M1 >> 0) & 0xffff) << 32) | (((M1 >> 32) & 0xffff) << 48); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT16(M0 >> 0, 0) | NZBIT16(M0 >> 16, 1) | + NZBIT16(M0 >> 32, 2) | NZBIT16(M0 >> 48, 3); +} + +void OPPROTO op_iwmmxt_packsq_M0_wRn(void) +{ + M0 = (M0 & 0xffffffff) | ((M1 & 0xffffffff) << 32); + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = + NZBIT32(M0 >> 0, 0) | NZBIT32(M0 >> 32, 1); +} + +void OPPROTO op_iwmmxt_muladdsl_M0_T0_T1(void) +{ + M0 += EXTEND32(T0) * EXTEND32(T1); +} + +void OPPROTO op_iwmmxt_muladdsw_M0_T0_T1(void) +{ + M0 += EXTEND32(EXTEND16((T0 >> 0) & 0xffff) * + EXTEND16((T1 >> 0) & 0xffff)); + M0 += EXTEND32(EXTEND16((T0 >> 16) & 0xffff) * + EXTEND16((T1 >> 16) & 0xffff)); +} + +void OPPROTO op_iwmmxt_muladdswl_M0_T0_T1(void) +{ + M0 += EXTEND32(EXTEND16(T0 & 0xffff) * EXTEND16(T1 & 0xffff)); +} Index: qemu/target-arm/op_mem.h =================================================================== --- qemu.orig/target-arm/op_mem.h 2005-11-26 18:38:39.000000000 +0800 +++ qemu/target-arm/op_mem.h 2007-02-12 15:21:33.000000000 +0800 @@ -67,4 +67,24 @@ #undef VFP_MEM_OP +/* iwMMXt load/store. Address is in T1 */ +#define MMX_MEM_OP(name, ldname) \ +void OPPROTO glue(op_iwmmxt_ld##name,MEMSUFFIX)(void) \ +{ \ + M0 = glue(ld##ldname,MEMSUFFIX)(T1); \ + FORCE_RET(); \ +} \ +void OPPROTO glue(op_iwmmxt_st##name,MEMSUFFIX)(void) \ +{ \ + glue(st##name,MEMSUFFIX)(T1, M0); \ + FORCE_RET(); \ +} + +MMX_MEM_OP(b, ub) +MMX_MEM_OP(w, uw) +MMX_MEM_OP(l, l) +MMX_MEM_OP(q, q) + +#undef MMX_MEM_OP + #undef MEMSUFFIX Index: qemu/target-arm/op_template64.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/target-arm/op_template64.h 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,39 @@ +/* + * ARM micro operations (templates for various register related + * operations) + * + * Copyright (c) 2007 OpenedHand Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SET_REG +#define SET_REG(x) REG = x +#endif + +void OPPROTO glue(glue(op_, REGNAME), _T0_T1)(void) +{ + SET_REG(((uint64_t) T0 << 32) | T1); +} + +void OPPROTO glue(op_T0_T1_, REGNAME)(void) +{ + T0 = REG >> 32; + T1 = REG & ~(uint32_t) 0; +} + +#undef REG +#undef REGNAME +#undef SET_REG Index: qemu/target-arm/translate.c =================================================================== --- qemu.orig/target-arm/translate.c 2006-10-26 01:43:33.000000000 +0800 +++ qemu/target-arm/translate.c 2007-02-15 05:26:03.000000000 +0800 @@ -3,6 +3,7 @@ * * Copyright (c) 2003 Fabrice Bellard * Copyright (c) 2005 CodeSourcery, LLC + * Copyright (c) 2007 OpenedHand, Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -391,9 +392,9 @@ if (insn & (1 << 22)) { /* immediate */ val = (insn & 0xf) | ((insn >> 4) & 0xf0); - val += extra; if (!(insn & (1 << 23))) val = -val; + val += extra; if (val != 0) gen_op_addl_T1_im(val); } else { @@ -490,6 +491,1020 @@ gen_op_vfp_setreg_F0s(vfp_reg_offset(dp, reg)); } +#define ARM_CP_RW_BIT (1 << 20) + +static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn) +{ + int rd; + uint32_t offset; + + rd = (insn >> 16) & 0xf; + gen_movl_T1_reg(s, rd); + + offset = (insn & 0xff) << ((insn >> 6) & 4); + if (insn & (1 << 24)) { + /* Pre indexed */ + if (insn & (1 << 23)) + gen_op_addl_T1_im(offset); + else + gen_op_addl_T1_im(-offset); + + if (insn & (1 << 21)) + gen_movl_reg_T1(s, rd); + } else if (insn & (1 << 21)) { + /* Post indexed */ + if (insn & (1 << 23)) + gen_op_movl_T0_im(offset); + else + gen_op_movl_T0_im(- offset); + gen_op_addl_T0_T1(); + gen_movl_reg_T0(s, rd); + } else if (!(insn & (1 << 23))) + return 1; + return 0; +} + +static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask) +{ + int rd = (insn >> 0) & 0xf; + + if (insn & (1 << 8)) + if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3) + return 1; + else + gen_op_iwmmxt_movl_T0_wCx(rd); + else + gen_op_iwmmxt_movl_T0_T1_wRn(rd); + + gen_op_movl_T1_im(mask); + gen_op_andl_T0_T1(); + return 0; +} + +/* Disassemble an iwMMXt instruction. Returns nonzero if an error occured + (ie. an undefined instruction). */ +static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn) +{ + int rd, wrd; + int rdhi, rdlo, rd0, rd1, i; + + if (!arm_feature(env, ARM_FEATURE_IWMMXT)) + return 1; + + if ((insn & 0x0e000e00) == 0x0c000000) { + if ((insn & 0x0fe00ff0) == 0x0c400000) { + wrd = insn & 0xf; + rdlo = (insn >> 12) & 0xf; + rdhi = (insn >> 16) & 0xf; + if (insn & ARM_CP_RW_BIT) { /* TMRRC */ + gen_op_iwmmxt_movl_T0_T1_wRn(wrd); + gen_movl_reg_T0(s, rdlo); + gen_movl_reg_T1(s, rdhi); + } else { /* TMCRR */ + gen_movl_T0_reg(s, rdlo); + gen_movl_T1_reg(s, rdhi); + gen_op_iwmmxt_movl_wRn_T0_T1(wrd); + gen_op_iwmmxt_set_mup(); + } + return 0; + } + + wrd = (insn >> 12) & 0xf; + if (gen_iwmmxt_address(s, insn)) + return 1; + if (insn & ARM_CP_RW_BIT) { + if ((insn >> 28) == 0xf) { /* WLDRW wCx */ + gen_ldst(ldl, s); + gen_op_iwmmxt_movl_wCx_T0(wrd); + } else { + if (insn & (1 << 8)) + if (insn & (1 << 22)) /* WLDRD */ + gen_ldst(iwmmxt_ldq, s); + else /* WLDRW wRd */ + gen_ldst(iwmmxt_ldl, s); + else + if (insn & (1 << 22)) /* WLDRH */ + gen_ldst(iwmmxt_ldw, s); + else /* WLDRB */ + gen_ldst(iwmmxt_ldb, s); + gen_op_iwmmxt_movq_wRn_M0(wrd); + } + } else { + if ((insn >> 28) == 0xf) { /* WSTRW wCx */ + gen_op_iwmmxt_movl_T0_wCx(wrd); + gen_ldst(stl, s); + } else { + gen_op_iwmmxt_movq_M0_wRn(wrd); + if (insn & (1 << 8)) + if (insn & (1 << 22)) /* WSTRD */ + gen_ldst(iwmmxt_stq, s); + else /* WSTRW wRd */ + gen_ldst(iwmmxt_stl, s); + else + if (insn & (1 << 22)) /* WSTRH */ + gen_ldst(iwmmxt_ldw, s); + else /* WSTRB */ + gen_ldst(iwmmxt_stb, s); + } + } + return 0; + } + + if ((insn & 0x0f000000) != 0x0e000000) + return 1; + + switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { + case 0x000: /* WOR */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_iwmmxt_orq_M0_wRn(rd1); + gen_op_iwmmxt_setpsr_nz(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x011: /* TMCR */ + if (insn & 0xf) + return 1; + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + switch (wrd) { + case ARM_IWMMXT_wCID: + case ARM_IWMMXT_wCASF: + break; + case ARM_IWMMXT_wCon: + gen_op_iwmmxt_set_cup(); + /* Fall through. */ + case ARM_IWMMXT_wCSSF: + gen_op_iwmmxt_movl_T0_wCx(wrd); + gen_movl_T1_reg(s, rd); + gen_op_bicl_T0_T1(); + gen_op_iwmmxt_movl_wCx_T0(wrd); + break; + case ARM_IWMMXT_wCGR0: + case ARM_IWMMXT_wCGR1: + case ARM_IWMMXT_wCGR2: + case ARM_IWMMXT_wCGR3: + gen_op_iwmmxt_set_cup(); + gen_movl_reg_T0(s, rd); + gen_op_iwmmxt_movl_wCx_T0(wrd); + break; + default: + return 1; + } + break; + case 0x100: /* WXOR */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_iwmmxt_xorq_M0_wRn(rd1); + gen_op_iwmmxt_setpsr_nz(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x111: /* TMRC */ + if (insn & 0xf) + return 1; + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + gen_op_iwmmxt_movl_T0_wCx(wrd); + gen_movl_reg_T0(s, rd); + break; + case 0x300: /* WANDN */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_iwmmxt_negq_M0(); + gen_op_iwmmxt_andq_M0_wRn(rd1); + gen_op_iwmmxt_setpsr_nz(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x200: /* WAND */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_iwmmxt_andq_M0_wRn(rd1); + gen_op_iwmmxt_setpsr_nz(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x810: case 0xa10: /* WMADD */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 21)) + gen_op_iwmmxt_maddsq_M0_wRn(rd1); + else + gen_op_iwmmxt_madduq_M0_wRn(rd1); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_op_iwmmxt_unpacklb_M0_wRn(rd1); + break; + case 1: + gen_op_iwmmxt_unpacklw_M0_wRn(rd1); + break; + case 2: + gen_op_iwmmxt_unpackll_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_op_iwmmxt_unpackhb_M0_wRn(rd1); + break; + case 1: + gen_op_iwmmxt_unpackhw_M0_wRn(rd1); + break; + case 2: + gen_op_iwmmxt_unpackhl_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 22)) + gen_op_iwmmxt_sadw_M0_wRn(rd1); + else + gen_op_iwmmxt_sadb_M0_wRn(rd1); + if (!(insn & (1 << 20))) + gen_op_iwmmxt_addl_M0_wRn(wrd); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 21)) + gen_op_iwmmxt_mulsw_M0_wRn(rd1, (insn & (1 << 20)) ? 16 : 0); + else + gen_op_iwmmxt_muluw_M0_wRn(rd1, (insn & (1 << 20)) ? 16 : 0); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 21)) + gen_op_iwmmxt_macsw_M0_wRn(rd1); + else + gen_op_iwmmxt_macuw_M0_wRn(rd1); + if (!(insn & (1 << 20))) { + if (insn & (1 << 21)) + gen_op_iwmmxt_addsq_M0_wRn(wrd); + else + gen_op_iwmmxt_adduq_M0_wRn(wrd); + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_op_iwmmxt_cmpeqb_M0_wRn(rd1); + break; + case 1: + gen_op_iwmmxt_cmpeqw_M0_wRn(rd1); + break; + case 2: + gen_op_iwmmxt_cmpeql_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 22)) + gen_op_iwmmxt_avgw_M0_wRn(rd1, (insn >> 20) & 1); + else + gen_op_iwmmxt_avgb_M0_wRn(rd1, (insn >> 20) & 1); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_iwmmxt_movl_T0_wCx(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3)); + gen_op_movl_T1_im(7); + gen_op_andl_T0_T1(); + gen_op_iwmmxt_align_M0_T0_wRn(rd1); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + gen_movl_T0_reg(s, rd); + gen_op_iwmmxt_movq_M0_wRn(wrd); + switch ((insn >> 6) & 3) { + case 0: + gen_op_movl_T1_im(0xff); + gen_op_iwmmxt_insr_M0_T0_T1((insn & 7) << 3); + break; + case 1: + gen_op_movl_T1_im(0xffff); + gen_op_iwmmxt_insr_M0_T0_T1((insn & 3) << 4); + break; + case 2: + gen_op_movl_T1_im(0xffffffff); + gen_op_iwmmxt_insr_M0_T0_T1((insn & 1) << 5); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + if (rd == 15) + return 1; + gen_op_iwmmxt_movq_M0_wRn(wrd); + switch ((insn >> 22) & 3) { + case 0: + if (insn & 8) + gen_op_iwmmxt_extrsb_T0_M0((insn & 7) << 3); + else { + gen_op_movl_T1_im(0xff); + gen_op_iwmmxt_extru_T0_M0_T1((insn & 7) << 3); + } + break; + case 1: + if (insn & 8) + gen_op_iwmmxt_extrsw_T0_M0((insn & 3) << 4); + else { + gen_op_movl_T1_im(0xffff); + gen_op_iwmmxt_extru_T0_M0_T1((insn & 3) << 4); + } + break; + case 2: + gen_op_movl_T1_im(0xffffffff); + gen_op_iwmmxt_extru_T0_M0_T1((insn & 1) << 5); + break; + case 3: + return 1; + } + gen_op_movl_reg_TN[0][rd](); + break; + case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ + if ((insn & 0x000ff008) != 0x0003f000) + return 1; + gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF); + switch ((insn >> 22) & 3) { + case 0: + gen_op_shrl_T1_im(((insn & 7) << 2) + 0); + break; + case 1: + gen_op_shrl_T1_im(((insn & 3) << 3) + 4); + break; + case 2: + gen_op_shrl_T1_im(((insn & 1) << 4) + 12); + break; + case 3: + return 1; + } + gen_op_shll_T1_im(28); + gen_op_movl_T0_T1(); + gen_op_movl_cpsr_T0(0xf0000000); + break; + case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + gen_movl_T0_reg(s, rd); + switch ((insn >> 6) & 3) { + case 0: + gen_op_iwmmxt_bcstb_M0_T0(); + break; + case 1: + gen_op_iwmmxt_bcstw_M0_T0(); + break; + case 2: + gen_op_iwmmxt_bcstl_M0_T0(); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ + if ((insn & 0x000ff00f) != 0x0003f000) + return 1; + gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF); + switch ((insn >> 22) & 3) { + case 0: + for (i = 0; i < 7; i ++) { + gen_op_shll_T1_im(4); + gen_op_andl_T0_T1(); + } + break; + case 1: + for (i = 0; i < 3; i ++) { + gen_op_shll_T1_im(8); + gen_op_andl_T0_T1(); + } + break; + case 2: + gen_op_shll_T1_im(16); + gen_op_andl_T0_T1(); + break; + case 3: + return 1; + } + gen_op_movl_cpsr_T0(0xf0000000); + break; + case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_op_iwmmxt_addcb_M0(); + break; + case 1: + gen_op_iwmmxt_addcw_M0(); + break; + case 2: + gen_op_iwmmxt_addcl_M0(); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ + if ((insn & 0x000ff00f) != 0x0003f000) + return 1; + gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF); + switch ((insn >> 22) & 3) { + case 0: + for (i = 0; i < 7; i ++) { + gen_op_shll_T1_im(4); + gen_op_orl_T0_T1(); + } + break; + case 1: + for (i = 0; i < 3; i ++) { + gen_op_shll_T1_im(8); + gen_op_orl_T0_T1(); + } + break; + case 2: + gen_op_shll_T1_im(16); + gen_op_orl_T0_T1(); + break; + case 3: + return 1; + } + gen_op_movl_T1_im(0xf0000000); + gen_op_andl_T0_T1(); + gen_op_movl_cpsr_T0(0xf0000000); + break; + case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ + rd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + if ((insn & 0xf) != 0) + return 1; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_op_iwmmxt_msbb_T0_M0(); + break; + case 1: + gen_op_iwmmxt_msbw_T0_M0(); + break; + case 2: + gen_op_iwmmxt_msbl_T0_M0(); + break; + case 3: + return 1; + } + gen_movl_reg_T0(s, rd); + break; + case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ + case 0x906: case 0xb06: case 0xd06: case 0xf06: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1); + else + gen_op_iwmmxt_cmpgtub_M0_wRn(rd1); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1); + else + gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1); + else + gen_op_iwmmxt_cmpgtul_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ + case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpacklsb_M0(); + else + gen_op_iwmmxt_unpacklub_M0(); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpacklsw_M0(); + else + gen_op_iwmmxt_unpackluw_M0(); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpacklsl_M0(); + else + gen_op_iwmmxt_unpacklul_M0(); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ + case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpackhsb_M0(); + else + gen_op_iwmmxt_unpackhub_M0(); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpackhsw_M0(); + else + gen_op_iwmmxt_unpackhuw_M0(); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpackhsl_M0(); + else + gen_op_iwmmxt_unpackhul_M0(); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ + case 0x214: case 0x614: case 0xa14: case 0xe14: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (gen_iwmmxt_shift(insn, 0xff)) + return 1; + switch ((insn >> 22) & 3) { + case 0: + return 1; + case 1: + gen_op_iwmmxt_srlw_M0_T0(); + break; + case 2: + gen_op_iwmmxt_srll_M0_T0(); + break; + case 3: + gen_op_iwmmxt_srlq_M0_T0(); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ + case 0x014: case 0x414: case 0x814: case 0xc14: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (gen_iwmmxt_shift(insn, 0xff)) + return 1; + switch ((insn >> 22) & 3) { + case 0: + return 1; + case 1: + gen_op_iwmmxt_sraw_M0_T0(); + break; + case 2: + gen_op_iwmmxt_sral_M0_T0(); + break; + case 3: + gen_op_iwmmxt_sraq_M0_T0(); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ + case 0x114: case 0x514: case 0x914: case 0xd14: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (gen_iwmmxt_shift(insn, 0xff)) + return 1; + switch ((insn >> 22) & 3) { + case 0: + return 1; + case 1: + gen_op_iwmmxt_sllw_M0_T0(); + break; + case 2: + gen_op_iwmmxt_slll_M0_T0(); + break; + case 3: + gen_op_iwmmxt_sllq_M0_T0(); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ + case 0x314: case 0x714: case 0xb14: case 0xf14: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + return 1; + case 1: + if (gen_iwmmxt_shift(insn, 0xf)) + return 1; + gen_op_iwmmxt_rorw_M0_T0(); + break; + case 2: + if (gen_iwmmxt_shift(insn, 0x1f)) + return 1; + gen_op_iwmmxt_rorl_M0_T0(); + break; + case 3: + if (gen_iwmmxt_shift(insn, 0x3f)) + return 1; + gen_op_iwmmxt_rorq_M0_T0(); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ + case 0x916: case 0xb16: case 0xd16: case 0xf16: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_minsb_M0_wRn(rd1); + else + gen_op_iwmmxt_minub_M0_wRn(rd1); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_minsw_M0_wRn(rd1); + else + gen_op_iwmmxt_minuw_M0_wRn(rd1); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_minsl_M0_wRn(rd1); + else + gen_op_iwmmxt_minul_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ + case 0x816: case 0xa16: case 0xc16: case 0xe16: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_maxsb_M0_wRn(rd1); + else + gen_op_iwmmxt_maxub_M0_wRn(rd1); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_maxsw_M0_wRn(rd1); + else + gen_op_iwmmxt_maxuw_M0_wRn(rd1); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_maxsl_M0_wRn(rd1); + else + gen_op_iwmmxt_maxul_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ + case 0x402: case 0x502: case 0x602: case 0x702: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_movl_T0_im((insn >> 20) & 3); + gen_op_iwmmxt_align_M0_T0_wRn(rd1); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ + case 0x41a: case 0x51a: case 0x61a: case 0x71a: + case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: + case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 20) & 0xf) { + case 0x0: + gen_op_iwmmxt_subnb_M0_wRn(rd1); + break; + case 0x1: + gen_op_iwmmxt_subub_M0_wRn(rd1); + break; + case 0x3: + gen_op_iwmmxt_subsb_M0_wRn(rd1); + break; + case 0x4: + gen_op_iwmmxt_subnw_M0_wRn(rd1); + break; + case 0x5: + gen_op_iwmmxt_subuw_M0_wRn(rd1); + break; + case 0x7: + gen_op_iwmmxt_subsw_M0_wRn(rd1); + break; + case 0x8: + gen_op_iwmmxt_subnl_M0_wRn(rd1); + break; + case 0x9: + gen_op_iwmmxt_subul_M0_wRn(rd1); + break; + case 0xb: + gen_op_iwmmxt_subsl_M0_wRn(rd1); + break; + default: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ + case 0x41e: case 0x51e: case 0x61e: case 0x71e: + case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: + case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_movl_T0_im(((insn >> 16) & 0xf0) | (insn & 0x0f)); + gen_op_iwmmxt_shufh_M0_T0(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ + case 0x418: case 0x518: case 0x618: case 0x718: + case 0x818: case 0x918: case 0xa18: case 0xb18: + case 0xc18: case 0xd18: case 0xe18: case 0xf18: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 20) & 0xf) { + case 0x0: + gen_op_iwmmxt_addnb_M0_wRn(rd1); + break; + case 0x1: + gen_op_iwmmxt_addub_M0_wRn(rd1); + break; + case 0x3: + gen_op_iwmmxt_addsb_M0_wRn(rd1); + break; + case 0x4: + gen_op_iwmmxt_addnw_M0_wRn(rd1); + break; + case 0x5: + gen_op_iwmmxt_adduw_M0_wRn(rd1); + break; + case 0x7: + gen_op_iwmmxt_addsw_M0_wRn(rd1); + break; + case 0x8: + gen_op_iwmmxt_addnl_M0_wRn(rd1); + break; + case 0x9: + gen_op_iwmmxt_addul_M0_wRn(rd1); + break; + case 0xb: + gen_op_iwmmxt_addsl_M0_wRn(rd1); + break; + default: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ + case 0x408: case 0x508: case 0x608: case 0x708: + case 0x808: case 0x908: case 0xa08: case 0xb08: + case 0xc08: case 0xd08: case 0xe08: case 0xf08: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (!(insn & (1 << 20))) + return 1; + switch ((insn >> 22) & 3) { + case 0: + return 1; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_packsw_M0_wRn(rd1); + else + gen_op_iwmmxt_packuw_M0_wRn(rd1); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_packsl_M0_wRn(rd1); + else + gen_op_iwmmxt_packul_M0_wRn(rd1); + break; + case 3: + if (insn & (1 << 21)) + gen_op_iwmmxt_packsq_M0_wRn(rd1); + else + gen_op_iwmmxt_packuq_M0_wRn(rd1); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x201: case 0x203: case 0x205: case 0x207: + case 0x209: case 0x20b: case 0x20d: case 0x20f: + case 0x211: case 0x213: case 0x215: case 0x217: + case 0x219: case 0x21b: case 0x21d: case 0x21f: + wrd = (insn >> 5) & 0xf; + rd0 = (insn >> 12) & 0xf; + rd1 = (insn >> 0) & 0xf; + if (rd0 == 0xf || rd1 == 0xf) + return 1; + gen_op_iwmmxt_movq_M0_wRn(wrd); + switch ((insn >> 16) & 0xf) { + case 0x0: /* TMIA */ + gen_op_movl_TN_reg[0][rd0](); + gen_op_movl_TN_reg[1][rd1](); + gen_op_iwmmxt_muladdsl_M0_T0_T1(); + break; + case 0x8: /* TMIAPH */ + gen_op_movl_TN_reg[0][rd0](); + gen_op_movl_TN_reg[1][rd1](); + gen_op_iwmmxt_muladdsw_M0_T0_T1(); + break; + case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ + gen_op_movl_TN_reg[1][rd0](); + if (insn & (1 << 16)) + gen_op_shrl_T1_im(16); + gen_op_movl_T0_T1(); + gen_op_movl_TN_reg[1][rd1](); + if (insn & (1 << 17)) + gen_op_shrl_T1_im(16); + gen_op_iwmmxt_muladdswl_M0_T0_T1(); + break; + default: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + default: + return 1; + } + + return 0; +} + +/* Disassemble system coprocessor instruction. Return nonzero if + instruction is not defined. */ +static int disas_cp_insn(DisasContext *s, uint32_t insn) +{ + uint32_t rd = (insn >> 12) & 0xf; + if (IS_USER(s)) { + return 1; + } + + if (insn & ARM_CP_RW_BIT) { + gen_op_movl_T0_cp(insn); + gen_movl_reg_T0(s, rd); + } else { + gen_op_movl_T0_im((uint32_t) s->pc); + gen_op_movl_reg_TN[0][15](); + gen_movl_T0_reg(s, rd); + gen_op_movl_cp_T0(insn); + s->is_jmp = DISAS_UPDATE; + } + + return 0; +} + /* Disassemble system coprocessor (cp15) instruction. Return nonzero if instruction is not defined. */ static int disas_cp15_insn(DisasContext *s, uint32_t insn) @@ -510,7 +1525,7 @@ return 0; } rd = (insn >> 12) & 0xf; - if (insn & (1 << 20)) { + if (insn & ARM_CP_RW_BIT) { gen_op_movl_T0_cp15(insn); /* If the destination register is r15 then sets condition codes. */ if (rd != 15) @@ -519,7 +1534,9 @@ gen_movl_T0_reg(s, rd); gen_op_movl_cp15_T0(insn); } +#if 0 gen_lookup_tb(s); +#endif return 0; } @@ -557,7 +1574,7 @@ we only set half the register. */ gen_mov_F0_vreg(1, rn); gen_op_vfp_mrrd(); - if (insn & (1 << 20)) { + if (insn & ARM_CP_RW_BIT) { /* vfp->arm */ if (insn & (1 << 21)) gen_movl_reg_T1(s, rd); @@ -574,7 +1591,7 @@ } } else { rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1); - if (insn & (1 << 20)) { + if (insn & ARM_CP_RW_BIT) { /* vfp->arm */ if (insn & (1 << 21)) { /* system register */ @@ -908,7 +1925,7 @@ } else rm = ((insn << 1) & 0x1e) | ((insn >> 5) & 1); - if (insn & (1 << 20)) { + if (insn & ARM_CP_RW_BIT) { /* vfp->arm */ if (dp) { gen_mov_F0_vreg(1, rm); @@ -975,7 +1992,7 @@ else offset = 4; for (i = 0; i < n; i++) { - if (insn & (1 << 20)) { + if (insn & ARM_CP_RW_BIT) { /* load */ gen_vfp_ld(s, dp); gen_mov_vreg_F0(dp, rd + i); @@ -1794,7 +2811,23 @@ case 0xe: /* Coprocessor. */ op1 = (insn >> 8) & 0xf; +#if 0 + if (arm_feature(env, ARM_FEATURE_XSCALE) && + ((env->cp15.c15_cpar ^ 0x3fff) & (1 << op1))) + goto illegal_op; +#endif switch (op1) { + case 0 ... 1: +#if 0 + if (disas_iwmmxt_insn(env, s, insn)) + goto illegal_op; +#endif + break; + case 2 ... 9: + case 12 ... 14: + if (disas_cp_insn(s, insn)) + goto illegal_op; + break; case 10: case 11: if (disas_vfp_insn (env, s, insn)) Index: qemu/tests/Makefile =================================================================== --- qemu.orig/tests/Makefile 2007-02-06 04:51:14.000000000 +0800 +++ qemu/tests/Makefile 2007-02-12 15:21:33.000000000 +0800 @@ -82,6 +82,9 @@ hello-arm.o: hello-arm.c arm-linux-gcc -Wall -g -O2 -c -o $@ $< +test-arm-iwmmxt: test-arm-iwmmxt.s + cpp < $< | arm-palmte-linux-gnu-gcc -Wall -static -march=iwmmxt -mabi=aapcs -x assembler - -o $@ + # MIPS test hello-mips: hello-mips.c mips-linux-gnu-gcc -nostdlib -static -mno-abicalls -fno-PIC -mabi=32 -Wall -Wextra -g -O2 -o $@ $< Index: qemu/tests/test-arm-iwmmxt.s =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/tests/test-arm-iwmmxt.s 2007-02-12 15:21:33.000000000 +0800 @@ -0,0 +1,49 @@ +@ Should print "testtesttesttest" if iwMMXt is functional. +.code 32 +.align 4 +.globl main + +main: +adr r0, data0 +adr r1, data1 +adr r2, data2 +#ifndef FPA +wldrd wr0, [r0, #0] +wldrd wr1, [r0, #8] +wldrd wr2, [r1, #0] +wldrd wr3, [r1, #8] +wsubb wr2, wr2, wr0 +wsubb wr3, wr3, wr1 +wldrd wr0, [r2, #0] +wldrd wr1, [r2, #8] +waddb wr0, wr0, wr2 +waddb wr1, wr1, wr3 +wstrd wr0, [r2, #0] +wstrd wr1, [r2, #8] +#else +ldfe f0, [r0, #0] +ldfe f1, [r0, #8] +ldfe f2, [r1, #0] +ldfe f3, [r1, #8] +adfdp f2, f2, f0 +adfdp f3, f3, f1 +ldfe f0, [r2, #0] +ldfe f1, [r2, #8] +adfd f0, f0, f2 +adfd f1, f1, f3 +stfe f0, [r2, #0] +stfe f1, [r2, #8] +#endif +mov r0, #1 +mov r1, r2 +mov r2, #0x11 +swi #0x900004 +mov r0, #0 +swi #0x900001 + +data0: +.string "aaaabbbbccccdddd" +data1: +.string "bbbbccccddddeeee" +data2: +.string "sdrssdrssdrssdrs\n" Index: qemu/vl.c =================================================================== --- qemu.orig/vl.c 2007-02-12 13:06:50.000000000 +0800 +++ qemu/vl.c 2007-03-06 10:25:40.000000000 +0800 @@ -55,6 +55,22 @@ #include #include #include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include // must come after ip.h +#include +#include +#include +#include +#include #endif #endif #endif @@ -147,6 +163,7 @@ #endif int graphic_depth = 15; int full_screen = 0; +int no_frame = 0; int no_quit = 0; CharDriverState *serial_hds[MAX_SERIAL_PORTS]; CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; @@ -167,6 +184,11 @@ int acpi_enabled = 1; int fd_bootchk = 1; int no_reboot = 0; +int cursor_hide = 1; +int snapshot = 0; +const char *sd_filename = 0; +const char *mtd_filename = 0; +int graphic_rotate = 0; int daemonize = 0; const char *option_rom[MAX_OPTION_ROMS]; int nb_option_roms; @@ -502,6 +524,7 @@ { QEMUPutMouseEvent *mouse_event; void *mouse_event_opaque; + int width; if (!qemu_put_mouse_event_current) { return; @@ -513,7 +536,16 @@ qemu_put_mouse_event_current->qemu_put_mouse_event_opaque; if (mouse_event) { - mouse_event(mouse_event_opaque, dx, dy, dz, buttons_state); + if (graphic_rotate) { + if (qemu_put_mouse_event_current->qemu_put_mouse_event_absolute) + width = 0x7fff; + else + width = graphic_width; + mouse_event(mouse_event_opaque, + width - dy, dx, dz, buttons_state); + } else + mouse_event(mouse_event_opaque, + dx, dy, dz, buttons_state); } } @@ -1212,6 +1244,218 @@ return chr; } +/* MUX driver for serial I/O splitting */ +static int term_timestamps; +static int64_t term_timestamps_start; +#define MAX_MUX 4 +typedef struct { + IOCanRWHandler *chr_can_read[MAX_MUX]; + IOReadHandler *chr_read[MAX_MUX]; + IOEventHandler *chr_event[MAX_MUX]; + void *ext_opaque[MAX_MUX]; + CharDriverState *drv; + int mux_cnt; + int term_got_escape; + int max_size; +} MuxDriver; + + +static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + MuxDriver *d = chr->opaque; + int ret; + if (!term_timestamps) { + ret = d->drv->chr_write(d->drv, buf, len); + } else { + int i; + + ret = 0; + for(i = 0; i < len; i++) { + ret += d->drv->chr_write(d->drv, buf+i, 1); + if (buf[i] == '\n') { + char buf1[64]; + int64_t ti; + int secs; + + ti = get_clock(); + if (term_timestamps_start == -1) + term_timestamps_start = ti; + ti -= term_timestamps_start; + secs = ti / 1000000000; + snprintf(buf1, sizeof(buf1), + "[%02d:%02d:%02d.%03d] ", + secs / 3600, + (secs / 60) % 60, + secs % 60, + (int)((ti / 1000000) % 1000)); + d->drv->chr_write(d->drv, buf1, strlen(buf1)); + } + } + } + return ret; +} + +static char *mux_help[] = { + "% h print this help\n\r", + "% x exit emulator\n\r", + "% s save disk data back to file (if -snapshot)\n\r", + "% t toggle console timestamps\n\r" + "% b send break (magic sysrq)\n\r", + "% c switch between console and monitor\n\r", + "% % sends %\n\r", + NULL +}; + +static int term_escape_char = 0x01; /* ctrl-a is used for escape */ +static void mux_print_help(CharDriverState *chr) +{ + int i, j; + char ebuf[15] = "Escape-Char"; + char cbuf[50] = "\n\r"; + + if (term_escape_char > 0 && term_escape_char < 26) { + sprintf(cbuf,"\n\r"); + sprintf(ebuf,"C-%c", term_escape_char - 1 + 'a'); + } else { + sprintf(cbuf,"\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r", term_escape_char); + } + chr->chr_write(chr, cbuf, strlen(cbuf)); + for (i = 0; mux_help[i] != NULL; i++) { + for (j=0; mux_help[i][j] != '\0'; j++) { + if (mux_help[i][j] == '%') + chr->chr_write(chr, ebuf, strlen(ebuf)); + else + chr->chr_write(chr, &mux_help[i][j], 1); + } + } +} + +static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) +{ + if (d->term_got_escape) { + d->term_got_escape = 0; + if (ch == term_escape_char) + goto send_char; + switch(ch) { + case '?': + case 'h': + mux_print_help(chr); + break; + case 'x': + { + char *term = "QEMU: Terminated\n\r"; + chr->chr_write(chr,term,strlen(term)); + exit(0); + break; + } + case 's': + { + int i; + for (i = 0; i < MAX_DISKS; i++) { + if (bs_table[i]) + bdrv_commit(bs_table[i]); + } + } + break; + case 'b': + if (chr->chr_event) + chr->chr_event(chr->opaque, CHR_EVENT_BREAK); + break; + case 'c': + /* Switch to the next registered device */ + chr->focus++; + if (chr->focus >= d->mux_cnt) + chr->focus = 0; + break; + case 't': + term_timestamps = !term_timestamps; + term_timestamps_start = -1; + break; + } + } else if (ch == term_escape_char) { + d->term_got_escape = 1; + } else { + send_char: + return 1; + } + return 0; +} + +static int mux_chr_can_read(void *opaque) +{ + CharDriverState *chr = opaque; + MuxDriver *d = chr->opaque; + if (d->chr_can_read[chr->focus]) + return d->chr_can_read[chr->focus](d->ext_opaque[chr->focus]); + return 0; +} + +static void mux_chr_read(void *opaque, const uint8_t *buf, int size) +{ + CharDriverState *chr = opaque; + MuxDriver *d = chr->opaque; + int i; + for(i = 0; i < size; i++) + if (mux_proc_byte(chr, d, buf[i])) + d->chr_read[chr->focus](d->ext_opaque[chr->focus], &buf[i], 1); +} + +static void mux_chr_event(void *opaque, int event) +{ + CharDriverState *chr = opaque; + MuxDriver *d = chr->opaque; + int i; + + /* Send the event to all registered listeners */ + for (i = 0; i < d->mux_cnt; i++) + if (d->chr_event[i]) + d->chr_event[i](d->ext_opaque[i], event); +} + +static void mux_chr_update_read_handler(CharDriverState *chr) +{ + MuxDriver *d = chr->opaque; + + if (d->mux_cnt >= MAX_MUX) { + fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n"); + return; + } + d->ext_opaque[d->mux_cnt] = chr->handler_opaque; + d->chr_can_read[d->mux_cnt] = chr->chr_can_read; + d->chr_read[d->mux_cnt] = chr->chr_read; + d->chr_event[d->mux_cnt] = chr->chr_event; + /* Fix up the real driver with mux routines */ + if (d->mux_cnt == 0) { + qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read, + mux_chr_event, chr); + } + chr->focus = d->mux_cnt; + d->mux_cnt++; +} + +CharDriverState *qemu_chr_open_mux(CharDriverState *drv) +{ + CharDriverState *chr; + MuxDriver *d; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + d = qemu_mallocz(sizeof(MuxDriver)); + if (!d) { + free(chr); + return NULL; + } + + chr->opaque = d; + d->drv = drv; + chr->focus = -1; + chr->chr_write = mux_chr_write; + chr->chr_update_read_handler = mux_chr_update_read_handler; + return chr; +} + + #ifdef _WIN32 static void socket_cleanup(void) @@ -1303,10 +1547,8 @@ int max_size; } FDCharDriver; -#define STDIO_MAX_CLIENTS 2 - -static int stdio_nb_clients; -static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS]; +#define STDIO_MAX_CLIENTS 1 +static int stdio_nb_clients = 0; static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { @@ -1419,162 +1661,45 @@ /* for STDIO, we handle the case where several clients use it (nographic mode) */ -#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */ - #define TERM_FIFO_MAX_SIZE 1 -static int term_got_escape, client_index; static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; static int term_fifo_size; -static int term_timestamps; -static int64_t term_timestamps_start; - -void term_print_help(void) -{ - printf("\n" - "C-a h print this help\n" - "C-a x exit emulator\n" - "C-a s save disk data back to file (if -snapshot)\n" - "C-a b send break (magic sysrq)\n" - "C-a t toggle console timestamps\n" - "C-a c switch between console and monitor\n" - "C-a C-a send C-a\n" - ); -} - -/* called when a char is received */ -static void stdio_received_byte(int ch) -{ - if (term_got_escape) { - term_got_escape = 0; - switch(ch) { - case 'h': - term_print_help(); - break; - case 'x': - exit(0); - break; - case 's': - { - int i; - for (i = 0; i < MAX_DISKS; i++) { - if (bs_table[i]) - bdrv_commit(bs_table[i]); - } - } - break; - case 'b': - if (client_index < stdio_nb_clients) { - CharDriverState *chr; - FDCharDriver *s; - - chr = stdio_clients[client_index]; - s = chr->opaque; - qemu_chr_event(chr, CHR_EVENT_BREAK); - } - break; - case 'c': - client_index++; - if (client_index >= stdio_nb_clients) - client_index = 0; - if (client_index == 0) { - /* send a new line in the monitor to get the prompt */ - ch = '\r'; - goto send_char; - } - break; - case 't': - term_timestamps = !term_timestamps; - term_timestamps_start = -1; - break; - case TERM_ESCAPE: - goto send_char; - } - } else if (ch == TERM_ESCAPE) { - term_got_escape = 1; - } else { - send_char: - if (client_index < stdio_nb_clients) { - uint8_t buf[1]; - CharDriverState *chr; - - chr = stdio_clients[client_index]; - if (qemu_chr_can_read(chr) > 0) { - buf[0] = ch; - qemu_chr_read(chr, buf, 1); - } else if (term_fifo_size == 0) { - term_fifo[term_fifo_size++] = ch; - } - } - } -} static int stdio_read_poll(void *opaque) { - CharDriverState *chr; + CharDriverState *chr = opaque; - if (client_index < stdio_nb_clients) { - chr = stdio_clients[client_index]; - /* try to flush the queue if needed */ - if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) { - qemu_chr_read(chr, term_fifo, 1); - term_fifo_size = 0; - } - /* see if we can absorb more chars */ - if (term_fifo_size == 0) - return 1; - else - return 0; - } else { - return 1; + /* try to flush the queue if needed */ + if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) { + qemu_chr_read(chr, term_fifo, 1); + term_fifo_size = 0; } + /* see if we can absorb more chars */ + if (term_fifo_size == 0) + return 1; + else + return 0; } static void stdio_read(void *opaque) { int size; uint8_t buf[1]; - + CharDriverState *chr = opaque; + size = read(0, buf, 1); if (size == 0) { /* stdin has been closed. Remove it from the active list. */ qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); return; } - if (size > 0) - stdio_received_byte(buf[0]); -} - -static int stdio_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - FDCharDriver *s = chr->opaque; - if (!term_timestamps) { - return unix_write(s->fd_out, buf, len); - } else { - int i; - char buf1[64]; - - for(i = 0; i < len; i++) { - unix_write(s->fd_out, buf + i, 1); - if (buf[i] == '\n') { - int64_t ti; - int secs; - - ti = get_clock(); - if (term_timestamps_start == -1) - term_timestamps_start = ti; - ti -= term_timestamps_start; - secs = ti / 1000000000; - snprintf(buf1, sizeof(buf1), - "[%02d:%02d:%02d.%03d] ", - secs / 3600, - (secs / 60) % 60, - secs % 60, - (int)((ti / 1000000) % 1000)); - unix_write(s->fd_out, buf1, strlen(buf1)); - } + if (size > 0) { + if (qemu_chr_can_read(chr) > 0) { + qemu_chr_read(chr, buf, 1); + } else if (term_fifo_size == 0) { + term_fifo[term_fifo_size++] = buf[0]; } - return len; } } @@ -1619,24 +1744,13 @@ { CharDriverState *chr; - if (nographic) { - if (stdio_nb_clients >= STDIO_MAX_CLIENTS) - return NULL; - chr = qemu_chr_open_fd(0, 1); - chr->chr_write = stdio_write; - if (stdio_nb_clients == 0) - qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL); - client_index = stdio_nb_clients; - } else { - if (stdio_nb_clients != 0) - return NULL; - chr = qemu_chr_open_fd(0, 1); - } - stdio_clients[stdio_nb_clients++] = chr; - if (stdio_nb_clients == 1) { - /* set the terminal in raw mode */ - term_init(); - } + if (stdio_nb_clients >= STDIO_MAX_CLIENTS) + return NULL; + chr = qemu_chr_open_fd(0, 1); + qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr); + stdio_nb_clients++; + term_init(); + return chr; } @@ -1798,9 +1912,26 @@ return chr; } +typedef struct { + int fd; + int mode; +} ParallelCharDriver; + +static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode) +{ + if (s->mode != mode) { + int m = mode; + if (ioctl(s->fd, PPSETMODE, &m) < 0) + return 0; + s->mode = mode; + } + return 1; +} + static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) { - int fd = (int)chr->opaque; + ParallelCharDriver *drv = chr->opaque; + int fd = drv->fd; uint8_t b; switch(cmd) { @@ -1817,7 +1948,10 @@ case CHR_IOCTL_PP_READ_CONTROL: if (ioctl(fd, PPRCONTROL, &b) < 0) return -ENOTSUP; - *(uint8_t *)arg = b; + /* Linux gives only the lowest bits, and no way to know data + direction! For better compatibility set the fixed upper + bits. */ + *(uint8_t *)arg = b | 0xc0; break; case CHR_IOCTL_PP_WRITE_CONTROL: b = *(uint8_t *)arg; @@ -1829,15 +1963,63 @@ return -ENOTSUP; *(uint8_t *)arg = b; break; + case CHR_IOCTL_PP_EPP_READ_ADDR: + if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) { + struct ParallelIOArg *parg = arg; + int n = read(fd, parg->buffer, parg->count); + if (n != parg->count) { + return -EIO; + } + } + break; + case CHR_IOCTL_PP_EPP_READ: + if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) { + struct ParallelIOArg *parg = arg; + int n = read(fd, parg->buffer, parg->count); + if (n != parg->count) { + return -EIO; + } + } + break; + case CHR_IOCTL_PP_EPP_WRITE_ADDR: + if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) { + struct ParallelIOArg *parg = arg; + int n = write(fd, parg->buffer, parg->count); + if (n != parg->count) { + return -EIO; + } + } + break; + case CHR_IOCTL_PP_EPP_WRITE: + if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) { + struct ParallelIOArg *parg = arg; + int n = write(fd, parg->buffer, parg->count); + if (n != parg->count) { + return -EIO; + } + } + break; default: return -ENOTSUP; } return 0; } +static void pp_close(CharDriverState *chr) +{ + ParallelCharDriver *drv = chr->opaque; + int fd = drv->fd; + + pp_hw_mode(drv, IEEE1284_MODE_COMPAT); + ioctl(fd, PPRELEASE); + close(fd); + qemu_free(drv); +} + static CharDriverState *qemu_chr_open_pp(const char *filename) { CharDriverState *chr; + ParallelCharDriver *drv; int fd; fd = open(filename, O_RDWR); @@ -1849,14 +2031,24 @@ return NULL; } + drv = qemu_mallocz(sizeof(ParallelCharDriver)); + if (!drv) { + close(fd); + return NULL; + } + drv->fd = fd; + drv->mode = IEEE1284_MODE_COMPAT; + chr = qemu_mallocz(sizeof(CharDriverState)); if (!chr) { + qemu_free(drv); close(fd); return NULL; } - chr->opaque = (void *)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; + chr->chr_close = pp_close; + chr->opaque = drv; qemu_chr_reset(chr); @@ -2721,6 +2913,16 @@ if (strstart(filename, "udp:", &p)) { return qemu_chr_open_udp(p); } else + if (strstart(filename, "mon:", &p)) { + CharDriverState *drv = qemu_chr_open(p); + if (drv) { + drv = qemu_chr_open_mux(drv); + monitor_init(drv, !nographic); + return drv; + } + printf("Unable to open driver: %s\n", p); + return 0; + } else #ifndef _WIN32 if (strstart(filename, "unix:", &p)) { return qemu_chr_open_tcp(p, 0, 1); @@ -3199,7 +3401,15 @@ uint8_t buf[4096]; int size; +#ifdef __sun__ + struct strbuf sbuf; + int f = 0; + sbuf.maxlen = sizeof(buf); + sbuf.buf = buf; + size = getmsg(s->fd, NULL, &sbuf, &f) >=0 ? sbuf.len : -1; +#else size = read(s->fd, buf, sizeof(buf)); +#endif if (size > 0) { qemu_send_packet(s->vc, buf, size); } @@ -3242,10 +3452,135 @@ return fd; } #elif defined(__sun__) +#define TUNNEWPPA (('T'<<16) | 0x0001) +/* + * Allocate TAP device, returns opened fd. + * Stores dev name in the first arg(must be large enough). + */ +int tap_alloc(char *dev) +{ + int tap_fd, if_fd, ppa = -1; + static int ip_fd = 0; + char *ptr; + + static int arp_fd = 0; + int ip_muxid, arp_muxid; + struct strioctl strioc_if, strioc_ppa; + int link_type = I_PLINK;; + struct lifreq ifr; + char actual_name[32] = ""; + + memset(&ifr, 0x0, sizeof(ifr)); + + if( *dev ){ + ptr = dev; + while( *ptr && !isdigit((int)*ptr) ) ptr++; + ppa = atoi(ptr); + } + + /* Check if IP device was opened */ + if( ip_fd ) + close(ip_fd); + + if( (ip_fd = open("/dev/udp", O_RDWR, 0)) < 0){ + syslog(LOG_ERR, "Can't open /dev/ip (actually /dev/udp)"); + return -1; + } + + if( (tap_fd = open("/dev/tap", O_RDWR, 0)) < 0){ + syslog(LOG_ERR, "Can't open /dev/tap"); + return -1; + } + + /* Assign a new PPA and get its unit number. */ + strioc_ppa.ic_cmd = TUNNEWPPA; + strioc_ppa.ic_timout = 0; + strioc_ppa.ic_len = sizeof(ppa); + strioc_ppa.ic_dp = (char *)&ppa; + if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0) + syslog (LOG_ERR, "Can't assign new interface"); + + if( (if_fd = open("/dev/tap", O_RDWR, 0)) < 0){ + syslog(LOG_ERR, "Can't open /dev/tap (2)"); + return -1; + } + if(ioctl(if_fd, I_PUSH, "ip") < 0){ + syslog(LOG_ERR, "Can't push IP module"); + return -1; + } + + if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) + syslog(LOG_ERR, "Can't get flags\n"); + + snprintf (actual_name, 32, "tap%d", ppa); + strncpy (ifr.lifr_name, actual_name, sizeof (ifr.lifr_name)); + + ifr.lifr_ppa = ppa; + /* Assign ppa according to the unit number returned by tun device */ + + if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0) + syslog (LOG_ERR, "Can't set PPA %d", ppa); + if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0) + syslog (LOG_ERR, "Can't get flags\n"); + /* Push arp module to if_fd */ + if (ioctl (if_fd, I_PUSH, "arp") < 0) + syslog (LOG_ERR, "Can't push ARP module (2)"); + + /* Push arp module to ip_fd */ + if (ioctl (ip_fd, I_POP, NULL) < 0) + syslog (LOG_ERR, "I_POP failed\n"); + if (ioctl (ip_fd, I_PUSH, "arp") < 0) + syslog (LOG_ERR, "Can't push ARP module (3)\n"); + /* Open arp_fd */ + if ((arp_fd = open ("/dev/tap", O_RDWR, 0)) < 0) + syslog (LOG_ERR, "Can't open %s\n", "/dev/tap"); + + /* Set ifname to arp */ + strioc_if.ic_cmd = SIOCSLIFNAME; + strioc_if.ic_timout = 0; + strioc_if.ic_len = sizeof(ifr); + strioc_if.ic_dp = (char *)𝔦 + if (ioctl(arp_fd, I_STR, &strioc_if) < 0){ + syslog (LOG_ERR, "Can't set ifname to arp\n"); + } + + if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){ + syslog(LOG_ERR, "Can't link TAP device to IP"); + return -1; + } + + if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0) + syslog (LOG_ERR, "Can't link TAP device to ARP"); + + close (if_fd); + + memset(&ifr, 0x0, sizeof(ifr)); + strncpy (ifr.lifr_name, actual_name, sizeof (ifr.lifr_name)); + ifr.lifr_ip_muxid = ip_muxid; + ifr.lifr_arp_muxid = arp_muxid; + + if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0) + { + ioctl (ip_fd, I_PUNLINK , arp_muxid); + ioctl (ip_fd, I_PUNLINK, ip_muxid); + syslog (LOG_ERR, "Can't set multiplexor id"); + } + + sprintf(dev, "tap%d", ppa); + return tap_fd; +} + static int tap_open(char *ifname, int ifname_size) { - fprintf(stderr, "warning: tap_open not yet implemented\n"); - return -1; + char dev[10]=""; + int fd; + if( (fd = tap_alloc(dev)) < 0 ){ + fprintf(stderr, "Cannot allocate TAP device\n"); + return -1; + } + pstrcpy(ifname, ifname_size, dev); + fcntl(fd, F_SETFL, O_NONBLOCK); + return fd; } #else static int tap_open(char *ifname, int ifname_size) @@ -3950,6 +4285,11 @@ dev = usb_tablet_init(); } else if (strstart(devname, "disk:", &p)) { dev = usb_msd_init(p); + } else if (strstart(devname, "net:", &p)) { + unsigned int nr = strtoul(p, NULL, 0); + if (nr >= (unsigned int)nb_nics || strcmp(nd_table[nr].model, "usb")) + return -1; + dev = usb_net_init(&nd_table[nr]); } else { return -1; } @@ -4067,6 +4407,48 @@ } /***********************************************************/ +/* PCMCIA/Cardbus */ + +static struct pcmcia_socket_entry_s { + struct pcmcia_socket_s *socket; + struct pcmcia_socket_entry_s *next; +} *pcmcia_sockets = 0; + +void pcmcia_socket_register(struct pcmcia_socket_s *socket) +{ + struct pcmcia_socket_entry_s *entry; + + entry = qemu_malloc(sizeof(struct pcmcia_socket_entry_s)); + entry->socket = socket; + entry->next = pcmcia_sockets; + pcmcia_sockets = entry; +} + +void pcmcia_socket_unregister(struct pcmcia_socket_s *socket) +{ + struct pcmcia_socket_entry_s *entry, **ptr; + + ptr = &pcmcia_sockets; + for (entry = *ptr; entry; ptr = &entry->next, entry = *ptr) + if (entry->socket == socket) { + *ptr = entry->next; + qemu_free(entry); + } +} + +void pcmcia_info(void) +{ + struct pcmcia_socket_entry_s *iter; + if (!pcmcia_sockets) + term_printf("No PCMCIA sockets\n"); + + for (iter = pcmcia_sockets; iter; iter = iter->next) + term_printf("%s: %s\n", iter->socket->slot_string, + iter->socket->attached ? iter->socket->card_string : + "Empty"); +} + +/***********************************************************/ /* pid file */ static char *pid_filename; @@ -4142,6 +4524,7 @@ IOCanRWHandler *fd_read_poll; IOHandler *fd_read; IOHandler *fd_write; + int deleted; void *opaque; /* temporary data */ struct pollfd *ufd; @@ -4167,8 +4550,7 @@ if (ioh == NULL) break; if (ioh->fd == fd) { - *pioh = ioh->next; - qemu_free(ioh); + ioh->deleted = 1; break; } pioh = &ioh->next; @@ -4189,6 +4571,7 @@ ioh->fd_read = fd_read; ioh->fd_write = fd_write; ioh->opaque = opaque; + ioh->deleted = 0; } return 0; } @@ -5837,7 +6220,7 @@ void main_loop_wait(int timeout) { - IOHandlerRecord *ioh, *ioh_next; + IOHandlerRecord *ioh; fd_set rfds, wfds, xfds; int ret, nfds; struct timeval tv; @@ -5872,6 +6255,8 @@ FD_ZERO(&wfds); FD_ZERO(&xfds); for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { + if (ioh->deleted) + continue; if (ioh->fd_read && (!ioh->fd_read_poll || ioh->fd_read_poll(ioh->opaque) != 0)) { @@ -5899,9 +6284,11 @@ #endif ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); if (ret > 0) { - /* XXX: better handling of removal */ - for(ioh = first_io_handler; ioh != NULL; ioh = ioh_next) { - ioh_next = ioh->next; + IOHandlerRecord **pioh; + + for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { + if (ioh->deleted) + continue; if (FD_ISSET(ioh->fd, &rfds)) { ioh->fd_read(ioh->opaque); } @@ -5909,6 +6296,17 @@ ioh->fd_write(ioh->opaque); } } + + /* remove deleted IO handlers */ + pioh = &first_io_handler; + while (*pioh) { + ioh = *pioh; + if (ioh->deleted) { + *pioh = ioh->next; + qemu_free(ioh); + } else + pioh = &ioh->next; + } } #if defined(CONFIG_SLIRP) if (slirp_inited) { @@ -6019,13 +6417,17 @@ "\n" "Standard options:\n" "-M machine select emulated machine (-M ? for list)\n" + "-cpu cpu select CPU (-C ? for list)\n" "-fda/-fdb file use 'file' as floppy disk 0/1 image\n" "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n" "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n" "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n" + "-mtdblock file use 'file' as on-board Flash memory image\n" + "-sd file use 'file' as SecureDigital card image\n" "-boot [a|c|d|n] boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)\n" "-snapshot write to temporary files instead of disk image files\n" #ifdef CONFIG_SDL + "-no-frame open SDL window without a frame and window decorations\n" "-no-quit disable SDL window close capability\n" #endif #ifdef TARGET_I386 @@ -6034,6 +6436,7 @@ "-m megs set virtual RAM size to megs MB [default=%d]\n" "-smp n set the number of CPUs to 'n' [default=1]\n" "-nographic disable graphical output and redirect serial I/Os to console\n" + "-vertical rotate graphical output left (only PXA targets)\n" #ifndef _WIN32 "-k language use keyboard layout (for example \"fr\" for French)\n" #endif @@ -6081,7 +6484,8 @@ " is provided, the default is '-net nic -net user'\n" "\n" #ifdef CONFIG_SLIRP - "-tftp prefix allow tftp access to files starting with prefix [-net user]\n" + "-tftp dir allow tftp access to files in dir [-net user]\n" + "-bootp file advertise file in BOOTP replies\n" #ifndef _WIN32 "-smb dir allow SMB access to files in 'dir' [-net user]\n" #endif @@ -6100,8 +6504,8 @@ "-parallel dev redirect the parallel port to char device 'dev'\n" "-pidfile file Write PID to 'file'\n" "-S freeze CPU at startup (use 'c' to start execution)\n" - "-s wait gdb connection to port %d\n" - "-p port change gdb connection port\n" + "-s wait gdb connection to port\n" + "-p port set gdb connection port [default=%s]\n" "-d item1,... output log to %s (use -d ? for a list of log items)\n" "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n" " translation (t=none or lba) (usually qemu can guess them)\n" @@ -6149,6 +6553,7 @@ QEMU_OPTION_h, QEMU_OPTION_M, + QEMU_OPTION_cpu, QEMU_OPTION_fda, QEMU_OPTION_fdb, QEMU_OPTION_hda, @@ -6156,6 +6561,8 @@ QEMU_OPTION_hdc, QEMU_OPTION_hdd, QEMU_OPTION_cdrom, + QEMU_OPTION_mtdblock, + QEMU_OPTION_sd, QEMU_OPTION_boot, QEMU_OPTION_snapshot, #ifdef TARGET_I386 @@ -6163,6 +6570,7 @@ #endif QEMU_OPTION_m, QEMU_OPTION_nographic, + QEMU_OPTION_vertical, #ifdef HAS_AUDIO QEMU_OPTION_audio_help, QEMU_OPTION_soundhw, @@ -6170,6 +6578,7 @@ QEMU_OPTION_net, QEMU_OPTION_tftp, + QEMU_OPTION_bootp, QEMU_OPTION_smb, QEMU_OPTION_redir, @@ -6189,11 +6598,13 @@ QEMU_OPTION_cirrusvga, QEMU_OPTION_g, QEMU_OPTION_std_vga, + QEMU_OPTION_echr, QEMU_OPTION_monitor, QEMU_OPTION_serial, QEMU_OPTION_parallel, QEMU_OPTION_loadvm, QEMU_OPTION_full_screen, + QEMU_OPTION_no_frame, QEMU_OPTION_no_quit, QEMU_OPTION_pidfile, QEMU_OPTION_no_kqemu, @@ -6205,6 +6616,7 @@ QEMU_OPTION_vnc, QEMU_OPTION_no_acpi, QEMU_OPTION_no_reboot, + QEMU_OPTION_show_cursor, QEMU_OPTION_daemonize, QEMU_OPTION_option_rom, QEMU_OPTION_semihosting @@ -6221,6 +6633,7 @@ { "help", 0, QEMU_OPTION_h }, { "M", HAS_ARG, QEMU_OPTION_M }, + { "cpu", HAS_ARG, QEMU_OPTION_cpu }, { "fda", HAS_ARG, QEMU_OPTION_fda }, { "fdb", HAS_ARG, QEMU_OPTION_fdb }, { "hda", HAS_ARG, QEMU_OPTION_hda }, @@ -6228,6 +6641,8 @@ { "hdc", HAS_ARG, QEMU_OPTION_hdc }, { "hdd", HAS_ARG, QEMU_OPTION_hdd }, { "cdrom", HAS_ARG, QEMU_OPTION_cdrom }, + { "mtdblock", HAS_ARG, QEMU_OPTION_mtdblock }, + { "sd", HAS_ARG, QEMU_OPTION_sd }, { "boot", HAS_ARG, QEMU_OPTION_boot }, { "snapshot", 0, QEMU_OPTION_snapshot }, #ifdef TARGET_I386 @@ -6235,6 +6650,7 @@ #endif { "m", HAS_ARG, QEMU_OPTION_m }, { "nographic", 0, QEMU_OPTION_nographic }, + { "vertical", 0, QEMU_OPTION_vertical }, { "k", HAS_ARG, QEMU_OPTION_k }, #ifdef HAS_AUDIO { "audio-help", 0, QEMU_OPTION_audio_help }, @@ -6244,6 +6660,7 @@ { "net", HAS_ARG, QEMU_OPTION_net}, #ifdef CONFIG_SLIRP { "tftp", HAS_ARG, QEMU_OPTION_tftp }, + { "bootp", HAS_ARG, QEMU_OPTION_bootp }, #ifndef _WIN32 { "smb", HAS_ARG, QEMU_OPTION_smb }, #endif @@ -6270,12 +6687,14 @@ #endif { "localtime", 0, QEMU_OPTION_localtime }, { "std-vga", 0, QEMU_OPTION_std_vga }, + { "echr", 1, QEMU_OPTION_echr }, { "monitor", 1, QEMU_OPTION_monitor }, { "serial", 1, QEMU_OPTION_serial }, { "parallel", 1, QEMU_OPTION_parallel }, { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, { "full-screen", 0, QEMU_OPTION_full_screen }, #ifdef CONFIG_SDL + { "no-frame", 0, QEMU_OPTION_no_frame }, { "no-quit", 0, QEMU_OPTION_no_quit }, #endif { "pidfile", HAS_ARG, QEMU_OPTION_pidfile }, @@ -6289,6 +6708,7 @@ { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, { "no-acpi", 0, QEMU_OPTION_no_acpi }, { "no-reboot", 0, QEMU_OPTION_no_reboot }, + { "show-cursor", 0, QEMU_OPTION_show_cursor }, { "daemonize", 0, QEMU_OPTION_daemonize }, { "option-rom", HAS_ARG, QEMU_OPTION_option_rom }, #if defined(TARGET_ARM) @@ -6308,6 +6728,24 @@ /* password input */ +int qemu_key_check(BlockDriverState *bs, const char *name) +{ + char password[256]; + int i; + + if (!bdrv_is_encrypted(bs)) + return 0; + + term_printf("%s is encrypted.\n", name); + for(i = 0; i < 3; i++) { + monitor_readline("Password: ", 1, password, sizeof(password)); + if (bdrv_set_key(bs, password) == 0) + return 0; + term_printf("invalid password\n"); + } + return -EPERM; +} + static BlockDriverState *get_bdrv(int index) { BlockDriverState *bs; @@ -6325,21 +6763,12 @@ static void read_passwords(void) { BlockDriverState *bs; - int i, j; - char password[256]; + int i; for(i = 0; i < 6; i++) { bs = get_bdrv(i); - if (bs && bdrv_is_encrypted(bs)) { - term_printf("%s is encrypted.\n", bdrv_get_device_name(bs)); - for(j = 0; j < 3; j++) { - monitor_readline("Password: ", - 1, password, sizeof(password)); - if (bdrv_set_key(bs, password) == 0) - break; - term_printf("invalid password\n"); - } - } + if (bs) + qemu_key_check(bs, bdrv_get_device_name(bs)); } } @@ -6377,6 +6806,7 @@ #ifdef HAS_AUDIO struct soundhw soundhw[] = { +#ifdef HAS_AUDIO_CHOICE #ifdef TARGET_I386 { "pcspk", @@ -6425,6 +6855,7 @@ 0, { .init_pci = es1370_init } }, +#endif { NULL, NULL, 0, 0, { NULL } } }; @@ -6501,10 +6932,11 @@ int main(int argc, char **argv) { #ifdef CONFIG_GDBSTUB - int use_gdbstub, gdbstub_port; + int use_gdbstub; + const char *gdbstub_port; #endif int i, cdrom_index; - int snapshot, linux_boot; + int linux_boot; const char *initrd_filename; const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD]; const char *kernel_filename, *kernel_cmdline; @@ -6522,6 +6954,7 @@ int parallel_device_index; const char *loadvm = NULL; QEMUMachine *machine; + const char *cpu_model; char usb_devices[MAX_USB_CMDLINE][128]; int usb_devices_index; int fds[2]; @@ -6559,6 +6992,7 @@ register_machines(); machine = first_machine; + cpu_model = NULL; initrd_filename = NULL; for(i = 0; i < MAX_FD; i++) fd_filename[i] = NULL; @@ -6650,6 +7084,17 @@ exit(1); } break; + case QEMU_OPTION_cpu: + /* hw initialization will check this */ + if (optarg[0] == '?') { +#if defined(TARGET_PPC) + ppc_cpu_list(stdout, &fprintf); +#endif + exit(1); + } else { + cpu_model = optarg; + } + break; case QEMU_OPTION_initrd: initrd_filename = optarg; break; @@ -6665,6 +7110,12 @@ cdrom_index = -1; } break; + case QEMU_OPTION_mtdblock: + mtd_filename = optarg; + break; + case QEMU_OPTION_sd: + sd_filename = optarg; + break; case QEMU_OPTION_snapshot: snapshot = 1; break; @@ -6705,10 +7156,13 @@ } break; case QEMU_OPTION_nographic: - pstrcpy(monitor_device, sizeof(monitor_device), "stdio"); pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio"); + pstrcpy(monitor_device, sizeof(monitor_device), "stdio"); nographic = 1; break; + case QEMU_OPTION_vertical: + graphic_rotate = 1; + break; case QEMU_OPTION_kernel: kernel_filename = optarg; break; @@ -6760,6 +7214,9 @@ case QEMU_OPTION_tftp: tftp_prefix = optarg; break; + case QEMU_OPTION_bootp: + bootp_filename = optarg; + break; #ifndef _WIN32 case QEMU_OPTION_smb: net_slirp_smb(optarg); @@ -6812,7 +7269,7 @@ use_gdbstub = 1; break; case QEMU_OPTION_p: - gdbstub_port = atoi(optarg); + gdbstub_port = optarg; break; #endif case QEMU_OPTION_L: @@ -6867,6 +7324,14 @@ graphic_depth = depth; } break; + case QEMU_OPTION_echr: + { + char *r; + term_escape_char = strtol(optarg, &r, 0); + if (r == optarg) + printf("Bad argument to echr\n"); + break; + } case QEMU_OPTION_monitor: pstrcpy(monitor_device, sizeof(monitor_device), optarg); break; @@ -6895,6 +7360,9 @@ full_screen = 1; break; #ifdef CONFIG_SDL + case QEMU_OPTION_no_frame: + no_frame = 1; + break; case QEMU_OPTION_no_quit: no_quit = 1; break; @@ -6945,6 +7413,9 @@ case QEMU_OPTION_no_reboot: no_reboot = 1; break; + case QEMU_OPTION_show_cursor: + cursor_hide = 0; + break; case QEMU_OPTION_daemonize: daemonize = 1; break; @@ -7018,6 +7489,7 @@ linux_boot = (kernel_filename != NULL); if (!linux_boot && + boot_device != 'n' && hd_filename[0] == '\0' && (cdrom_index >= 0 && hd_filename[cdrom_index] == '\0') && fd_filename[0] == '\0') @@ -7126,7 +7598,7 @@ fd_table[i] = bdrv_new(buf); bdrv_set_type_hint(fd_table[i], BDRV_TYPE_FLOPPY); } - if (fd_filename[i] != '\0') { + if (fd_filename[i][0] != '\0') { if (bdrv_open(fd_table[i], fd_filename[i], snapshot ? BDRV_O_SNAPSHOT : 0) < 0) { fprintf(stderr, "qemu: could not open floppy disk image '%s'\n", @@ -7149,7 +7621,7 @@ vnc_display_init(ds, vnc_display); } else { #if defined(CONFIG_SDL) - sdl_display_init(ds, full_screen); + sdl_display_init(ds, full_screen, no_frame); #elif defined(CONFIG_COCOA) cocoa_display_init(ds, full_screen); #else @@ -7157,12 +7629,27 @@ #endif } - monitor_hd = qemu_chr_open(monitor_device); - if (!monitor_hd) { - fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device); - exit(1); + /* Maintain compatibility with multiple stdio monitors */ + if (!strcmp(monitor_device,"stdio")) { + for (i = 0; i < MAX_SERIAL_PORTS; i++) { + if (!strcmp(serial_devices[i],"mon:stdio")) { + monitor_device[0] = '\0'; + break; + } else if (!strcmp(serial_devices[i],"stdio")) { + monitor_device[0] = '\0'; + pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "mon:stdio"); + break; + } + } + } + if (monitor_device[0] != '\0') { + monitor_hd = qemu_chr_open(monitor_device); + if (!monitor_hd) { + fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device); + exit(1); + } + monitor_init(monitor_hd, !nographic); } - monitor_init(monitor_hd, !nographic); for(i = 0; i < MAX_SERIAL_PORTS; i++) { const char *devname = serial_devices[i]; @@ -7194,7 +7681,7 @@ machine->init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, - kernel_filename, kernel_cmdline, initrd_filename); + kernel_filename, kernel_cmdline, initrd_filename, cpu_model); /* init USB devices */ if (usb_enabled) { @@ -7213,8 +7700,8 @@ if (use_gdbstub) { /* XXX: use standard host:port notation and modify options accordingly. */ - if (gdbserver_start_port(gdbstub_port) < 0) { - fprintf(stderr, "qemu: could not open gdbstub device on port '%d'\n", + if (gdbserver_start(gdbstub_port) < 0) { + fprintf(stderr, "qemu: could not open gdbstub device on port '%s'\n", gdbstub_port); exit(1); } Index: qemu/vl.h =================================================================== --- qemu.orig/vl.h 2007-02-09 07:09:59.000000000 +0800 +++ qemu/vl.h 2007-03-06 10:25:40.000000000 +0800 @@ -156,9 +156,15 @@ extern int win2k_install_hack; extern int usb_enabled; extern int smp_cpus; +extern int cursor_hide; +extern int snapshot; +extern const char *sd_filename; +extern const char *mtd_filename; +extern int graphic_rotate; extern int no_quit; extern int semihosting_enabled; extern int autostart; +extern const char *bootp_filename; #define MAX_OPTION_ROMS 16 extern const char *option_rom[MAX_OPTION_ROMS]; @@ -285,6 +291,10 @@ #define CHR_IOCTL_PP_READ_CONTROL 5 #define CHR_IOCTL_PP_WRITE_CONTROL 6 #define CHR_IOCTL_PP_READ_STATUS 7 +#define CHR_IOCTL_PP_EPP_READ_ADDR 8 +#define CHR_IOCTL_PP_EPP_READ 9 +#define CHR_IOCTL_PP_EPP_WRITE_ADDR 10 +#define CHR_IOCTL_PP_EPP_WRITE 11 typedef void IOEventHandler(void *opaque, int event); @@ -299,6 +309,7 @@ void (*chr_send_event)(struct CharDriverState *chr, int event); void (*chr_close)(struct CharDriverState *chr); void *opaque; + int focus; QEMUBH *bh; } CharDriverState; @@ -349,6 +360,11 @@ extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; +struct ParallelIOArg { + void *buffer; + int count; +}; + /* VLANs support */ typedef struct VLANClientState VLANClientState; @@ -619,6 +635,8 @@ void qemu_aio_wait(void); void qemu_aio_wait_end(void); +int qemu_key_check(BlockDriverState *bs, const char *name); + /* Ensure contents are flushed to disk. */ void bdrv_flush(BlockDriverState *bs); @@ -684,7 +702,7 @@ int boot_device, DisplayState *ds, const char **fd_filename, int snapshot, const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename); + const char *initrd_filename, const char *cpu_model); typedef struct QEMUMachine { const char *name; @@ -698,6 +716,10 @@ typedef void SetIRQFunc(void *opaque, int irq_num, int level); typedef void IRQRequestFunc(void *opaque, int level); +#if defined(TARGET_PPC) +void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +#endif + /* ISA bus */ extern target_phys_addr_t isa_mem_base; @@ -901,7 +923,7 @@ unsigned long vga_ram_offset, int vga_ram_size); /* sdl.c */ -void sdl_display_init(DisplayState *ds, int full_screen); +void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); /* cocoa.m */ void cocoa_display_init(DisplayState *ds, int full_screen); @@ -930,6 +952,10 @@ 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); +/* ds1225y.c */ +typedef struct ds1225y_t ds1225y_t; +ds1225y_t *ds1225y_init(target_ulong mem_base, const char *filename); + /* es1370.c */ int es1370_init (PCIBus *bus, AudioState *s); @@ -1335,7 +1361,7 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, - int board_id); + int board_id, target_phys_addr_t loader_start); /* sh7750.c */ struct SH7750State; @@ -1368,6 +1394,101 @@ uint16_t id0, uint16_t id1, uint16_t id2, uint16_t id3); +/* nand.c */ +struct nand_flash_s; +struct nand_flash_s *nand_init(int manf_id, int chip_id); +void nand_done(struct nand_flash_s *s); +void nand_setpins(struct nand_flash_s *s, + int cle, int ale, int ce, int wp, int gnd); +void nand_getpins(struct nand_flash_s *s, int *rb); +void nand_setio(struct nand_flash_s *s, uint8_t value); +uint8_t nand_getio(struct nand_flash_s *s); + +#define NAND_MFR_TOSHIBA 0x98 +#define NAND_MFR_SAMSUNG 0xec +#define NAND_MFR_FUJITSU 0x04 +#define NAND_MFR_NATIONAL 0x8f +#define NAND_MFR_RENESAS 0x07 +#define NAND_MFR_STMICRO 0x20 +#define NAND_MFR_HYNIX 0xad +#define NAND_MFR_MICRON 0x2c + +#include "ecc.h" + +/* max111x.c */ +struct max111x_s; +uint32_t max111x_read(void *opaque); +void max111x_write(void *opaque, uint32_t value); +struct max111x_s *max1110_init(void (*cb)(void *opaque), void *opaque); +struct max111x_s *max1111_init(void (*cb)(void *opaque), void *opaque); +void max111x_set_input(struct max111x_s *s, int line, uint8_t value); + +/* 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( + void (*penirq)(void *opaque, int level), void *opaque); + +/* PCMCIA/Cardbus */ + +struct pcmcia_socket_s { + void (*set_irq)(void *opaque, int irq, int level); + void *opaque; + 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, uint16_t address); + void (*attr_write)(void *state, uint16_t address, uint8_t value); + uint16_t (*common_read)(void *state, uint16_t address); + void (*common_write)(void *state, uint16_t address, uint16_t value); + uint16_t (*io_read)(void *state, uint16_t address); + void (*io_write)(void *state, uint16_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); + +typedef void (*gpio_handler_t)(int line, int level, void *opaque); + +#include "hw/i2c.h" + +#ifdef TARGET_ARM +#include "hw/pxa.h" +#endif + #include "gdbstub.h" #endif /* defined(QEMU_TOOL) */