diff options
-rw-r--r-- | Makefile.android | 11 | ||||
-rw-r--r-- | Makefile.common | 59 | ||||
-rw-r--r-- | Makefile.target | 4 | ||||
-rw-r--r-- | android/avd/hw-config-defs.h | 305 | ||||
-rw-r--r-- | android/avd/info.c | 25 | ||||
-rw-r--r-- | android/avd/info.h | 7 | ||||
-rw-r--r-- | android/avd/util.c | 18 | ||||
-rw-r--r-- | android/avd/util.h | 10 | ||||
-rw-r--r-- | android/build/binary.make | 5 | ||||
-rw-r--r-- | android/build/common.sh | 4 | ||||
-rw-r--r-- | android/build/definitions.make | 13 | ||||
-rw-r--r-- | android/hw-pipe-net.c | 76 | ||||
-rw-r--r-- | android/hw-qemud.c | 470 | ||||
-rw-r--r-- | android/main-common.c | 2 | ||||
-rw-r--r-- | android/main.c | 26 | ||||
-rw-r--r-- | android/skin/window.c | 6 | ||||
-rw-r--r-- | android/utils/setenv.c | 18 | ||||
-rw-r--r-- | docs/ANDROID-QEMUD.TXT | 17 | ||||
-rw-r--r-- | images/android_icon.ico | bin | 300318 -> 56119 bytes | |||
-rw-r--r-- | images/android_icon_16.png | bin | 460 -> 1760 bytes | |||
-rw-r--r-- | images/android_icon_256.png | bin | 13369 -> 39249 bytes | |||
-rw-r--r-- | images/android_icon_32.png | bin | 1321 -> 2965 bytes | |||
-rw-r--r-- | vl-android.c | 4 |
23 files changed, 652 insertions, 428 deletions
diff --git a/Makefile.android b/Makefile.android index 4139226..01cb35b 100644 --- a/Makefile.android +++ b/Makefile.android @@ -55,7 +55,7 @@ ifeq ($(HOST_OS),freebsd) endif ifeq ($(HOST_OS),windows) - MY_CFLAGS += -D_WIN32 -mno-cygwin + MY_CFLAGS += -D_WIN32 # we need Win32 features that are available since Windows 2000 Professional/Server (NT 5.0) MY_CFLAGS += -DWINVER=0x501 endif @@ -149,17 +149,17 @@ start-emulator-library = \ $(eval LOCAL_LDLIBS := $(MY_LDLIBS)) \ $(eval LOCAL_MODULE_TAGS := debug) \ $(eval LOCAL_MODULE := $1) \ + $(eval LOCAL_MODULE_CLASS := STATIC_LIBRARIES) # Used with start-emulator-library end-emulator-library = \ - $(eval include $(BUILD_HOST_STATIC_LIBRARY)) \ - $(eval EMULATOR_MODULE_TYPE := STATIC_LIBRARY) + $(eval include $(BUILD_HOST_STATIC_LIBRARY)) # A variant of start-emulator-library to start the definition of a host # program instead. Use with end-emulator-program start-emulator-program = \ $(call start-emulator-library,$1) \ - $(eval EMULATOR_MODULE_TYPE := EXECUTABLES) + $(eval LOCAL_MODULE_CLASS := EXECUTABLES) # A varient of end-emulator-library for host programs instead end-emulator-program = \ @@ -242,6 +242,7 @@ LOCAL_SRC_FILES := \ android/snapshot.c \ android/main-common.c \ android/main.c \ + android/utils/setenv.c \ vl-android-ui.c \ android/protocol/core-connection.c \ android/protocol/attach-ui-impl.c \ @@ -250,6 +251,8 @@ LOCAL_SRC_FILES := \ android/protocol/core-commands-proxy.c \ android/protocol/user-events-proxy.c \ +$(call gen-hw-config-defs,android/main-common.c) + LOCAL_SRC_FILES += $(SDLMAIN_SOURCES) LOCAL_STATIC_LIBRARIES += $(SDL_STATIC_LIBRARIES) diff --git a/Makefile.common b/Makefile.common index 222a1ae..93f15be 100644 --- a/Makefile.common +++ b/Makefile.common @@ -13,6 +13,42 @@ ############################################################################## ############################################################################## ### +### gen-hw-config-defs: Generate hardware configuration definitions header +### +### The 'gen-hw-config.py' script is used to generate the hw-config-defs.h +### header from the an .ini file like android/avd/hardware-properties.ini +### +### Due to the way the Android build system works, we need to regenerate +### it for each module (the output will go into a module-specific directory). +### +### This defines a function that can be used inside a module definition +### +### $(call gen-hw-config-defs) +### + +# First, define a rule to generate a dummy "emulator_hw_config_defs" module +# which purpose is simply to host the generated header in its output directory. +intermediates := $(call intermediates-dir-for,SHARED_LIBRARIES,emulator_hw_config_defs,true) + +QEMU_HARDWARE_PROPERTIES_INI := $(LOCAL_PATH)/android/avd/hardware-properties.ini +QEMU_HW_CONFIG_DEFS_H := $(intermediates)/android/avd/hw-config-defs.h +$(QEMU_HW_CONFIG_DEFS_H): PRIVATE_PATH := $(LOCAL_PATH) +$(QEMU_HW_CONFIG_DEFS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_PATH)/android/tools/gen-hw-config.py $< $@ +$(QEMU_HW_CONFIG_DEFS_H): $(QEMU_HARDWARE_PROPERTIES_INI) $(LOCAL_PATH)/android/tools/gen-hw-config.py + $(hide) rm -f $@ + $(transform-generated-source) + +QEMU_HW_CONFIG_DEFS_INCLUDES := $(intermediates) + +# Second, define a function that needs to be called inside each module that contains +# a source file that includes the generated header file. +gen-hw-config-defs = \ + $(eval LOCAL_GENERATED_SOURCES += $(QEMU_HW_CONFIG_DEFS_H))\ + $(eval LOCAL_C_INCLUDES += $(QEMU_HW_CONFIG_DEFS_INCLUDES)) + +############################################################################## +############################################################################## +### ### emulator-common: LIBRARY OF COMMON FUNCTIONS ### ### THESE ARE POTENTIALLY USED BY ALL COMPONENTS @@ -90,6 +126,8 @@ LOCAL_SRC_FILES += \ android/utils/tempfile.c \ android/utils/vector.c \ +$(call gen-hw-config-defs) + LOCAL_CFLAGS += $(EMULATOR_COMMON_CFLAGS) $(call end-emulator-library) @@ -215,6 +253,8 @@ LOCAL_SRC_FILES += \ android/qemulator.c \ android/keycode.c \ +$(call gen-hw-config-defs) + # enable MMX code for our skin scaler ifeq ($(HOST_ARCH),x86) LOCAL_CFLAGS += -DUSE_MMX=1 -mmmx @@ -383,6 +423,8 @@ CORE_MISC_SOURCES = \ android/snapshot.c \ android/utils/timezone.c \ +$(call gen-hw-config-defs) + ifeq ($(HOST_ARCH),x86) CORE_MISC_SOURCES += i386-dis.c endif @@ -475,19 +517,6 @@ LOCAL_SRC_FILES += \ qlist.c \ qstring.c \ -# hw-config-defs.h is generated from android/avd/hardware-properties.ini -# -QEMU_HARDWARE_PROPERTIES_INI := $(LOCAL_PATH)/android/avd/hardware-properties.ini -QEMU_HW_CONFIG_DEFS_H := $(LOCAL_PATH)/android/avd/hw-config-defs.h -$(QEMU_HW_CONFIG_DEFS_H): PRIVATE_PATH := $(LOCAL_PATH) -$(QEMU_HW_CONFIG_DEFS_H): PRIVATE_SOURCES := $(QEMU_HARDWARE_PROPERTIES_INI) -$(QEMU_HW_CONFIG_DEFS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_PATH)/android/tools/gen-hw-config.py $(QEMU_HARDWARE_PROPERTIES_INI) $@ -$(QEMU_HW_CONFIG_DEFS_H): $(QEMU_HARDWARE_PROPERTIES_INI) $(LOCAL_PATH)/android/tools/gen-hw-config.py - $(hide) rm -f $@ - $(transform-generated-source) - -$(LOCAL_PATH)/android/avd/hw-config.h: $(QEMU_HW_CONFIG_DEFS_H) - # gdbstub-xml.c contains C-compilable arrays corresponding to the content # of $(LOCAL_PATH)/gdb-xml/, and is generated with the 'feature_to_c.sh' script. # @@ -603,7 +632,7 @@ $(call end-emulator-library) gen-hx-header = $(eval $(call gen-hx-header-ev,$1,$2,$3)) define gen-hx-header-ev -intermediates := $$(call intermediates-dir-for,$$(EMULATOR_MODULE_TYPE),$$(LOCAL_MODULE),true) +intermediates := $$(call intermediates-dir-for,$$(LOCAL_MODULE_CLASS),$$(LOCAL_MODULE),true) QEMU_HEADER_H := $$(intermediates)/$$2 $$(QEMU_HEADER_H): PRIVATE_PATH := $$(LOCAL_PATH) @@ -613,7 +642,5 @@ $$(QEMU_HEADER_H): $$(LOCAL_PATH)/$$1 $$(LOCAL_PATH)/hxtool LOCAL_GENERATED_SOURCES += $$(QEMU_HEADER_H) LOCAL_C_INCLUDES += $$(intermediates) -_objects := $$(patsubst %,$$(intermediates)/%,$$(3:.c=.o)) -$$(_objects): $$(QEMU_HEADER_H) endef diff --git a/Makefile.target b/Makefile.target index 5f7a8b2..ccc86b4 100644 --- a/Makefile.target +++ b/Makefile.target @@ -86,6 +86,8 @@ HW_SOURCES := \ usb.c \ watchdog.c +$(call gen-hw-config-defs) + ifeq ($(EMULATOR_TARGET_ARCH),arm) HW_SOURCES += android_arm.c \ arm_pic.c \ @@ -277,6 +279,7 @@ LOCAL_SRC_FILES := \ $(call gen-hx-header,qemu-monitor.hx,qemu-monitor.h,monitor.c) $(call gen-hx-header,qemu-options.hx,qemu-options.def,vl-android.c qemu-options.h) +$(call gen-hw-config-defs) ifeq ($(HOST_OS),darwin) FRAMEWORKS := OpenGL Cocoa QuickTime ApplicationServices Carbon IOKit @@ -356,6 +359,7 @@ LOCAL_SRC_FILES := \ $(call gen-hx-header,qemu-monitor.hx,qemu-monitor.h,monitor.c) $(call gen-hx-header,qemu-options.hx,qemu-options.def,vl-android.c qemu-options.h) +$(call gen-hw-config-defs) # The following files cannot be in static libraries because they contain # constructor functions that are otherwise stripped by the final linker diff --git a/android/avd/hw-config-defs.h b/android/avd/hw-config-defs.h deleted file mode 100644 index bb523d5..0000000 --- a/android/avd/hw-config-defs.h +++ /dev/null @@ -1,305 +0,0 @@ -/* this file is automatically generated from 'hardware-properties.ini' - * DO NOT EDIT IT. To re-generate it, use android/tools/gen-hw-config.py' - */ -#ifndef HWCFG_INT -#error HWCFG_INT not defined -#endif -#ifndef HWCFG_BOOL -#error HWCFG_BOOL not defined -#endif -#ifndef HWCFG_DISKSIZE -#error HWCFG_DISKSIZE not defined -#endif -#ifndef HWCFG_STRING -#error HWCFG_STRING not defined -#endif -#ifndef HWCFG_DOUBLE -#error HWCFG_DOUBLE not defined -#endif - -HWCFG_STRING( - hw_cpu_arch, - "hw.cpu.arch", - "arm", - "CPU Architecture", - "The CPU Architecture to emulator") - -HWCFG_STRING( - hw_cpu_model, - "hw.cpu.model", - "", - "CPU model", - "The CPU model (QEMU-specific string)") - -HWCFG_INT( - hw_ramSize, - "hw.ramSize", - 0, - "Device ram size", - "The amount of physical RAM on the device, in megabytes.") - -HWCFG_BOOL( - hw_touchScreen, - "hw.touchScreen", - "yes", - "Touch-screen support", - "Whether there is a touch screen or not on the device.") - -HWCFG_BOOL( - hw_trackBall, - "hw.trackBall", - "yes", - "Track-ball support", - "Whether there is a trackball on the device.") - -HWCFG_BOOL( - hw_keyboard, - "hw.keyboard", - "yes", - "Keyboard support", - "Whether the device has a QWERTY keyboard.") - -HWCFG_BOOL( - hw_keyboard_lid, - "hw.keyboard.lid", - "yes", - "Keyboard lid support", - "Whether the QWERTY keyboard can be opened/closed.") - -HWCFG_BOOL( - hw_dPad, - "hw.dPad", - "yes", - "DPad support", - "Whether the device has DPad keys") - -HWCFG_BOOL( - hw_gsmModem, - "hw.gsmModem", - "yes", - "GSM modem support", - "Whether there is a GSM modem in the device.") - -HWCFG_BOOL( - hw_camera, - "hw.camera", - "no", - "Camera support", - "Whether the device has a camera.") - -HWCFG_INT( - hw_camera_maxHorizontalPixels, - "hw.camera.maxHorizontalPixels", - 640, - "Maximum horizontal camera pixels", - "") - -HWCFG_INT( - hw_camera_maxVerticalPixels, - "hw.camera.maxVerticalPixels", - 480, - "Maximum vertical camera pixels", - "") - -HWCFG_BOOL( - hw_gps, - "hw.gps", - "yes", - "GPS support", - "Whether there is a GPS in the device.") - -HWCFG_BOOL( - hw_battery, - "hw.battery", - "yes", - "Battery support", - "Whether the device can run on a battery.") - -HWCFG_BOOL( - hw_accelerometer, - "hw.accelerometer", - "yes", - "Accelerometer", - "Whether there is an accelerometer in the device.") - -HWCFG_BOOL( - hw_audioInput, - "hw.audioInput", - "yes", - "Audio recording support", - "Whether the device can record audio") - -HWCFG_BOOL( - hw_audioOutput, - "hw.audioOutput", - "yes", - "Audio playback support", - "Whether the device can play audio") - -HWCFG_BOOL( - hw_sdCard, - "hw.sdCard", - "yes", - "SD Card support", - "Whether the device supports insertion/removal of virtual SD Cards.") - -HWCFG_STRING( - hw_sdCard_path, - "hw.sdCard.path", - "", - "SD Card image path", - "") - -HWCFG_BOOL( - disk_cachePartition, - "disk.cachePartition", - "yes", - "Cache partition support", - "Whether we use a /cache partition on the device.") - -HWCFG_STRING( - disk_cachePartition_path, - "disk.cachePartition.path", - "", - "Cache partition", - "Cache partition to use on the device. Ignored if disk.cachePartition is not 'yes'.") - -HWCFG_DISKSIZE( - disk_cachePartition_size, - "disk.cachePartition.size", - "66MB", - "Cache partition size", - "") - -HWCFG_INT( - hw_lcd_width, - "hw.lcd.width", - 320, - "LCD pixel width", - "") - -HWCFG_INT( - hw_lcd_height, - "hw.lcd.height", - 640, - "LCD pixel height", - "") - -HWCFG_INT( - hw_lcd_depth, - "hw.lcd.depth", - 16, - "LCD color depth", - "Must be 16 or 32. Color bit depth of emulated framebuffer.") - -HWCFG_INT( - hw_lcd_density, - "hw.lcd.density", - 160, - "Abstracted LCD density", - "Must be one of 120, 160 or 240. A value used to roughly describe the density of the LCD screen for automatic resource/asset selection.") - -HWCFG_BOOL( - hw_lcd_backlight, - "hw.lcd.backlight", - "yes", - "LCD backlight", - "Enable/Disable LCD backlight simulation,yes-enabled,no-disabled.") - -HWCFG_INT( - vm_heapSize, - "vm.heapSize", - 0, - "Max VM application heap size", - "The maximum heap size a Dalvik application might allocate before being killed by the system. Value is in megabytes.") - -HWCFG_BOOL( - hw_sensors_proximity, - "hw.sensors.proximity", - "yes", - "Proximity support", - "Whether there is an proximity in the device.") - -HWCFG_STRING( - kernel_path, - "kernel.path", - "", - "Path to the kernel image", - "Path to the kernel image.") - -HWCFG_STRING( - kernel_parameters, - "kernel.parameters", - "", - "kernel boot parameters string.", - "") - -HWCFG_STRING( - disk_ramdisk_path, - "disk.ramdisk.path", - "", - "Path to the ramdisk image", - "Path to the ramdisk image.") - -HWCFG_STRING( - disk_systemPartition_path, - "disk.systemPartition.path", - "", - "Path to runtime system partition image", - "") - -HWCFG_STRING( - disk_systemPartition_initPath, - "disk.systemPartition.initPath", - "", - "Initial system partition image", - "") - -HWCFG_DISKSIZE( - disk_systemPartition_size, - "disk.systemPartition.size", - "0", - "Ideal size of system partition", - "") - -HWCFG_STRING( - disk_dataPartition_path, - "disk.dataPartition.path", - "<temp>", - "Path to data partition file", - "Path to data partition file. Cannot be empty. Special value <temp> means using a temporary file. If disk.dataPartition.initPath is not empty, its content will be copied to the disk.dataPartition.path file at boot-time.") - -HWCFG_STRING( - disk_dataPartition_initPath, - "disk.dataPartition.initPath", - "", - "Initial data partition", - "If not empty, its content will be copied to the disk.dataPartition.path file at boot-time.") - -HWCFG_DISKSIZE( - disk_dataPartition_size, - "disk.dataPartition.size", - "0", - "Ideal size of data partition", - "") - -HWCFG_STRING( - disk_snapStorage_path, - "disk.snapStorage.path", - "", - "Path to snapshot storage", - "Path to a 'snapshot storage' file, where all snapshots are stored.") - -HWCFG_STRING( - avd_name, - "avd.name", - "<build>", - "Name of the AVD being run", - "") - -#undef HWCFG_INT -#undef HWCFG_BOOL -#undef HWCFG_DISKSIZE -#undef HWCFG_STRING -#undef HWCFG_DOUBLE -/* end of auto-generated file */ diff --git a/android/avd/info.c b/android/avd/info.c index 1ab066c..f1514f2 100644 --- a/android/avd/info.c +++ b/android/avd/info.c @@ -886,9 +886,20 @@ avdInfo_getKernelPath( AvdInfo* i ) * for our target architecture. */ char temp[PATH_MAX], *p = temp, *end = p + sizeof(temp); + const char* suffix = ""; + char* abi; + + /* If the target ABI is armeabi-v7a, then look for + * kernel-qemu-armv7 instead of kernel-qemu in the prebuilt + * directory. */ + abi = path_getBuildTargetAbi(i->androidOut); + if (!strcmp(abi,"armeabi-v7a")) { + suffix = "-armv7"; + } + AFREE(abi); - p = bufprint(temp, end, "%s/prebuilt/android-%s/kernel/kernel-qemu", - i->androidBuildRoot, i->targetArch); + p = bufprint(temp, end, "%s/prebuilt/android-%s/kernel/kernel-qemu%s", + i->androidBuildRoot, i->targetArch, suffix); if (p >= end || !path_exists(temp)) { derror("bad workspace: cannot find prebuilt kernel in: %s", temp); exit(1); @@ -1024,6 +1035,16 @@ avdInfo_inAndroidBuild( AvdInfo* i ) } char* +avdInfo_getTargetAbi( AvdInfo* i ) +{ + /* For now, we can't get the ABI from SDK AVDs */ + if (!i->inAndroidBuild) + return NULL; + + return path_getBuildTargetAbi(i->androidOut); +} + +char* avdInfo_getTracePath( AvdInfo* i, const char* traceName ) { char tmp[MAX_PATH], *p=tmp, *end=p + sizeof(tmp); diff --git a/android/avd/info.h b/android/avd/info.h index 2469f68..5192bb9 100644 --- a/android/avd/info.h +++ b/android/avd/info.h @@ -218,6 +218,13 @@ void avdInfo_getSkinInfo( AvdInfo* i, char** pSkinName, char** pSkinDir /* Returns TRUE iff in the Android build system */ int avdInfo_inAndroidBuild( AvdInfo* i ); +/* Returns the target ABI for the corresponding platform image. + * This may return NULL if it cannot be determined. Otherwise this is + * a string like "armeabi", "armeabi-v7a" or "x86" that must be freed + * by the caller. + */ +char* avdInfo_getTargetAbi( AvdInfo* i ); + /* Reads the AVD's hardware configuration into 'hw'. returns -1 on error, 0 otherwise */ int avdInfo_initHwConfig( AvdInfo* i, AndroidHwConfig* hw ); diff --git a/android/avd/util.c b/android/avd/util.c index 2ba4117..304b98f 100644 --- a/android/avd/util.c +++ b/android/avd/util.c @@ -264,6 +264,24 @@ path_getBuildTargetArch( const char* androidOut ) return result; } +char* +path_getBuildTargetAbi( const char* androidOut ) +{ + const char* defaultAbi = "armeabi"; + char* result = NULL; + char* cpuAbi = _getBuildProperty(androidOut, "ro.product.cpu.abi"); + + if (cpuAbi == NULL) { + D("Coult not find CPU ABI in build properties!"); + D("Default target ABI: %s", defaultAbi); + result = ASTRDUP(defaultAbi); + } else { + D("Found target ABI=%s", cpuAbi); + result = cpuAbi; + } + return result; +} + int path_getBuildTargetApiLevel( const char* androidOut ) diff --git a/android/avd/util.h b/android/avd/util.h index 27f8f28..877d5aa 100644 --- a/android/avd/util.h +++ b/android/avd/util.h @@ -48,6 +48,16 @@ char* path_getAvdTargetArch( const char* avdName ); */ char* path_getBuildTargetArch( const char* androidOut ); +/* Retrieves a string corresponding to the target CPU ABI + * when in the Android platform tree. The only way to do that + * properly for now is to look at $OUT/system/build.prop: + * + * ro.product.cpu-abi=<abi> + * + * Where <abi> can be 'armeabi', 'armeabi-v7a' or 'x86'. + */ +char* path_getBuildTargetAbi( const char* androidOut ); + /* Retrieve the target API level when in the Android platform tree. * This can be a very large number like 1000 if the value cannot * be extracted from the appropriate file diff --git a/android/build/binary.make b/android/build/binary.make index f6542dc..3bc4fe9 100644 --- a/android/build/binary.make +++ b/android/build/binary.make @@ -25,6 +25,8 @@ LOCAL_GENERATED_C_SOURCES := $(filter %.c,$(LOCAL_GENERATED_SOURCES)) LOCAL_CXX_SOURCES := $(filter %$(LOCAL_CPP_EXTENSION),$(LOCAL_SRC_FILES) $(LOCAL_GENERATED_SOURCES)) LOCAL_OBJC_SOURCES := $(filter %.m,$(LOCAL_SRC_FILES) $(LOCAL_GENERATED_SOURCES)) +LOCAL_CFLAGS := $(strip $(patsubst %,-I%,$(LOCAL_C_INCLUDES)) $(LOCAL_CFLAGS)) + $(foreach src,$(LOCAL_C_SOURCES), \ $(eval $(call compile-c-source,$(src))) \ ) @@ -41,4 +43,7 @@ $(foreach src,$(LOCAL_OBJC_SOURCES), \ $(eval $(call compile-objc-source,$(src))) \ ) +# Ensure that we build all generated sources before the objects +$(LOCAL_OBJECTS): | $(LOCAL_GENERATED_SOURCES) + CLEAN_OBJS_DIRS += $(LOCAL_OBJS_DIR) diff --git a/android/build/common.sh b/android/build/common.sh index 050fd90..de4e3c1 100644 --- a/android/build/common.sh +++ b/android/build/common.sh @@ -457,11 +457,11 @@ check_android_build () unset ANDROID_TOP IN_ANDROID_BUILD=no - if [ -z "$ANDROID_PRODUCT_OUT" ] ; then + if [ -z "$ANDROID_BUILD_TOP" ] ; then return ; fi - ANDROID_TOP=`cd $ANDROID_PRODUCT_OUT/../../../.. && pwd` + ANDROID_TOP=$ANDROID_BUILD_TOP log "ANDROID_TOP found at $ANDROID_TOP" # $ANDROID_TOP/config/envsetup.make is for the old tree layout # $ANDROID_TOP/build/envsetup.sh is for the new one diff --git a/android/build/definitions.make b/android/build/definitions.make index e31131b..ddf9150 100644 --- a/android/build/definitions.make +++ b/android/build/definitions.make @@ -13,6 +13,19 @@ # limitations under the License. # +# this turns off the suffix rules built into make +.SUFFIXES: + +# this turns off the RCS / SCCS implicit rules of GNU Make +% : RCS/%,v +% : RCS/% +% : %,v +% : s.% +% : SCCS/s.% + +# If a rule fails, delete $@. +.DELETE_ON_ERROR: + # shared definitions ifeq ($(strip $(SHOW)),) define pretty diff --git a/android/hw-pipe-net.c b/android/hw-pipe-net.c index d83d8b1..dade446 100644 --- a/android/hw-pipe-net.c +++ b/android/hw-pipe-net.c @@ -68,7 +68,7 @@ typedef struct { int wakeWanted; LoopIo io[1]; AsyncConnector connector[1]; - + int shouldSetSocketOpt; } NetPipe; static void @@ -192,6 +192,7 @@ netPipe_initFromAddress( void* hwpipe, const SockAddress* address, Looper* loop pipe->hwpipe = hwpipe; pipe->state = STATE_INIT; + pipe->shouldSetSocketOpt = 0; { AsyncStatus status; @@ -245,13 +246,26 @@ netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuf const GoldfishPipeBuffer* buff = buffers; const GoldfishPipeBuffer* buffEnd = buff + numBuffers; +#ifdef _WIN32 + if (pipe->shouldSetSocketOpt == 1) { + int sndbuf = 128 * 1024; + int len = sizeof(sndbuf); + if (setsockopt(pipe->io->fd, SOL_SOCKET, SO_SNDBUF, + (char*)&sndbuf, len) == SOCKET_ERROR) { + D("Failed to set SO_SNDBUF to %d error=0x%x\n", + sndbuf, WSAGetLastError()); + } + pipe->shouldSetSocketOpt = 0; + } +#endif + for (; buff < buffEnd; buff++) count += buff->size; buff = buffers; while (count > 0) { int avail = buff->size - buffStart; - int len = write(pipe->io->fd, buff->data + buffStart, avail); + int len = socket_send(pipe->io->fd, buff->data + buffStart, avail); /* the write succeeded */ if (len > 0) { @@ -272,10 +286,6 @@ netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuf break; } - /* loop on EINTR */ - if (errno == EINTR) - continue; - /* if we already wrote some stuff, simply return */ if (ret > 0) { break; @@ -309,7 +319,7 @@ netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers buff = buffers; while (count > 0) { int avail = buff->size - buffStart; - int len = read(pipe->io->fd, buff->data + buffStart, avail); + int len = socket_recv(pipe->io->fd, buff->data + buffStart, avail); /* the read succeeded */ if (len > 0) { @@ -330,10 +340,6 @@ netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers break; } - /* loop on EINTR */ - if (errno == EINTR) - continue; - /* if we already read some stuff, simply return */ if (ret > 0) { break; @@ -381,40 +387,17 @@ void* netPipe_initTcp( void* hwpipe, void* _looper, const char* args ) { /* Build SockAddress from arguments. Acceptable formats are: - * * <port> - * <host>:<port> */ SockAddress address; + uint16_t port; void* ret; if (args == NULL) { D("%s: Missing address!", __FUNCTION__); return NULL; } - D("%s: Address is '%s'", __FUNCTION__, args); - - char host[256]; /* max size of regular FDQN+1 */ - int hostlen = 0; - int port; - const char* p; - - /* Assume that anything after the last ':' is a port number - * And that what is before it is a port number. Should handle IPv6 - * notation. */ - p = strrchr(args, ':'); - if (p != NULL) { - hostlen = p - args; - if (hostlen >= sizeof(host)) { - D("%s: Address too long!", __FUNCTION__); - return NULL; - } - memcpy(host, args, hostlen); - host[hostlen] = '\0'; - args = p + 1; - } else { - snprintf(host, sizeof host, "127.0.0.1"); - } + D("%s: Port is '%s'", __FUNCTION__, args); /* Now, look at the port number */ { @@ -423,12 +406,9 @@ netPipe_initTcp( void* hwpipe, void* _looper, const char* args ) if (end == NULL || *end != '\0' || val <= 0 || val > 65535) { D("%s: Invalid port number: '%s'", __FUNCTION__, args); } - port = (int)val; - } - if (sock_address_init_resolve(&address, host, port, 0) < 0) { - D("%s: Could not resolve address", __FUNCTION__); - return NULL; + port = (uint16_t)val; } + sock_address_init_inet(&address, SOCK_ADDRESS_INET_LOOPBACK, port); ret = netPipe_initFromAddress(hwpipe, &address, _looper); @@ -436,6 +416,7 @@ netPipe_initTcp( void* hwpipe, void* _looper, const char* args ) return ret; } +#ifndef _WIN32 void* netPipe_initUnix( void* hwpipe, void* _looper, const char* args ) { @@ -459,7 +440,7 @@ netPipe_initUnix( void* hwpipe, void* _looper, const char* args ) sock_address_done(&address); return ret; } - +#endif /********************************************************************** ********************************************************************** @@ -477,6 +458,7 @@ static const GoldfishPipeFuncs netPipeTcp_funcs = { netPipe_wakeOn, }; +#ifndef _WIN32 static const GoldfishPipeFuncs netPipeUnix_funcs = { netPipe_initUnix, netPipe_closeFromGuest, @@ -485,7 +467,7 @@ static const GoldfishPipeFuncs netPipeUnix_funcs = { netPipe_poll, netPipe_wakeOn, }; - +#endif #define DEFAULT_OPENGLES_PORT 22468 @@ -493,10 +475,14 @@ static void* openglesPipe_init( void* hwpipe, void* _looper, const char* args ) { char temp[32]; + NetPipe *pipe; /* For now, simply connect through tcp */ snprintf(temp, sizeof temp, "%d", DEFAULT_OPENGLES_PORT); - return netPipe_initTcp(hwpipe, _looper, temp); + pipe = (NetPipe *)netPipe_initTcp(hwpipe, _looper, temp); + pipe->shouldSetSocketOpt = 1; + + return pipe; } static const GoldfishPipeFuncs openglesPipe_funcs = { @@ -515,6 +501,8 @@ android_net_pipes_init(void) Looper* looper = looper_newCore(); goldfish_pipe_add_type( "tcp", looper, &netPipeTcp_funcs ); +#ifndef _WIN32 goldfish_pipe_add_type( "unix", looper, &netPipeUnix_funcs ); +#endif goldfish_pipe_add_type( "opengles", looper, &openglesPipe_funcs ); } diff --git a/android/hw-qemud.c b/android/hw-qemud.c index e91ec78..9bfeaff 100644 --- a/android/hw-qemud.c +++ b/android/hw-qemud.c @@ -14,7 +14,9 @@ #include "android/utils/misc.h" #include "android/utils/system.h" #include "android/utils/bufprint.h" +#include "android/looper.h" #include "hw/hw.h" +#include "hw/goldfish_pipe.h" #include "qemu-char.h" #include "charpipe.h" #include "cbuffer.h" @@ -575,18 +577,54 @@ qemud_serial_send( QemudSerial* s, /** CLIENTS **/ +/* Descriptor for a data buffer pending to be sent to a qemud pipe client. + * + * When a service decides to send data to the client, there could be cases when + * client is not ready to read them. In this case there is no GoldfishPipeBuffer + * available to write service's data to, So, we need to cache that data into the + * client descriptor, and "send" them over to the client in _qemudPipe_recvBuffers + * callback. Pending service data is stored in the client descriptor as a list + * of QemudPipeMessage instances. + */ +typedef struct QemudPipeMessage QemudPipeMessage; +struct QemudPipeMessage { + /* Message to send. */ + uint8_t* message; + /* Message size. */ + size_t size; + /* Offset in the message buffer of the chunk, that has not been sent + * to the pipe yet. */ + size_t offset; + /* Links next message in the client. */ + QemudPipeMessage* next; +}; + + /* A QemudClient models a single client as seen by the emulator. - * Each client has its own channel id, and belongs to a given - * QemudService (see below). + * Each client has its own channel id (for the serial qemud), or pipe descriptor + * (for the pipe based qemud), and belongs to a given QemudService (see below). * - * There is a global list of clients used to multiplex incoming - * messages from the channel id (see qemud_multiplexer_serial_recv()). + * There is a global list of serial clients used to multiplex incoming + * messages from the channel id (see qemud_multiplexer_serial_recv()). Pipe + * clients don't need multiplexing, because they are communicated via qemud pipes + * that are unique for each client. * */ +/* Defines type of the client: pipe, or serial. + */ +typedef enum QemudProtocol { + /* Client is communicating via pipe. */ + QEMUD_PROTOCOL_PIPE, + /* Client is communicating via serial port. */ + QEMUD_PROTOCOL_SERIAL +} QemudProtocol; + struct QemudClient { - int channel; - QemudSerial* serial; + /* Defines protocol, used by the client. */ + QemudProtocol protocol; + + /* Fields that are common for all protocols. */ void* clie_opaque; QemudClientRecv clie_recv; QemudClientClose clie_close; @@ -604,8 +642,28 @@ struct QemudClient { QemudSink header[1]; uint8_t header0[FRAME_HEADER_SIZE]; QemudSink payload[1]; + + /* Fields that are protocol-specific. */ + union { + /* Serial-specific fields. */ + struct { + int channel; + QemudSerial* serial; + } Serial; + /* Pipe-specific fields. */ + struct { + void* hwpipe; + QemudPipeMessage* messages; + } Pipe; + } ProtocolSelector; }; +static ABool +_is_pipe_client(QemudClient* client) +{ + return (client-> protocol == QEMUD_PROTOCOL_PIPE) ? true : false; +} + static void qemud_service_remove_client( QemudService* service, QemudClient* client ); @@ -716,6 +774,11 @@ qemud_client_recv( void* opaque, uint8_t* msg, int msglen ) } } +/* Sends data to a pipe-based client. + */ +static void +_qemud_pipe_send(QemudClient* client, const uint8_t* msg, int msglen); + /* disconnect a client. this automatically frees the QemudClient. * note that this also removes the client from the global list * and from its service's list, if any. @@ -734,10 +797,15 @@ qemud_client_disconnect( void* opaque ) qemud_client_remove(c); /* send a disconnect command to the daemon */ - if (c->channel > 0) { + if (_is_pipe_client(c)) { char tmp[128], *p=tmp, *end=p+sizeof(tmp); - p = bufprint(tmp, end, "disconnect:%02x", c->channel); - qemud_serial_send(c->serial, 0, 0, (uint8_t*)tmp, p-tmp); + p = bufprint(tmp, end, "disconnect:00"); + _qemud_pipe_send(c, (uint8_t*)tmp, p-tmp); + } else if (c->ProtocolSelector.Serial.channel > 0) { + char tmp[128], *p=tmp, *end=p+sizeof(tmp); + p = bufprint(tmp, end, "disconnect:%02x", + c->ProtocolSelector.Serial.channel); + qemud_serial_send(c->ProtocolSelector.Serial.serial, 0, 0, (uint8_t*)tmp, p-tmp); } /* call the client close callback */ @@ -756,7 +824,10 @@ qemud_client_disconnect( void* opaque ) AFREE(c); } -/* allocate a new QemudClient object */ +/* allocate a new QemudClient object + * NOTE: channel_id valie is used as a selector between serial and pipe clients. + * Since channel_id < 0 is an invalid value for a serial client, it would + * indicate that creating client is a pipe client. */ static QemudClient* qemud_client_alloc( int channel_id, void* clie_opaque, @@ -771,14 +842,25 @@ qemud_client_alloc( int channel_id, ANEW0(c); - c->serial = serial; - c->channel = channel_id; + if (channel_id < 0) { + /* Allocating a pipe client. */ + c->protocol = QEMUD_PROTOCOL_PIPE; + c->ProtocolSelector.Pipe.messages = NULL; + c->ProtocolSelector.Pipe.hwpipe = NULL; + } else { + /* Allocating a serial client. */ + c->protocol = QEMUD_PROTOCOL_SERIAL; + c->ProtocolSelector.Serial.serial = serial; + c->ProtocolSelector.Serial.channel = channel_id; + } c->clie_opaque = clie_opaque; c->clie_recv = clie_recv; c->clie_close = clie_close; c->clie_save = clie_save; c->clie_load = clie_load; - + c->service = NULL; + c->next_serv = NULL; + c->next = NULL; c->framing = 0; c->need_header = 1; qemud_sink_reset(c->header, FRAME_HEADER_SIZE, c->header0); @@ -794,7 +876,7 @@ static char* qemud_service_load_name( QEMUFile* f ); static QemudService* qemud_service_find( QemudService* service_list, const char* service_name ); static QemudClient* qemud_service_connect_client( QemudService *sv, - int channel_id ); + int channel_id); /* Saves the client state needed to re-establish connections on load. */ @@ -803,7 +885,10 @@ qemud_client_save(QEMUFile* f, QemudClient* c) { /* save generic information */ qemud_service_save_name(f, c->service); - qemu_put_be32(f, c->channel); + qemu_put_be32(f, c->protocol); + if (_is_pipe_client(c)) { + qemu_put_be32(f, c->ProtocolSelector.Serial.channel); + } /* save client-specific state */ if (c->clie_save) @@ -840,8 +925,13 @@ qemud_client_load(QEMUFile* f, QemudService* current_services ) return -EIO; } + /* get protocol. */ + QemudProtocol protocol = qemu_get_be32(f); /* get channel id */ - int channel = qemu_get_be32(f); + int channel = -1; + if (protocol == QEMUD_PROTOCOL_SERIAL) { + qemu_get_be32(f); + } if (channel == 0) { D("%s: illegal snapshot: client for control channel must no be saved\n", __FUNCTION__); @@ -976,8 +1066,8 @@ qemud_service_remove_client( QemudService* s, QemudClient* c ) for (;;) { node = *pnode; if (node == NULL) { - D("%s: could not find client %d for service '%s'", - __FUNCTION__, c->channel, s->name); + D("%s: could not find client for service '%s'", + __FUNCTION__, s->name); return; } if (node == c) @@ -1004,7 +1094,6 @@ qemud_service_connect_client(QemudService *sv, int channel_id) __FUNCTION__, sv->name); return NULL; } - D("%s: registered client channel %d for '%s' service", __FUNCTION__, channel_id, sv->name); return client; @@ -1153,7 +1242,7 @@ qemud_multiplexer_serial_recv( void* opaque, * QemudClient that is setup in qemud_multiplexer_init() */ for ( ; c != NULL; c = c->next ) { - if (c->channel == channel) { + if (!_is_pipe_client(c) && c->ProtocolSelector.Serial.channel == channel) { qemud_client_recv(c, msg, msglen); return; } @@ -1203,13 +1292,13 @@ qemud_multiplexer_disconnect( QemudMultiplexer* m, /* find the client by its channel id, then disconnect it */ for (c = m->clients; c; c = c->next) { - if (c->channel == channel) { + if (!_is_pipe_client(c) && c->ProtocolSelector.Serial.channel == channel) { D("%s: disconnecting client %d", __FUNCTION__, channel); /* note thatt this removes the client from * m->clients automatically. */ - c->channel = -1; /* no need to send disconnect:<id> */ + c->ProtocolSelector.Serial.channel = -1; /* no need to send disconnect:<id> */ qemud_client_disconnect(c); return; } @@ -1233,12 +1322,13 @@ qemud_multiplexer_disconnect_noncontrol( QemudMultiplexer* m ) c = next; next = c->next; /* disconnect frees c, remember next in advance */ - if (c->channel > 0) { /* skip control channel */ + if (!_is_pipe_client(c) && c->ProtocolSelector.Serial.channel > 0) { + /* skip control channel */ D("%s: disconnecting client %d", - __FUNCTION__, c->channel); + __FUNCTION__, c->ProtocolSelector.Serial.channel); D("%s: disconnecting client %d\n", - __FUNCTION__, c->channel); - c->channel = -1; /* do not send disconnect:<id> */ + __FUNCTION__, c->ProtocolSelector.Serial.channel); + c->ProtocolSelector.Serial.channel = -1; /* do not send disconnect:<id> */ qemud_client_disconnect(c); } } @@ -1442,13 +1532,91 @@ qemud_client_new( QemudService* service, return c; } +/* Caches a service message into the client's descriptor. + * + * See comments on QemudPipeMessage structure for more info. + */ +static void +_qemud_pipe_cache_buffer(QemudClient* client, const uint8_t* msg, int msglen) +{ + QemudPipeMessage* buf; + QemudPipeMessage** ins_at = &client->ProtocolSelector.Pipe.messages; + + /* Allocate descriptor big enough to contain message as well. */ + buf = (QemudPipeMessage*)malloc(msglen + sizeof(QemudPipeMessage)); + if (buf != NULL) { + /* Message starts right after the descriptor. */ + buf->message = (uint8_t*)buf + sizeof(QemudPipeMessage); + buf->size = msglen; + memcpy(buf->message, msg, msglen); + buf->offset = 0; + buf->next = NULL; + while (*ins_at != NULL) { + ins_at = &(*ins_at)->next; + } + *ins_at = buf; + /* Notify the pipe that there is data to read. */ + goldfish_pipe_wake(client->ProtocolSelector.Pipe.hwpipe, PIPE_WAKE_READ); + } +} + +/* Sends service message to the client. + */ +static void +_qemud_pipe_send(QemudClient* client, const uint8_t* msg, int msglen) +{ + uint8_t frame[FRAME_HEADER_SIZE]; + int avail, len = msglen; + int framing = client->framing; + + if (msglen <= 0) + return; + + D("%s: len=%3d '%s'", + __FUNCTION__, msglen, quote_bytes((const void*)msg, msglen)); + + if (framing) { + len += FRAME_HEADER_SIZE; + } + + /* packetize the payload for the serial MTU */ + while (len > 0) + { + avail = len; + if (avail > MAX_SERIAL_PAYLOAD) + avail = MAX_SERIAL_PAYLOAD; + + /* insert frame header when needed */ + if (framing) { + int2hex(frame, FRAME_HEADER_SIZE, msglen); + T("%s: '%.*s'", __FUNCTION__, FRAME_HEADER_SIZE, frame); + _qemud_pipe_cache_buffer(client, frame, FRAME_HEADER_SIZE); + avail -= FRAME_HEADER_SIZE; + len -= FRAME_HEADER_SIZE; + framing = 0; + } + + /* write message content */ + T("%s: '%.*s'", __FUNCTION__, avail, msg); + _qemud_pipe_cache_buffer(client, msg, avail); + msg += avail; + len -= avail; + } +} + /* this can be used by a service implementation to send an answer * or message to a specific client. */ void qemud_client_send ( QemudClient* client, const uint8_t* msg, int msglen ) { - qemud_serial_send(client->serial, client->channel, client->framing != 0, msg, msglen); + if (_is_pipe_client(client)) { + _qemud_pipe_send(client, msg, msglen); + } else { + qemud_serial_send(client->ProtocolSelector.Serial.serial, + client->ProtocolSelector.Serial.channel, + client->framing != 0, msg, msglen); + } } /* enable framing for this client. When TRUE, this will @@ -1488,7 +1656,8 @@ qemud_client_save_count(QEMUFile* f, QemudClient* c) { unsigned int client_count = 0; for( ; c; c = c->next) // walk over linked list - if (c->channel > 0) // skip control channel, which is not saved + // skip control channel, which is not saved + if (_is_pipe_client(c) || c->ProtocolSelector.Serial.channel > 0) client_count++; qemu_put_be32(f, client_count); @@ -1530,7 +1699,8 @@ qemud_save(QEMUFile* f, void* opaque) qemud_client_save_count(f, m->clients); QemudClient *c; for (c = m->clients; c; c = c->next) { - if (c->channel > 0) { /* skip control channel client */ + /* skip control channel client */ + if (_is_pipe_client(c) || c->ProtocolSelector.Serial.channel > 0) { qemud_client_save(f, c); } } @@ -1604,6 +1774,211 @@ qemud_load(QEMUFile *f, void* opaque, int version) return 0; } +/*------------------------------------------------------------------------------ + * + * QEMUD PIPE service callbacks + * + * ----------------------------------------------------------------------------*/ + +/* Descriptor for a QEMUD pipe connection. + * + * Every time a client connects to the QEMUD via pipe, an instance of this + * structure is created to represent a connection used by new pipe client. + */ +typedef struct QemudPipe { + /* Pipe descriptor. */ + void* hwpipe; + /* Looper used for I/O */ + void* looper; + /* Service for this pipe. */ + QemudService* service; + /* Client for this pipe. */ + QemudClient* client; +} QemudPipe; + +/* This is a callback that gets invoked when guest is connecting to the service. + * + * Here we will create a new client as well as pipe descriptor representing new + * connection. + */ +static void* +_qemudPipe_init(void* hwpipe, void* _looper, const char* args) +{ + QemudMultiplexer *m = _multiplexer; + QemudService* sv = m->services; + QemudClient* client; + QemudPipe* pipe = NULL; + + /* 'args' passed in this callback represents name of the service the guest is + * connecting to. It can't be NULL. */ + if (args == NULL) { + D("%s: Missing address!", __FUNCTION__); + return NULL; + } + + /* Lookup registered service by its name. */ + while (sv != NULL && strcmp(sv->name, args)) { + sv = sv->next; + } + if (sv == NULL) { + D("%s: Service '%s' has not been registered!", __FUNCTION__, args); + return NULL; + } + + /* Create a client for this connection. -1 as a channel ID signals that this + * is a pipe client. */ + client = qemud_service_connect_client(sv, -1); + if (client != NULL) { + ANEW0(pipe); + pipe->hwpipe = hwpipe; + pipe->looper = _looper; + pipe->service = sv; + pipe->client = client; + client->ProtocolSelector.Pipe.hwpipe = hwpipe; + } + + return pipe; +} + +/* Called when the guest wants to close the channel. +*/ +static void +_qemudPipe_closeFromGuest( void* opaque ) +{ + QemudPipe* pipe = opaque; + QemudClient* client = pipe->client; + D("%s", __FUNCTION__); + qemud_client_disconnect(client); +} + +/* Called when the guest has sent some data to the client. + */ +static int +_qemudPipe_sendBuffers(void* opaque, + const GoldfishPipeBuffer* buffers, + int numBuffers) +{ + QemudPipe* pipe = opaque; + QemudClient* client = pipe->client; + size_t transferred = 0; + + if (numBuffers == 1) { + /* Simple case: all data are in one buffer. */ + D("%s: %s", __FUNCTION__, quote_bytes((char*)buffers->data, buffers->size)); + qemud_client_recv(client, buffers->data, buffers->size); + transferred = buffers->size; + } else { + /* If there are multiple buffers involved, collect all data in one buffer + * before calling the high level client. */ + uint8_t* msg, *wrk; + int n; + for (n = 0; n < numBuffers; n++) { + transferred += buffers[n].size; + } + msg = malloc(transferred); + wrk = msg; + for (n = 0; n < numBuffers; n++) { + memcpy(wrk, buffers[n].data, buffers[n].size); + wrk += buffers[n].size; + } + D("%s: %s", __FUNCTION__, quote_bytes((char*)msg, transferred)); + qemud_client_recv(client, msg, transferred); + free(msg); + } + + return transferred; +} + +#define min(a, b) (((a) < (b)) ? (a) : (b)) + +/* Called when the guest is reading data from the client. + */ +static int +_qemudPipe_recvBuffers(void* opaque, GoldfishPipeBuffer* buffers, int numBuffers) +{ + QemudPipe* pipe = opaque; + QemudClient* client = pipe->client; + QemudPipeMessage** msg_list = &client->ProtocolSelector.Pipe.messages; + GoldfishPipeBuffer* buff = buffers; + GoldfishPipeBuffer* endbuff = buffers + numBuffers; + size_t sent_bytes = 0; + size_t off_in_buff = 0; + + if (*msg_list == NULL) { + /* No data to send. Let it block until we wake it up with + * PIPE_WAKE_READ when service sends data to the client. */ + return PIPE_ERROR_AGAIN; + } + + /* Fill in goldfish buffers while they are still available, and there are + * messages in the client's message list. */ + while (buff != endbuff && *msg_list != NULL) { + QemudPipeMessage* msg = *msg_list; + /* Message data fiting the current pipe's buffer. */ + size_t to_copy = min(msg->size - msg->offset, buff->size); + memcpy(buff->data + off_in_buff, msg->message + msg->offset, to_copy); + /* Update offsets. */ + off_in_buff += to_copy; + msg->offset += to_copy; + sent_bytes += to_copy; + if (msg->size == msg->offset) { + /* We're done with the current message. Go to the next one. */ + *msg_list = msg->next; + free(msg); + } + if (off_in_buff == buff->size) { + /* Current pipe buffer is full. Continue with the next one. */ + buff++; + } + } + + D("%s: -> %u (of %u)", __FUNCTION__, sent_bytes, buffers->size); + + return sent_bytes; +} + +static unsigned +_qemudPipe_poll(void* opaque) +{ + QemudPipe* pipe = opaque; + QemudClient* client = pipe->client; + unsigned ret = PIPE_WAKE_WRITE; + if (client->ProtocolSelector.Pipe.messages != NULL) { + ret |= PIPE_WAKE_READ; + } + + return ret; +} + +static void +_qemudPipe_wakeOn(void* opaque, int flags) +{ + D("%s: -> %X", __FUNCTION__, flags); +} + +/* QEMUD pipe functions. + */ +static const GoldfishPipeFuncs _qemudPipe_funcs = { + _qemudPipe_init, + _qemudPipe_closeFromGuest, + _qemudPipe_sendBuffers, + _qemudPipe_recvBuffers, + _qemudPipe_poll, + _qemudPipe_wakeOn, +}; + +/* Initializes QEMUD pipe interface. + */ +static void +_android_qemud_pipe_init(void) +{ + static ABool _qemud_pipe_initialized = false; + + if (!_qemud_pipe_initialized) { + goldfish_pipe_add_type( "qemud", looper_newCore(), &_qemudPipe_funcs ); + _qemud_pipe_initialized = true; + } +} /* this is the end of the serial charpipe that must be passed * to the emulated tty implementation. The other end of the @@ -1611,8 +1986,10 @@ qemud_load(QEMUFile *f, void* opaque, int version) */ static CharDriverState* android_qemud_cs; -extern void -android_qemud_init( void ) +/* Initializes QEMUD serial interface. + */ +static void +_android_qemud_serial_init(void) { CharDriverState* cs; @@ -1631,6 +2008,18 @@ android_qemud_init( void ) qemud_save, qemud_load, _multiplexer); } +extern void +android_qemud_init( void ) +{ + D("%s", __FUNCTION__); + /* We don't know in advance whether the guest system supports qemud pipes, + * so we will initialize both qemud machineries, the legacy (over serial + * port), and the new one (over qemu pipe). Then we let the guest to connect + * via one, or the other. */ + _android_qemud_serial_init(); + _android_qemud_pipe_init(); +} + /* return the serial charpipe endpoint that must be used * by the emulated tty implementation. */ @@ -1663,20 +2052,19 @@ qemud_service_register( const char* service_name, QemudServiceSave serv_save, QemudServiceLoad serv_load ) { - QemudMultiplexer* m = _multiplexer; QemudService* sv; + QemudMultiplexer* m = _multiplexer; - if (android_qemud_cs == NULL) - android_qemud_init(); + android_qemud_init(); sv = qemud_service_new(service_name, - max_clients, - serv_opaque, - serv_connect, - serv_save, - serv_load, - &m->services); - + max_clients, + serv_opaque, + serv_connect, + serv_save, + serv_load, + &m->services); + D("Registered QEMUD service %s", service_name); return sv; } diff --git a/android/main-common.c b/android/main-common.c index 94accf7..a0e5f56 100644 --- a/android/main-common.c +++ b/android/main-common.c @@ -524,7 +524,7 @@ init_sdl_ui(AConfig* skinConfig, #endif /* we're not a game, so allow the screensaver to run */ - putenv("SDL_VIDEO_ALLOW_SCREENSAVER=1"); + setenv("SDL_VIDEO_ALLOW_SCREENSAVER","1",1); flags = SDL_INIT_NOPARACHUTE; if (!opts->no_window) diff --git a/android/main.c b/android/main.c index ec7ea61..814d2ec 100644 --- a/android/main.c +++ b/android/main.c @@ -169,6 +169,8 @@ int main(int argc, char **argv) int serial = 2; int shell_serial = 0; + int forceArmv7 = 0; + AndroidHwConfig* hw; AvdInfo* avd; AConfig* skinConfig; @@ -430,8 +432,7 @@ int main(int argc, char **argv) */ kernelFileLen = strlen(kernelFile); if (kernelFileLen > 6 && !memcmp(kernelFile + kernelFileLen - 6, "-armv7", 6)) { - args[n++] = "-cpu"; - args[n++] = "cortex-a8"; + forceArmv7 = 1; } } @@ -1067,6 +1068,27 @@ int main(int argc, char **argv) } args[n] = 0; + /* If the target ABI is armeabi-v7a, we can auto-detect the cpu model + * as a cortex-a8, instead of the default (arm926) which only emulates + * an ARMv5TE CPU. + */ + if (!forceArmv7 && hw->hw_cpu_model[0] == '\0') + { + char* abi = avdInfo_getTargetAbi(avd); + if (abi != NULL) { + if (!strcmp(abi, "armeabi-v7a")) { + forceArmv7 = 1; + } + AFREE(abi); + } + } + + if (forceArmv7 != 0) { + AFREE(hw->hw_cpu_model); + hw->hw_cpu_model = ASTRDUP("cortex-a8"); + D("Auto-config: -qemu -cpu %s", hw->hw_cpu_model); + } + /* Generate a hardware-qemu.ini for this AVD. The real hardware * configuration is ususally stored in several files, e.g. the AVD's * config.ini plus the skin-specific hardware.ini. diff --git a/android/skin/window.c b/android/skin/window.c index 0d8788e..9a72db5 100644 --- a/android/skin/window.c +++ b/android/skin/window.c @@ -1315,15 +1315,9 @@ skin_window_resize( SkinWindow* window ) { char temp[32]; -#ifdef HAVE_SETENV sprintf(temp, "%d,%d", window_x, window_y); setenv("SDL_VIDEO_WINDOW_POS", temp, 1); setenv("SDL_VIDEO_WINDOW_FORCE_VISIBLE", "1", 1); -#else - sprintf(temp,"SDL_VIDEO_WINDOW_POS=%d,%d",window_x,window_y); - putenv(temp); - putenv("SDL_VIDEO_WINDOW_FORCE_VISIBLE=1"); -#endif } flags = SDL_SWSURFACE; diff --git a/android/utils/setenv.c b/android/utils/setenv.c new file mode 100644 index 0000000..916c5f3 --- /dev/null +++ b/android/utils/setenv.c @@ -0,0 +1,18 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef _WIN32 +int setenv(const char *name, const char *value, int overwrite) +{ + int result = 0; + if (overwrite || !getenv(name)) { + size_t length = strlen(name) + strlen(value) + 2; + char *string = malloc(length); + snprintf(string, length, "%s=%s", name, value); + result = putenv(string); + } + return result; +} + +#endif diff --git a/docs/ANDROID-QEMUD.TXT b/docs/ANDROID-QEMUD.TXT index 7841399..8b4f8d6 100644 --- a/docs/ANDROID-QEMUD.TXT +++ b/docs/ANDROID-QEMUD.TXT @@ -6,7 +6,9 @@ I. Overview: The Android system image includes a small daemon program named "qemud" which is started at boot time. Its purpose is to provide a multiplexing communication channel between the emulated system and the emulator program -itself. +itself. Another way to support communication between the emulated system and +the emulator program is using qemu pipes (see ANDROID-QEMU-PIPE.TXT for details +on qemu pipes). Its goal is to allow certain parts of the system to talk directly to the emulator without requiring special kernel support; this simplifies a lot of @@ -169,6 +171,17 @@ Since the "cupcake" platform, this works as follows: Certain services do not need it at all (GSM, GPS) so it is optional and must be used depending on which service you talk to by clients. +- QEMU pipe communication model works similarly to the serial port multiplexing, + but also has some differences as far as connecting client with the service is + concerned: + + emulator <-+--> /dev/qemu_pipe/qemud:srv1 <---> client1 + | + +--> /dev/qemu_pipe/qemud:srv2 <---> client2 + + In the pipe model each client gets connected to the emulator through a unique + handle to /dev/qemu_pipe (a "pipe"), so there is no need for multiplexing the + channels. III. Legacy 'qemud': -------------------- @@ -254,7 +267,7 @@ only uses a single socket and allows concurrent clients for a all services. IV. State snapshots: -------------------- -Support for snapshots relies on the symmetric qemud_*_save and qemud_*_load +Support for snapshots relies on the symmetric qemud_*_save and qemud_*_load functions which save the state of the various Qemud* structs defined in android/hw-qemud.c. The high-level process is as follows. diff --git a/images/android_icon.ico b/images/android_icon.ico Binary files differindex bd25179..798d96b 100644 --- a/images/android_icon.ico +++ b/images/android_icon.ico diff --git a/images/android_icon_16.png b/images/android_icon_16.png Binary files differindex 0b0744b..5df26d4 100644 --- a/images/android_icon_16.png +++ b/images/android_icon_16.png diff --git a/images/android_icon_256.png b/images/android_icon_256.png Binary files differindex 2d1dc05..0fcac26 100644 --- a/images/android_icon_256.png +++ b/images/android_icon_256.png diff --git a/images/android_icon_32.png b/images/android_icon_32.png Binary files differindex 72aa861..4ba97a5 100644 --- a/images/android_icon_32.png +++ b/images/android_icon_32.png diff --git a/vl-android.c b/vl-android.c index 8436d68..32b5eac 100644 --- a/vl-android.c +++ b/vl-android.c @@ -3721,13 +3721,11 @@ int main(int argc, char **argv, char **envp) /* Initialize audio. */ if (android_op_audio) { - char temp[128]; if ( !audio_check_backend_name( 0, android_op_audio ) ) { PANIC("'%s' is not a valid audio output backend. see -help-audio-out", android_op_audio); } - snprintf(temp, sizeof temp, "QEMU_AUDIO_DRV=%s", android_op_audio); - putenv(temp); + setenv("QEMU_AUDIO_DRV", android_op_audio, 1); } /* Initialize OpenGLES emulation */ |