diff options
48 files changed, 2259 insertions, 2899 deletions
@@ -1,4 +1,4 @@ -images/android_icon.o +images/emulator_icon.o objs/* *.*~ diff --git a/Makefile.android b/Makefile.android index 6fccf71..d61264b 100644 --- a/Makefile.android +++ b/Makefile.android @@ -116,6 +116,9 @@ ifneq ($(BUILD_STANDALONE_EMULATOR),true) endif ccache := endif + + QEMU_OPENGLES_INCLUDE := sdk/emulator/opengl/host/include + QEMU_OPENGLES_LIBS := $(HOST_OUT) endif @@ -129,6 +132,10 @@ ifneq ($(combo_target)$(TARGET_SIMULATOR),HOST_true) MY_LDLIBS += -m32 endif endif + + ifneq ($(BUILD_HOST_static),) + MY_LDLIBS += -static + endif endif # Enable warning, except those related to missing field initializers @@ -202,8 +209,8 @@ endif ifeq ($(HOST_OS),darwin) QEMU_SYSTEM_LDLIBS += -Wl,-framework,Cocoa,-framework,QTKit,-framework,CoreVideo - ifneq ($(filter 10.7 10.7.%,$(DARWIN_VERSION)),) - # Lion/XCode4 needs to be explicitly told the dynamic library + ifneq ($(filter 10.7 10.7.% 10.8 10.8.%,$(DARWIN_VERSION)),) + # 10.7+ with XCode4 needs to be explicitly told the dynamic library # lookup symbols in the precompiled libSDL are resolved at # runtime QEMU_SYSTEM_LDLIBS += -undefined dynamic_lookup @@ -212,6 +219,31 @@ endif include $(LOCAL_PATH)/Makefile.common +ifeq ($(HOST_OS),windows) + # on Windows, link the icon file as well into the executable + # unfortunately, our build system doesn't help us much, so we need + # to use some weird pathnames to make this work... + + # Locate windres executable + WINDRES := windres + ifneq ($(USE_MINGW),) + # When building the Windows emulator under Linux, use the MinGW one + WINDRES := i586-mingw32msvc-windres + endif + + # Usage: $(eval $(call insert-windows-icon)) + define insert-windows-icon + LOCAL_PREBUILT_OBJ_FILES += images/emulator_icon.o + endef + +# This seems to be the only way to add an object file that was not generated from +# a C/C++/Java source file to our build system. and very unfortunately, +# $(TOPDIR)/$(LOCALPATH) will always be prepended to this value, which forces +# us to put the object file in the source directory. +$(LOCAL_PATH)/images/emulator_icon.o: $(LOCAL_PATH)/images/android_icon.rc + $(WINDRES) $< -I $(LOCAL_PATH)/images -o $@ +endif + # We want to build all variants of the emulator binaries. This makes # it easier to catch target-specific regressions during emulator development. EMULATOR_TARGET_ARCH := arm @@ -231,6 +263,10 @@ $(call start-emulator-program, emulator) LOCAL_SRC_FILES := android/main-emulator.c LOCAL_STATIC_LIBRARIES := emulator-common +ifeq ($(HOST_OS),windows) +$(eval $(call insert-windows-icon)) +endif + $(call end-emulator-program) ############################################################################## @@ -246,7 +282,6 @@ LOCAL_STATIC_LIBRARIES := \ emulator-libui \ emulator-common \ - LOCAL_CFLAGS += -DCONFIG_STANDALONE_UI=1 LOCAL_CFLAGS += $(EMULATOR_COMMON_CFLAGS) $(EMULATOR_LIBUI_CFLAGS) diff --git a/Makefile.common b/Makefile.common index f9d98af..4382bd2 100644 --- a/Makefile.common +++ b/Makefile.common @@ -164,7 +164,9 @@ endif # HOST_OS == linux common_LOCAL_CFLAGS = common_LOCAL_SRC_FILES = -EMULATOR_LIBUI_CFLAGS := +ifneq ($(QEMU_OPENGLES_INCLUDE),) + EMULATOR_LIBUI_CFLAGS := -I$(QEMU_OPENGLES_INCLUDE) +endif common_LOCAL_CFLAGS += $(EMULATOR_COMMON_CFLAGS) @@ -476,7 +478,6 @@ CORE_MISC_SOURCES = \ android/hw-pipe-net.c \ android/qemu-setup.c \ android/snapshot.c \ - android/android-device.c \ android/async-socket-connector.c \ android/async-socket.c \ android/sdk-controller-socket.c \ diff --git a/Makefile.target b/Makefile.target index 1961acf..5b4ec5f 100644 --- a/Makefile.target +++ b/Makefile.target @@ -274,8 +274,11 @@ LOCAL_CFLAGS += \ $(EMULATOR_TARGET_CFLAGS) \ -DCONFIG_STANDALONE_CORE \ -LOCAL_CFLAGS += -Wno-missing-field-initializers +ifneq ($(QEMU_OPENGLES_INCLUDE),) + LOCAL_CFLAGS += -I$(QEMU_OPENGLES_INCLUDE) +endif +LOCAL_CFLAGS += -Wno-missing-field-initializers LOCAL_STATIC_LIBRARIES := \ emulator-libqemu \ @@ -323,7 +326,7 @@ endif # Generate a completely static executable if needed. # Note that this means no sound and graphics on Linux. # -ifeq ($(CONFIG_STATIC_EXECUTABLE),true) +ifneq ($(strip $(CONFIG_STATIC_EXECUTABLE)$(BUILD_HOST_static)),) LOCAL_SRC_FILES += dynlink-static.c LOCAL_LDLIBS += -static endif @@ -409,7 +412,7 @@ common_LOCAL_SRC_FILES += $(SDLMAIN_SOURCES) # Generate a completely static executable if needed. # Note that this means no sound and graphics on Linux. # -ifeq ($(CONFIG_STATIC_EXECUTABLE),true) +ifneq ($(strip $(CONFIG_STATIC_EXECUTABLE)$(BUILD_HOST_static)),) common_LOCAL_SRC_FILES += dynlink-static.c common_LOCAL_LDLIBS += -static endif @@ -429,6 +432,11 @@ LOCAL_SRC_FILES += $(common_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),windows) +$(eval $(call insert-windows-icon)) +endif + $(call end-emulator-program) @@ -452,3 +460,4 @@ ifeq ($(HOST_OS),linux) $(call end-emulator-program) endif # BUILD_STANDALONE_EMULATOR == nil endif # HOST_OS == linux + diff --git a/android-configure.sh b/android-configure.sh index 5620e0e..9510ece 100755 --- a/android-configure.sh +++ b/android-configure.sh @@ -196,15 +196,9 @@ if [ "$IN_ANDROID_BUILD" = "yes" ] ; then # use ccache if USE_CCACHE is defined and the corresponding # binary is available. # - # note: located in PREBUILT/ccache/ccache in the new tree layout - # located in PREBUILT/ccache in the old one - # if [ -n "$USE_CCACHE" ] ; then CCACHE="$ANDROID_PREBUILT/ccache/ccache$EXE" if [ ! -f $CCACHE ] ; then - CCACHE="$ANDROID_PREBUILT/ccache$EXE" - fi - if [ ! -f $CCACHE ] ; then CCACHE="$ANDROID_PREBUILTS/ccache/ccache$EXE" fi if [ -f $CCACHE ] ; then @@ -240,7 +234,7 @@ if [ "$IN_ANDROID_BUILD" = "yes" ] ; then GLES_SUPPORT=yes if [ -z "$GLES_INCLUDE" ]; then log "GLES : Probing for headers" - GLES_INCLUDE=$ANDROID_TOP/development/tools/emulator/opengl/host/include + GLES_INCLUDE=$ANDROID_TOP/sdk/emulator/opengl/host/include if [ -d "$GLES_INCLUDE" ]; then log "GLES : Headers in $GLES_INCLUDE" else @@ -261,6 +255,32 @@ if [ "$IN_ANDROID_BUILD" = "yes" ] ; then fi fi fi +else + if [ "$GLES_PROBE" = "yes" ]; then + GLES_SUPPORT=yes + if [ -z "$GLES_INCLUDE" ]; then + log "GLES : Probing for headers" + GLES_INCLUDE=../../sdk/emulator/opengl/host/include + if [ -d "$GLES_INCLUDE" ]; then + log "GLES : Headers in $GLES_INCLUDE" + else + echo "Warning: Could not find OpenGLES emulation include dir: $GLES_INCLUDE" + echo "Disabling GLES emulation from this build!" + GLES_SUPPORT=no + fi + fi + if [ -z "$GLES_LIBS" ]; then + log "GLES : Probing for host libraries" + GLES_LIBS=../../out/host/$OS/lib + if [ -d "$GLES_LIBS" ]; then + echo "GLES : Libs in $GLES_LIBS" + else + echo "Warning: Could nof find OpenGLES emulation libraries in: $GLES_LIBS" + echo "Disabling GLES emulation from this build!" + GLES_SUPPORT=no + fi + fi + fi fi # IN_ANDROID_BUILD = no if [ "$GLES_SUPPORT" = "yes" ]; then diff --git a/android/adb-qemud.c b/android/adb-qemud.c index 1d2498e..9d82251 100644 --- a/android/adb-qemud.c +++ b/android/adb-qemud.c @@ -33,7 +33,8 @@ #define SERVICE_NAME "adb" #define DEBUG_SERVICE_NAME "adb-debug" - +/* Maximum length of the message that can be received from the guest. */ +#define ADB_MAX_MSG_LEN 8 /* Enumerates ADB client state values. */ typedef enum AdbClientState { /* Waiting on a connection from ADB host. */ @@ -58,6 +59,10 @@ struct AdbClient { QemudClient* qemud_client; /* Connection state. */ AdbClientState state; + /* Buffer, collecting accept / stop messages from client. */ + char msg_buffer[ADB_MAX_MSG_LEN]; + /* Current position in message buffer. */ + int msg_cur; }; /* ADB debugging client descriptor. */ @@ -183,17 +188,36 @@ _adb_client_recv(void* opaque, uint8_t* msg, int msglen, QemudClient* client) D("ADB client %p(o=%p) received from guest %d bytes in %s", adb_client, adb_client->opaque, msglen, QB(msg, msglen)); + if (adb_client->state == ADBC_STATE_CONNECTED) { + /* Connection is fully established. Dispatch the message to the host. */ + adb_server_on_guest_message(adb_client->opaque, msg, msglen); + return; + } + + /* + * At this point we expect either "accept", or "start" messages. Depending + * on the state of the pipe (although small) these messages could be broken + * into pieces. So, simply checking msg for "accept", or "start" may not + * work. Lets collect them first in internal buffer, and then will see. + */ + + /* Make sure tha message doesn't overflow the buffer. */ + if ((msglen + adb_client->msg_cur) > sizeof(adb_client->msg_buffer)) { + D("Unexpected message in ADB client."); + adb_client->msg_cur = 0; + return; + } + /* Append to current message. */ + memcpy(adb_client->msg_buffer + adb_client->msg_cur, msg, msglen); + adb_client->msg_cur += msglen; + /* Properly dispatch the message, depending on the client state. */ switch (adb_client->state) { - case ADBC_STATE_CONNECTED: - /* Connection is fully established. Dispatch the message to the - * host. */ - adb_server_on_guest_message(adb_client->opaque, msg, msglen); - break; - case ADBC_STATE_WAIT_ON_HOST: /* At this state the only message that is allowed is 'accept' */ - if (msglen == 6 && !memcmp(msg, "accept", 6)) { + if (adb_client->msg_cur == 6 && + !memcmp(adb_client->msg_buffer, "accept", 6)) { + adb_client->msg_cur = 0; /* Register ADB guest connection with the ADB server. */ adb_client->opaque = adb_server_register_guest(adb_client, &_adb_client_routines); @@ -210,7 +234,9 @@ _adb_client_recv(void* opaque, uint8_t* msg, int msglen, QemudClient* client) case ADBC_STATE_HOST_CONNECTED: /* At this state the only message that is allowed is 'start' */ - if (msglen == 5 && !memcmp(msg, "start", 5)) { + if (adb_client->msg_cur && + !memcmp(adb_client->msg_buffer, "start", 5)) { + adb_client->msg_cur = 0; adb_client->state = ADBC_STATE_CONNECTED; adb_server_complete_connection(adb_client->opaque); } else { diff --git a/android/android-device.c b/android/android-device.c deleted file mode 100644 index 5f88108..0000000 --- a/android/android-device.c +++ /dev/null @@ -1,1526 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Encapsulates exchange protocol between the emulator, and an Android device - * that is connected to the host via USB. The communication is established over - * a TCP port forwarding, enabled by ADB. - */ - -#include "android/android-device.h" -#include "utils/panic.h" -#include "iolooper.h" - -#define E(...) derror(__VA_ARGS__) -#define W(...) dwarning(__VA_ARGS__) -#define D(...) VERBOSE_PRINT(adevice,__VA_ARGS__) -#define D_ACTIVE VERBOSE_CHECK(adevice) - -/******************************************************************************** - * Common android device socket - *******************************************************************************/ - -/* Milliseconds between retrying asynchronous connections to the device. */ -#define ADS_RETRY_CONNECTION_TIMEOUT 3000 - -/* Socket type. */ -typedef enum ADSType { - /* Query socket. */ - ADS_TYPE_QUERY = 0, - /* Events socket. */ - ADS_TYPE_EVENT = 1 -} ADSType; - -/* Status of the socket. */ -typedef enum ADSStatus { - /* Socket is disconnected. */ - ADS_DISCONNECTED, - /* Connection process has been started. */ - ADS_CONNECTING, - /* Connection has been established. */ - ADS_CONNECTED, - /* Socket has been registered with the server. */ - ADS_REGISTERED, -} ADSStatus; - -/* Identifies socket as a "query" socket with the server. */ -static const char* _ads_query_socket_id = "query"; -/* Identifies socket as an "event" socket with the server. */ -static const char* _ads_event_socket_id = "event"; - -/* Android device socket descriptor. */ -typedef struct AndroidDevSocket AndroidDevSocket; - -/* - * Callback routines. - */ - -/* Callback routine that is called when a socket is connected. - * Param: - * opaque - Opaque pointer associated with the socket. Typicaly it's the same - * pointer that is associated with AndroidDevice instance. - * ads - Connection socket. - * failure - If zero, indicates that socket has been successuly connected. If a - * connection error has occured, this parameter contains the error code (as - * in 'errno). - */ -typedef void (*ads_socket_connected_cb)(void* opaque, - struct AndroidDevSocket* ads, - int failure); - -/* Android device socket descriptor. */ -struct AndroidDevSocket { - /* Socket type. */ - ADSType type; - /* Socket status. */ - ADSStatus socket_status; - /* TCP address for the socket. */ - SockAddress address; - /* Android device descriptor that owns the socket. */ - AndroidDevice* ad; - /* Opaque pointer associated with the socket. Typicaly it's the same - * pointer that is associated with AndroidDevice instance.*/ - void* opaque; - /* Deadline for current I/O performed on the socket. */ - Duration deadline; - /* Socket's file descriptor. */ - int fd; -}; - -/* Query socket descriptor. */ -typedef struct AndroidQuerySocket { - /* Common device socket. */ - AndroidDevSocket dev_socket; -} AndroidQuerySocket; - -/* Describes data to send via an asynchronous socket. */ -typedef struct AsyncSendBuffer { - /* Next buffer to send. */ - struct AsyncSendBuffer* next; - /* Callback to invoke when data transfer is completed. */ - async_send_cb complete_cb; - /* An opaque pointer to pass to the transfer completion callback. */ - void* complete_opaque; - /* Data to send. */ - uint8_t* data; - /* Size of the entire data buffer. */ - int data_size; - /* Remaining bytes to send. */ - int data_remaining; - /* Boolean flag indicating whether to free data buffer upon completion. */ - int free_on_completion; -} AsyncSendBuffer; - -/* Event socket descriptor. */ -typedef struct AndroidEventSocket { - /* Common socket descriptor. */ - AndroidDevSocket dev_socket; - /* Asynchronous connector to the device. */ - AsyncConnector connector[1]; - /* I/O port for asynchronous I/O on this socket. */ - LoopIo io[1]; - /* Asynchronous string reader. */ - AsyncLineReader alr; - /* Callback to call at the end of the asynchronous connection to this socket. - * Can be NULL. */ - ads_socket_connected_cb on_connected; - /* Callback to call when an event is received on this socket. Can be NULL. */ - event_cb on_event; - /* Lists buffers that are pending to be sent. */ - AsyncSendBuffer* send_pending; -} AndroidEventSocket; - -/* Android device descriptor. */ -struct AndroidDevice { - /* Query socket for the device. */ - AndroidQuerySocket query_socket; - /* Event socket for the device. */ - AndroidEventSocket event_socket; - /* An opaque pointer associated with this descriptor. */ - void* opaque; - /* I/O looper for synchronous I/O on the sockets for this device. */ - IoLooper* io_looper; - /* Timer that is used to retry asynchronous connections. */ - LoopTimer timer[1]; - /* I/O looper for asynchronous I/O. */ - Looper* looper; - /* Callback to call when device is fully connected. */ - device_connected_cb on_connected; - /* I/O failure callback .*/ - io_failure_cb on_io_failure; -}; - -/* Creates descriptor for a buffer to send asynchronously. - * Param: - * data, size - Buffer to send. - * free_on_close - Boolean flag indicating whether to free data buffer upon - * completion. - * cb - Callback to invoke when data transfer is completed. - * opaque - An opaque pointer to pass to the transfer completion callback. - */ -static AsyncSendBuffer* -_async_send_buffer_create(void* data, - int size, - int free_on_close, - async_send_cb cb, - void* opaque) -{ - AsyncSendBuffer* desc = malloc(sizeof(AsyncSendBuffer)); - if (desc == NULL) { - APANIC("Unable to allocate %d bytes for AsyncSendBuffer", - sizeof(AsyncSendBuffer)); - } - desc->next = NULL; - desc->data = (uint8_t*)data; - desc->data_size = desc->data_remaining = size; - desc->free_on_completion = free_on_close; - desc->complete_cb = cb; - desc->complete_opaque = opaque; - - return desc; -} - -/* Completes data transfer for the given descriptor. - * Note that this routine will free the descriptor. - * Param: - * desc - Asynchronous data transfer descriptor. Will be freed upon the exit - * from this routine. - * res - Data transfer result. - */ -static void -_async_send_buffer_complete(AsyncSendBuffer* desc, ATResult res) -{ - /* Invoke completion callback (if present) */ - if (desc->complete_cb) { - desc->complete_cb(desc->complete_opaque, res, desc->data, desc->data_size, - desc->data_size - desc->data_remaining); - } - - /* Free data buffer (if required) */ - if (desc->free_on_completion) { - free(desc->data); - } - - /* Free the descriptor itself. */ - free(desc); -} - -/******************************************************************************** - * Common socket declarations - *******************************************************************************/ - -/* Initializes common device socket. - * Param: - * ads - Socket descriptor to initialize. - * opaque - An opaque pointer to associate with the socket. Typicaly it's the - * same pointer that is associated with AndroidDevice instance. - * ad - Android device descriptor that owns the socket. - * port - Socket's TCP port. - * type - Socket type (query, or event). - */ -static int _android_dev_socket_init(AndroidDevSocket* ads, - void* opaque, - AndroidDevice* ad, - int port, - ADSType type); - -/* Destroys socket descriptor. */ -static void _android_dev_socket_destroy(AndroidDevSocket* ads); - -/* Callback that is ivoked from _android_dev_socket_connect when a file - * descriptor has been created for a socket. - * Param: - * ads - Socket descritor. - * opaque - An opaque pointer associated with the callback. - */ -typedef void (*on_socked_fd_created)(AndroidDevSocket* ads, void* opaque); - -/* Synchronously connects to the socket, and registers it with the server. - * Param: - * ads - Socket to connect. Must have 'deadline' field properly setup. - * cb, opaque - A callback to invoke (and opaque parameters to pass to the - * callback) when a file descriptor has been created for a socket. These - * parameters are optional and can be NULL. - * Return: - * 0 on success, -1 on failure with errno containing the reason for failure. - */ -static int _android_dev_socket_connect(AndroidDevSocket* ads, - on_socked_fd_created cb, - void* opaque); - -/* Synchronously registers a connected socket with the server. - * Param: - * ads - Socket to register. Must be connected, and must have 'deadline' field - * properly setup. - * Return: - * 0 on success, -1 on failure with errno containing the reason for failure. - */ -static int _android_dev_socket_register(AndroidDevSocket* ads); - -/* Disconnects the socket (if it was connected) */ -static void _android_dev_socket_disconnect(AndroidDevSocket* ads); - -/* Synchronously sends data to the socket. - * Param: - * ads - Socket to send the data to. Must be connected, and must have 'deadline' - * field properly setup. - * buff, buffsize - Buffer to send. - * Return: - * Number of bytes sent on success, or -1 on failure with errno containing the - * reason for failure. - */ -static int _android_dev_socket_send(AndroidDevSocket* ads, - const char* buff, - int buffsize); - -/* Synchronously receives data from the socket. - * Param: - * ads - Socket to receive the data from. Must be connected, and must have - * 'deadline' field properly setup. - * buff, buffsize - Buffer where to receive data. - * Return: - * Number of bytes received on success, or -1 on failure with errno containing - * the reason for failure. - */ -static int _android_dev_socket_recv(AndroidDevSocket* ads, - char* buf, - int bufsize); - -/* Synchronously reads zero-terminated string from the socket. - * Param: - * ads - Socket to read the string from. Must be connected, and must have - * 'deadline' field properly setup. - * str, strsize - Buffer where to read the string. - * Return: - * Number of charactes read into the string buffer (including zero-terminator) - * on success, or -1 on failure with 'errno' containing the reason for failure. - * If this routine returns -1, and errno contains ENOMEM, this is an indicator - * that supplied string buffer was too small for the receiving string. - */ -static int _android_dev_socket_read_string(AndroidDevSocket* ads, - char* str, - int strsize); - -/* Synchronously reads zero-terminated query response from the socket. - * All queries respond with an 'ok', or 'ko' prefix, indicating a success, or - * failure. Prefix can be followed by more query response data, separated from - * the prefix with a ':' character. This routine helps separating prefix from the - * data, by placing only the query response data into provided buffer. 'ko' or - * 'ok' will be encoded in the return value. - * Param: - * ads - Socket to read the response from. Must be connected, and must have - * 'deadline' field properly setup. - * data, datasize - Buffer where to read the query response data. - * Return: - * Number of charactes read into the data buffer (including zero-terminator) on - * success, or -1 on failure with errno containing the reason for failure. - * If the query has been completed with 'ko', this routine will return -1, with - * errno set to 0. If this routine returned -1, and errno is set to EINVAL, this - * indicates that reply string didn't match expected query reply format. - */ -static int _android_dev_socket_read_response(AndroidDevSocket* ads, - char* str, - int strsize); - -/* Gets ID string for the channel. */ -AINLINED const char* -_ads_id_str(AndroidDevSocket* ads) -{ - return (ads->type == ADS_TYPE_QUERY) ? _ads_query_socket_id : - _ads_event_socket_id; -} - -/* Gets socket's TCP port. */ -AINLINED int -_ads_port(AndroidDevSocket* ads) -{ - return sock_address_get_port(&ads->address); -} - -/* Gets synchronous I/O looper for the socket. */ -AINLINED IoLooper* -_ads_io_looper(AndroidDevSocket* ads) -{ - return ads->ad->io_looper; -} - -/* Sets deadline on a socket operation, given relative timeout. - * Param: - * ads - Socket descriptor to set deadline for. - * to - Relative timeout (in millisec) for the operation. - * AD_INFINITE_WAIT passed in this parameter means "no deadline". - */ -AINLINED void -_ads_set_deadline(AndroidDevSocket* ads, int to) -{ - ads->deadline = (to == AD_INFINITE_WAIT) ? DURATION_INFINITE : - iolooper_now() + to; -} - -/******************************************************************************** - * Common socket implementation - *******************************************************************************/ - -static int -_android_dev_socket_init(AndroidDevSocket* ads, - void* opaque, - AndroidDevice* ad, - int port, - ADSType type) -{ - ads->type = type; - ads->socket_status = ADS_DISCONNECTED; - ads->opaque = opaque; - ads->ad = ad; - ads->fd = -1; - sock_address_init_inet(&ads->address, SOCK_ADDRESS_INET_LOOPBACK, port); - - return 0; -} - -static void -_android_dev_socket_destroy(AndroidDevSocket* ads) -{ - /* Make sure it's disconnected. */ - _android_dev_socket_disconnect(ads); - - /* Finalize socket address. */ - sock_address_done(&ads->address); - memset(&ads->address, 0, sizeof(ads->address)); -} - -static int -_android_dev_socket_connect(AndroidDevSocket* ads, - on_socked_fd_created cb, - void* opaque) -{ - int res; - - /* Create communication socket. */ - ads->fd = socket_create_inet(SOCKET_STREAM); - if (ads->fd < 0) { - D("Unable to create socket for channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - return -1; - } - socket_set_nonblock(ads->fd); - - /* Invoke FD creation callback (if required) */ - if (cb != NULL) { - cb(ads, opaque); - } - - /* Synchronously connect to it. */ - ads->socket_status = ADS_CONNECTING; - iolooper_add_write(_ads_io_looper(ads), ads->fd); - res = socket_connect(ads->fd, &ads->address); - while (res < 0 && errno == EINTR) { - res = socket_connect(ads->fd, &ads->address); - } - - if (res && (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN)) { - /* Connection is delayed. Wait for it until timeout expires. */ - res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline); - if (res > 0) { - /* Pick up on possible connection error. */ - errno = socket_get_error(ads->fd); - res = (errno == 0) ? 0 : -1; - } else { - res = -1; - } - } - iolooper_del_write(_ads_io_looper(ads), ads->fd); - - if (res == 0) { - D("Channel '%s'@%d is connected", _ads_id_str(ads), _ads_port(ads)); - /* Socket is connected. Now register it with the server. */ - ads->socket_status = ADS_CONNECTED; - res = _android_dev_socket_register(ads); - } else { - D("Unable to connect channel '%s' to port %d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - } - - if (res) { - _android_dev_socket_disconnect(ads); - } - - return res; -} - -static int -_android_dev_socket_register(AndroidDevSocket* ads) -{ - /* Make sure that socket is connected. */ - if (ads->socket_status < ADS_CONNECTED) { - D("Attempt to register a disconnected channel '%s'@%d", - _ads_id_str(ads), _ads_port(ads)); - errno = ECONNRESET; - return -1; - } - - /* Register this socket accordingly to its type. */ - const char* reg_str = _ads_id_str(ads); - int res = _android_dev_socket_send(ads, reg_str, strlen(reg_str) + 1); - if (res > 0) { - /* Receive reply. Note that according to the protocol, the server should - * reply to channel registration with 'ok', or 'ko' (just like with queries), - * so we can use query reply reader here. */ - char reply[256]; - res = _android_dev_socket_read_response(ads, reply, sizeof(reply)); - if (res >= 0) { - /* Socket is now registered. */ - ads->socket_status = ADS_REGISTERED; - D("Channel '%s'@%d is registered", _ads_id_str(ads), _ads_port(ads)); - res = 0; - } else { - if (errno == 0) { - /* 'ko' condition */ - D("Device failed registration of channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), reply); - errno = EINVAL; - } else { - D("I/O failure while registering channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - } - res = -1; - } - } else { - D("Unable to send registration query for channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - res = -1; - } - - return res; -} - -static void -_android_dev_socket_disconnect(AndroidDevSocket* ads) -{ - /* Preserve errno */ - const int save_error = errno; - if (ads->socket_status != ADS_DISCONNECTED) { - /* Reset I/O looper for this socket. */ - iolooper_modify(_ads_io_looper(ads), ads->fd, - IOLOOPER_READ | IOLOOPER_WRITE, 0); - - /* Mark as disconnected. */ - ads->socket_status = ADS_DISCONNECTED; - - /* Close socket. */ - if (ads->fd >= 0) { - socket_close(ads->fd); - ads->fd = -1; - } - } - errno = save_error; -} - -static int -_android_dev_socket_send(AndroidDevSocket* ads, const char* buff, int to_send) -{ - int sent = 0; - - /* Make sure that socket is connected. */ - if (ads->socket_status < ADS_CONNECTED) { - D("Attempt to send via disconnected channel '%s'@%d", - _ads_id_str(ads), _ads_port(ads)); - errno = ECONNRESET; - return -1; - } - - iolooper_add_write(_ads_io_looper(ads), ads->fd); - do { - int res = socket_send(ads->fd, buff + sent, to_send - sent); - if (res == 0) { - /* Disconnection. */ - errno = ECONNRESET; - sent = -1; - break; - } - - if (res < 0) { - if (errno == EINTR) { - /* loop on EINTR */ - continue; - } - - if (errno == EWOULDBLOCK || errno == EAGAIN) { - res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline); - if (res > 0) { - /* Ready to write. */ - continue; - } - } - sent = -1; - break; - } - sent += res; - } while (sent < to_send); - iolooper_del_write(_ads_io_looper(ads), ads->fd); - - /* In case of an I/O failure we have to invoke failure callback. Note that we - * report I/O failures only on registered sockets. */ - if (sent < 0) { - D("I/O error while sending data via channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - - if (ads->ad->on_io_failure != NULL && ads->socket_status > ADS_CONNECTED) { - const char save_error = errno; - ads->ad->on_io_failure(ads->opaque, ads->ad, save_error); - errno = save_error; - } - } - - return sent; -} - -static int -_android_dev_socket_recv(AndroidDevSocket* ads, char* buf, int bufsize) -{ - int recvd = 0; - - /* Make sure that socket is connected. */ - if (ads->socket_status < ADS_CONNECTED) { - D("Attempt to receive from disconnected channel '%s'@%d", - _ads_id_str(ads), _ads_port(ads)); - errno = ECONNRESET; - return -1; - } - - iolooper_add_read(_ads_io_looper(ads), ads->fd); - do { - int res = socket_recv(ads->fd, buf + recvd, bufsize - recvd); - if (res == 0) { - /* Disconnection. */ - errno = ECONNRESET; - recvd = -1; - break; - } - - if (res < 0) { - if (errno == EINTR) { - /* loop on EINTR */ - continue; - } - - if (errno == EWOULDBLOCK || errno == EAGAIN) { - res = iolooper_wait_absolute(_ads_io_looper(ads), ads->deadline); - if (res > 0) { - /* Ready to read. */ - continue; - } - } - recvd = -1; - break; - } - recvd += res; - } while (recvd < bufsize); - iolooper_del_read(_ads_io_looper(ads), ads->fd); - - /* In case of an I/O failure we have to invoke failure callback. Note that we - * report I/O failures only on registered sockets. */ - if (recvd < 0) { - D("I/O error while receiving from channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - - if (ads->ad->on_io_failure != NULL && ads->socket_status > ADS_CONNECTED) { - const char save_error = errno; - ads->ad->on_io_failure(ads->opaque, ads->ad, save_error); - errno = save_error; - } - } - - return recvd; -} - -static int -_android_dev_socket_read_string(AndroidDevSocket* ads, char* str, int strsize) -{ - int n; - - /* Char by char read from the socket, until zero-terminator is read. */ - for (n = 0; n < strsize; n++) { - if (_android_dev_socket_recv(ads, str + n, 1) > 0) { - if (str[n] == '\0') { - /* Done. */ - return n + 1; /* Including zero-terminator. */ - } - } else { - /* I/O error. */ - return -1; - } - } - - /* Buffer was too small. Report that by setting errno to ENOMEM. */ - D("Buffer %d is too small to receive a string from channel '%s'@%d", - strsize, _ads_id_str(ads), _ads_port(ads)); - errno = ENOMEM; - return -1; -} - -static int -_android_dev_socket_read_response(AndroidDevSocket* ads, char* data, int datasize) -{ - int n, res; - int success = 0; - int failure = 0; - int bad_format = 0; - char ok[4]; - - *data = '\0'; - - /* Char by char read from the socket, until ok/ko is read. */ - for (n = 0; n < 2; n++) { - res = _android_dev_socket_recv(ads, ok + n, 1); - if (res > 0) { - if (ok[n] == '\0') { - /* EOS is unexpected here! */ - D("Bad query reply format on channel '%s'@%d: '%s' is too short.", - _ads_id_str(ads), _ads_port(ads), ok); - errno = EINVAL; - return -1; - } - } else { - /* I/O error. */ - return -1; - } - } - - /* Next character must be either ':', or '\0' */ - res = _android_dev_socket_recv(ads, ok + n, 1); - if (res <= 0) { - /* I/O error. */ - return -1; - } - - /* - * Verify format. - */ - - /* Check ok / ko */ - success = memcmp(ok, "ok", 2) == 0; - failure = memcmp(ok, "ko", 2) == 0; - - /* Check the prefix: 'ok'|'ko' & ':'|'\0' */ - if ((success || failure) && (ok[n] == '\0' || ok[n] == ':')) { - /* Format is good. */ - if (ok[n] == '\0') { - /* We're done: no extra data in response. */ - errno = 0; - return success ? 0 : -1; - } - /* Reset buffer offset, so we will start to read the remaining query - * data to the beginning of the supplied buffer. */ - n = 0; - } else { - /* Bad format. Lets move what we've read to the main buffer, and - * continue filling it in. */ - bad_format = 1; - n++; - memcpy(data, ok, n); - } - - /* Read the remainder of reply to the supplied data buffer. */ - res = _android_dev_socket_read_string(ads, data + n, datasize - n); - if (res < 0) { - return res; - } - - /* Lets see if format was bad */ - if (bad_format) { - D("Bad query reply format on channel '%s'@%d: %s.", - _ads_id_str(ads), _ads_port(ads), data); - errno = EINVAL; - return -1; - } else { - errno = 0; - return success ? n : -1; - } -} - -/******************************************************************************** - * Query socket declarations - *******************************************************************************/ - -/* Initializes query socket descriptor. - * Param: - * adsquery - Socket descriptor to initialize. - * opaque - An opaque pointer to associate with the socket. Typicaly it's the - * same pointer that is associated with AndroidDevice instance. - * ad - Android device descriptor that owns the socket. - * port - TCP socket port. - */ -static int _android_query_socket_init(AndroidQuerySocket* adsquery, - void* opaque, - AndroidDevice* ad, - int port); - -/* Destroys query socket descriptor. */ -static void _android_query_socket_destroy(AndroidQuerySocket* adsquery); - -/* Synchronously connects the query socket, and registers it with the server. - * Param: - * adsquery - Descriptor for the query socket to connect. Must have 'deadline' - * field properly setup. - * cb - Callback to invoke when socket connection is completed. Can be NULL. - * Return: - * Zero on success, or non-zero on failure. - */ -static int _android_query_socket_connect(AndroidQuerySocket* adsquery); - -/* Disconnects the query socket. */ -static void _android_query_socket_disconnect(AndroidQuerySocket* adsquery); - -/******************************************************************************** - * Query socket implementation - *******************************************************************************/ - -static int -_android_query_socket_init(AndroidQuerySocket* adsquery, - void* opaque, - AndroidDevice* ad, - int port) -{ - return _android_dev_socket_init(&adsquery->dev_socket, opaque, ad, port, - ADS_TYPE_QUERY); -} - -static void -_android_query_socket_destroy(AndroidQuerySocket* adsquery) -{ - _android_query_socket_disconnect(adsquery); - _android_dev_socket_destroy(&adsquery->dev_socket); -} - -static int -_android_query_socket_connect(AndroidQuerySocket* adsquery) -{ - return _android_dev_socket_connect(&adsquery->dev_socket, NULL, NULL); -} - -static void -_android_query_socket_disconnect(AndroidQuerySocket* adsquery) -{ - _android_dev_socket_disconnect(&adsquery->dev_socket); -} - -/******************************************************************************** - * Events socket declarations - *******************************************************************************/ - -/* Initializes event socket descriptor. - * Param: - * adsevent - Socket descriptor to initialize. - * opaque - An opaque pointer to associate with the socket. Typicaly it's the - * same pointer that is associated with AndroidDevice instance. - * ad - Android device descriptor that owns the socket. - * port - TCP socket port. - */ -static int _android_event_socket_init(AndroidEventSocket* adsevent, - void* opaque, - AndroidDevice* ad, - int port); - -/* Destroys the event socket descriptor. */ -static void _android_event_socket_destroy(AndroidEventSocket* adsevent); - -/* Synchronously connects event socket. - * Param: - * adsevent - Descriptor for the event socket to connect. Must have 'deadline' - * field properly setup. - * Return: - * Zero on success, or non-zero on failure. - */ -static int _android_event_socket_connect_sync(AndroidEventSocket* adsevent); - -/* Initiates asynchronous event socket connection. - * Param: - * adsevent - Descriptor for the event socket to connect. Must have 'deadline' - * field properly setup. - * cb - Callback to invoke when socket connection is completed. Can be NULL. - * Return: - * Zero on success, or non-zero on failure. - */ -static int _android_event_socket_connect_async(AndroidEventSocket* adsevent, - ads_socket_connected_cb cb); - -/* Disconnects the event socket. */ -static void _android_event_socket_disconnect(AndroidEventSocket* adsevent); - -/* Initiates listening on the event socket. - * Param: - * adsevent - Descriptor for the event socket to listen on. - * str, strsize - Buffer where to read the string. - * cb - A callback to call when the event string is read. Can be NULL. - * Return: - * Zero on success, or non-zero on failure. - */ -static int _android_event_socket_listen(AndroidEventSocket* adsevent, - char* str, - int strsize, - event_cb cb); - -/* Asynchronously sends data via event socket. - * Param: - * adsevent - Descriptor for the event socket to send data to. - * data, size - Buffer containing data to send. - * free_on_close - A boolean flag indicating whether the data buffer should be - * freed upon data transfer completion. - * cb - Callback to invoke when data transfer is completed. - * opaque - An opaque pointer to pass to the transfer completion callback. - */ -static int _android_event_socket_send(AndroidEventSocket* adsevent, - void* data, - int size, - int free_on_close, - async_send_cb cb, - void* opaque); - -/* Cancels all asynchronous data transfers on the event socket. - * Param: - * adsevent - Descriptor for the event socket to cancel data transfer. - * reason - Reason for the cancellation. - */ -static void _android_event_socket_cancel_send(AndroidEventSocket* adsevent, - ATResult reason); - -/* Event socket's asynchronous I/O looper callback. - * Param: - * opaque - AndroidEventSocket instance. - * fd - Socket's FD. - * events - I/O type bitsmask (read | write). - */ -static void _on_event_socket_io(void* opaque, int fd, unsigned events); - -/* Callback that is invoked when asynchronous event socket connection is - * completed. */ -static void _on_event_socket_connected(AndroidEventSocket* adsevent, int failure); - -/* Callback that is invoked when an event is received from the device. */ -static void _on_event_received(AndroidEventSocket* adsevent); - -/* Gets I/O looper for asynchronous I/O on event socket. */ -AINLINED Looper* -_aes_looper(AndroidEventSocket* adsevent) -{ - return adsevent->dev_socket.ad->looper; -} - -/******************************************************************************** - * Events socket implementation - *******************************************************************************/ - -static int -_android_event_socket_init(AndroidEventSocket* adsevent, - void* opaque, - AndroidDevice* ad, - int port) -{ - return _android_dev_socket_init(&adsevent->dev_socket, opaque, ad, port, - ADS_TYPE_EVENT); -} - -static void -_android_event_socket_destroy(AndroidEventSocket* adsevent) -{ - _android_event_socket_disconnect(adsevent); - _android_dev_socket_destroy(&adsevent->dev_socket); -} - -/* A callback invoked when file descriptor is created for the event socket. - * We use this callback to initialize the event socket for async I/O right after - * the FD has been created. - */ -static void -_on_event_fd_created(AndroidDevSocket* ads, void* opaque) -{ - AndroidEventSocket* adsevent = (AndroidEventSocket*)opaque; - /* Prepare for async I/O on the event socket. */ - loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd, - _on_event_socket_io, adsevent); -} - -static int -_android_event_socket_connect_sync(AndroidEventSocket* adsevent) -{ - return _android_dev_socket_connect(&adsevent->dev_socket, - _on_event_fd_created, adsevent); -} - -static int -_android_event_socket_connect_async(AndroidEventSocket* adsevent, - ads_socket_connected_cb cb) -{ - AsyncStatus status; - AndroidDevSocket* ads = &adsevent->dev_socket; - - /* Create asynchronous socket. */ - ads->fd = socket_create_inet(SOCKET_STREAM); - if (ads->fd < 0) { - D("Unable to create socket for channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - if (cb != NULL) { - cb(ads->opaque, ads, errno); - } - return -1; - } - socket_set_nonblock(ads->fd); - - /* Prepare for async I/O on the event socket. */ - loopIo_init(adsevent->io, _aes_looper(adsevent), ads->fd, - _on_event_socket_io, adsevent); - - /* Try to connect. */ - ads->socket_status = ADS_CONNECTING; - adsevent->on_connected = cb; - status = asyncConnector_init(adsevent->connector, &ads->address, adsevent->io); - switch (status) { - case ASYNC_COMPLETE: - /* We're connected to the device socket. */ - ads->socket_status = ADS_CONNECTED; - _on_event_socket_connected(adsevent, 0); - break; - case ASYNC_ERROR: - _on_event_socket_connected(adsevent, errno); - break; - case ASYNC_NEED_MORE: - /* Attempt to connect would block, so connection competion is - * delegates to the looper's I/O routine. */ - default: - break; - } - - return 0; -} - -static void -_android_event_socket_disconnect(AndroidEventSocket* adsevent) -{ - AndroidDevSocket* ads = &adsevent->dev_socket; - - if (ads->socket_status != ADS_DISCONNECTED) { - /* Cancel data transfer. */ - _android_event_socket_cancel_send(adsevent, ATR_DISCONNECT); - - /* Stop all async I/O. */ - loopIo_done(adsevent->io); - - /* Disconnect common socket. */ - _android_dev_socket_disconnect(ads); - } -} - -static int -_android_event_socket_listen(AndroidEventSocket* adsevent, - char* str, - int strsize, - event_cb cb) -{ - AsyncStatus status; - AndroidDevSocket* ads = &adsevent->dev_socket; - - /* Make sure that device is connected. */ - if (ads->socket_status < ADS_CONNECTED) { - D("Attempt to listen on a disconnected channel '%s'@%d", - _ads_id_str(ads), _ads_port(ads)); - errno = ECONNRESET; - return -1; - } - - /* NOTE: only one reader at any given time! */ - adsevent->on_event = cb; - asyncLineReader_init(&adsevent->alr, str, strsize, adsevent->io); - /* Default EOL for the line reader was '\n'. */ - asyncLineReader_setEOL(&adsevent->alr, '\0'); - status = asyncLineReader_read(&adsevent->alr); - if (status == ASYNC_COMPLETE) { - /* Data has been transferred immediately. Do the callback here. */ - _on_event_received(adsevent); - } else if (status == ASYNC_ERROR) { - D("Error while listening on channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - /* There is one special failure here, when buffer was too small to - * contain the entire string. This is not an I/O, but rather a - * protocol error. So we don't report it to the I/O failure - * callback. */ - if (errno == ENOMEM) { - _on_event_received(adsevent); - } else { - if (ads->ad->on_io_failure != NULL) { - ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno); - } - } - return -1; - } - return 0; -} - -static int -_android_event_socket_send(AndroidEventSocket* adsevent, - void* data, - int size, - int free_on_close, - async_send_cb cb, - void* opaque) -{ - /* Create data transfer descriptor, and place it at the end of the list. */ - AsyncSendBuffer* const desc = - _async_send_buffer_create(data, size, free_on_close, cb, opaque); - AsyncSendBuffer** place = &adsevent->send_pending; - while (*place != NULL) { - place = &((*place)->next); - } - *place = desc; - - /* We're ready to transfer data. */ - loopIo_wantWrite(adsevent->io); - - return 0; -} - -static void -_android_event_socket_cancel_send(AndroidEventSocket* adsevent, ATResult reason) -{ - while (adsevent->send_pending != NULL) { - AsyncSendBuffer* const to_cancel = adsevent->send_pending; - adsevent->send_pending = to_cancel->next; - _async_send_buffer_complete(to_cancel, reason); - } - loopIo_dontWantWrite(adsevent->io); -} - -static void -_on_event_socket_io(void* opaque, int fd, unsigned events) -{ - AsyncStatus status; - AndroidEventSocket* adsevent = (AndroidEventSocket*)opaque; - AndroidDevSocket* ads = &adsevent->dev_socket; - - /* Lets see if we're still wating on a connection to occur. */ - if (ads->socket_status == ADS_CONNECTING) { - /* Complete socket connection. */ - status = asyncConnector_run(adsevent->connector); - if (status == ASYNC_COMPLETE) { - /* We're connected to the device socket. */ - ads->socket_status = ADS_CONNECTED; - D("Channel '%s'@%d is connected asynchronously", - _ads_id_str(ads), _ads_port(ads)); - _on_event_socket_connected(adsevent, 0); - } else if (status == ASYNC_ERROR) { - _on_event_socket_connected(adsevent, adsevent->connector->error); - } - return; - } - - /* - * Device is connected. Continue with the data transfer. - */ - - if ((events & LOOP_IO_READ) != 0) { - /* Continue reading data. */ - status = asyncLineReader_read(&adsevent->alr); - if (status == ASYNC_COMPLETE) { - errno = 0; - _on_event_received(adsevent); - } else if (status == ASYNC_ERROR) { - D("I/O failure while reading from channel '%s'@%d: %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - /* There is one special failure here, when buffer was too small to - * contain the entire string. This is not an I/O, but rather a - * protocol error. So we don't report it to the I/O failure - * callback. */ - if (errno == ENOMEM) { - _on_event_received(adsevent); - } else { - if (ads->ad->on_io_failure != NULL) { - ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno); - } - } - } - } - - if ((events & LOOP_IO_WRITE) != 0) { - while (adsevent->send_pending != NULL) { - AsyncSendBuffer* to_send = adsevent->send_pending; - const int offset = to_send->data_size - to_send->data_remaining; - const int sent = socket_send(ads->fd, to_send->data + offset, - to_send->data_remaining); - if (sent < 0) { - if (errno == EWOULDBLOCK) { - /* Try again later. */ - return; - } else { - /* An error has occured. */ - _android_event_socket_cancel_send(adsevent, ATR_IO_ERROR); - if (ads->ad->on_io_failure != NULL) { - ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno); - } - return; - } - } else if (sent == 0) { - /* Disconnect condition. */ - _android_event_socket_cancel_send(adsevent, ATR_DISCONNECT); - if (ads->ad->on_io_failure != NULL) { - ads->ad->on_io_failure(ads->ad->opaque, ads->ad, errno); - } - return; - } else if (sent == to_send->data_remaining) { - /* All data is sent. */ - errno = 0; - adsevent->send_pending = to_send->next; - _async_send_buffer_complete(to_send, ATR_SUCCESS); - } else { - /* Chunk is sent. */ - to_send->data_remaining -= sent; - return; - } - } - loopIo_dontWantWrite(adsevent->io); - } -} - -static void -_on_event_socket_connected(AndroidEventSocket* adsevent, int failure) -{ - int res; - AndroidDevSocket* ads = &adsevent->dev_socket; - - if (failure) { - _android_event_socket_disconnect(adsevent); - if (adsevent->on_connected != NULL) { - adsevent->on_connected(ads->opaque, ads, failure); - } - return; - } - - /* Complete event socket connection by identifying it as "event" socket with - * the application. */ - ads->socket_status = ADS_CONNECTED; - res = _android_dev_socket_register(ads); - - if (res) { - const int save_error = errno; - _android_event_socket_disconnect(adsevent); - errno = save_error; - } - - /* Notify callback about connection completion. */ - if (adsevent->on_connected != NULL) { - if (res) { - adsevent->on_connected(ads->opaque, ads, errno); - } else { - adsevent->on_connected(ads->opaque, ads, 0); - } - } -} - -static void -_on_event_received(AndroidEventSocket* adsevent) -{ - if (adsevent->on_event != NULL) { - AndroidDevice* ad = adsevent->dev_socket.ad; - adsevent->on_event(ad->opaque, ad, (char*)adsevent->alr.buffer, - adsevent->alr.pos); - } -} - -/******************************************************************************** - * Android device connection - *******************************************************************************/ - -/* Callback that is invoked when event socket is connected and registered as part - * of the _android_device_connect_async API. - * Param: - * opaque - Opaque pointer associated with AndroidDevice instance. - * ads - Common socket descriptor for the event socket. - * failure - If zero connection has succeeded, otherwise contains 'errno'-reason - * for connection failure. - */ -static void -_on_android_device_connected_async(void* opaque, - AndroidDevSocket* ads, - int failure) -{ - int res; - AndroidDevice* ad = ads->ad; - - if (failure) { - /* Depending on the failure code we will either retry, or bail out. */ - switch (failure) { - case EPIPE: - case EAGAIN: - case EINPROGRESS: - case EALREADY: - case EHOSTUNREACH: - case EHOSTDOWN: - case ECONNREFUSED: - case ESHUTDOWN: - case ENOTCONN: - case ECONNRESET: - case ECONNABORTED: - case ENETRESET: - case ENETUNREACH: - case ENETDOWN: - case EBUSY: -#if !defined(_DARWIN_C_SOURCE) && !defined(_WIN32) - case ERESTART: - case ECOMM: - case ENONET: -#endif /* !_DARWIN_C_SOURCE && !_WIN32 */ - /* Device is not available / reachable at the moment. - * Retry connection later. */ - loopTimer_startRelative(ad->timer, ADS_RETRY_CONNECTION_TIMEOUT); - return; - default: - D("Failed to asynchronously connect channel '%s':%d %s", - _ads_id_str(ads), _ads_port(ads), strerror(errno)); - if (ad->on_connected != NULL) { - ad->on_connected(ad->opaque, ad, failure); - } - break; - } - return; - } - - /* Event socket is connected. Connect the query socket now. Give it 5 - * seconds to connect. */ - _ads_set_deadline(&ad->query_socket.dev_socket, 5000); - res = _android_query_socket_connect(&ad->query_socket); - if (res == 0) { - /* Query socket is connected. */ - if (ad->on_connected != NULL) { - ad->on_connected(ad->opaque, ad, 0); - } - } else { - /* If connection completion has failed - disconnect the sockets. */ - _android_event_socket_disconnect(&ad->event_socket); - _android_query_socket_disconnect(&ad->query_socket); - - if (ad->on_connected != NULL) { - ad->on_connected(ad->opaque, ad, errno); - } - } -} - -static void -_on_timer(void* opaque) -{ - /* Retry the connection. */ - AndroidDevice* ad = (AndroidDevice*)opaque; - android_device_connect_async(ad, ad->on_connected); -} - -/* Destroys and frees the descriptor. */ -static void -_android_device_free(AndroidDevice* ad) -{ - if (ad != NULL) { - _android_event_socket_destroy(&ad->event_socket); - _android_query_socket_destroy(&ad->query_socket); - - /* Delete asynchronous I/O looper. */ - if (ad->looper != NULL ) { - loopTimer_done(ad->timer); - looper_free(ad->looper); - } - - /* Delete synchronous I/O looper. */ - if (ad->io_looper != NULL) { - iolooper_reset(ad->io_looper); - iolooper_free(ad->io_looper); - } - - AFREE(ad); - } -} - -/******************************************************************************** - * Android device API - *******************************************************************************/ - -AndroidDevice* -android_device_init(void* opaque, int port, io_failure_cb on_io_failure) -{ - int res; - AndroidDevice* ad; - - ANEW0(ad); - - ad->opaque = opaque; - ad->on_io_failure = on_io_failure; - - /* Create I/O looper for synchronous I/O on the device. */ - ad->io_looper = iolooper_new(); - if (ad->io_looper == NULL) { - E("Unable to create synchronous I/O looper for android device."); - _android_device_free(ad); - return NULL; - } - - /* Create a looper for asynchronous I/O on the device. */ - ad->looper = looper_newCore(); - if (ad->looper != NULL) { - /* Create a timer that will be used for connection retries. */ - loopTimer_init(ad->timer, ad->looper, _on_timer, ad); - } else { - E("Unable to create asynchronous I/O looper for android device."); - _android_device_free(ad); - return NULL; - } - - /* Init query socket. */ - res = _android_query_socket_init(&ad->query_socket, opaque, ad, port); - if (res) { - _android_device_free(ad); - return NULL; - } - - /* Init event socket. */ - res = _android_event_socket_init(&ad->event_socket, opaque, ad, port); - if (res) { - _android_device_free(ad); - return NULL; - } - - return ad; -} - -void -android_device_destroy(AndroidDevice* ad) -{ - if (ad != NULL) { - _android_device_free(ad); - } -} - -int -android_device_connect_sync(AndroidDevice* ad, int to) -{ - int res; - - /* Setup deadline for the connections. */ - _ads_set_deadline(&ad->query_socket.dev_socket, to); - ad->event_socket.dev_socket.deadline = ad->query_socket.dev_socket.deadline; - - /* Connect the query socket first. */ - res = _android_query_socket_connect(&ad->query_socket); - if (!res) { - /* Connect to the event socket next. */ - res = _android_event_socket_connect_sync(&ad->event_socket); - } - - return res; -} - -int -android_device_connect_async(AndroidDevice* ad, device_connected_cb on_connected) -{ - /* No deadline for async connections. */ - ad->query_socket.dev_socket.deadline = DURATION_INFINITE; - ad->event_socket.dev_socket.deadline = DURATION_INFINITE; - - /* Connect to the event socket first, and delegate query socket connection - * into callback invoked when event socket is connected. NOTE: In case of - * failure 'on_connected' callback has already been called from - * _on_android_device_connected_async routine. */ - ad->on_connected = on_connected; - return _android_event_socket_connect_async(&ad->event_socket, - _on_android_device_connected_async); -} - -void -android_device_disconnect(AndroidDevice* ad) -{ - _android_event_socket_disconnect(&ad->event_socket); - _android_query_socket_disconnect(&ad->query_socket); -} - -int -android_device_query(AndroidDevice* ad, - const char* query, - char* buff, - size_t buffsize, - int to) -{ - int res; - - /* Setup deadline for the query. */ - _ads_set_deadline(&ad->query_socket.dev_socket, to); - - /* Send the query. */ - res = _android_dev_socket_send(&ad->query_socket.dev_socket, query, - strlen(query) + 1); - if (res > 0) { - /* Receive the response. */ - res = _android_dev_socket_read_response(&ad->query_socket.dev_socket, - buff, buffsize); - return (res >= 0) ? 0 : -1; - } - - return -1; -} - -int -android_device_start_query(AndroidDevice* ad, const char* query, int to) -{ - int res; - - /* Setup deadline for the query. */ - _ads_set_deadline(&ad->query_socket.dev_socket, to); - - /* Send the query header. */ - res = _android_dev_socket_send(&ad->query_socket.dev_socket, query, - strlen(query) + 1); - return (res > 0) ? 0 : -1; -} - -int -android_device_send_query_data(AndroidDevice* ad, const void* data, int size) -{ - return _android_dev_socket_send(&ad->query_socket.dev_socket, data, size); -} - -int -android_device_complete_query(AndroidDevice* ad, char* buff, size_t buffsize) -{ - /* Receive the response to the query. */ - const int res = _android_dev_socket_read_response(&ad->query_socket.dev_socket, - buff, buffsize); - return (res >= 0) ? 0 : -1; -} - -int -android_device_listen(AndroidDevice* ad, - char* buff, - int buffsize, - event_cb on_event) -{ - return _android_event_socket_listen(&ad->event_socket, buff, buffsize, - on_event); -} - -int -android_device_send_async(AndroidDevice* ad, - void* data, - int size, - int free_on_close, - async_send_cb cb, - void* opaque) -{ - return _android_event_socket_send(&ad->event_socket, data, size, - free_on_close, cb, opaque); -} diff --git a/android/android-device.h b/android/android-device.h deleted file mode 100644 index 6825819..0000000 --- a/android/android-device.h +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_ANDROID_DEVICE_H_ -#define ANDROID_ANDROID_DEVICE_H_ - -/* - * Encapsulates an exchange protocol between the emulator, and an Android device - * that is connected to the host via USB. The communication is established over - * a TCP port forwarding, enabled by ADB (always use 'adb -d forward ...' variant - * of this command, so ADB will know to enable port forwarding on the connected - * device, and not on the emulator's guest system). - * - * Exchange protocol contains two channel: - * - * - Query channel. - * - Event channel. - * - * Both channels are implemented on top of TCP sockets that are connected to the - * same port. - * - * I QUERY CHANNEL. - * Query channel is intended to send queries to and receive responses from the - * connected device. It is implemented on top of iolooper_xxx API (see iolooper.h) - * because it must work outside of the main event loop. This is required to enable - * proper initialization of components (such as sensors) that must be set up - * before emulator enters the main loop. - * - * II EVENT CHANNEL. - * Event channel is intended to listen on events sent from the device, and - * asynchronously report them back to the client of this API by invoking an event - * callback that was registered by the client. Event channel is implemented on - * top of asyncXxx API (see android/async-utils.*). Note that using of asyncXxx - * API limits the use of event channel to the time after the emulator has entered - * its main event loop. The only exception is if event channel is connected from - * android_device_connect_sync API, in which case iolooper_xxx API is used to - * establish the connection. However, even in this case listening for events will - * not be available until after the emulator enters its event loop, since event - * listening always uses asyncXxx API. - * - * III. ESTABLISHING CONNECTION. - * ADB port forwarding requires that the server socket is to be run on the device, - * while emulator must use a client socket for communication. Thus, it's the - * emulator that initiates the connection. - * - * There are two ways how emulator can initiate the connection: - * - * - Synchronous connection. - * - Asynchronous connection. - * - * III.I SYNCHROUNOUS CONNECTION. - * Synchronous connection is initiated via android_device_connect_sync API, and - * completes synchronously. - * - * This API should be used when connection with the device is required at the time - * of the call. For instance, when initializing sensor emulation, connection with - * the device is required to properly set up the emulator before the guest system - * starts, and before emulator enters its main event loop. - * - * III.II ASYNCHRONOUS CONNECTION. - * Asynchronous connection is initiated via android_device_connect_async API. The - * main difference with the synchronous connection is that this API will not fail - * if connection is not immediately available. If connection is not available at - * the time of the call, the API will schedule a retry (based on a timer), and - * will continue reprying untill connection becomes available, or until an error - * occurs that prevent further retries. - * - * This API should be used when ... Well, whenever appropriate. For instance, - * sensor emulation will use this API to restore lost connection with the device. - * - * NOTE: Asynchronous connection will complete no sooner than the emulator enters - * its main loop. - * - * IV EXCHANGE PROTOCOL. - * Obviously, there must be some application running on the device, that implements - * a socket server listening on the forwarded TCP port, and accepting the clients. - * - * IV.I Query vs. event channel. - * The exchange protocol assumes, that when a channel is connected, it will - * identify itself by sending a string containing channel type. Only after such - * identification has been made the channel becomes available for use. - * - * IV.II Message format. - * All data that is transferred in both directions over both channels are zero- - * terminated strings. - */ - -#include "qemu-common.h" -#include "android/async-utils.h" -#include "android/utils/debug.h" - -/* TCP port reserved for sensor emulation. */ -#define AD_SENSOR_PORT 1968 - -/* Definis infinite timeout. */ -#define AD_INFINITE_WAIT -1 - -/* Enumerates results of asynchronous data transfer. - */ -typedef enum ATResult { - /* Data transfer has been completed. */ - ATR_SUCCESS, - /* Socket got disconnected while data transfer has been in progress. */ - ATR_DISCONNECT, - /* An I/O error has occured. 'errno' contains error value. */ - ATR_IO_ERROR, -} ATResult; - -/* Android device descriptor. */ -typedef struct AndroidDevice AndroidDevice; - -/******************************************************************************** - * Callback declarations - *******************************************************************************/ - -/* Callback routine that is invoked when android device is connected, or failed - * to connect. As discussed above, this callback is called when both, query and - * event channels have been connected. This callback is used only for asynchronous - * connections. - * Param: - * opaque - Opaque pointer that was passed to android_device_init API. - * ad - Androd device descriptor for the connection. - * failure - Zero indicates that connection with the device has been successfuly - * established. Non-zero vaule passed in this parameter indicates a failure, - * and contains 'errno'-reason for failure. - */ -typedef void (*device_connected_cb)(void* opaque, AndroidDevice* ad, int failure); - -/* Callback routine that is invoked on an event received in the event channel. - * NOTE: It's important to check 'errno' in this callback. If 'errno' is set to - * ENOMEM, this signals that buffer passed to android_device_listen was too small - * to contain the entire event message. - * Param: - * opaque - Opaque pointer that was passed to android_device_init API. - * ad - Androd device descriptor for the connection. - * msg - Event message (a zero-terminated string) received from the device. - * msgsize - Event message size (including zero-terminator). - */ -typedef void (*event_cb)(void* opaque, AndroidDevice* ad, char* msg, int msgsize); - -/* Callback routine that is invoked when an I/O failure occurs on a channel. - * Note that this callback will not be invoked on connection failures. - * Param: - * opaque - Opaque pointer that was passed to android_device_init API. - * ad - Android device instance - * failure - Contains 'errno' indicating the reason for failure. - */ -typedef void (*io_failure_cb)(void* opaque, AndroidDevice* ad, int failure); - -/* Callback routine that is invoked when an asynchronous data send has been - * completed. - * Param: - * opaque - An opaque pointer associated with the data. - * res - Result of data transfer. - * data, size - Transferred data buffer. - * sent - Number of sent bytes. - */ -typedef void (*async_send_cb)(void* opaque, - ATResult res, - void* data, - int size, - int sent); - -/******************************************************************************** - * Android Device API. - *******************************************************************************/ - -/* Initializes android device descriptor. - * Param: - * opaque - An opaque pointer to associate with the descriptor. This pointer - * will be passed to all callbacks (see above) that were invoked by the - * initializing android device instance. - * port - TCP port to use for connection. - * on_io_failure - Callback to invoke when an I/O failure occurs on a channel - * used by the initializing android device instance. Can be NULL. - * Return: - * Initialized android device descriptor on success, or NULL on failure. - */ -extern AndroidDevice* android_device_init(void* opaque, - int port, - io_failure_cb on_io_failure); - -/* Disconnects and destroys android device descriptor. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * Note that memory allocated for this descriptor will be freed in this - * routine. - */ -extern void android_device_destroy(AndroidDevice* ad); - -/* Synchronously connects to the device. See notes above for more details. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * to - Milliseconds to wait for connection to be established. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set. - */ -extern int android_device_connect_sync(AndroidDevice* ad, int to); - -/* Asynchronously connects to the device. See notes above for more details. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * on_connected - Callback to invoke when connection is completed (i,e, both, - * event, and query channels have been connected). This parameter can be - * NULL. Note that connection errors will be also reported through this - * callback. Also note that this callback will be invoked even if this - * routine returns with a failure. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set. - */ -extern int android_device_connect_async(AndroidDevice* ad, - device_connected_cb on_connected); - -/* Disconnects from the android device. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - */ -extern void android_device_disconnect(AndroidDevice* ad); - -/* Queries the device via query channel. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * query - Zero-terminated query string. - * buff, buffsize - Buffer where to receive the response to the query. - * to - Milliseconds to wait for the entire query to complete. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set: - * - 0 Indicates that the server has failed the query. - * - Anything else indicates an I/O error. - */ -extern int android_device_query(AndroidDevice* ad, - const char* query, - char* buff, - size_t buffsize, - int to); - -/* Starts a query that may require more than one buffer transfer. - * This routine allows to initiate a query that may require more than one call to - * send_data, or may have a format that differs from the usual (a zero-terminated - * string). For instance, sending a BLOB data should use this routine to start a - * a query, then use android_device_send_query_data to transfer the data, and - * then call android_device_complete_query to obtain the response. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * query - Zero-terminated query string. - * to - Milliseconds to wait for the entire query to complete. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set: - * - 0 Indicates that the server has failed the query. - * - Anything else indicates an I/O error. - */ -extern int android_device_start_query(AndroidDevice* ad, - const char* query, - int to); - -/* Sends data block for a query started with android_device_start_query - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * data, size - Data to transfer. - * Return: - * Number of bytes transferred on success, or -1 on failure with errno - * containing the reason for failure. - */ -extern int android_device_send_query_data(AndroidDevice* ad, - const void* data, - int size); - -/* Completes a query started with android_device_start_query, and receives the - * query response. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * buff, buffsize - Buffer where to receive the response to the query. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set. - */ -extern int android_device_complete_query(AndroidDevice* ad, char* buff, size_t buffsize); - -/* Start listening on the event channel. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * buff, buffsize - Buffer where to receive the event message. - * on_event - Callback to invoke on event. Note that this callback will be - * invoked even if this routine returns with a failure. - * Return: - * Zero on success, or non-zero value on failure with 'errno' properly set. - */ -extern int android_device_listen(AndroidDevice* ad, - char* buff, - int buffsize, - event_cb on_event); - -/* Asynchronously sends data to the android device. - * Param: - * ad - Android device descriptor, returned from android_device_init API. - * data, size - Buffer containing data to send. - * free_on_close - A boolean flag indicating whether the data buffer should be - * freed upon data transfer completion. - * cb - Callback to invoke when data transfer is completed. - * opaque - An opaque pointer to pass to the transfer completion callback. - */ -extern int android_device_send_async(AndroidDevice* ad, - void* data, - int size, - int free_on_close, - async_send_cb cb, - void* opaque); - -#endif /* ANDROID_ANDROID_DEVICE_H_ */ diff --git a/android/android.h b/android/android.h index 189b5c2..e32f9f5 100644 --- a/android/android.h +++ b/android/android.h @@ -97,6 +97,13 @@ extern int android_parse_network_speed(const char* speed); * accordingly. returns -1 on error, 0 on success */ extern int android_parse_network_latency(const char* delay); +/** in qemu_setup.c */ + +#define ANDROID_GLSTRING_BUF_SIZE 128 +extern char android_gl_vendor[ANDROID_GLSTRING_BUF_SIZE]; +extern char android_gl_renderer[ANDROID_GLSTRING_BUF_SIZE]; +extern char android_gl_version[ANDROID_GLSTRING_BUF_SIZE]; + extern void android_emulation_setup( void ); extern void android_emulation_teardown( void ); diff --git a/android/async-socket-connector.c b/android/async-socket-connector.c index 836016c..f4d51b5 100644 --- a/android/async-socket-connector.c +++ b/android/async-socket-connector.c @@ -20,8 +20,6 @@ * a TCP port forwarding, enabled by ADB. */ -#include "qemu-common.h" -#include "android/async-utils.h" #include "android/utils/debug.h" #include "android/async-socket-connector.h" #include "utils/panic.h" @@ -314,11 +312,15 @@ async_socket_connector_new(const SockAddress* address, connector->ref_count = 1; /* Copy socket address. */ +#ifdef _WIN32 + connector->address = *address; +#else if (sock_address_get_family(address) == SOCKET_UNIX) { sock_address_init_unix(&connector->address, sock_address_get_path(address)); } else { connector->address = *address; } +#endif /* Create a looper for asynchronous I/O. */ if (looper == NULL) { diff --git a/android/async-socket-connector.h b/android/async-socket-connector.h index bedc2df..d49d203 100644 --- a/android/async-socket-connector.h +++ b/android/async-socket-connector.h @@ -17,7 +17,9 @@ #ifndef ANDROID_ASYNC_SOCKET_CONNECTOR_H_ #define ANDROID_ASYNC_SOCKET_CONNECTOR_H_ +#include "qemu-common.h" #include "android/async-io-common.h" +#include "android/async-utils.h" /* * Contains declaration of an API that allows asynchronous connection to a diff --git a/android/async-socket.c b/android/async-socket.c index 5e2ae29..ab4e4b6 100644 --- a/android/async-socket.c +++ b/android/async-socket.c @@ -20,8 +20,6 @@ * a TCP port forwarding, enabled by ADB. */ -#include "qemu-common.h" -#include "android/async-utils.h" #include "android/utils/debug.h" #include "android/async-socket-connector.h" #include "android/async-socket.h" @@ -683,10 +681,10 @@ static void _async_socket_close_socket(AsyncSocket* as) { if (as->fd >= 0) { - loopIo_done(as->io); - socket_close(as->fd); T("ASocket %s: Socket handle %d is closed.", _async_socket_string(as), as->fd); + loopIo_done(as->io); + socket_close(as->fd); as->fd = -1; } } @@ -1217,15 +1215,22 @@ async_socket_read_abs(AsyncSocket* as, AsyncSocketIO* const asr = _async_socket_reader_new(as, buffer, len, reader_cb, reader_opaque, deadline); - /* Add new reader to the list. Note that we use initial reference from I/O - * 'new' routine as "in the list" reference counter. */ - if (as->readers_head == NULL) { - as->readers_head = as->readers_tail = asr; + if (async_socket_is_connected(as)) { + /* Add new reader to the list. Note that we use initial reference from I/O + * 'new' routine as "in the list" reference counter. */ + if (as->readers_head == NULL) { + as->readers_head = as->readers_tail = asr; + } else { + as->readers_tail->next = asr; + as->readers_tail = asr; + } + loopIo_wantRead(as->io); } else { - as->readers_tail->next = asr; - as->readers_tail = asr; + D("ASocket %s: Read on a disconnected socket.", _async_socket_string(as)); + errno = ECONNRESET; + reader_cb(reader_opaque, asr, ASIO_STATE_FAILED); + async_socket_io_release(asr); } - loopIo_wantRead(as->io); } void @@ -1253,15 +1258,22 @@ async_socket_write_abs(AsyncSocket* as, AsyncSocketIO* const asw = _async_socket_writer_new(as, buffer, len, writer_cb, writer_opaque, deadline); - /* Add new writer to the list. Note that we use initial reference from I/O - * 'new' routine as "in the list" reference counter. */ - if (as->writers_head == NULL) { - as->writers_head = as->writers_tail = asw; + if (async_socket_is_connected(as)) { + /* Add new writer to the list. Note that we use initial reference from I/O + * 'new' routine as "in the list" reference counter. */ + if (as->writers_head == NULL) { + as->writers_head = as->writers_tail = asw; + } else { + as->writers_tail->next = asw; + as->writers_tail = asw; + } + loopIo_wantWrite(as->io); } else { - as->writers_tail->next = asw; - as->writers_tail = asw; + D("ASocket %s: Write on a disconnected socket.", _async_socket_string(as)); + errno = ECONNRESET; + writer_cb(writer_opaque, asw, ASIO_STATE_FAILED); + async_socket_io_release(asw); } - loopIo_wantWrite(as->io); } void @@ -1294,3 +1306,9 @@ async_socket_get_port(const AsyncSocket* as) { return sock_address_get_port(&as->address); } + +int +async_socket_is_connected(const AsyncSocket* as) +{ + return as->fd >= 0; +} diff --git a/android/async-socket.h b/android/async-socket.h index 503907b..bdfd272 100644 --- a/android/async-socket.h +++ b/android/async-socket.h @@ -17,7 +17,9 @@ #ifndef ANDROID_ASYNC_SOCKET_H_ #define ANDROID_ASYNC_SOCKET_H_ +#include "qemu-common.h" #include "android/async-io-common.h" +#include "android/async-utils.h" /* * Contains declaration of an API that encapsulates communication via an @@ -264,4 +266,10 @@ extern void* async_socket_get_client_opaque(const AsyncSocket* as); /* Gets TCP port for the socket. */ extern int async_socket_get_port(const AsyncSocket* as); +/* Checks if socket is connected. + * Return: + * Boolean: 1 - socket is connected, 0 - socket is not connected. + */ +extern int async_socket_is_connected(const AsyncSocket* as); + #endif /* ANDROID_ASYNC_SOCKET_H_ */ diff --git a/android/build/common.sh b/android/build/common.sh index c7235ae..e3e190b 100644 --- a/android/build/common.sh +++ b/android/build/common.sh @@ -499,10 +499,10 @@ locate_android_prebuilt () { # locate prebuilt directory ANDROID_PREBUILT_HOST_TAG=$OS - ANDROID_PREBUILT=$ANDROID_TOP/prebuilt/$ANDROID_PREBUILT_HOST_TAG - ANDROID_PREBUILTS=$ANDROID_TOP/prebuilts/misc/$ANDROID_PREBUILT_HOST_TAG + ANDROID_PREBUILT=$ANDROID_TOP/prebuilt/$ANDROID_PREBUILT_HOST_TAG # AOSP still has it + ANDROID_PREBUILTS=$ANDROID_TOP/prebuilts/misc/$ANDROID_PREBUILT_HOST_TAG # AOSP does't have it yet if [ ! -d $ANDROID_PREBUILT ] ; then - # this can happen when building on x86_64 + # this can happen when building on x86_64, or in AOSP case $OS in linux-x86_64) ANDROID_PREBUILT_HOST_TAG=linux-x86 @@ -511,8 +511,7 @@ locate_android_prebuilt () *) esac if [ ! -d $ANDROID_PREBUILT ] ; then - echo "Can't find the prebuilt directory $ANDROID_PREBUILT in Android build" - exit 1 + ANDROID_PREBUILT= fi fi if [ ! -d $ANDROID_PREBUILTS ] ; then @@ -525,8 +524,7 @@ locate_android_prebuilt () *) esac if [ ! -d $ANDROID_PREBUILTS ] ; then - echo "Can't find the prebuilts directory $ANDROID_PREBUILTS in Android build" - exit 1 + ANDROID_PREBUILTS= fi fi log "Prebuilt : ANDROID_PREBUILT=$ANDROID_PREBUILT" diff --git a/android/camera/camera-capture-windows.c b/android/camera/camera-capture-windows.c index e1c5538..7f9df39 100755 --- a/android/camera/camera-capture-windows.c +++ b/android/camera/camera-capture-windows.c @@ -19,6 +19,7 @@ * This code uses capXxx API, available via capCreateCaptureWindow. */ +#include <windows.h> #include <vfw.h> #include "android/camera/camera-capture.h" #include "android/camera/camera-format-converters.h" diff --git a/android/cmdline-options.h b/android/cmdline-options.h index eb8ede0..5e8d59f 100644 --- a/android/cmdline-options.h +++ b/android/cmdline-options.h @@ -72,6 +72,7 @@ CFG_PARAM( initdata, "<file>", "same as '-init-data <file>'" ) CFG_PARAM( data, "<file>", "data image (default <datadir>/userdata-qemu.img" ) CFG_PARAM( partition_size, "<size>", "system/data partition size in MBs" ) CFG_PARAM( cache, "<file>", "cache partition image (default is temporary file)" ) +OPT_PARAM( cache_size, "<size>", "cache partition size in MBs" ) CFG_FLAG ( no_cache, "disable the cache partition" ) CFG_FLAG ( nocache, "same as -no-cache" ) OPT_PARAM( sdcard, "<file>", "SD card image (default <system>/sdcard.img") diff --git a/android/config/darwin-x86/config-host.h b/android/config/darwin-x86/config-host.h index 97a83dc..d5e8507 100644 --- a/android/config/darwin-x86/config-host.h +++ b/android/config/darwin-x86/config-host.h @@ -26,3 +26,4 @@ #define CONFIG_ANDROID 1 #define CONFIG_POSIX 1 #define CONFIG_MADVISE 1 +#define CONFIG_ANDROID_OPENGLES 1 diff --git a/android/config/linux-x86/config-host.h b/android/config/linux-x86/config-host.h index 70b9ce7..d9f04fa 100644 --- a/android/config/linux-x86/config-host.h +++ b/android/config/linux-x86/config-host.h @@ -26,3 +26,4 @@ #define CONFIG_POSIX 1 #define CONFIG_ANDROID 1 #define CONFIG_MADVISE 1 +#define CONFIG_ANDROID_OPENGLES 1 diff --git a/android/config/windows/config-host.h b/android/config/windows/config-host.h index 1b50927..2d2fdc6 100644 --- a/android/config/windows/config-host.h +++ b/android/config/windows/config-host.h @@ -18,3 +18,4 @@ #define QEMU_PKGVERSION "Android" #define CONFIG_WIN32 1 #define CONFIG_ANDROID 1 +#define CONFIG_ANDROID_OPENGLES 1 diff --git a/android/help.c b/android/help.c index c6ffb87..1570160 100644 --- a/android/help.c +++ b/android/help.c @@ -585,6 +585,15 @@ help_cache(stralloc_t* out) } static void +help_cache_size(stralloc_t* out) +{ + PRINTF( + " use '-cache <size>' to specify a /cache partition size in MB. By default,\n" + " the cache partition size is set to 66MB\n\n" + ); +} + +static void help_no_cache(stralloc_t* out) { PRINTF( diff --git a/android/hw-events.h b/android/hw-events.h index 488c299..b8340bd 100644 --- a/android/hw-events.h +++ b/android/hw-events.h @@ -41,6 +41,17 @@ typedef enum { /* BEWARE: The following codes are defined by the Linux kernel headers. * The Android "Menu" key is KEY_SOFT1, *not* KEY_MENU */ +/* NOTE: mingw's winnt.h define DELETE to constant + i586-mingw32msvc: #define DELETE 0x00010000L + x86_64-w64-mingw32-gcc: #define DELETE (0x00010000L) + + KEY_CODE belows glues "KEY_" and "DELETE". + While KEY_0x00010000L may not mean anything, + KEY_(0x00010000L) is absolutely harmful to compiler. + Undefine DELETE below + */ +#undef DELETE + #define EVENT_KEY_LIST \ KEY_CODE(ESC ,1) \ KEY_CODE(1 ,2) \ diff --git a/android/hw-sensors.c b/android/hw-sensors.c index 17b2491..a7daea5 100644 --- a/android/hw-sensors.c +++ b/android/hw-sensors.c @@ -438,7 +438,7 @@ _hwSensorClient_receive( HwSensorClient* cl, uint8_t* msg, int msglen ) } /* If emulating device is connected update sensor state there too. */ - if (hw->sensors_port != NULL && sensors_port_is_connected(hw->sensors_port)) { + if (hw->sensors_port != NULL) { if (enabled) { sensors_port_enable_sensor(hw->sensors_port, (const char*)msg); } else { @@ -692,11 +692,6 @@ _hwSensors_init( HwSensors* h ) h->sensors[ANDROID_SENSOR_TEMPERATURE].enabled = 1; } - if (h->sensors_port != NULL) { - /* Init sensors on the attached device. */ - sensors_port_init_sensors(h->sensors_port); - } - /* XXX: TODO: Add other tests when we add the corresponding * properties to hardware-properties.ini et al. */ diff --git a/android/main-common.c b/android/main-common.c index 04d200a..2d535c7 100644 --- a/android/main-common.c +++ b/android/main-common.c @@ -232,7 +232,7 @@ sdl_set_window_icon( void ) SDL_GetWMInfo(&wminfo); - SetClassLong( wminfo.window, GCL_HICON, (LONG)icon ); + SetClassLongPtr( wminfo.window, GCLP_HICON, (LONG)icon ); #else /* !_WIN32 */ unsigned icon_w, icon_h; size_t icon_bytes; diff --git a/android/main-emulator.c b/android/main-emulator.c index 0981a71..ac4d2e9 100644 --- a/android/main-emulator.c +++ b/android/main-emulator.c @@ -148,6 +148,8 @@ int main(int argc, char** argv) avdArch = "arm"; D("Can't determine target AVD architecture: defaulting to %s\n", avdArch); } + if (!strcmp(avdArch, "mips")) + force_32bit = 1; /* emulator64-mips segfaults currently, 4-19-2012 */ /* Find the architecture-specific program in the same directory */ emulatorPath = getTargetEmulatorPath(argv[0], avdArch, force_32bit); diff --git a/android/main.c b/android/main.c index 51d481c..d9d2274 100644 --- a/android/main.c +++ b/android/main.c @@ -723,6 +723,18 @@ int main(int argc, char **argv) } } + if (hw->disk_cachePartition_path && opts->cache_size) { + /* Set cache partition size per user options. */ + char* end; + long sizeMB = strtol(opts->cache_size, &end, 0); + + if (sizeMB < 0 || *end != 0) { + derror( "-cache-size must be followed by a positive integer" ); + exit(1); + } + hw->disk_cachePartition_size = (uint64_t) sizeMB * ONE_MB; + } + /** SD CARD PARTITION */ if (!hw->hw_sdCard) { diff --git a/android/multitouch-port.c b/android/multitouch-port.c index 9a9313c..88a76fe 100644 --- a/android/multitouch-port.c +++ b/android/multitouch-port.c @@ -19,47 +19,102 @@ #include "android/hw-events.h" #include "android/charmap.h" #include "android/multitouch-screen.h" +#include "android/sdk-controller-socket.h" #include "android/multitouch-port.h" #include "android/globals.h" /* for android_hw */ +#include "android/opengles.h" #include "android/utils/misc.h" #include "android/utils/jpeg-compress.h" +#include "android/utils/debug.h" #define E(...) derror(__VA_ARGS__) #define W(...) dwarning(__VA_ARGS__) #define D(...) VERBOSE_PRINT(mtport,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(mtport) -/* Query timeout in milliseconds. */ -#define MTSP_QUERY_TIMEOUT 3000 -#define MTSP_MAX_MSG 2048 -#define MTSP_MAX_EVENT 2048 +#define TRACE_ON 1 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(mtport,__VA_ARGS__) +#else +#define T(...) +#endif + +/* Timeout (millisec) to use when communicating with SDK controller. */ +#define SDKCTL_MT_TIMEOUT 3000 + +/* + * Message types used in multi-touch emulation. + */ + +/* Pointer move message. */ +#define SDKCTL_MT_MOVE 1 +/* First pointer down message. */ +#define SDKCTL_MT_FISRT_DOWN 2 +/* Last pointer up message. */ +#define SDKCTL_MT_LAST_UP 3 +/* Pointer down message. */ +#define SDKCTL_MT_POINTER_DOWN 4 +/* Pointer up message. */ +#define SDKCTL_MT_POINTER_UP 5 +/* Sends framebuffer update. */ +#define SDKCTL_MT_FB_UPDATE 6 +/* Framebuffer update has been received. */ +#define SDKCTL_MT_FB_UPDATE_RECEIVED 7 +/* Framebuffer update has been handled. */ +#define SDKCTL_MT_FB_UPDATE_HANDLED 8 /* Multi-touch port descriptor. */ struct AndroidMTSPort { /* Caller identifier. */ - void* opaque; - /* Connected android device. */ - AndroidDevice* device; + void* opaque; + /* Communication socket. */ + SDKCtlSocket* sdkctl; /* Initialized JPEG compressor instance. */ - AJPEGDesc* jpeg_compressor; - /* Connection status: 1 connected, 0 - disconnected. */ - int is_connected; - /* Buffer where to receive multitouch messages. */ - char mts_msg[MTSP_MAX_MSG]; - /* Buffer where to receive multitouch events. */ - char events[MTSP_MAX_EVENT]; + AJPEGDesc* jpeg_compressor; + /* Direct packet descriptor for framebuffer updates. */ + SDKCtlDirectPacket* fb_packet; }; +/* Data sent with SDKCTL_MT_QUERY_START */ +typedef struct QueryDispData { + /* Width of the emulator display. */ + int width; + /* Height of the emulator display. */ + int height; +} QueryDispData; + +/* Multi-touch event structure received from SDK controller port. */ +typedef struct AndroidMTEvent { + /* Pointer identifier. */ + int pid; + /* Pointer 'x' coordinate. */ + int x; + /* Pointer 'y' coordinate. */ + int y; + /* Pointer pressure. */ + int pressure; +} AndroidMTEvent; + +/* Multi-touch pointer descriptor received from SDK controller port. */ +typedef struct AndroidMTPtr { + /* Pointer identifier. */ + int pid; +} AndroidMTPtr; + /* Destroys and frees the descriptor. */ static void _mts_port_free(AndroidMTSPort* mtsp) { if (mtsp != NULL) { + if (mtsp->fb_packet != NULL) { + sdkctl_direct_packet_release(mtsp->fb_packet); + } if (mtsp->jpeg_compressor != NULL) { jpeg_compressor_destroy(mtsp->jpeg_compressor); } - if (mtsp->device != NULL) { - android_device_destroy(mtsp->device); + if (mtsp->sdkctl != NULL) { + sdkctl_socket_release(mtsp->sdkctl); } AFREE(mtsp); } @@ -114,170 +169,186 @@ _on_action_move(int tracking_id, int x, int y, int pressure) * Multi-touch event handlers *******************************************************************************/ -/* Handles "pointer move" event. */ +/* Handles "pointer move" event. + * Param: + * param - Array of moving pointers. + * pointers_count - Number of pointers in the array. + */ static void -_on_move(const char* param) +_on_move(const AndroidMTEvent* param, int pointers_count) { - const char* pid = param; - D(">>> MOVE: %s", param); - while (pid && *pid) { - int pid_val, x, y, pressure = 0; - if (!get_token_value_int(pid, "pid", &pid_val) && - !get_token_value_int(pid, "x", &x) && - !get_token_value_int(pid, "y", &y)) { - get_token_value_int(pid, "pressure", &pressure); - _on_action_move(pid_val, x, y, pressure); - pid = strstr(pid + 1, "pid"); - } else { - break; - } + int n; + for (n = 0; n < pointers_count; n++, param++) { + T("Multi-touch: MOVE(%d): %d-> %d:%d:%d", + n, param->pid, param->x, param->y, param->pressure); + _on_action_move(param->pid, param->x, param->y, param->pressure); } } /* Handles "first pointer down" event. */ static void -_on_down(const char* param) +_on_down(const AndroidMTEvent* param) { - int pid_val, x, y, pressure = 0; - D(">>> 1-ST DOWN: %s", param); - if (!get_token_value_int(param, "pid", &pid_val) && - !get_token_value_int(param, "x", &x) && - !get_token_value_int(param, "y", &y)) { - get_token_value_int(param, "pressure", &pressure); - _on_action_down(pid_val, x, y, pressure); - } else { - W("Invalid parameters '%s' for MTS 'down' event", param); - } + T("Multi-touch: 1-ST DOWN: %d-> %d:%d:%d", + param->pid, param->x, param->y, param->pressure); + _on_action_down(param->pid, param->x, param->y, param->pressure); } /* Handles "last pointer up" event. */ static void -_on_up(const char* param) +_on_up(const AndroidMTPtr* param) { - int pid_val; - D(">>> LAST UP: %s", param); - if (!get_token_value_int(param, "pid", &pid_val)) { - _on_action_up(pid_val); - } else { - W("Invalid parameters '%s' for MTS 'up' event", param); - } + T("Multi-touch: LAST UP: %d", param->pid); + _on_action_up(param->pid); } /* Handles "next pointer down" event. */ static void -_on_pdown(const char* param) +_on_pdown(const AndroidMTEvent* param) { - int pid_val, x, y, pressure = 0; - D(">>> DOWN: %s", param); - if (!get_token_value_int(param, "pid", &pid_val) && - !get_token_value_int(param, "x", &x) && - !get_token_value_int(param, "y", &y)) { - get_token_value_int(param, "pressure", &pressure); - _on_action_pointer_down(pid_val, x, y, pressure); - } else { - W("Invalid parameters '%s' for MTS 'pointer down' event", param); - } + T("Multi-touch: DOWN: %d-> %d:%d:%d", + param->pid, param->x, param->y, param->pressure); + _on_action_pointer_down(param->pid, param->x, param->y, param->pressure); } /* Handles "next pointer up" event. */ static void -_on_pup(const char* param) +_on_pup(const AndroidMTPtr* param) { - int pid_val; - D(">>> UP: %s", param); - if (!get_token_value_int(param, "pid", &pid_val)) { - _on_action_pointer_up(pid_val); - } else { - W("Invalid parameters '%s' for MTS 'up' event", param); - } + T("Multi-touch: UP: %d", param->pid); + _on_action_pointer_up(param->pid); } /******************************************************************************** * Device communication callbacks *******************************************************************************/ -/* Main event handler. - * This routine is invoked when an event message has been received from the - * device. - */ -static void -_on_event_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize) +/* A callback that is invoked on SDK controller socket connection events. */ +static AsyncIOAction +_on_multitouch_socket_connection(void* opaque, + SDKCtlSocket* sdkctl, + AsyncIOState status) { - char* action; - int res; - AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque; - - if (errno) { - D("Multi-touch notification has failed: %s", strerror(errno)); - return; - } - - /* Dispatch the event to an appropriate handler. */ - res = get_token_value_alloc(msg, "action", &action); - if (!res) { - const char* param = strchr(msg, ' '); - if (param) { - param++; + if (status == ASIO_STATE_FAILED) { + /* Reconnect (after timeout delay) on failures */ + if (sdkctl_socket_is_handshake_ok(sdkctl)) { + sdkctl_socket_reconnect(sdkctl, SDKCTL_DEFAULT_TCP_PORT, + SDKCTL_MT_TIMEOUT); } - if (!strcmp(action, "move")) { - _on_move(param); - } else if (!strcmp(action, "down")) { - _on_down(param); - } else if (!strcmp(action, "up")) { - _on_up(param); - } else if (!strcmp(action, "pdown")) { - _on_pdown(param); - } else if (!strcmp(action, "pup")) { - _on_pup(param); - } else { - D("Unknown multi-touch event action '%s'", action); - } - free(action); } - - /* Listen to the next event. */ - android_device_listen(ad, mtsp->events, sizeof(mtsp->events), - _on_event_received); + return ASIO_ACTION_DONE; } -/* A callback that is invoked when android device is connected (i.e. both, - * command and event channels have been established). - * Param: - * opaque - AndroidMTSPort instance. - * ad - Android device used by this port. - * failure - Connections status. - */ +/* A callback that is invoked on SDK controller port connection events. */ static void -_on_device_connected(void* opaque, AndroidDevice* ad, int failure) +_on_multitouch_port_connection(void* opaque, + SDKCtlSocket* sdkctl, + SdkCtlPortStatus status) { - if (!failure) { - AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque; - mtsp->is_connected = 1; - D("Multi-touch emulation has started"); - android_device_listen(mtsp->device, mtsp->events, sizeof(mtsp->events), - _on_event_received); - mts_port_start(mtsp); + switch (status) { + case SDKCTL_PORT_CONNECTED: + D("Multi-touch: SDK Controller is connected"); + break; + + case SDKCTL_PORT_DISCONNECTED: + D("Multi-touch: SDK Controller is disconnected"); + // Disable OpenGLES framebuffer updates. + if (android_hw->hw_gpu_enabled) { + android_setPostCallback(NULL, NULL); + } + break; + + case SDKCTL_PORT_ENABLED: + D("Multi-touch: SDK Controller port is enabled."); + // Enable OpenGLES framebuffer updates. + if (android_hw->hw_gpu_enabled) { + android_setPostCallback(multitouch_opengles_fb_update, NULL); + } + /* Refresh (possibly stale) device screen. */ + multitouch_refresh_screen(); + break; + + case SDKCTL_PORT_DISABLED: + D("Multi-touch: SDK Controller port is disabled."); + // Disable OpenGLES framebuffer updates. + if (android_hw->hw_gpu_enabled) { + android_setPostCallback(NULL, NULL); + } + break; + + case SDKCTL_HANDSHAKE_CONNECTED: + D("Multi-touch: Handshake succeeded with connected port."); + break; + + case SDKCTL_HANDSHAKE_NO_PORT: + D("Multi-touch: Handshake succeeded with disconnected port."); + break; + + case SDKCTL_HANDSHAKE_DUP: + W("Multi-touch: Handshake failed due to port duplication."); + sdkctl_socket_disconnect(sdkctl); + break; + + case SDKCTL_HANDSHAKE_UNKNOWN_QUERY: + W("Multi-touch: Handshake failed due to unknown query."); + sdkctl_socket_disconnect(sdkctl); + break; + + case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE: + default: + W("Multi-touch: Handshake failed due to unknown reason."); + sdkctl_socket_disconnect(sdkctl); + break; } } -/* Invoked when an I/O failure occurs on a socket. - * Note that this callback will not be invoked on connection failures. - * Param: - * opaque - AndroidMTSPort instance. - * ad - Android device instance - * ads - Connection socket where failure has occured. - * failure - Contains 'errno' indicating the reason for failure. - */ +/* A callback that is invoked when a message is received from the device. */ static void -_on_io_failure(void* opaque, AndroidDevice* ad, int failure) +_on_multitouch_message(void* client_opaque, + SDKCtlSocket* sdkctl, + SDKCtlMessage* message, + int msg_type, + void* msg_data, + int msg_size) { - AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque; - E("Multi-touch port got disconnected: %s", strerror(failure)); - mtsp->is_connected = 0; - android_device_disconnect(ad); + switch (msg_type) { + case SDKCTL_MT_MOVE: { + assert((msg_size / sizeof(AndroidMTEvent)) && !(msg_size % sizeof(AndroidMTEvent))); + _on_move((const AndroidMTEvent*)msg_data, msg_size / sizeof(AndroidMTEvent)); + break; + } + + case SDKCTL_MT_FISRT_DOWN: + assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent))); + _on_down((const AndroidMTEvent*)msg_data); + break; + + case SDKCTL_MT_LAST_UP: + _on_up((const AndroidMTPtr*)msg_data); + break; + + case SDKCTL_MT_POINTER_DOWN: + assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent))); + _on_pdown((const AndroidMTEvent*)msg_data); + break; - /* Try to reconnect again. */ - android_device_connect_async(ad, _on_device_connected); + case SDKCTL_MT_POINTER_UP: + _on_pup((const AndroidMTPtr*)msg_data); + break; + + case SDKCTL_MT_FB_UPDATE_RECEIVED: + D("Framebuffer update ACK."); + break; + + case SDKCTL_MT_FB_UPDATE_HANDLED: + D("Framebuffer update handled."); + multitouch_fb_updated(); + break; + + default: + W("Multi-touch: Unknown message %d", msg_type); + break; + } } /******************************************************************************** @@ -288,31 +359,32 @@ AndroidMTSPort* mts_port_create(void* opaque) { AndroidMTSPort* mtsp; - int res; ANEW0(mtsp); - mtsp->opaque = opaque; - mtsp->is_connected = 0; + mtsp->opaque = opaque; /* Initialize default MTS descriptor. */ multitouch_init(mtsp); - /* Create JPEG compressor. Put "$BLOB:%09d\0" + MTFrameHeader header in front - * of the compressed data. this way we will have entire query ready to be + /* Create JPEG compressor. Put message header + MTFrameHeader in front of the + * compressed data. this way we will have entire query ready to be * transmitted to the device. */ - mtsp->jpeg_compressor = jpeg_compressor_create(16 + sizeof(MTFrameHeader), 4096); + mtsp->jpeg_compressor = + jpeg_compressor_create(sdkctl_message_get_header_size() + sizeof(MTFrameHeader), 4096); - mtsp->device = android_device_init(mtsp, AD_MULTITOUCH_PORT, _on_io_failure); - if (mtsp->device == NULL) { - _mts_port_free(mtsp); - return NULL; - } + mtsp->sdkctl = sdkctl_socket_new(SDKCTL_MT_TIMEOUT, "multi-touch", + _on_multitouch_socket_connection, + _on_multitouch_port_connection, + _on_multitouch_message, mtsp); + sdkctl_init_recycler(mtsp->sdkctl, 64, 8); - res = android_device_connect_async(mtsp->device, _on_device_connected); - if (res != 0) { - mts_port_destroy(mtsp); - return NULL; - } + /* Create a direct packet that will wrap up framebuffer updates. Note that + * we need to do this after we have initialized the recycler! */ + mtsp->fb_packet = sdkctl_direct_packet_new(mtsp->sdkctl); + + /* Now we can initiate connection witm MT port on the device. */ + sdkctl_socket_connect(mtsp->sdkctl, SDKCTL_DEFAULT_TCP_PORT, + SDKCTL_MT_TIMEOUT); return mtsp; } @@ -323,75 +395,6 @@ mts_port_destroy(AndroidMTSPort* mtsp) _mts_port_free(mtsp); } -int -mts_port_is_connected(AndroidMTSPort* mtsp) -{ - return mtsp->is_connected; -} - -int -mts_port_start(AndroidMTSPort* mtsp) -{ - char qresp[MTSP_MAX_MSG]; - char query[256]; - AndroidHwConfig* config = android_hw; - - /* Query the device to start capturing multi-touch events, also providing - * the device with width / height of the emulator's screen. This is required - * so device can properly adjust multi-touch event coordinates, and display - * emulator's framebuffer. */ - snprintf(query, sizeof(query), "start:%dx%d", - config->hw_lcd_width, config->hw_lcd_height); - int res = android_device_query(mtsp->device, query, qresp, sizeof(qresp), - MTSP_QUERY_TIMEOUT); - if (!res) { - /* By protocol device should reply with its view dimensions. */ - if (*qresp) { - int width, height; - if (sscanf(qresp, "%dx%d", &width, &height) == 2) { - multitouch_set_device_screen_size(width, height); - D("Multi-touch emulation has started. Device dims: %dx%d", - width, height); - } else { - E("Unexpected reply to MTS 'start' query: %s", qresp); - android_device_query(mtsp->device, "stop", qresp, sizeof(qresp), - MTSP_QUERY_TIMEOUT); - res = -1; - } - } else { - E("MTS protocol error: no reply to query 'start'"); - android_device_query(mtsp->device, "stop", qresp, sizeof(qresp), - MTSP_QUERY_TIMEOUT); - res = -1; - } - } else { - if (errno) { - D("Query 'start' failed on I/O: %s", strerror(errno)); - } else { - D("Query 'start' failed on device: %s", qresp); - } - } - return res; -} - -int -mts_port_stop(AndroidMTSPort* mtsp) -{ - char qresp[MTSP_MAX_MSG]; - const int res = - android_device_query(mtsp->device, "stop", qresp, sizeof(qresp), - MTSP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query 'stop' failed on I/O: %s", strerror(errno)); - } else { - D("Query 'stop' failed on device: %s", qresp); - } - } - - return res; -} - /******************************************************************************** * Handling framebuffer updates *******************************************************************************/ @@ -412,6 +415,8 @@ _fb_compress(const AndroidMTSPort* mtsp, int jpeg_quality, int ydir) { + T("Multi-touch: compressing %d bytes frame buffer", fmt->w * fmt->h * fmt->bpp); + jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w, fmt->h, fmt->disp_height, fmt->bpp, fmt->bpl, fb, jpeg_quality, ydir); @@ -421,15 +426,12 @@ int mts_port_send_frame(AndroidMTSPort* mtsp, MTFrameHeader* fmt, const uint8_t* fb, - async_send_cb cb, + on_sdkctl_direct_cb cb, void* cb_opaque, int ydir) { - char* query; - int blob_size, off; - /* Make sure that port is connected. */ - if (!mts_port_is_connected(mtsp)) { + if (!sdkctl_socket_is_port_ready(mtsp->sdkctl)) { return -1; } @@ -437,31 +439,32 @@ mts_port_send_frame(AndroidMTSPort* mtsp, fmt->format = MTFB_JPEG; _fb_compress(mtsp, fmt, fb, 10, ydir); - /* Total size of the blob: header + JPEG image. */ - blob_size = sizeof(MTFrameHeader) + - jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor); + /* Total size of the update data: header + JPEG image. */ + const int update_size = + sizeof(MTFrameHeader) + jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor); + + /* Update message starts at the beginning of the buffer allocated by the + * compressor's destination manager. */ + uint8_t* const msg = (uint8_t*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor); - /* Query starts at the beginning of the buffer allocated by the compressor's - * destination manager. */ - query = (char*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor); + /* Initialize message header. */ + sdkctl_init_message_header(msg, SDKCTL_MT_FB_UPDATE, update_size); - /* Build the $BLOB query to transfer to the device. */ - snprintf(query, jpeg_compressor_get_header_size(mtsp->jpeg_compressor), - "$BLOB:%09d", blob_size); - off = strlen(query) + 1; + /* Copy framebuffer update header to the message. */ + memcpy(msg + sdkctl_message_get_header_size(), fmt, sizeof(MTFrameHeader)); - /* Copy framebuffer update header to the query. */ - memcpy(query + off, fmt, sizeof(MTFrameHeader)); + /* Compression rate... */ + const float comp_rate = ((float)jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor) / (fmt->w * fmt->h * fmt->bpp)) * 100; /* Zeroing the rectangle in the update header we indicate that it contains * no updates. */ fmt->x = fmt->y = fmt->w = fmt->h = 0; - /* Initiate asynchronous transfer of the updated framebuffer rectangle. */ - if (android_device_send_async(mtsp->device, query, off + blob_size, 0, cb, cb_opaque)) { - D("Unable to send query '%s': %s", query, strerror(errno)); - return -1; - } + /* Send update to the device. */ + sdkctl_direct_packet_send(mtsp->fb_packet, msg, cb, cb_opaque); + + T("Multi-touch: Sent %d bytes in framebuffer update. Compression rate is %.2f%%", + update_size, comp_rate); return 0; } diff --git a/android/multitouch-port.h b/android/multitouch-port.h index d99b3fd..5652b43 100644 --- a/android/multitouch-port.h +++ b/android/multitouch-port.h @@ -17,17 +17,14 @@ #ifndef ANDROID_ANDROID_MULTITOUCH_PORT_H_ #define ANDROID_ANDROID_MULTITOUCH_PORT_H_ +#include "android/sdk-controller-socket.h" + /* * Encapsulates exchange protocol between the multi-touch screen emulator, and an * application running on an Android device that provides touch events, and is * connected to the host via USB. */ -#include "android/android-device.h" - -/* TCP port reserved for multi-touch emulation. */ -#define AD_MULTITOUCH_PORT 1969 - /* * Codes that define transmitted framebuffer format: * @@ -89,28 +86,6 @@ extern AndroidMTSPort* mts_port_create(void* opaque); /* Disconnects from the multi-touch port, and destroys the descriptor. */ extern void mts_port_destroy(AndroidMTSPort* amtp); -/* Checks if port is connected to a MT-emulating application on the device. - * Note that connection can go out and then be restored at any time after - * mts_port_create API succeeded. - */ -extern int mts_port_is_connected(AndroidMTSPort* amtp); - -/* Queries the connected application to start delivering multi-touch events. - * Param: - * amtp - Android multi-touch port instance returned from mts_port_create. - * Return: - * Zero on success, failure otherwise. - */ -extern int mts_port_start(AndroidMTSPort* amtp); - -/* Queries the connected application to stop delivering multi-touch events. - * Param: - * amtp - Android multi-touch port instance returned from mts_port_create. - * Return: - * Zero on success, failure otherwise. - */ -extern int mts_port_stop(AndroidMTSPort* amtp); - /* Sends framebuffer update to the multi-touch emulation application, running on * the android device. * Param: @@ -129,7 +104,7 @@ extern int mts_port_stop(AndroidMTSPort* amtp); extern int mts_port_send_frame(AndroidMTSPort* mtsp, MTFrameHeader* fmt, const uint8_t* fb, - async_send_cb cb, + on_sdkctl_direct_cb cb, void* cb_opaque, int ydir); diff --git a/android/multitouch-screen.c b/android/multitouch-screen.c index bd96f79..36f937d 100644 --- a/android/multitouch-screen.c +++ b/android/multitouch-screen.c @@ -29,6 +29,15 @@ #define D(...) VERBOSE_PRINT(mtscreen,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(mtscreen) +#define TRACE_ON 1 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(mtscreen,__VA_ARGS__) +#else +#define T(...) +#endif + + /* Maximum number of pointers, supported by multi-touch emulation. */ #define MTS_POINTERS_NUM 10 /* Signals that pointer is not tracked (or is "up"). */ @@ -54,10 +63,6 @@ typedef struct MTSState { AndroidMTSPort* mtsp; /* Emulator's display state. */ DisplayState* ds; - /* Screen width of the device that emulates multi-touch. */ - int device_width; - /* Screen height of the device that emulates multi-touch. */ - int device_height; /* Number of tracked pointers. */ int tracked_ptr_num; /* Index in the 'tracked_pointers' array of the last pointer for which @@ -82,7 +87,7 @@ typedef struct MTSState { } MTSState; /* Default multi-touch screen descriptor */ -static MTSState _MTSState; +static MTSState _MTSState = { 0 }; /* Pushes event to the event device. */ static void @@ -239,24 +244,27 @@ static int _is_mt_initialized = 0; /* Callback that is invoked when framebuffer update has been transmitted to the * device. */ -static void -_on_fb_sent(void* opaque, ATResult res, void* data, int size, int sent) +static AsyncIOAction +_on_fb_sent(void* opaque, SDKCtlDirectPacket* packet, AsyncIOState status) { MTSState* const mts_state = (MTSState*)opaque; - /* Lets see if we have accumulated more changes while transmission has been - * in progress. */ - if (mts_state->fb_header.w && mts_state->fb_header.h) { - /* Send accumulated updates. */ - if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header, - mts_state->current_fb, _on_fb_sent, mts_state, - mts_state->ydir)) { - mts_state->fb_transfer_in_progress = 0; + if (status == ASIO_STATE_SUCCEEDED) { + /* Lets see if we have accumulated more changes while transmission has been + * in progress. */ + if (mts_state->fb_header.w && mts_state->fb_header.h && + !mts_state->fb_transfer_in_progress) { + mts_state->fb_transfer_in_progress = 1; + /* Send accumulated updates. */ + if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header, + mts_state->current_fb, _on_fb_sent, mts_state, + mts_state->ydir)) { + mts_state->fb_transfer_in_progress = 0; + } } - } else { - /* Framebuffer transfer is completed, and no more updates are pending. */ - mts_state->fb_transfer_in_progress = 0; } + + return ASIO_ACTION_DONE; } /* Common handler for framebuffer updates invoked by both, software, and OpenGLES @@ -324,6 +332,9 @@ _mt_fb_update(void* opaque, int x, int y, int w, int h) MTSState* const mts_state = (MTSState*)opaque; const DisplaySurface* const surface = mts_state->ds->surface; + T("Multi-touch: Software renderer framebuffer update: %d:%d -> %dx%d", + x, y, w, h); + /* TODO: For sofware renderer general framebuffer properties can change on * the fly. Find a callback that can catch that. For now, just copy FB * properties over in every FB update. */ @@ -336,6 +347,7 @@ _mt_fb_update(void* opaque, int x, int y, int w, int h) _mt_fb_common_update(mts_state, x, y, w, h); } + void multitouch_opengles_fb_update(void* context, int w, int h, int ydir, @@ -349,6 +361,8 @@ multitouch_opengles_fb_update(void* context, return; } + T("Multi-touch: openGLES framebuffer update: 0:0 -> %dx%d", w, h); + /* GLES format is always RGBA8888 */ mts_state->fb_header.bpp = 4; mts_state->fb_header.bpl = 4 * w; @@ -362,6 +376,44 @@ multitouch_opengles_fb_update(void* context, } void +multitouch_refresh_screen(void) +{ + MTSState* const mts_state = &_MTSState; + + /* Make sure MT port is initialized. */ + if (!_is_mt_initialized) { + return; + } + + /* Lets see if any updates have been received so far. */ + if (NULL != mts_state->current_fb) { + _mt_fb_common_update(mts_state, 0, 0, mts_state->fb_header.disp_width, + mts_state->fb_header.disp_height); + } +} + +void +multitouch_fb_updated(void) +{ + MTSState* const mts_state = &_MTSState; + + /* This concludes framebuffer update. */ + mts_state->fb_transfer_in_progress = 0; + + /* Lets see if we have accumulated more changes while transmission has been + * in progress. */ + if (mts_state->fb_header.w && mts_state->fb_header.h) { + mts_state->fb_transfer_in_progress = 1; + /* Send accumulated updates. */ + if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header, + mts_state->current_fb, _on_fb_sent, mts_state, + mts_state->ydir)) { + mts_state->fb_transfer_in_progress = 0; + } + } +} + +void multitouch_init(AndroidMTSPort* mtsp) { if (!_is_mt_initialized) { @@ -380,8 +432,6 @@ multitouch_init(AndroidMTSPort* mtsp) for (index = 0; index < MTS_POINTERS_NUM; index++) { mts_state->tracked_pointers[index].tracking_id = MTS_POINTER_UP; } - mts_state->device_width = android_hw->hw_lcd_width; - mts_state->device_height = android_hw->hw_lcd_height; mts_state->mtsp = mtsp; mts_state->fb_header.header_size = sizeof(MTFrameHeader); mts_state->fb_transfer_in_progress = 0; @@ -453,12 +503,3 @@ multitouch_get_max_slot() { return MTS_POINTERS_NUM - 1; } - -void -multitouch_set_device_screen_size(int width, int height) -{ - MTSState* const mts_state = &_MTSState; - - mts_state->device_width = width; - mts_state->device_height = height; -} diff --git a/android/multitouch-screen.h b/android/multitouch-screen.h index 433944d..baca224 100644 --- a/android/multitouch-screen.h +++ b/android/multitouch-screen.h @@ -17,6 +17,7 @@ #ifndef ANDROID_MULTITOUCH_SCREEN_H_ #define ANDROID_MULTITOUCH_SCREEN_H_ +#include "android/sdk-controller-socket.h" #include "android/multitouch-port.h" /* @@ -61,9 +62,6 @@ extern void multitouch_update_pointer(MTESource source, /* Gets maximum slot index available for the multi-touch emulation. */ extern int multitouch_get_max_slot(); -/* Saves screen size reported by the device that emulates multi-touch. */ -extern void multitouch_set_device_screen_size(int width, int height); - /* A callback set to monitor OpenGLES framebuffer updates. * This callback is called by the renderer just before each new frame is * displayed, providing a copy of the framebuffer contents. @@ -97,4 +95,12 @@ extern void multitouch_opengles_fb_update(void* context, int type, unsigned char* pixels); +/* Pushes the entire framebuffer to the device. This will force the device to + * refresh the entire screen. + */ +extern void multitouch_refresh_screen(void); + +/* Framebuffer update has been handled by the device. */ +extern void multitouch_fb_updated(void); + #endif /* ANDROID_MULTITOUCH_SCREEN_H_ */ diff --git a/android/opengles.c b/android/opengles.c index f116f25..f56252c 100644 --- a/android/opengles.c +++ b/android/opengles.c @@ -12,20 +12,28 @@ #include "config-host.h" #include "android/opengles.h" +#include <assert.h> + +/* Declared in "android/globals.h" */ +int android_gles_fast_pipes = 1; + +#if CONFIG_ANDROID_OPENGLES + #include "android/globals.h" #include <android/utils/debug.h> #include <android/utils/path.h> #include <android/utils/bufprint.h> #include <android/utils/dll.h> + +#define RENDER_API_NO_PROTOTYPES 1 +#include <libOpenglRender/render_api.h> + #include <stdio.h> #include <stdlib.h> #define D(...) VERBOSE_PRINT(init,__VA_ARGS__) #define DD(...) VERBOSE_PRINT(gles,__VA_ARGS__) -/* Declared in "android/globals.h" */ -int android_gles_fast_pipes = 1; - /* Name of the GLES rendering library we're going to use */ #if HOST_LONG_BITS == 32 #define RENDERER_LIB_NAME "libOpenglRender" @@ -35,22 +43,16 @@ int android_gles_fast_pipes = 1; #error Unknown HOST_LONG_BITS #endif -/* These definitions *must* match those under: - * development/tools/emulator/opengl/host/include/libOpenglRender/render_api.h - */ #define DYNLINK_FUNCTIONS \ - DYNLINK_FUNC(int,initLibrary,(void),(),return) \ - DYNLINK_FUNC(int,setStreamMode,(int a),(a),return) \ - DYNLINK_FUNC(int,initOpenGLRenderer,(int width, int height, int port, OnPostFn onPost, void* onPostContext),(width,height,port,onPost,onPostContext),return) \ - DYNLINK_FUNC(int,createOpenGLSubwindow,(void* window, int x, int y, int width, int height, float zRot),(window,x,y,width,height,zRot),return)\ - DYNLINK_FUNC(int,destroyOpenGLSubwindow,(void),(),return)\ - DYNLINK_FUNC(void,repaintOpenGLDisplay,(void),(),)\ - DYNLINK_FUNC(void,stopOpenGLRenderer,(void),(),) - -#define STREAM_MODE_DEFAULT 0 -#define STREAM_MODE_TCP 1 -#define STREAM_MODE_UNIX 2 -#define STREAM_MODE_PIPE 3 + DYNLINK_FUNC(initLibrary) \ + DYNLINK_FUNC(setStreamMode) \ + DYNLINK_FUNC(initOpenGLRenderer) \ + DYNLINK_FUNC(setPostCallback) \ + DYNLINK_FUNC(getHardwareStrings) \ + DYNLINK_FUNC(createOpenGLSubwindow) \ + DYNLINK_FUNC(destroyOpenGLSubwindow) \ + DYNLINK_FUNC(repaintOpenGLDisplay) \ + DYNLINK_FUNC(stopOpenGLRenderer) #ifndef CONFIG_STANDALONE_UI /* Defined in android/hw-pipe-net.c */ @@ -59,15 +61,10 @@ extern int android_init_opengles_pipes(void); static ADynamicLibrary* rendererLib; -/* Define the pointers and the wrapper functions to call them */ -#define DYNLINK_FUNC(result,name,sig,params,ret) \ - static result (*_ptr_##name) sig; \ - static result name sig { \ - ret (*_ptr_##name) params ; \ - } - +/* Define the function pointers */ +#define DYNLINK_FUNC(name) \ + static name##Fn name = NULL; DYNLINK_FUNCTIONS - #undef DYNLINK_FUNC static int @@ -75,10 +72,11 @@ initOpenglesEmulationFuncs(ADynamicLibrary* rendererLib) { void* symbol; char* error; -#define DYNLINK_FUNC(result,name,sig,params,ret) \ - symbol = adynamicLibrary_findSymbol( rendererLib, #name, &error ); \ + +#define DYNLINK_FUNC(name) \ + symbol = adynamicLibrary_findSymbol(rendererLib, #name, &error); \ if (symbol != NULL) { \ - _ptr_##name = symbol; \ + name = symbol; \ } else { \ derror("GLES emulation: Could not find required symbol (%s): %s", #name, error); \ free(error); \ @@ -86,6 +84,7 @@ initOpenglesEmulationFuncs(ADynamicLibrary* rendererLib) } DYNLINK_FUNCTIONS #undef DYNLINK_FUNC + return 0; } @@ -126,10 +125,10 @@ android_initOpenglesEmulation(void) /* XXX: NEED Win32 pipe implementation */ setStreamMode(STREAM_MODE_TCP); #else - setStreamMode(STREAM_MODE_UNIX); + setStreamMode(STREAM_MODE_UNIX); #endif } else { - setStreamMode(STREAM_MODE_TCP); + setStreamMode(STREAM_MODE_TCP); } return 0; @@ -141,14 +140,14 @@ BAD_EXIT: } int -android_startOpenglesRenderer(int width, int height, OnPostFn onPost, void* onPostContext) +android_startOpenglesRenderer(int width, int height) { if (!rendererLib) { D("Can't start OpenGLES renderer without support libraries"); return -1; } - if (initOpenGLRenderer(width, height, ANDROID_OPENGLES_BASE_PORT, onPost, onPostContext) != 0) { + if (!initOpenGLRenderer(width, height, ANDROID_OPENGLES_BASE_PORT)) { D("Can't start OpenGLES renderer?"); return -1; } @@ -156,6 +155,70 @@ android_startOpenglesRenderer(int width, int height, OnPostFn onPost, void* onPo } void +android_setPostCallback(OnPostFunc onPost, void* onPostContext) +{ + if (rendererLib) { + setPostCallback(onPost, onPostContext); + } +} + +static void strncpy_safe(char* dst, const char* src, size_t n) +{ + strncpy(dst, src, n); + dst[n-1] = '\0'; +} + +static void extractBaseString(char* dst, const char* src, size_t dstSize) +{ + size_t len = strlen(src); + const char* begin = strchr(src, '('); + const char* end = strrchr(src, ')'); + + if (!begin || !end) { + strncpy_safe(dst, src, dstSize); + return; + } + begin += 1; + + // "foo (bar)" + // ^ ^ + // b e + // = 5 8 + // substring with NUL-terminator is end-begin+1 bytes + if (end - begin + 1 > dstSize) { + end = begin + dstSize - 1; + } + + strncpy_safe(dst, begin, end - begin + 1); +} + +void +android_getOpenglesHardwareStrings(char* vendor, size_t vendorBufSize, + char* renderer, size_t rendererBufSize, + char* version, size_t versionBufSize) +{ + const char *vendorSrc, *rendererSrc, *versionSrc; + + getHardwareStrings(&vendorSrc, &rendererSrc, &versionSrc); + if (!vendorSrc) vendorSrc = ""; + if (!rendererSrc) rendererSrc = ""; + if (!versionSrc) versionSrc = ""; + + /* Special case for the default ES to GL translators: extract the strings + * of the underlying OpenGL implementation. */ + if (strncmp(vendorSrc, "Google", 6) == 0 && + strncmp(rendererSrc, "Android Emulator OpenGL ES Translator", 37) == 0) { + extractBaseString(vendor, vendorSrc, vendorBufSize); + extractBaseString(renderer, rendererSrc, rendererBufSize); + extractBaseString(version, versionSrc, versionBufSize); + } else { + strncpy_safe(vendor, vendorSrc, vendorBufSize); + strncpy_safe(renderer, rendererSrc, rendererBufSize); + strncpy_safe(version, versionSrc, versionBufSize); + } +} + +void android_stopOpenglesRenderer(void) { if (rendererLib) { @@ -167,7 +230,8 @@ int android_showOpenglesWindow(void* window, int x, int y, int width, int height, float rotation) { if (rendererLib) { - return createOpenGLSubwindow(window, x, y, width, height, rotation); + int success = createOpenGLSubwindow((FBNativeWindowType)window, x, y, width, height, rotation); + return success ? 0 : -1; } else { return -1; } @@ -177,7 +241,8 @@ int android_hideOpenglesWindow(void) { if (rendererLib) { - return destroyOpenGLSubwindow(); + int success = destroyOpenGLSubwindow(); + return success ? 0 : -1; } else { return -1; } @@ -205,3 +270,52 @@ android_gles_unix_path(char* buff, size_t buffsize, int port) } p = bufprint(p, end, "qemu-gles-%d", port); } + +#else // CONFIG_ANDROID_OPENGLES + +int android_initOpenglesEmulation(void) +{ + return -1; +} + +int android_startOpenglesRenderer(int width, int height) +{ + return -1; +} + +void +android_setPostCallback(OnPostFunc onPost, void* onPostContext) +{ +} + +void android_getOpenglesHardwareStrings(char* vendor, size_t vendorBufSize, + char* renderer, size_t rendererBufSize, + char* version, size_t versionBufSize) +{ + assert(vendorBufSize > 0 && rendererBufSize > 0 && versionBufSize > 0); + assert(vendor != NULL && renderer != NULL && version != NULL); + vendor[0] = renderer[0] = version[0] = 0; +} + +void android_stopOpenglesRenderer(void) +{} + +int android_showOpenglesWindow(void* window, int x, int y, int width, int height, float rotation) +{ + return -1; +} + +int android_hideOpenglesWindow(void) +{ + return -1; +} + +void android_redrawOpenglesWindow(void) +{} + +void android_gles_unix_path(char* buff, size_t buffsize, int port) +{ + buff[0] = '\0'; +} + +#endif // !CONFIG_ANDROID_OPENGLES diff --git a/android/opengles.h b/android/opengles.h index 7bb6a3a..aac6249 100644 --- a/android/opengles.h +++ b/android/opengles.h @@ -16,10 +16,6 @@ #define ANDROID_OPENGLES_BASE_PORT 22468 -/* See the description in render_api.h. */ -typedef void (*OnPostFn)(void* context, int width, int height, int ydir, - int format, int type, unsigned char* pixels); - /* Call this function to initialize the hardware opengles emulation. * This function will abort if we can't find the corresponding host * libraries through dlopen() or equivalent. @@ -30,8 +26,24 @@ int android_initOpenglesEmulation(void); * At the moment, this must be done before the VM starts. The onPost callback * may be NULL. */ -int android_startOpenglesRenderer(int width, int height, - OnPostFn onPost, void* onPostContext); +int android_startOpenglesRenderer(int width, int height); + +/* See the description in render_api.h. */ +typedef void (*OnPostFunc)(void* context, int width, int height, int ydir, + int format, int type, unsigned char* pixels); +void android_setPostCallback(OnPostFunc onPost, void* onPostContext); + +/* Retrieve the Vendor/Renderer/Version strings describing the underlying GL + * implementation. The call only works while the renderer is started. + * + * Each string is copied into the corresponding buffer. If the original string + * (including NUL terminator) is more than xxBufSize bytes, it will be + * truncated. In all cases, including failure, the buffer will be NUL- + * terminated when this function returns. + */ +void android_getOpenglesHardwareStrings(char* vendor, size_t vendorBufSize, + char* renderer, size_t rendererBufSize, + char* version, size_t versionBufSize); int android_showOpenglesWindow(void* window, int x, int y, int width, int height, float rotation); diff --git a/android/qemu-setup.c b/android/qemu-setup.c index 181c95b..8b8f0b0 100644 --- a/android/qemu-setup.c +++ b/android/qemu-setup.c @@ -47,6 +47,11 @@ char* op_http_proxy = NULL; /* Base port for the emulated system. */ int android_base_port; +/* Strings describing the host system's OpenGL implementation */ +char android_gl_vendor[ANDROID_GLSTRING_BUF_SIZE]; +char android_gl_renderer[ANDROID_GLSTRING_BUF_SIZE]; +char android_gl_version[ANDROID_GLSTRING_BUF_SIZE]; + /*** APPLICATION DIRECTORY *** Where are we ? ***/ @@ -483,6 +488,14 @@ void android_emulation_setup( void ) char tmp[PATH_MAX]; const char* appdir = get_app_dir(); + const size_t ARGSLEN = + PATH_MAX + // max ping program path + 10 + // max VERSION_STRING length + 3*ANDROID_GLSTRING_BUF_SIZE + // max GL string lengths + 29 + // static args characters + 1; // NUL terminator + char args[ARGSLEN]; + if (snprintf( tmp, PATH_MAX, "%s%s%s", appdir, PATH_SEP, _ANDROID_PING_PROGRAM ) >= PATH_MAX) { dprint( "Application directory too long: %s", appdir); @@ -507,10 +520,12 @@ void android_emulation_setup( void ) if (!comspec) comspec = "cmd.exe"; // Run - char args[PATH_MAX + 30]; - if (snprintf( args, PATH_MAX, "/C \"%s\" ping emulator " VERSION_STRING, - tmp) >= PATH_MAX ) { - D( "DDMS path too long: %s", tmp); + if (snprintf(args, ARGSLEN, + "/C \"%s\" ping emulator " VERSION_STRING " \"%s\" \"%s\" \"%s\"", + tmp, android_gl_vendor, android_gl_renderer, android_gl_version) + >= ARGSLEN) + { + D( "DDMS command line too long: %s", args); return; } @@ -540,13 +555,17 @@ void android_emulation_setup( void ) int fd = open("/dev/null", O_WRONLY); dup2(fd, 1); dup2(fd, 2); - execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL ); + execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, + android_gl_vendor, android_gl_renderer, android_gl_version, + NULL ); } END_NOSIGALRM /* don't do anything in the parent or in case of error */ - strncat( tmp, " ping emulator " VERSION_STRING, PATH_MAX - strlen(tmp) ); - D( "ping command: %s", tmp ); + snprintf(args, ARGSLEN, + "%s ping emulator " VERSION_STRING " \"%s\" \"%s\" \"%s\"", + tmp, android_gl_vendor, android_gl_renderer, android_gl_version); + D( "ping command: %s", args ); #endif } } diff --git a/android/sdk-controller-socket.c b/android/sdk-controller-socket.c index a300169..8b0d813 100644 --- a/android/sdk-controller-socket.c +++ b/android/sdk-controller-socket.c @@ -20,8 +20,6 @@ * a TCP port forwarding, enabled by ADB. */ -#include "qemu-common.h" -#include "android/async-utils.h" #include "android/utils/debug.h" #include "android/async-socket-connector.h" #include "android/async-socket.h" @@ -34,7 +32,7 @@ #define D(...) VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(sdkctlsocket) -#define TRACE_ON 1 +#define TRACE_ON 0 #if TRACE_ON #define T(...) VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__) @@ -53,12 +51,8 @@ struct SDKCtlRecycled { }; }; -/******************************************************************************** - * SDKCtlPacket declarations - *******************************************************************************/ - /* - * Types of the packets of data sent via SDK controller socket. + * Types of the data packets sent via SDK controller socket. */ /* The packet is a message. */ @@ -68,12 +62,56 @@ struct SDKCtlRecycled { /* The packet is a response to a query. */ #define SDKCTL_PACKET_QUERY_RESPONSE 3 +/* + * Types of intenal port messages sent via SDK controller socket. + */ + +/* Port is connected. + * This message is sent by SDK controller when the service connects a socket with + * a port that provides requested emulation functionality. + */ +#define SDKCTL_MSG_PORT_CONNECTED -1 +/* Port is disconnected. + * This message is sent by SDK controller when a port that provides requested + * emulation functionality disconnects from the socket. + */ +#define SDKCTL_MSG_PORT_DISCONNECTED -2 +/* Port is enabled. + * This message is sent by SDK controller when a port that provides requested + * emulation functionality is ready to do the emulation. + */ +#define SDKCTL_MSG_PORT_ENABLED -3 +/* Port is disabled. + * This message is sent by SDK controller when a port that provides requested + * emulation functionality is not ready to do the emulation. + */ +#define SDKCTL_MSG_PORT_DISABLED -4 + +/* + * Types of internal queries sent via SDK controller socket. + */ + +/* Handshake query. + * This query is sent to SDK controller service as part of the connection + * protocol implementation. + */ +#define SDKCTL_QUERY_HANDSHAKE -1 + +/******************************************************************************** + * SDKCtlPacket declarations + *******************************************************************************/ + +/* Packet signature value ('SDKC'). */ +static const int _sdkctl_packet_sig = 0x53444B43; + /* Data packet descriptor. * * All packets, sent and received via SDK controller socket begin with this * header, with packet data immediately following this header. */ typedef struct SDKCtlPacketHeader { + /* Signature. */ + int signature; /* Total size of the data to transfer with this packet, including this * header. The transferring data should immediatelly follow this header. */ int size; @@ -83,42 +121,60 @@ typedef struct SDKCtlPacketHeader { } SDKCtlPacketHeader; /* Packet descriptor, allocated by this API for data packets to be sent to SDK - * controller service on the device. + * controller. * * When packet descriptors are allocated by this API, they are allocated large * enough to contain this header, and packet data to send to the service, * immediately following this descriptor. */ -struct SDKCtlPacket { +typedef struct SDKCtlPacket { /* Supports recycling. Don't put anything in front: recycler expects this * to be the first field in recyclable descriptor. */ - SDKCtlRecycled recycling; + SDKCtlRecycled recycling; - /* Next packet in the list of packets to send. */ - SDKCtlPacket* next; /* SDK controller socket that transmits this packet. */ - SDKCtlSocket* sdkctl; + SDKCtlSocket* sdkctl; /* Number of outstanding references to the packet. */ - int ref_count; + int ref_count; /* Common packet header. Packet data immediately follows this header, so it - * must be last field in SDKCtlPacket descriptor. */ - SDKCtlPacketHeader header; -}; + * must be the last field in SDKCtlPacket descriptor. */ + SDKCtlPacketHeader header; +} SDKCtlPacket; /******************************************************************************** - * SDKCtlQuery declarations + * SDKCtlDirectPacket declarations *******************************************************************************/ -/* - * Types of queries sent via SDK controller socket. +/* Direct packet descriptor, allocated by this API for direct data packets to be + * sent to SDK controller service on the device. + * + * Direct packet (unlike SDKCtlPacket) don't contain data buffer, but rather + * reference data allocated by the client. This is useful when client sends large + * amount of data (such as framebuffer updates sent my multi-touch port), and + * regular packet descriptors for such large transfer cannot be obtained from the + * recycler. */ +struct SDKCtlDirectPacket { + /* Supports recycling. Don't put anything in front: recycler expects this + * to be the first field in recyclable descriptor. */ + SDKCtlRecycled recycling; -/* Handshake query. - * This query is sent to SDK controller service as part of the connection - * protocol implementation. - */ -#define SDKCTL_QUERY_HANDSHAKE -1 + /* SDKCtlSocket that owns this packet. */ + SDKCtlSocket* sdkctl; + /* Packet to send. */ + SDKCtlPacketHeader* packet; + /* Callback to invoke on packet transmission events. */ + on_sdkctl_direct_cb on_sent; + /* An opaque pointer to pass to on_sent callback. */ + void* on_sent_opaque; + /* Number of outstanding references to the packet. */ + int ref_count; +}; + +/******************************************************************************** + * SDKCtlQuery declarations + *******************************************************************************/ /* Query packet descriptor. * @@ -131,8 +187,7 @@ typedef struct SDKCtlQueryHeader { /* A unique query identifier. This ID is used to track the query in the * asynchronous environment in whcih SDK controller socket operates. */ int query_id; - /* Query type. See SDKCTL_QUERY_XXX for the list of query types used by SDK - * controller. */ + /* Query type. */ int query_type; } SDKCtlQueryHeader; @@ -148,12 +203,12 @@ struct SDKCtlQuery { * to be the first field in recyclable descriptor. */ SDKCtlRecycled recycling; - /* Next query in the list of active, or recycled queries. */ + /* Next query in the list of active queries. */ SDKCtlQuery* next; /* A timer to run time out on this query after it has been sent. */ LoopTimer timer[1]; /* Absolute time for this query's deadline. This is the value that query's - * timer is set for after query has been transmitted to the service. */ + * timer is set to after query has been transmitted to the service. */ Duration deadline; /* SDK controller socket that owns the query. */ SDKCtlSocket* sdkctl; @@ -163,20 +218,21 @@ struct SDKCtlQuery { void* query_opaque; /* Points to an address of a buffer where to save query response. */ void** response_buffer; - /* Points to a variable containing size of the response buffer (on the way in), - * or actual query response size (when query is completed). */ + /* Points to a variable containing size of the response buffer (on the way + * in), or actual query response size (when query is completed). */ uint32_t* response_size; /* Internal response buffer, allocated if query creator didn't provide its * own. This field is valid only if response_buffer field is NULL, or is * pointing to this field. */ void* internal_resp_buffer; - /* Internal response buffer size This field is valid only if response_size - * field is NULL, or is pointing to this field. */ + /* Internal response buffer size used if query creator didn't provide its + * own. This field is valid only if response_size field is NULL, or is + * pointing to this field. */ uint32_t internal_resp_size; /* Number of outstanding references to the query. */ int ref_count; - /* Common packet header. Query data immediately follows this header, so it + /* Common query header. Query data immediately follows this header, so it * must be last field in SDKCtlQuery descriptor. */ SDKCtlQueryHeader header; }; @@ -195,6 +251,34 @@ typedef struct SDKCtlQueryReplyHeader { } SDKCtlQueryReplyHeader; /******************************************************************************** + * SDKCtlMessage declarations + *******************************************************************************/ + +/* Message packet descriptor. + * + * All messages, sent and received via SDK controller socket begin with this + * header, with message data immediately following this header. + */ +typedef struct SDKCtlMessageHeader { + /* Data packet header for this query. */ + SDKCtlPacketHeader packet; + /* Message type. */ + int msg_type; +} SDKCtlMessageHeader; + +/* Message packet descriptor. + * + * All messages, sent and received via SDK controller socket begin with this + * header, with message data immediately following this header. + */ +struct SDKCtlMessage { + /* Data packet descriptor for this message. */ + SDKCtlPacket packet; + /* Message type. */ + int msg_type; +}; + +/******************************************************************************** * SDK Control Socket declarations *******************************************************************************/ @@ -229,13 +313,15 @@ typedef struct SDKCtlIODispatcher { /* Unites all types of headers used in SDK controller data exchange. */ union { /* Common packet header. */ - SDKCtlPacketHeader header; + SDKCtlPacketHeader packet_header; /* Header for a query packet. */ SDKCtlQueryHeader query_header; + /* Header for a message packet. */ + SDKCtlMessageHeader message_header; /* Header for a query response packet. */ SDKCtlQueryReplyHeader query_reply_header; }; - /* Descriptor of a packet packet received from SDK controller. */ + /* Descriptor of a packet that is being received from SDK controller. */ SDKCtlPacket* packet; /* A query for which a reply is currently being received. */ SDKCtlQuery* current_query; @@ -244,42 +330,43 @@ typedef struct SDKCtlIODispatcher { /* SDK controller socket descriptor. */ struct SDKCtlSocket { /* SDK controller socket state */ - SDKCtlSocketState state; + SDKCtlSocketState state; + /* SDK controller port status */ + SdkCtlPortStatus port_status; /* I/O dispatcher for the socket. */ - SDKCtlIODispatcher io_dispatcher; + SDKCtlIODispatcher io_dispatcher; /* Asynchronous socket connected to SDK Controller on the device. */ - AsyncSocket* as; + AsyncSocket* as; /* Client callback that monitors this socket connection. */ - on_sdkctl_connection_cb on_connection; - /* A callback to invoke when handshake message is received from the - * SDK controller. */ - on_sdkctl_handshake_cb on_handshake; + on_sdkctl_socket_connection_cb on_socket_connection; + /* Client callback that monitors SDK controller prt connection. */ + on_sdkctl_port_connection_cb on_port_connection; /* A callback to invoke when a message is received from the SDK controller. */ - on_sdkctl_message_cb on_message; + on_sdkctl_message_cb on_message; /* An opaque pointer associated with this socket. */ - void* opaque; - /* Name of an SDK controller service this socket is connected to. */ - char* service_name; + void* opaque; + /* Name of an SDK controller port this socket is connected to. */ + char* service_name; /* I/O looper for timers. */ - Looper* looper; + Looper* looper; /* Head of the active query list. */ - SDKCtlQuery* query_head; + SDKCtlQuery* query_head; /* Tail of the active query list. */ - SDKCtlQuery* query_tail; + SDKCtlQuery* query_tail; /* Query ID generator that gets incremented for each new query. */ - int next_query_id; + int next_query_id; /* Timeout before trying to reconnect after disconnection. */ - int reconnect_to; + int reconnect_to; /* Number of outstanding references to this descriptor. */ - int ref_count; + int ref_count; /* Head of the recycled memory */ - SDKCtlRecycled* recycler; + SDKCtlRecycled* recycler; /* Recyclable block size. */ - uint32_t recycler_block_size; + uint32_t recycler_block_size; /* Maximum number of blocks to recycle. */ - int recycler_max; + int recycler_max; /* Number of blocs in the recycler. */ - int recycler_count; + int recycler_count; }; /******************************************************************************** @@ -294,16 +381,18 @@ _sdkctl_socket_alloc_recycler(SDKCtlSocket* sdkctl, uint32_t size) SDKCtlRecycled* block = NULL; if (sdkctl->recycler != NULL && size <= sdkctl->recycler_block_size) { + assert(sdkctl->recycler_count > 0); /* There are blocks in the recycler, and requested size fits. */ block = sdkctl->recycler; sdkctl->recycler = block->next; block->size = sdkctl->recycler_block_size; sdkctl->recycler_count--; } else if (size <= sdkctl->recycler_block_size) { - /* There are no blocks in the recycler, but requested size fits. */ + /* There are no blocks in the recycler, but requested size fits. Lets + * allocate block that we can later recycle. */ block = malloc(sdkctl->recycler_block_size); if (block == NULL) { - APANIC("SDKCtl %s: Unable to allocate %d bytes block", + APANIC("SDKCtl %s: Unable to allocate %d bytes block.", sdkctl->service_name, sdkctl->recycler_block_size); } block->size = sdkctl->recycler_block_size; @@ -324,18 +413,19 @@ _sdkctl_socket_alloc_recycler(SDKCtlSocket* sdkctl, uint32_t size) static void _sdkctl_socket_free_recycler(SDKCtlSocket* sdkctl, void* mem) { - SDKCtlRecycled* block = (SDKCtlRecycled*)mem; + SDKCtlRecycled* const block = (SDKCtlRecycled*)mem; - if (sdkctl->recycler_count == sdkctl->recycler_max || - block->size != sdkctl->recycler_block_size) { - /* Recycler is full, or block cannot be recycled. */ + if (block->size != sdkctl->recycler_block_size || + sdkctl->recycler_count == sdkctl->recycler_max) { + /* Recycler is full, or block cannot be recycled. Just free the memory. */ free(mem); - return; + } else { + /* Add that block to the recycler. */ + assert(sdkctl->recycler_count >= 0); + block->next = sdkctl->recycler; + sdkctl->recycler = block; + sdkctl->recycler_count++; } - - block->next = sdkctl->recycler; - sdkctl->recycler = block; - sdkctl->recycler_count++; } /* Empties the recycler for a given SDKCtlSocket. */ @@ -344,7 +434,7 @@ _sdkctl_socket_empty_recycler(SDKCtlSocket* sdkctl) { SDKCtlRecycled* block = sdkctl->recycler; while (block != NULL) { - void* to_free = block; + void* const to_free = block; block = block->next; free(to_free); } @@ -366,6 +456,7 @@ _sdkctl_socket_add_query(SDKCtlQuery* query) { SDKCtlSocket* const sdkctl = query->sdkctl; if (sdkctl->query_head == NULL) { + assert(sdkctl->query_tail == NULL); sdkctl->query_head = sdkctl->query_tail = query; } else { sdkctl->query_tail->next = query; @@ -378,7 +469,9 @@ _sdkctl_socket_add_query(SDKCtlQuery* query) /* Removes a query from the list of active queries. * Param: - * query - Query to remove from the list of active queries. + * query - Query to remove from the list of active queries. If query has been + * removed from the list, it will be dereferenced to offset the reference + * that wad made when the query has been added to the list. * Return: * Boolean: 1 if query has been removed, or 0 if query has not been found in the * list of active queries. @@ -390,11 +483,11 @@ _sdkctl_socket_remove_query(SDKCtlQuery* query) SDKCtlQuery* prev = NULL; SDKCtlQuery* head = sdkctl->query_head; - /* Quick check: the query could be currently handled by dispatcher. */ + /* Quick check: the query could be currently handled by the dispatcher. */ if (sdkctl->io_dispatcher.current_query == query) { /* Release the query from dispatcher. */ - sdkctl_query_release(query); sdkctl->io_dispatcher.current_query = NULL; + sdkctl_query_release(query); return 1; } @@ -403,32 +496,34 @@ _sdkctl_socket_remove_query(SDKCtlQuery* query) prev = head; head = head->next; } - if (head != NULL) { - if (prev == NULL) { - /* Query is at the head of the list. */ - assert(query == sdkctl->query_head); - sdkctl->query_head = query->next; - } else { - /* Query is in the middle / at the end of the list. */ - assert(query != sdkctl->query_head); - prev->next = query->next; - } - if (sdkctl->query_tail == query) { - /* Query is at the tail of the list. */ - assert(query->next == NULL); - sdkctl->query_tail = prev; - } - query->next = NULL; + if (head == NULL) { + D("SDKCtl %s: Query %p is not found in the list.", + sdkctl->service_name, query); + return 0; + } - /* Release query that is now removed from the list. Note that query - * passed to this routine should hold an extra reference, owned by the - * caller. */ - sdkctl_query_release(query); - return 1; + if (prev == NULL) { + /* Query is at the head of the list. */ + assert(query == sdkctl->query_head); + sdkctl->query_head = query->next; } else { - D("%s: Query %p is not found in the list.", sdkctl->service_name, query); - return 0; + /* Query is in the middle / at the end of the list. */ + assert(query != sdkctl->query_head); + prev->next = query->next; + } + if (sdkctl->query_tail == query) { + /* Query is at the tail of the list. */ + assert(query->next == NULL); + sdkctl->query_tail = prev; } + query->next = NULL; + + /* Release query that is now removed from the list. Note that query + * passed to this routine should hold an extra reference, owned by the + * caller. */ + sdkctl_query_release(query); + + return 1; } /* Removes a query (based on query ID) from the list of active queries. @@ -437,11 +532,14 @@ _sdkctl_socket_remove_query(SDKCtlQuery* query) * query_id - Identifies the query to remove. * Return: * A query removed from the list of active queries, or NULL if query with the - * given ID has not been found in the list. + * given ID has not been found in the list. Note that query returned from this + * routine still holds the reference made when the query has been added to the + * list. */ static SDKCtlQuery* _sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id) { + SDKCtlQuery* query = NULL; SDKCtlQuery* prev = NULL; SDKCtlQuery* head = sdkctl->query_head; @@ -449,7 +547,7 @@ _sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id) if (sdkctl->io_dispatcher.current_query != NULL && sdkctl->io_dispatcher.current_query->header.query_id == query_id) { /* Release the query from dispatcher. */ - SDKCtlQuery* const query = sdkctl->io_dispatcher.current_query; + query = sdkctl->io_dispatcher.current_query; sdkctl->io_dispatcher.current_query = NULL; return query; } @@ -459,37 +557,38 @@ _sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id) prev = head; head = head->next; } - if (head != NULL) { - /* Query is found in the list. */ - SDKCtlQuery* const query = head; - if (prev == NULL) { - /* Query is at the head of the list. */ - assert(query == sdkctl->query_head); - sdkctl->query_head = query->next; - } else { - /* Query is in the middle, or at the end of the list. */ - assert(query != sdkctl->query_head); - prev->next = query->next; - } - if (sdkctl->query_tail == query) { - /* Query is at the tail of the list. */ - assert(query->next == NULL); - sdkctl->query_tail = prev; - } - query->next = NULL; - return query; - } else { - D("%s: Query ID %d is not found in the list.", + if (head == NULL) { + D("SDKCtl %s: Query ID %d is not found in the list.", sdkctl->service_name, query_id); return NULL; } + + /* Query is found in the list. */ + query = head; + if (prev == NULL) { + /* Query is at the head of the list. */ + assert(query == sdkctl->query_head); + sdkctl->query_head = query->next; + } else { + /* Query is in the middle, or at the end of the list. */ + assert(query != sdkctl->query_head); + prev->next = query->next; + } + if (sdkctl->query_tail == query) { + /* Query is at the tail of the list. */ + assert(query->next == NULL); + sdkctl->query_tail = prev; + } + query->next = NULL; + + return query; } /* Pulls the first query from the list of active queries. * Param: * sdkctl - SDKCtlSocket instance that owns the query. * Return: - * A query removed pulled from the list of active queries, or NULL if query + * A query removed from the head of the list of active queries, or NULL if query * list is empty. */ static SDKCtlQuery* @@ -519,19 +618,24 @@ _sdkctl_socket_next_query_id(SDKCtlSocket* sdkctl) /* Alocates a packet. */ static SDKCtlPacket* -_sdkctl_packet_new(SDKCtlSocket* sdkctl, int size, int type) +_sdkctl_packet_new(SDKCtlSocket* sdkctl, uint32_t size, int type) { - const uint32_t total_size = sizeof(SDKCtlPacket) + size; - SDKCtlPacket* const packet = _sdkctl_socket_alloc_recycler(sdkctl, total_size); + /* Allocate packet descriptor large enough to contain packet data. */ + SDKCtlPacket* const packet = + _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlPacket) + size); - packet->sdkctl = sdkctl; - packet->ref_count = 1; - packet->header.size = size; - packet->header.type = type; + packet->sdkctl = sdkctl; + packet->ref_count = 1; + packet->header.signature = _sdkctl_packet_sig; + packet->header.size = size; + packet->header.type = type; /* Refence SDKCTlSocket that owns this packet. */ sdkctl_socket_reference(sdkctl); + T("SDKCtl %s: Packet %p of type %d is allocated for %d bytes transfer.", + sdkctl->service_name, packet, type, size); + return packet; } @@ -541,23 +645,27 @@ _sdkctl_packet_free(SDKCtlPacket* packet) { SDKCtlSocket* const sdkctl = packet->sdkctl; - /* Free allocated resources. */ + /* Recycle packet. */ _sdkctl_socket_free_recycler(packet->sdkctl, packet); + T("SDKCtl %s: Packet %p is freed.", sdkctl->service_name, packet); + /* Release SDKCTlSocket that owned this packet. */ sdkctl_socket_release(sdkctl); } +/* References a packet. */ int -sdkctl_packet_reference(SDKCtlPacket* packet) +_sdkctl_packet_reference(SDKCtlPacket* packet) { assert(packet->ref_count > 0); packet->ref_count++; return packet->ref_count; } +/* Releases a packet. */ int -sdkctl_packet_release(SDKCtlPacket* packet) +_sdkctl_packet_release(SDKCtlPacket* packet) { assert(packet->ref_count > 0); packet->ref_count--; @@ -569,6 +677,272 @@ sdkctl_packet_release(SDKCtlPacket* packet) return packet->ref_count; } +/* An I/O callback invoked on packet transmission. + * Param: + * io_opaque SDKCtlPacket instance of the packet that's being sent with this I/O. + * asio - Write I/O descriptor. + * status - I/O status. + */ +static AsyncIOAction +_on_sdkctl_packet_send_io(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status) +{ + SDKCtlPacket* const packet = (SDKCtlPacket*)io_opaque; + AsyncIOAction action = ASIO_ACTION_DONE; + + /* Reference the packet while we're in this callback. */ + _sdkctl_packet_reference(packet); + + /* Lets see what's going on with query transmission. */ + switch (status) { + case ASIO_STATE_SUCCEEDED: + /* Packet has been sent to the service. */ + T("SDKCtl %s: Packet %p transmission has succeeded.", + packet->sdkctl->service_name, packet); + break; + + case ASIO_STATE_CANCELLED: + T("SDKCtl %s: Packet %p is cancelled.", + packet->sdkctl->service_name, packet); + break; + + case ASIO_STATE_FAILED: + T("SDKCtl %s: Packet %p has failed: %d -> %s", + packet->sdkctl->service_name, packet, errno, strerror(errno)); + break; + + case ASIO_STATE_FINISHED: + /* Time to disassociate the packet with the I/O. */ + _sdkctl_packet_release(packet); + break; + + default: + /* Transitional state. */ + break; + } + + _sdkctl_packet_release(packet); + + return action; +} + +/* Transmits a packet to SDK Controller. + * Param: + * packet - Packet to transmit. + */ +static void +_sdkctl_packet_transmit(SDKCtlPacket* packet) +{ + assert(packet->header.signature == _sdkctl_packet_sig); + + /* Reference to associate with the I/O */ + _sdkctl_packet_reference(packet); + + /* Transmit the packet to SDK controller. */ + async_socket_write_rel(packet->sdkctl->as, &packet->header, packet->header.size, + _on_sdkctl_packet_send_io, packet, -1); + + T("SDKCtl %s: Packet %p size %d is being sent.", + packet->sdkctl->service_name, packet, packet->header.size); +} + +/******************************************************************************** + * SDKCtlDirectPacket implementation + ********************************************************************************/ + +SDKCtlDirectPacket* +sdkctl_direct_packet_new(SDKCtlSocket* sdkctl) +{ + SDKCtlDirectPacket* const packet = + _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlDirectPacket)); + + packet->sdkctl = sdkctl; + packet->ref_count = 1; + + /* Refence SDKCTlSocket that owns this packet. */ + sdkctl_socket_reference(packet->sdkctl); + + T("SDKCtl %s: Direct packet %p is allocated.", sdkctl->service_name, packet); + + return packet; +} + +/* Frees a direct packet. */ +static void +_sdkctl_direct_packet_free(SDKCtlDirectPacket* packet) +{ + SDKCtlSocket* const sdkctl = packet->sdkctl; + + /* Free allocated resources. */ + _sdkctl_socket_free_recycler(packet->sdkctl, packet); + + T("SDKCtl %s: Direct packet %p is freed.", sdkctl->service_name, packet); + + /* Release SDKCTlSocket that owned this packet. */ + sdkctl_socket_release(sdkctl); +} + +/* References a packet. */ +int +sdkctl_direct_packet_reference(SDKCtlDirectPacket* packet) +{ + assert(packet->ref_count > 0); + packet->ref_count++; + return packet->ref_count; +} + +/* Releases a packet. */ +int +sdkctl_direct_packet_release(SDKCtlDirectPacket* packet) +{ + assert(packet->ref_count > 0); + packet->ref_count--; + if (packet->ref_count == 0) { + /* Last reference has been dropped. Destroy this object. */ + _sdkctl_direct_packet_free(packet); + return 0; + } + return packet->ref_count; +} + +/* An I/O callback invoked on direct packet transmission. + * Param: + * io_opaque SDKCtlDirectPacket instance of the packet that's being sent with + * this I/O. + * asio - Write I/O descriptor. + * status - I/O status. + */ +static AsyncIOAction +_on_sdkctl_direct_packet_send_io(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status) +{ + SDKCtlDirectPacket* const packet = (SDKCtlDirectPacket*)io_opaque; + AsyncIOAction action = ASIO_ACTION_DONE; + + /* Reference the packet while we're in this callback. */ + sdkctl_direct_packet_reference(packet); + + /* Lets see what's going on with query transmission. */ + switch (status) { + case ASIO_STATE_SUCCEEDED: + /* Packet has been sent to the service. */ + T("SDKCtl %s: Direct packet %p transmission has succeeded.", + packet->sdkctl->service_name, packet); + packet->on_sent(packet->on_sent_opaque, packet, status); + break; + + case ASIO_STATE_CANCELLED: + T("SDKCtl %s: Direct packet %p is cancelled.", + packet->sdkctl->service_name, packet); + packet->on_sent(packet->on_sent_opaque, packet, status); + break; + + case ASIO_STATE_FAILED: + T("SDKCtl %s: Direct packet %p has failed: %d -> %s", + packet->sdkctl->service_name, packet, errno, strerror(errno)); + packet->on_sent(packet->on_sent_opaque, packet, status); + break; + + case ASIO_STATE_FINISHED: + /* Time to disassociate with the I/O. */ + sdkctl_direct_packet_release(packet); + break; + + default: + /* Transitional state. */ + break; + } + + sdkctl_direct_packet_release(packet); + + return action; +} + +void +sdkctl_direct_packet_send(SDKCtlDirectPacket* packet, + void* data, + on_sdkctl_direct_cb cb, + void* cb_opaque) +{ + packet->packet = (SDKCtlPacketHeader*)data; + packet->on_sent = cb; + packet->on_sent_opaque = cb_opaque; + assert(packet->packet->signature == _sdkctl_packet_sig); + + /* Reference for I/O */ + sdkctl_direct_packet_reference(packet); + + /* Transmit the packet to SDK controller. */ + async_socket_write_rel(packet->sdkctl->as, packet->packet, packet->packet->size, + _on_sdkctl_direct_packet_send_io, packet, -1); + + T("SDKCtl %s: Direct packet %p size %d is being sent", + packet->sdkctl->service_name, packet, packet->packet->size); +} + +/******************************************************************************** + * SDKCtlMessage implementation + *******************************************************************************/ + +/* Alocates a message descriptor. */ +static SDKCtlMessage* +_sdkctl_message_new(SDKCtlSocket* sdkctl, uint32_t msg_size, int msg_type) +{ + SDKCtlMessage* const msg = + (SDKCtlMessage*)_sdkctl_packet_new(sdkctl, + sizeof(SDKCtlMessageHeader) + msg_size, + SDKCTL_PACKET_MESSAGE); + msg->msg_type = msg_type; + + return msg; +} + +int +sdkctl_message_reference(SDKCtlMessage* msg) +{ + return _sdkctl_packet_reference(&msg->packet); +} + +int +sdkctl_message_release(SDKCtlMessage* msg) +{ + return _sdkctl_packet_release(&msg->packet); +} + +SDKCtlMessage* +sdkctl_message_send(SDKCtlSocket* sdkctl, + int msg_type, + const void* data, + uint32_t size) +{ + SDKCtlMessage* const msg = _sdkctl_message_new(sdkctl, size, msg_type); + if (size != 0 && data != NULL) { + memcpy(msg + 1, data, size); + } + _sdkctl_packet_transmit(&msg->packet); + + return msg; +} + +int +sdkctl_message_get_header_size(void) +{ + return sizeof(SDKCtlMessageHeader); +} + +void +sdkctl_init_message_header(void* msg, int msg_type, int msg_size) +{ + SDKCtlMessageHeader* const msg_header = (SDKCtlMessageHeader*)msg; + + msg_header->packet.signature = _sdkctl_packet_sig; + msg_header->packet.size = sizeof(SDKCtlMessageHeader) + msg_size; + msg_header->packet.type = SDKCTL_PACKET_MESSAGE; + msg_header->msg_type = msg_type; +} + /******************************************************************************** * SDKCtlQuery implementation *******************************************************************************/ @@ -579,19 +953,21 @@ _sdkctl_query_free(SDKCtlQuery* query) { if (query != NULL) { SDKCtlSocket* const sdkctl = query->sdkctl; - T("SDKCtl %s: Query %p ID %d is freed.", - sdkctl->service_name, query, query->header.query_id); - - /* Free allocated resources. */ if (query->internal_resp_buffer != NULL && (query->response_buffer == NULL || query->response_buffer == &query->internal_resp_buffer)) { + /* This query used its internal buffer to receive the response. + * Free it. */ free(query->internal_resp_buffer); } loopTimer_done(query->timer); + + /* Recyle the descriptor. */ _sdkctl_socket_free_recycler(sdkctl, query); + T("SDKCtl %s: Query %p is freed.", sdkctl->service_name, query); + /* Release socket that owned this query. */ sdkctl_socket_release(sdkctl); } @@ -609,8 +985,8 @@ _sdkctl_query_cancel_timeout(SDKCtlQuery* query) { loopTimer_stop(query->timer); - T("SDKCtl %s: Query %p ID %d deadline is cancelled.", - query->sdkctl->service_name, query, query->header.query_id); + T("SDKCtl %s: Query %p ID %d deadline %lld is cancelled.", + query->sdkctl->service_name, query, query->header.query_id, query->deadline); } /* @@ -639,7 +1015,7 @@ _on_sdkctl_query_cancelled(SDKCtlQuery* query) * Query cancellation means that SDK controller is disconnected. In turn, * this means that SDK controller socket will handle disconnection in its * connection callback. So, at this point all we need to do here is to inform - * the client, and then unlist the query. + * the client about query cancellation. */ /* Cancel deadline, and inform the client about query cancellation. */ @@ -676,12 +1052,11 @@ _on_skdctl_query_timeout(void* opaque) sdkctl_query_release(query); } -/* A callback that is invoked when query has been sent to the SDK controller - * service. */ +/* A callback that is invoked when query has been sent to the SDK controller. */ static void _on_sdkctl_query_sent(SDKCtlQuery* query) { - T("SDKCtl %s: sent %d bytes of query %p ID %d of type %d", + T("SDKCtl %s: Sent %d bytes of query %p ID %d of type %d", query->sdkctl->service_name, query->header.packet.size, query, query->header.query_id, query->header.query_type); @@ -710,31 +1085,25 @@ _on_sdkctl_query_send_io(void* io_opaque, /* Reference the query while we're in this callback. */ sdkctl_query_reference(query); - if (status == ASIO_STATE_SUCCEEDED) { - /* Query has been sent to the service. */ - _on_sdkctl_query_sent(query); - - sdkctl_query_release(query); - - return ASIO_ACTION_DONE; - } - /* Lets see what's going on with query transmission. */ switch (status) { + case ASIO_STATE_SUCCEEDED: + /* Query has been sent to the service. */ + _on_sdkctl_query_sent(query); + break; + case ASIO_STATE_CANCELLED: - T("SDKCtl %s: Query %p ID %d is cancelled in %s I/O.", - query->sdkctl->service_name, query, query->header.query_id, - async_socket_io_is_read(asio) ? "READ" : "WRITE"); + T("SDKCtl %s: Query %p ID %d is cancelled in transmission.", + query->sdkctl->service_name, query, query->header.query_id); /* Remove the query from the list of active queries. */ _sdkctl_socket_remove_query(query); _on_sdkctl_query_cancelled(query); break; case ASIO_STATE_TIMED_OUT: - D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out in %s I/O at %lld", + D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out in transmission at %lld", query->sdkctl->service_name, query, query->header.query_id, - query->deadline, async_socket_io_is_read(asio) ? "READ" : "WRITE", - async_socket_deadline(query->sdkctl->as, 0)); + query->deadline, async_socket_deadline(query->sdkctl->as, 0)); /* Invoke query's callback. */ action = query->query_cb(query->query_opaque, query, status); /* For actions other than retry we need to stop the query. */ @@ -744,9 +1113,8 @@ _on_sdkctl_query_send_io(void* io_opaque, break; case ASIO_STATE_FAILED: - T("SDKCtl %s: Query %p ID %d failed in %s I/O: %d -> %s", + T("SDKCtl %s: Query %p ID %d failed in transmission: %d -> %s", query->sdkctl->service_name, query, query->header.query_id, - async_socket_io_is_read(asio) ? "READ" : "WRITE", errno, strerror(errno)); /* Invoke query's callback. Note that we will let the client to * decide what to do on I/O failure. */ @@ -779,23 +1147,23 @@ _on_sdkctl_query_send_io(void* io_opaque, SDKCtlQuery* sdkctl_query_new(SDKCtlSocket* sdkctl, int query_type, uint32_t in_data_size) { - const uint32_t total_size = sizeof(SDKCtlQuery) + in_data_size; - - SDKCtlQuery* const query = _sdkctl_socket_alloc_recycler(sdkctl, total_size); - query->next = NULL; - query->sdkctl = sdkctl; - query->response_buffer = NULL; - query->response_size = NULL; - query->internal_resp_buffer = NULL; - query->internal_resp_size = 0; - query->query_cb = NULL; - query->query_opaque = NULL; - query->deadline = DURATION_INFINITE; - query->ref_count = 1; - query->header.packet.size = sizeof(SDKCtlQueryHeader) + in_data_size; - query->header.packet.type = SDKCTL_PACKET_QUERY; - query->header.query_id = _sdkctl_socket_next_query_id(sdkctl); - query->header.query_type = query_type; + SDKCtlQuery* const query = + _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlQuery) + in_data_size); + query->next = NULL; + query->sdkctl = sdkctl; + query->response_buffer = NULL; + query->response_size = NULL; + query->internal_resp_buffer = NULL; + query->internal_resp_size = 0; + query->query_cb = NULL; + query->query_opaque = NULL; + query->deadline = DURATION_INFINITE; + query->ref_count = 1; + query->header.packet.signature = _sdkctl_packet_sig; + query->header.packet.size = sizeof(SDKCtlQueryHeader) + in_data_size; + query->header.packet.type = SDKCTL_PACKET_QUERY; + query->header.query_id = _sdkctl_socket_next_query_id(sdkctl); + query->header.query_type = query_type; /* Initialize timer to fire up on query deadline expiration. */ loopTimer_init(query->timer, sdkctl->looper, _on_skdctl_query_timeout, query); @@ -822,21 +1190,19 @@ sdkctl_query_new_ex(SDKCtlSocket* sdkctl, { SDKCtlQuery* const query = sdkctl_query_new(sdkctl, query_type, in_data_size); - query->response_buffer = response_buffer; + query->response_buffer = response_buffer; if (query->response_buffer == NULL) { /* Creator didn't supply a buffer. Use internal one instead. */ query->response_buffer = &query->internal_resp_buffer; - query->internal_resp_buffer = NULL; } - query->response_size = response_size; + query->response_size = response_size; if (query->response_size == NULL) { /* Creator didn't supply a buffer for response size. Use internal one * instead. */ query->response_size = &query->internal_resp_size; - query->internal_resp_size = 0; } - query->query_cb = query_cb; - query->query_opaque = query_opaque; + query->query_cb = query_cb; + query->query_opaque = query_opaque; /* Init query's input buffer. */ if (in_data_size != 0 && in_data != NULL) { memcpy(query + 1, in_data, in_data_size); @@ -859,11 +1225,12 @@ sdkctl_query_send(SDKCtlQuery* query, int to) /* Reference query associated with write I/O. */ sdkctl_query_reference(query); + assert(query->header.packet.signature == _sdkctl_packet_sig); /* Transmit the query to SDK controller. */ async_socket_write_abs(sdkctl->as, &query->header, query->header.packet.size, _on_sdkctl_query_send_io, query, query->deadline); - T("SDKCtl %s: Query %p ID %d type %d is sent with deadline at %lld", + T("SDKCtl %s: Query %p ID %d type %d is being sent with deadline at %lld", query->sdkctl->service_name, query, query->header.query_id, query->header.query_type, query->deadline); } @@ -908,20 +1275,73 @@ sdkctl_query_release(SDKCtlQuery* query) return query->ref_count; } +void* +sdkctl_query_get_buffer_in(SDKCtlQuery* query) +{ + /* Query buffer starts right after the header. */ + return query + 1; +} + +void* +sdkctl_query_get_buffer_out(SDKCtlQuery* query) +{ + return query->response_buffer != NULL ? *query->response_buffer : + query->internal_resp_buffer; +} + /******************************************************************************** * SDKCtlPacket implementation *******************************************************************************/ -/* A packet has been received from SDK controller. */ +/* A packet has been received from SDK controller. + * Note that we expect the packet to be a message, since queries, and query + * replies are handled separately. */ static void _on_sdkctl_packet_received(SDKCtlSocket* sdkctl, SDKCtlPacket* packet) { T("SDKCtl %s: Received packet size: %d, type: %d", sdkctl->service_name, packet->header.size, packet->header.type); - /* Dispatch received packet to the client. */ - sdkctl->on_message(sdkctl->opaque, sdkctl, packet, packet->header.type, - packet + 1, packet->header.size - sizeof(SDKCtlPacketHeader)); + assert(packet->header.signature == _sdkctl_packet_sig); + if (packet->header.type == SDKCTL_PACKET_MESSAGE) { + SDKCtlMessage* const msg = (SDKCtlMessage*)packet; + /* Lets see if this is an internal protocol message. */ + switch (msg->msg_type) { + case SDKCTL_MSG_PORT_CONNECTED: + sdkctl->port_status = SDKCTL_PORT_CONNECTED; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, + SDKCTL_PORT_CONNECTED); + break; + + case SDKCTL_MSG_PORT_DISCONNECTED: + sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, + SDKCTL_PORT_DISCONNECTED); + break; + + case SDKCTL_MSG_PORT_ENABLED: + sdkctl->port_status = SDKCTL_PORT_ENABLED; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, + SDKCTL_PORT_ENABLED); + break; + + case SDKCTL_MSG_PORT_DISABLED: + sdkctl->port_status = SDKCTL_PORT_DISABLED; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, + SDKCTL_PORT_DISABLED); + break; + + default: + /* This is a higher-level message. Dispatch the message to the + * client. */ + sdkctl->on_message(sdkctl->opaque, sdkctl, msg, msg->msg_type, msg + 1, + packet->header.size - sizeof(SDKCtlMessageHeader)); + break; + } + } else { + E("SDKCtl %s: Received unknown packet type %d size %d", + sdkctl->service_name, packet->header.type, packet->header.size); + } } /******************************************************************************** @@ -949,7 +1369,7 @@ _sdkctl_io_dispatcher_start(SDKCtlSocket* sdkctl) { dispatcher->current_query = NULL; /* Register a packet header reader with the socket. */ - async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->header, + async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, dispatcher, -1); } @@ -969,12 +1389,14 @@ _sdkctl_io_dispatcher_reset(SDKCtlSocket* sdkctl) { /* Free packet data buffer. */ if (dispatcher->packet != NULL) { - sdkctl_packet_release(dispatcher->packet); + _sdkctl_packet_release(dispatcher->packet); dispatcher->packet = NULL; } /* Reset dispatcher state. */ dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; + + T("SDKCtl %s: I/O Dispatcher is reset", sdkctl->service_name); } /* @@ -999,7 +1421,7 @@ _on_io_dispatcher_io_failure(SDKCtlIODispatcher* dispatcher, /* Report disconnection to the client, and let it restore connection in this * callback. */ - sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); } /* A callback that is invoked when dispatcher's reader has been cancelled. */ @@ -1020,7 +1442,7 @@ _on_io_dispatcher_io_cancelled(SDKCtlIODispatcher* dispatcher, /* Discard packet data we've received so far. */ if (dispatcher->packet != NULL) { - sdkctl_packet_release(dispatcher->packet); + _sdkctl_packet_release(dispatcher->packet); dispatcher->packet = NULL; } } @@ -1033,8 +1455,20 @@ _on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher, SDKCtlSocket* const sdkctl = dispatcher->sdkctl; T("SDKCtl %s: Packet header type %d, size %d is received.", - dispatcher->sdkctl->service_name, dispatcher->header.type, - dispatcher->header.size); + dispatcher->sdkctl->service_name, dispatcher->packet_header.type, + dispatcher->packet_header.size); + + /* Make sure we have a valid packet header. */ + if (dispatcher->packet_header.signature != _sdkctl_packet_sig) { + E("SDKCtl %s: Invalid packet signature %x for packet type %d, size %d", + sdkctl->service_name, dispatcher->packet_header.signature, + dispatcher->packet_header.type, dispatcher->packet_header.size); + /* This is a protocol failure. Treat it as I/O failure: disconnect, and + * let the client to decide what to do next. */ + errno = EINVAL; + _on_io_dispatcher_io_failure(dispatcher, asio); + return ASIO_ACTION_DONE; + } /* Here we have three choices for the packet, that define the rest of * the data that follow it: @@ -1044,7 +1478,7 @@ _on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher, * Update the state accordingly, and initiate reading of the * remaining of the packet. */ - if (dispatcher->header.type == SDKCTL_PACKET_QUERY_RESPONSE) { + if (dispatcher->packet_header.type == SDKCTL_PACKET_QUERY_RESPONSE) { /* This is a response to the query. Before receiving response data we * need to locate the relevant query, and use its response buffer to read * the data. For that we need to obtain query ID firts. So, initiate @@ -1059,11 +1493,11 @@ _on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher, * there. */ dispatcher->state = SDKCTL_IODISP_EXPECT_DATA; dispatcher->packet = - _sdkctl_packet_new(sdkctl, dispatcher->header.size, - dispatcher->header.type); + _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size, + dispatcher->packet_header.type); /* Initiate reading of the packet data. */ async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, - dispatcher->header.size - sizeof(SDKCtlPacketHeader), + dispatcher->packet_header.size - sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, dispatcher, -1); } @@ -1075,18 +1509,19 @@ static AsyncIOAction _on_io_dispatcher_packet(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio) { SDKCtlSocket* const sdkctl = dispatcher->sdkctl; + SDKCtlPacket* const packet = dispatcher->packet; + dispatcher->packet = NULL; T("SDKCtl %s: Packet type %d, size %d is received.", - dispatcher->sdkctl->service_name, dispatcher->header.type, - dispatcher->header.size); + dispatcher->sdkctl->service_name, dispatcher->packet_header.type, + dispatcher->packet_header.size); - _on_sdkctl_packet_received(sdkctl, dispatcher->packet); - sdkctl_packet_release(dispatcher->packet); - dispatcher->packet = NULL; + _on_sdkctl_packet_received(sdkctl, packet); + _sdkctl_packet_release(packet); /* Get ready for the next I/O cycle. */ dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; - async_socket_read_rel(sdkctl->as, &dispatcher->header, sizeof(SDKCtlPacketHeader), + async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, dispatcher, -1); return ASIO_ACTION_DONE; } @@ -1107,6 +1542,9 @@ _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, dispatcher->current_query = _sdkctl_socket_remove_query_id(sdkctl, dispatcher->query_reply_header.query_id); query = dispatcher->current_query; + const uint32_t query_data_size = + dispatcher->packet_header.size - sizeof(SDKCtlQueryReplyHeader); + dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA; if (query == NULL) { D("%s: Query #%d is not found by dispatcher", @@ -1116,13 +1554,12 @@ _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, * and then discard when it's over. */ dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA; dispatcher->packet = - _sdkctl_packet_new(sdkctl, dispatcher->header.size, - dispatcher->header.type); + _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size, + dispatcher->packet_header.type); /* Copy query reply info to the packet. */ memcpy(&dispatcher->packet->header, &dispatcher->query_reply_header, sizeof(SDKCtlQueryReplyHeader)); - async_socket_read_rel(sdkctl->as, &dispatcher->query_header + 1, - dispatcher->header.size - sizeof(SDKCtlQueryReplyHeader), + async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, query_data_size, _on_sdkctl_io_dispatcher_io, dispatcher, -1); } else { /* Prepare to receive query reply. For the simplicity sake, cancel query @@ -1130,9 +1567,6 @@ _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, * receiving query's reply. */ _sdkctl_query_cancel_timeout(query); - /* Adjust the reply buffer set for the query (if needed). */ - const uint32_t query_data_size = - dispatcher->header.size - sizeof(SDKCtlQueryReplyHeader); if (*query->response_size < query_data_size) { *query->response_buffer = malloc(query_data_size); if (*query->response_buffer == NULL) { @@ -1144,7 +1578,6 @@ _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, *query->response_size = query_data_size; /* Start reading query response. */ - dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA; async_socket_read_rel(sdkctl->as, *query->response_buffer, *query->response_size, _on_sdkctl_io_dispatcher_io, dispatcher, -1); @@ -1176,14 +1609,14 @@ _on_io_dispatcher_query_reply(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asi /* This was "read up in the air" for a cancelled query. Just discard the * read data. */ if (dispatcher->packet != NULL) { - sdkctl_packet_release(dispatcher->packet); + _sdkctl_packet_release(dispatcher->packet); dispatcher->packet = NULL; } } /* Get ready for the next I/O cycle. */ dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; - async_socket_read_rel(sdkctl->as, &dispatcher->header, sizeof(SDKCtlPacketHeader), + async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, dispatcher, -1); return ASIO_ACTION_DONE; } @@ -1351,6 +1784,7 @@ _sdkctl_socket_disconnect_socket(SDKCtlSocket* sdkctl) } sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; + sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; } /* Frees SDKCtlSocket instance. */ @@ -1358,6 +1792,8 @@ static void _sdkctl_socket_free(SDKCtlSocket* sdkctl) { if (sdkctl != NULL) { + T("SDKCtl %s: descriptor is destroing.", sdkctl->service_name); + /* Disconnect, and release the socket. */ if (sdkctl->as != NULL) { async_socket_disconnect(sdkctl->as); @@ -1394,7 +1830,7 @@ _on_async_socket_connected(SDKCtlSocket* sdkctl) /* Notify the client that connection is established. */ const AsyncIOAction action = - sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED); if (action == ASIO_ACTION_DONE) { /* Initialize, and start main I/O dispatcher. */ @@ -1418,8 +1854,8 @@ _on_async_socket_disconnected(SDKCtlSocket* sdkctl) _sdkctl_socket_disconnect_socket(sdkctl); - AsyncIOAction action = sdkctl->on_connection(sdkctl->opaque, sdkctl, - ASIO_STATE_FAILED); + AsyncIOAction action = sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, + ASIO_STATE_FAILED); if (action == ASIO_ACTION_DONE) { /* Default action for disconnect is to reestablish the connection. */ action = ASIO_ACTION_RETRY; @@ -1481,35 +1917,38 @@ _on_async_socket_connection(void* client_opaque, SDKCtlSocket* sdkctl_socket_new(int reconnect_to, const char* service_name, - on_sdkctl_connection_cb on_connection, - on_sdkctl_handshake_cb on_handshake, + on_sdkctl_socket_connection_cb on_socket_connection, + on_sdkctl_port_connection_cb on_port_connection, on_sdkctl_message_cb on_message, void* opaque) { SDKCtlSocket* sdkctl; ANEW0(sdkctl); - sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; - sdkctl->opaque = opaque; - sdkctl->service_name = ASTRDUP(service_name); - sdkctl->on_connection = on_connection; - sdkctl->on_handshake = on_handshake; - sdkctl->on_message = on_message; - sdkctl->reconnect_to = reconnect_to; - sdkctl->as = NULL; - sdkctl->next_query_id = 0; - sdkctl->query_head = sdkctl->query_tail = NULL; - sdkctl->ref_count = 1; - sdkctl->recycler = NULL; - sdkctl->recycler_block_size = 0; - sdkctl->recycler_max = 0; - sdkctl->recycler_count = 0; + sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; + sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; + sdkctl->opaque = opaque; + sdkctl->service_name = ASTRDUP(service_name); + sdkctl->on_socket_connection = on_socket_connection; + sdkctl->on_port_connection = on_port_connection; + sdkctl->on_message = on_message; + sdkctl->reconnect_to = reconnect_to; + sdkctl->as = NULL; + sdkctl->next_query_id = 0; + sdkctl->query_head = sdkctl->query_tail = NULL; + sdkctl->ref_count = 1; + sdkctl->recycler = NULL; + sdkctl->recycler_block_size = 0; + sdkctl->recycler_max = 0; + sdkctl->recycler_count = 0; + + T("SDKCtl %s: descriptor is created.", sdkctl->service_name); sdkctl->looper = looper_newCore(); if (sdkctl->looper == NULL) { E("Unable to create I/O looper for SDKCtl socket '%s'", service_name); - on_connection(opaque, sdkctl, ASIO_STATE_FAILED); + on_socket_connection(opaque, sdkctl, ASIO_STATE_FAILED); _sdkctl_socket_free(sdkctl); return NULL; } @@ -1569,7 +2008,7 @@ sdkctl_socket_connect(SDKCtlSocket* sdkctl, int port, int retry_to) if (sdkctl->as == NULL) { E("Unable to allocate AsyncSocket for SDKCtl socket '%s'", sdkctl->service_name); - sdkctl->on_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); } else { async_socket_connect(sdkctl->as, retry_to); } @@ -1599,11 +2038,54 @@ sdkctl_socket_disconnect(SDKCtlSocket* sdkctl) _sdkctl_socket_disconnect_socket(sdkctl); } +int +sdkctl_socket_is_connected(SDKCtlSocket* sdkctl) +{ + return (sdkctl->state == SDKCTL_SOCKET_CONNECTED) ? 1 : 0; +} + +int +sdkctl_socket_is_port_ready(SDKCtlSocket* sdkctl) +{ + return (sdkctl->port_status == SDKCTL_PORT_ENABLED) ? 1 : 0; +} + +SdkCtlPortStatus +sdkctl_socket_get_port_status(SDKCtlSocket* sdkctl) +{ + return sdkctl->port_status; +} + +int +sdkctl_socket_is_handshake_ok(SDKCtlSocket* sdkctl) +{ + switch (sdkctl->port_status) { + case SDKCTL_HANDSHAKE_DUP: + case SDKCTL_HANDSHAKE_UNKNOWN_QUERY: + case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE: + return 0; + default: + return 1; + } +} /******************************************************************************** * Handshake query *******************************************************************************/ +/* + * Handshake result values. + */ + +/* Handshake has succeeded completed, and service-side port is connected. */ +#define SDKCTL_HANDSHAKE_RESP_CONNECTED 0 +/* Handshake has succeeded completed, but service-side port is not connected. */ +#define SDKCTL_HANDSHAKE_RESP_NOPORT 1 +/* Handshake has failed due to duplicate connection request. */ +#define SDKCTL_HANDSHAKE_RESP_DUP -1 +/* Handshake has failed due to unknown query. */ +#define SDKCTL_HANDSHAKE_RESP_QUERY_UNKNOWN -2 + /* A callback that is ivoked on handshake I/O events. */ static AsyncIOAction _on_handshake_io(void* query_opaque, @@ -1613,12 +2095,41 @@ _on_handshake_io(void* query_opaque, SDKCtlSocket* const sdkctl = (SDKCtlSocket*)query_opaque; if (status == ASIO_STATE_SUCCEEDED) { - D("SDKCtl %s: %d bytes of handshake reply is received.", - sdkctl->service_name, *query->response_size); + const int* res = (const int*)(*query->response_buffer); + SdkCtlPortStatus handshake_status; + switch (*res) { + case SDKCTL_HANDSHAKE_RESP_CONNECTED: + D("SDKCtl %s: Handshake succeeded. Port is connected", + sdkctl->service_name); + handshake_status = SDKCTL_HANDSHAKE_CONNECTED; + break; + + case SDKCTL_HANDSHAKE_RESP_NOPORT: + D("SDKCtl %s: Handshake succeeded. Port is not connected", + sdkctl->service_name); + handshake_status = SDKCTL_HANDSHAKE_NO_PORT; + break; + + case SDKCTL_HANDSHAKE_RESP_DUP: + D("SDKCtl %s: Handshake failed: duplicate connection.", + sdkctl->service_name); + handshake_status = SDKCTL_HANDSHAKE_DUP; + break; - /* Handshake is received. Inform the client. */ - sdkctl->on_handshake(sdkctl->opaque, sdkctl, *query->response_buffer, - *query->response_size, status); + case SDKCTL_HANDSHAKE_RESP_QUERY_UNKNOWN: + D("SDKCtl %s: Handshake failed: unknown query.", + sdkctl->service_name); + handshake_status = SDKCTL_HANDSHAKE_UNKNOWN_QUERY; + break; + + default: + E("SDKCtl %s: Unknown handshake response: %d", + sdkctl->service_name, *res); + handshake_status = SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE; + break; + } + sdkctl->port_status = handshake_status; + sdkctl->on_port_connection(sdkctl->opaque, sdkctl, handshake_status); } else { /* Something is going on with the handshake... */ switch (status) { @@ -1627,9 +2138,8 @@ _on_handshake_io(void* query_opaque, case ASIO_STATE_CANCELLED: D("SDKCtl %s: Handshake failed: I/O state %d. Error: %d -> %s", sdkctl->service_name, status, errno, strerror(errno)); - sdkctl->on_handshake(sdkctl->opaque, sdkctl, - *query->response_buffer, - *query->response_size, status); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, + ASIO_STATE_FAILED); break; default: @@ -1663,7 +2173,7 @@ _on_sdkctl_endianness_io(void* io_opaque, case ASIO_STATE_CANCELLED: D("SDKCtl %s: endianness failed: I/O state %d. Error: %d -> %s", sdkctl->service_name, status, errno, strerror(errno)); - sdkctl->on_handshake(sdkctl->opaque, sdkctl, NULL, 0, status); + sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); break; default: @@ -1682,7 +2192,7 @@ static const char _host_end = 0; static const char _host_end = 1; #endif - D("SDKCtl %s: Sending endianness: %d...", sdkctl->service_name, _host_end); + D("SDKCtl %s: Sending endianness: %d", sdkctl->service_name, _host_end); /* Before we can send any structured data to the SDK controller we need to * report endianness of the host. */ diff --git a/android/sdk-controller-socket.h b/android/sdk-controller-socket.h index 03b12f4..e58a4e2 100644 --- a/android/sdk-controller-socket.h +++ b/android/sdk-controller-socket.h @@ -18,6 +18,7 @@ #define ANDROID_SDKCONTROL_SOCKET_H_ #include "android/async-socket.h" +#include "android/async-utils.h" /* * Contains declaration of an API that encapsulates communication protocol with @@ -102,20 +103,29 @@ * interested in that particular descriptor. * * There are three types of data in the exchange protocol: - * - A packet - the simplest type of data that doesn't require any replies. + * - A message - the simplest type of data that doesn't require any replies. * - A query - A message that require a reply, and * - A query reply - A message that delivers query reply. */ +/* Default TCP port to use for connection with SDK controller. */ +#define SDKCTL_DEFAULT_TCP_PORT 1970 + /* Declares SDK controller socket descriptor. */ typedef struct SDKCtlSocket SDKCtlSocket; -/* Declares SDK controller data packet descriptor. */ -typedef struct SDKCtlPacket SDKCtlPacket; +/* Declares SDK controller message descriptor. */ +typedef struct SDKCtlMessage SDKCtlMessage; /* Declares SDK controller query descriptor. */ typedef struct SDKCtlQuery SDKCtlQuery; +/* Declares SDK controller direct packet descriptor. + * Direct packet (unlike message, or query packets) doesn't contain data buffer, + * but rather references message, or query data allocated by the client. + */ +typedef struct SDKCtlDirectPacket SDKCtlDirectPacket; + /* Defines client's callback set to monitor SDK controller socket connection. * * SDKCtlSocket will invoke this callback when connection to TCP port is @@ -137,30 +147,52 @@ typedef struct SDKCtlQuery SDKCtlQuery; * Return: * One of the AsyncIOAction values. */ -typedef AsyncIOAction (*on_sdkctl_connection_cb)(void* client_opaque, - SDKCtlSocket* sdkctl, - AsyncIOState status); +typedef AsyncIOAction (*on_sdkctl_socket_connection_cb)(void* client_opaque, + SDKCtlSocket* sdkctl, + AsyncIOState status); + +/* Enumerates port connection statuses passed to port connection callback. + */ +typedef enum SdkCtlPortStatus { + /* Service-side port has connected to the socket. */ + SDKCTL_PORT_DISCONNECTED, + /* Service-side port has disconnected from the socket. */ + SDKCTL_PORT_CONNECTED, + /* Service-side port has enabled emulation */ + SDKCTL_PORT_ENABLED, + /* Service-side port has disabled emulation */ + SDKCTL_PORT_DISABLED, + /* Handshake request has succeeded, and service-side port is connected. */ + SDKCTL_HANDSHAKE_CONNECTED, + /* Handshake request has succeeded, but service-side port is not connected. */ + SDKCTL_HANDSHAKE_NO_PORT, + /* Handshake request has failed due to port duplication. */ + SDKCTL_HANDSHAKE_DUP, + /* Handshake request has failed on an unknown query. */ + SDKCTL_HANDSHAKE_UNKNOWN_QUERY, + /* Handshake request has failed on an unknown response. */ + SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE, +} SdkCtlPortStatus; -/* Defines client's callback set to receive handshake reply from the SdkController - * service running on the device. +/* Defines client's callback set to receive port connection status. * - * Successful handshake means that connection between the client and SDK - * controller service has been established. + * Port connection is different than socket connection, and indicates whether + * or not a service-side port that provides requested emulation functionality is + * hooked up with the connected socket. For instance, multi-touch port may be + * inactive at the time when socket is connected. So, there is a successful + * socket connection, but there is no service at the device end that provides + * multi-touch functionality. So, for multi-touch example, this callback will be + * invoked when multi-touch port at the device end becomes active, and hooks up + * with the socket that was connected before. * * Param: * client_opaque - An opaque pointer associated with the client. * sdkctl - Initialized SDKCtlSocket instance. - * handshake - Handshake message received from the SDK controller service. - * handshake_size - Size of the fandshake message received from the SDK - * controller service. - * status - Handshake status. Note that handshake, and handshake_size are valid - * only if this parameter is set to ASIO_STATE_SUCCEEDED. - */ -typedef void (*on_sdkctl_handshake_cb)(void* client_opaque, - SDKCtlSocket* sdkctl, - void* handshake, - uint32_t handshake_size, - AsyncIOState status); + * status - Port connection status. + */ +typedef void (*on_sdkctl_port_connection_cb)(void* client_opaque, + SDKCtlSocket* sdkctl, + SdkCtlPortStatus status); /* Defines a message notification callback. * Param: @@ -176,7 +208,7 @@ typedef void (*on_sdkctl_handshake_cb)(void* client_opaque, */ typedef void (*on_sdkctl_message_cb)(void* client_opaque, SDKCtlSocket* sdkctl, - SDKCtlPacket* message, + SDKCtlMessage* message, int msg_type, void* msg_data, int msg_size); @@ -203,27 +235,114 @@ typedef AsyncIOAction (*on_sdkctl_query_cb)(void* query_opaque, SDKCtlQuery* query, AsyncIOState status); +/* Defines direct packet completion callback. + * Param: + * opaque - An opaque pointer associated with the direct packet by the client. + * packet - Packet descriptor. Note that packet descriptor will be released + * upon exit from this callback (thus, could be freed). If the client is + * interested in working with that packet after the callback returns, it + * should reference the packet descriptor in this callback. + * status - Packet status. Can be one of these: + * - ASIO_STATE_SUCCEEDED : Packet has been successfully sent. + * - ASIO_STATE_FAILED : Packet has failed on an I/O. + * - ASIO_STATE_CANCELLED : Packet has been cancelled due to socket + * disconnection. + * Return: + * One of the AsyncIOAction values. + */ +typedef AsyncIOAction (*on_sdkctl_direct_cb)(void* opaque, + SDKCtlDirectPacket* packet, + AsyncIOState status); + /******************************************************************************** - * SDKCtlPacket API + * SDKCtlDirectPacket API ********************************************************************************/ -/* References SDKCtlPacket object. +/* Creates new SDKCtlDirectPacket descriptor. * Param: - * packet - Initialized SDKCtlPacket instance. + * sdkctl - Initialized SDKCtlSocket instance to create a direct packet for. + * Return: + * Referenced SDKCtlDirectPacket instance. + */ +extern SDKCtlDirectPacket* sdkctl_direct_packet_new(SDKCtlSocket* sdkctl); + +/* References SDKCtlDirectPacket object. + * Param: + * packet - Initialized SDKCtlDirectPacket instance. * Return: * Number of outstanding references to the object. */ -extern int sdkctl_packet_reference(SDKCtlPacket* packet); +extern int sdkctl_direct_packet_reference(SDKCtlDirectPacket* packet); -/* Releases SDKCtlPacket object. +/* Releases SDKCtlDirectPacket object. * Note that upon exiting from this routine the object might be destroyed, even * if this routine returns value other than zero. * Param: - * packet - Initialized SDKCtlPacket instance. + * packet - Initialized SDKCtlDirectPacket instance. * Return: * Number of outstanding references to the object. */ -extern int sdkctl_packet_release(SDKCtlPacket* packet); +extern int sdkctl_direct_packet_release(SDKCtlDirectPacket* packet); + +/* Sends direct packet. + * Param: + * packet - Packet descriptor for the direct packet to send. + * data - Data to send with the packet. Must be fully initialized message, or + * query header. + * cb, cb_opaque - Callback to invoke on packet transmission events. + */ +extern void sdkctl_direct_packet_send(SDKCtlDirectPacket* packet, + void* data, + on_sdkctl_direct_cb cb, + void* cb_opaque); + +/******************************************************************************** + * SDKCtlMessage API + ********************************************************************************/ + +/* References SDKCtlMessage object. + * Param: + * msg - Initialized SDKCtlMessage instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_message_reference(SDKCtlMessage* msg); + +/* Releases SDKCtlMessage object. + * Note that upon exiting from this routine the object might be destroyed, even + * if this routine returns value other than zero. + * Param: + * msg - Initialized SDKCtlMessage instance. + * Return: + * Number of outstanding references to the object. + */ +extern int sdkctl_message_release(SDKCtlMessage* msg); + +/* Builds and sends a message to the device. + * Param: + * sdkctl - SDKCtlSocket instance for the message. + * msg_type - Defines message type. + * data - Message data. Can be NULL if there is no data associated with the + * message. + * size - Byte size of the data buffer. + * Return: + * Referenced SDKCtlQuery descriptor. + */ +extern SDKCtlMessage* sdkctl_message_send(SDKCtlSocket* sdkctl, + int msg_type, + const void* data, + uint32_t size); + +/* Gets message header size */ +extern int sdkctl_message_get_header_size(void); + +/* Initializes message header. + * Param: + * msg - Beginning of the message packet. + * msg_type - Message type. + * msg_size - Message data size. + */ +extern void sdkctl_init_message_header(void* msg, int msg_type, int msg_size); /******************************************************************************** * SDKCtlQuery API @@ -349,6 +468,14 @@ extern int sdkctl_query_release(SDKCtlQuery* query); */ extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query); +/* Gets address of query's output data buffer (response data). + * Param: + * query - Query to get data buffer for. + * Return: + * Address of query's output data buffer. + */ +extern void* sdkctl_query_get_buffer_out(SDKCtlQuery* query); + /******************************************************************************** * SDKCtlSocket API ********************************************************************************/ @@ -359,8 +486,8 @@ extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query); * attempts after disconnection, or on connection failures. * service_name - Name of the SdkController service for this socket (such as * 'sensors', 'milti-touch', etc.) - * on_connection - A callback to invoke on socket connection events. - * on_handshake - A callback to invoke on handshake events. + * on_socket_connection - A callback to invoke on socket connection events. + * on_port_connection - A callback to invoke on port connection events. * on_message - A callback to invoke when a message is received from the SDK * controller. * opaque - An opaque pointer to associate with the socket. @@ -369,8 +496,8 @@ extern void* sdkctl_query_get_buffer_in(SDKCtlQuery* query); */ extern SDKCtlSocket* sdkctl_socket_new(int reconnect_to, const char* service_name, - on_sdkctl_connection_cb on_connection, - on_sdkctl_handshake_cb on_handshake, + on_sdkctl_socket_connection_cb on_socket_connection, + on_sdkctl_port_connection_cb on_port_connection, on_sdkctl_message_cb on_message, void* opaque); @@ -437,4 +564,36 @@ extern void sdkctl_socket_reconnect(SDKCtlSocket* sdkctl, int port, int retry_to */ extern void sdkctl_socket_disconnect(SDKCtlSocket* sdkctl); +/* Checks if SDK controller socket is connected. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Boolean: 1 if socket is connected, 0 if socket is not connected. + */ +extern int sdkctl_socket_is_connected(SDKCtlSocket* sdkctl); + +/* Checks if SDK controller port is ready for emulation. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Boolean: 1 if port is ready, 0 if port is not ready. + */ +extern int sdkctl_socket_is_port_ready(SDKCtlSocket* sdkctl); + +/* Gets status of the SDK controller port for this socket. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Status of the SDK controller port for this socket. + */ +extern SdkCtlPortStatus sdkctl_socket_get_port_status(SDKCtlSocket* sdkctl); + +/* Checks if handshake was successful. + * Param: + * sdkctl - Initialized SDKCtlSocket instance. + * Return: + * Boolean: 1 if handshake was successful, 0 if handshake was not successful. + */ +extern int sdkctl_socket_is_handshake_ok(SDKCtlSocket* sdkctl); + #endif /* ANDROID_SDKCONTROL_SOCKET_H_ */ diff --git a/android/sensors-port.c b/android/sensors-port.c index 8620783..6831fe3 100644 --- a/android/sensors-port.c +++ b/android/sensors-port.c @@ -14,370 +14,474 @@ * limitations under the License. */ +#include "android/sdk-controller-socket.h" #include "android/sensors-port.h" #include "android/hw-sensors.h" +#include "android/utils/debug.h" #define E(...) derror(__VA_ARGS__) #define W(...) dwarning(__VA_ARGS__) #define D(...) VERBOSE_PRINT(sensors_port,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(sensors_port) -/* Maximum number of sensors supported. */ -#define ASP_MAX_SENSOR 12 +#define TRACE_ON 1 + +#if TRACE_ON +#define T(...) VERBOSE_PRINT(sensors_port,__VA_ARGS__) +#else +#define T(...) +#endif + +/* Timeout (millisec) to use when communicating with SDK controller. */ +#define SDKCTL_SENSORS_TIMEOUT 3000 -/* Maximum length of a sensor message. */ -#define ASP_MAX_SENSOR_MSG 1024 +/* + * Queries sent to sensors port of the SDK controller. + */ -/* Maximum length of a sensor event. */ -#define ASP_MAX_SENSOR_EVENT 256 +/* Queries the port for list of available sensors. */ +#define SDKCTL_SENSORS_QUERY_LIST 1 -/* Query timeout in milliseconds. */ -#define ASP_QUERY_TIMEOUT 3000 +/* + * Messages sent between the emuator, and sensors port of the SDK controller. + */ + +/* Starts sensor emulation. */ +#define SDKCTL_SENSORS_START 1 +/* Stops sensor emulation. */ +#define SENSOR_SENSORS_STOP 2 +/* Enables emulation for a sensor. */ +#define SDKCTL_SENSORS_ENABLE 3 +/* Disables emulation for a sensor. */ +#define SDKCTL_SENSORS_DISABLE 4 +/* This message delivers sensor values. */ +#define SDKCTL_SENSORS_SENSOR_EVENT 5 + + +/* Describes a sensor on the device. + * When SDK controller sensors port replies to a "list" query, it replies with + * a flat buffer containing entries of this type following each other. End of + * each entry is a zero-terminator for its 'sensor_name' field. The end of the + * entire list is marked with an entry, containing -1 at its 'sensor_id' field. + */ +typedef struct SensorEntry { + /* Identifies sensor on the device. Value -1 indicates list terminator, + * rather than a valid sensor descriptor. */ + int sensor_id; + /* Beginning of zero-terminated sensor name. */ + char sensor_name[1]; +} SensorEntry; + +/* Describes a sensor in the array of emulated sensors. */ +typedef struct SensorDescriptor { + /* Identifies sensor on the device. */ + int sensor_id; + /* Identifies sensor in emulator. */ + int emulator_id; + /* Sensor name. */ + char* sensor_name; +} SensorDescriptor; + +/* Sensor event message descriptor. + * Entries of this type are sent along with SDKCTL_SENSORS_SENSOR_EVENT message + */ +typedef struct SensorEvent { + /* Identifies a device sensor for which values have been delivered. */ + int sensor_id; + /* Sensor values. */ + float fvalues[3]; +} SensorEvent; /* Sensors port descriptor. */ struct AndroidSensorsPort { /* Caller identifier. */ - void* opaque; - /* Connected android device. */ - AndroidDevice* device; - /* String containing list of all available sensors. */ - char sensors[ASP_MAX_SENSOR * 64]; - /* Array of available sensor names. Note that each string in this array - * points inside the 'sensors' buffer. */ - const char* sensor_list[ASP_MAX_SENSOR]; - /* Number of available sensors. */ - int sensors_num; - /* Connection status: 1 connected, 0 - disconnected. */ - int is_connected; - /* Buffer where to receive sensor messages. */ - char sensor_msg[ASP_MAX_SENSOR_MSG]; - /* Buffer where to receive sensor events. */ - char events[ASP_MAX_SENSOR_EVENT]; + void* opaque; + /* Communication socket. */ + SDKCtlSocket* sdkctl; + /* Lists sensors available for emulation. */ + SensorDescriptor** sensors; + /* Number of sensors in 'sensors' list. */ + int sensors_count; }; +/******************************************************************************** + * Sensors port internals + *******************************************************************************/ + +/* Checks if sensor descriptor is the terminator. + * Return: + * Boolean, 1 if it is a terminator, 0 if it is not. + */ +static int +_sensor_entry_is_terminator(const SensorEntry* entry) +{ + return entry == NULL || entry->sensor_id == -1; +} + +/* Gets next sensor descriptor. + * Return: + * Next sensor desciptor, or NULL if there are no more descriptors in the list. + */ +static const SensorEntry* +_sensor_entry_next(const SensorEntry* entry) +{ + if (!_sensor_entry_is_terminator(entry)) { + /* Next descriptor begins right after zero-terminator for the sensor_name + * field of this descriptor. */ + entry = (const SensorEntry*)(entry->sensor_name + strlen(entry->sensor_name) + 1); + if (!_sensor_entry_is_terminator(entry)) { + return entry; + } + } + return NULL; +} + +/* Gets number of entries in the list. */ +static int +_sensor_entry_list_size(const SensorEntry* entry) { + int ret = 0; + while (!_sensor_entry_is_terminator(entry)) { + ret++; + entry = _sensor_entry_next(entry); + } + return ret; +} + +/* Discards sensors saved in AndroidSensorsPort's array. */ +static void +_sensors_port_discard_sensors(AndroidSensorsPort* asp) +{ + if (asp->sensors != NULL) { + int n; + for (n = 0; n < asp->sensors_count; n++) { + if (asp->sensors[n] != NULL) { + free(asp->sensors[n]->sensor_name); + AFREE(asp->sensors[n]); + } + } + free(asp->sensors); + asp->sensors = NULL; + } + asp->sensors_count = 0; +} + + /* Destroys and frees the descriptor. */ static void _sensors_port_free(AndroidSensorsPort* asp) { if (asp != NULL) { - if (asp->device != NULL) { - android_device_destroy(asp->device); + _sensors_port_discard_sensors(asp); + if (asp->sdkctl != NULL) { + sdkctl_socket_release(asp->sdkctl); } AFREE(asp); } } -/******************************************************************************** - * Sensors port callbacks - *******************************************************************************/ +/* Parses flat sensor list, and saves its entries into 'sensors' array filed of + * the AndroidSensorsPort descriptor. */ +static void +_sensors_port_save_sensors(AndroidSensorsPort* asp, const SensorEntry* list) +{ + const int count = _sensor_entry_list_size(list); + if (count != 0) { + int n; + /* Allocate array for sensor descriptors. */ + asp->sensors = malloc(sizeof(SensorDescriptor*) * count); + + /* Iterate through the flat sensor list, filling up array of emulated + * sensors. */ + const SensorEntry* entry = _sensor_entry_is_terminator(list) ? NULL : list; + for (n = 0; n < count && entry != NULL; n++) { + /* Get emulator-side ID for the sensor. < 0 value indicates that + * sensor is not supported by the emulator. */ + const int emulator_id = + android_sensors_get_id_from_name((char*)entry->sensor_name); + if (emulator_id >= 0) { + SensorDescriptor* desc; + ANEW0(desc); + desc->emulator_id = emulator_id; + desc->sensor_id = entry->sensor_id; + desc->sensor_name = ASTRDUP(entry->sensor_name); + + asp->sensors[asp->sensors_count++] = desc; + D("Sensors: Emulated sensor '%s': Device id = %d, Emulator id = %d", + desc->sensor_name, desc->sensor_id, desc->emulator_id); + } else { + D("Sensors: Sensor '%s' is not support by emulator", + entry->sensor_name); + } + entry = _sensor_entry_next(entry); + } + D("Sensors: Emulating %d sensors", asp->sensors_count); + } +} -/* A callback that invoked on sensor events. +/* Finds sensor descriptor for an SDK controller-side ID. */ +static const SensorDescriptor* +_sensor_from_sdkctl_id(AndroidSensorsPort* asp, int id) +{ + int n; + for (n = 0; n < asp->sensors_count; n++) { + if (asp->sensors[n]->sensor_id == id) { + return asp->sensors[n]; + } + } + return NULL; +} + +/* Initiates sensor emulation. * Param: - * opaque - AndroidSensorsPort instance. - * ad - Android device used by this sensors port. - * msg, msgsize - Sensor event message - * failure - Message receiving status. + * asp - Android sensors port instance returned from sensors_port_create. + * Return: + * Zero on success, failure otherwise. */ static void -_on_sensor_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize) +_sensors_port_start(AndroidSensorsPort* asp) { - float fvalues[3] = {0, 0, 0}; - char sensor[ASP_MAX_SENSOR_MSG]; - char* value; - int id; - AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque; - - if (errno) { - D("Sensors notification has failed on sensors port: %s", strerror(errno)); - return; - } + int n; - /* Parse notification, separating sensor name from parameters. */ - memcpy(sensor, msg, msgsize); - value = strchr(sensor, ':'); - if (value == NULL) { - W("Bad format for sensor notification: %s", msg); + if (!sdkctl_socket_is_port_ready(asp->sdkctl)) { + /* SDK controller side is not ready for emulation. Retreat... */ + D("Sensors: SDK controller side is not ready for emulation."); return; } - sensor[value-sensor] = '\0'; - value++; - - id = android_sensors_get_id_from_name(sensor); - if (id >= 0) { - /* Parse the value part to get the sensor values(a, b, c) */ - int i; - char* pnext; - char* pend = value + strlen(value); - for (i = 0; i < 3; i++, value = pnext + 1) { - pnext=strchr( value, ':' ); - if (pnext) { - *pnext = 0; - } else { - pnext = pend; - } - if (pnext > value) { - if (1 != sscanf( value,"%g", &fvalues[i] )) { - W("Bad parameters in sensor notification %s", msg); - return; - } - } + /* Disable all sensors, and reenable only those that are emulated by + * hardware. */ + sensors_port_disable_sensor(asp, "all"); + + /* Walk throuh the list of enabled sensors enabling them on the device. */ + for (n = 0; n < asp->sensors_count; n++) { + if (android_sensors_get_sensor_status(asp->sensors[n]->emulator_id) == 1) { + /* Reenable emulation for this sensor. */ + sensors_port_enable_sensor(asp, asp->sensors[n]->sensor_name); + D("Sensors: Sensor '%s' is enabled on SDK controller.", + asp->sensors[n]->sensor_name); } - android_sensors_set(id, fvalues[0], fvalues[1], fvalues[2]); - } else { - W("Unknown sensor name '%s' in '%s'", sensor, msg); } - /* Listen to the next event. */ - android_device_listen(ad, asp->events, sizeof(asp->events), _on_sensor_received); + /* Start the emulation. */ + SDKCtlMessage* const msg = + sdkctl_message_send(asp->sdkctl, SDKCTL_SENSORS_START, NULL, 0); + sdkctl_message_release(msg); + + D("Sensors: Emulation has been started."); } -/* A callback that is invoked when android device is connected (i.e. both, command - * and event channels have been stablished. - * Param: - * opaque - AndroidSensorsPort instance. - * ad - Android device used by this sensors port. - * failure - Connections status. - */ -static void -_on_device_connected(void* opaque, AndroidDevice* ad, int failure) +/******************************************************************************** + * Sensors port callbacks + *******************************************************************************/ + +/* Completion for the "list" query. */ +static AsyncIOAction +_on_sensor_list_query(void* query_opaque, + SDKCtlQuery* query, + AsyncIOState status) { - if (!failure) { - AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque; - asp->is_connected = 1; - D("Sensor emulation has started"); - /* Initialize sensors on device. */ - sensors_port_init_sensors(asp); + AndroidSensorsPort* const asp = (AndroidSensorsPort*)(query_opaque); + if (status != ASIO_STATE_SUCCEEDED) { + /* We don't really care about failures at this point. They will + * eventually surface up in another place. */ + return ASIO_ACTION_DONE; } + + /* Parse query response which is a flat list of SensorEntry entries. */ + const SensorEntry* const list = + (const SensorEntry*)sdkctl_query_get_buffer_out(query); + D("Sensors: Sensor list received with %d sensors.", + _sensor_entry_list_size(list)); + _sensors_port_save_sensors(asp, list); + + /* At this point we are ready to statr sensor emulation. */ + _sensors_port_start(asp); + + return ASIO_ACTION_DONE; } -/* Invoked when an I/O failure occurs on a socket. - * Note that this callback will not be invoked on connection failures. +/* A callback that is invoked on sensor events. * Param: - * opaque - AndroidSensorsPort instance. - * ad - Android device instance - * ads - Connection socket where failure has occured. - * failure - Contains 'errno' indicating the reason for failure. + * asp - AndroidSensorsPort instance. + * event - Sensor event. */ static void -_on_io_failure(void* opaque, AndroidDevice* ad, int failure) +_on_sensor_event(AndroidSensorsPort* asp, const SensorEvent* event) { - AndroidSensorsPort* asp = (AndroidSensorsPort*)opaque; - E("Sensors port got disconnected: %s", strerror(failure)); - asp->is_connected = false; - android_device_disconnect(ad); - android_device_connect_async(ad, _on_device_connected); + /* Find corresponding server descriptor. */ + const SensorDescriptor* const desc = + _sensor_from_sdkctl_id(asp, event->sensor_id); + if (desc != NULL) { + T("Sensors: %s -> %f, %f, %f", desc->sensor_name, + event->fvalues[0], event->fvalues[1], + event->fvalues[2]); + /* Fire up sensor change in the guest. */ + android_sensors_set(desc->emulator_id, event->fvalues[0], + event->fvalues[1], event->fvalues[2]); + } else { + W("Sensors: No descriptor for sensor %d", event->sensor_id); + } } -/******************************************************************************** - * Sensors port API - *******************************************************************************/ - -#include "android/sdk-controller-socket.h" - +/* A callback that is invoked on SDK controller socket connection events. */ static AsyncIOAction -_on_sdkctl_connection(void* client_opaque, SDKCtlSocket* sdkctl, AsyncIOState status) +_on_sensors_socket_connection(void* client_opaque, + SDKCtlSocket* sdkctl, + AsyncIOState status) { + AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque; if (status == ASIO_STATE_FAILED) { - sdkctl_socket_reconnect(sdkctl, 1970, 20); + /* Disconnection could mean that user is swapping devices. New device may + * have different set of sensors, so we need to re-query sensor list on + * reconnection. */ + _sensors_port_discard_sensors(asp); + + /* Reconnect (after timeout delay) on failures */ + if (sdkctl_socket_is_handshake_ok(sdkctl)) { + sdkctl_socket_reconnect(sdkctl, SDKCTL_DEFAULT_TCP_PORT, + SDKCTL_SENSORS_TIMEOUT); + } } return ASIO_ACTION_DONE; } -void on_sdkctl_handshake(void* client_opaque, - SDKCtlSocket* sdkctl, - void* handshake, - uint32_t handshake_size, - AsyncIOState status) +/* A callback that is invoked on SDK controller port connection events. */ +static void +_on_sensors_port_connection(void* client_opaque, + SDKCtlSocket* sdkctl, + SdkCtlPortStatus status) { - if (status == ASIO_STATE_SUCCEEDED) { - printf("---------- Handshake %d bytes received.\n", handshake_size); - } else { - printf("!!!!!!!!!! Handshake failed with status %d: %d -> %s\n", - status, errno, strerror(errno)); - sdkctl_socket_reconnect(sdkctl, 1970, 20); + AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque; + switch (status) { + case SDKCTL_PORT_CONNECTED: { + D("Sensors: SDK Controller is connected."); + /* Query list of available sensors. */ + SDKCtlQuery* const query = + sdkctl_query_build_and_send(asp->sdkctl, SDKCTL_SENSORS_QUERY_LIST, + 0, NULL, NULL, NULL, + _on_sensor_list_query, asp, + SDKCTL_SENSORS_TIMEOUT); + sdkctl_query_release(query); + break; + } + + case SDKCTL_PORT_DISCONNECTED: + _sensors_port_discard_sensors(asp); + D("Sensors: SDK Controller is disconnected."); + break; + + case SDKCTL_PORT_ENABLED: + _sensors_port_start(asp); + D("Sensors: SDK Controller is enabled."); + break; + + case SDKCTL_PORT_DISABLED: + D("Sensors: SDK Controller is disabled."); + break; + + case SDKCTL_HANDSHAKE_CONNECTED: + D("Sensors: SDK Controller has succeeded handshake, and port is connected."); + break; + + case SDKCTL_HANDSHAKE_NO_PORT: + D("Sensors: SDK Controller has succeeded handshake, and port is not connected."); + break; + + case SDKCTL_HANDSHAKE_DUP: + E("Sensors: SDK Controller has failed the handshake due to port duplication."); + sdkctl_socket_disconnect(sdkctl); + break; + + case SDKCTL_HANDSHAKE_UNKNOWN_QUERY: + E("Sensors: SDK Controller has failed the handshake due to unknown query."); + sdkctl_socket_disconnect(sdkctl); + break; + + case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE: + default: + E("Sensors: Handshake has failed due to unknown reasons."); + sdkctl_socket_disconnect(sdkctl); + break; } } -void on_sdkctl_message(void* client_opaque, - SDKCtlSocket* sdkctl, - SDKCtlPacket* message, - int msg_type, - void* msg_data, - int msg_size) +/* A callback that is invoked when a message is received from SDK controller. */ +static void +_on_sensors_message(void* client_opaque, + SDKCtlSocket* sdkctl, + SDKCtlMessage* message, + int msg_type, + void* msg_data, + int msg_size) { - printf("########################################################\n"); + AndroidSensorsPort* const asp = (AndroidSensorsPort*)client_opaque; + switch (msg_type) { + case SDKCTL_SENSORS_SENSOR_EVENT: + _on_sensor_event(asp, (const SensorEvent*)msg_data); + break; + + default: + E("Sensors: Unknown message type %d", msg_type); + break; + } } +/******************************************************************************** + * Sensors port API + *******************************************************************************/ + AndroidSensorsPort* sensors_port_create(void* opaque) { AndroidSensorsPort* asp; - char* wrk; - int res; - - SDKCtlSocket* sdkctl = sdkctl_socket_new(20, "test", _on_sdkctl_connection, - on_sdkctl_handshake, on_sdkctl_message, - NULL); -// sdkctl_init_recycler(sdkctl, 64, 8); - sdkctl_socket_connect(sdkctl, 1970, 20); ANEW0(asp); asp->opaque = opaque; - asp->is_connected = 0; - - asp->device = android_device_init(asp, AD_SENSOR_PORT, _on_io_failure); - if (asp->device == NULL) { - _sensors_port_free(asp); - return NULL; - } - - res = android_device_connect_sync(asp->device, ASP_QUERY_TIMEOUT); - if (res != 0) { - sensors_port_destroy(asp); - return NULL; - } - - res = android_device_query(asp->device, "list", - asp->sensors, sizeof(asp->sensors), - ASP_QUERY_TIMEOUT); - if (res != 0) { - sensors_port_destroy(asp); - return NULL; - } - - /* Parse sensor list. */ - asp->sensors_num = 0; - wrk = asp->sensors; - - while (wrk != NULL && *wrk != '\0' && *wrk != '\n') { - asp->sensor_list[asp->sensors_num] = wrk; - asp->sensors_num++; - wrk = strchr(wrk, '\n'); - if (wrk != NULL) { - *wrk = '\0'; wrk++; - } - } - - android_device_listen(asp->device, asp->events, sizeof(asp->events), - _on_sensor_received); + asp->sensors = NULL; + asp->sensors_count = 0; + asp->sdkctl = sdkctl_socket_new(SDKCTL_SENSORS_TIMEOUT, "sensors", + _on_sensors_socket_connection, + _on_sensors_port_connection, + _on_sensors_message, asp); + sdkctl_init_recycler(asp->sdkctl, 76, 8); + sdkctl_socket_connect(asp->sdkctl, SDKCTL_DEFAULT_TCP_PORT, + SDKCTL_SENSORS_TIMEOUT); return asp; } -int -sensors_port_init_sensors(AndroidSensorsPort* asp) -{ - int res, id; - - /* Disable all sensors for now. Reenable only those that are emulated. */ - res = sensors_port_disable_sensor(asp, "all"); - if (res) { - return res; - } - - /* Start listening on sensor events. */ - res = android_device_listen(asp->device, asp->events, sizeof(asp->events), - _on_sensor_received); - if (res) { - return res; - } - - /* Walk throuh the list of enabled sensors enabling them on the device. */ - for (id = 0; id < MAX_SENSORS; id++) { - if (android_sensors_get_sensor_status(id) == 1) { - res = sensors_port_enable_sensor(asp, android_sensors_get_name_from_id(id)); - if (res == 0) { - D("Sensor '%s' is enabled on the device.", - android_sensors_get_name_from_id(id)); - } - } - } - - /* Start sensor events. */ - return sensors_port_start(asp); -} - void sensors_port_destroy(AndroidSensorsPort* asp) { + if (asp->sdkctl != NULL) { + sdkctl_socket_disconnect(asp->sdkctl); + } _sensors_port_free(asp); } int -sensors_port_is_connected(AndroidSensorsPort* asp) -{ - return asp->is_connected; -} - -int sensors_port_enable_sensor(AndroidSensorsPort* asp, const char* name) { - char query[1024]; - char qresp[1024]; - snprintf(query, sizeof(query), "enable:%s", name); - const int res = - android_device_query(asp->device, query, qresp, sizeof(qresp), - ASP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query '%s' failed on I/O: %s", query, strerror(errno)); - } else { - D("Query '%s' failed on device: %s", query, qresp); - } + if (asp->sdkctl != NULL && sdkctl_socket_is_port_ready(asp->sdkctl)) { + SDKCtlMessage* const msg = sdkctl_message_send(asp->sdkctl, + SDKCTL_SENSORS_ENABLE, + name, strlen(name)); + sdkctl_message_release(msg); + return 0; + } else { + return -1; } - return res; } int sensors_port_disable_sensor(AndroidSensorsPort* asp, const char* name) { - char query[1024]; - char qresp[1024]; - snprintf(query, sizeof(query), "disable:%s", name); - const int res = - android_device_query(asp->device, query, qresp, sizeof(qresp), - ASP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query '%s' failed on I/O: %s", query, strerror(errno)); - } else { - D("Query '%s' failed on device: %s", query, qresp); - } - } - return res; -} - -int -sensors_port_start(AndroidSensorsPort* asp) -{ - char qresp[ASP_MAX_SENSOR_MSG]; - const int res = - android_device_query(asp->device, "start", qresp, sizeof(qresp), - ASP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query 'start' failed on I/O: %s", strerror(errno)); - } else { - D("Query 'start' failed on device: %s", qresp); - } - } - return res; -} - -int -sensors_port_stop(AndroidSensorsPort* asp) -{ - char qresp[ASP_MAX_SENSOR_MSG]; - const int res = - android_device_query(asp->device, "stop", qresp, sizeof(qresp), - ASP_QUERY_TIMEOUT); - if (res) { - if (errno) { - D("Query 'stop' failed on I/O: %s", strerror(errno)); - } else { - D("Query 'stop' failed on device: %s", qresp); - } + if (asp->sdkctl != NULL && sdkctl_socket_is_port_ready(asp->sdkctl)) { + SDKCtlMessage* const msg = sdkctl_message_send(asp->sdkctl, + SDKCTL_SENSORS_DISABLE, + name, strlen(name)); + sdkctl_message_release(msg); + return 0; + } else { + return -1; } - - return res; } diff --git a/android/sensors-port.h b/android/sensors-port.h index dc5c966..a35d700 100644 --- a/android/sensors-port.h +++ b/android/sensors-port.h @@ -23,8 +23,6 @@ * the host via USB. */ -#include "android/android-device.h" - /* Declares sensors port descriptor. */ typedef struct AndroidSensorsPort AndroidSensorsPort; @@ -42,15 +40,6 @@ extern AndroidSensorsPort* sensors_port_create(void* opaque); /* Disconnects from the sensors port, and destroys the descriptor. */ extern void sensors_port_destroy(AndroidSensorsPort* asp); -/* Initializes sensors on the connected device. */ -extern int sensors_port_init_sensors(AndroidSensorsPort* asp); - -/* Checks if port is connected to a sensor reading application on the device. - * Note that connection can go out and then be restored at any time after - * sensors_port_create API succeeded. - */ -extern int sensors_port_is_connected(AndroidSensorsPort* asp); - /* Enables events from a particular sensor. * Param: * asp - Android sensors port instance returned from sensors_port_create. @@ -72,20 +61,4 @@ extern int sensors_port_enable_sensor(AndroidSensorsPort* asp, const char* name) */ extern int sensors_port_disable_sensor(AndroidSensorsPort* asp, const char* name); -/* Queries the connected application to start delivering sensor events. - * Param: - * asp - Android sensors port instance returned from sensors_port_create. - * Return: - * Zero on success, failure otherwise. - */ -extern int sensors_port_start(AndroidSensorsPort* asp); - -/* Queries the connected application to stop delivering sensor events. - * Param: - * asp - Android sensors port instance returned from sensors_port_create. - * Return: - * Zero on success, failure otherwise. - */ -extern int sensors_port_stop(AndroidSensorsPort* asp); - #endif /* ANDROID_SENSORS_PORT_H_ */ diff --git a/android/utils/path.c b/android/utils/path.c index 1bcdc4e..3e9d97b 100644 --- a/android/utils/path.c +++ b/android/utils/path.c @@ -78,8 +78,12 @@ path_parent( const char* path, int levels ) while (base > path && !ispathsep(base[-1])) base--; - if (base <= path) /* we can't go that far */ + if (base <= path) { + if (end == base+1 && base[0] == '.' && levels == 1) + return strdup(".."); + /* we can't go that far */ return NULL; + } if (end == base+1 && base[0] == '.') goto Next; diff --git a/distrib/jpeg-6b/jdphuff.c b/distrib/jpeg-6b/jdphuff.c index 922017e..cccfdd9 100644 --- a/distrib/jpeg-6b/jdphuff.c +++ b/distrib/jpeg-6b/jdphuff.c @@ -82,6 +82,10 @@ METHODDEF(boolean) decode_mcu_DC_refine JPP((j_decompress_ptr cinfo, JBLOCKROW *MCU_data)); METHODDEF(boolean) decode_mcu_AC_refine JPP((j_decompress_ptr cinfo, JBLOCKROW *MCU_data)); +GLOBAL(void) jpeg_configure_huffman_decoder_progressive( + j_decompress_ptr cinfo, huffman_offset_data offset); +GLOBAL(void) jpeg_get_huffman_decoder_configuration_progressive( + j_decompress_ptr cinfo, huffman_offset_data *offset); /* * Initialize for a Huffman-compressed scan. diff --git a/distrib/sdl-1.2.12/src/video/x11/SDL_x11dyn.c b/distrib/sdl-1.2.12/src/video/x11/SDL_x11dyn.c index 7b99d73..6e97c32 100644 --- a/distrib/sdl-1.2.12/src/video/x11/SDL_x11dyn.c +++ b/distrib/sdl-1.2.12/src/video/x11/SDL_x11dyn.c @@ -109,6 +109,21 @@ char *(*pXGetICValues)(XIC, ...) = NULL; #undef SDL_X11_SYM +static void *SDL_XGetRequest_workaround(Display* dpy, CARD8 type, size_t len) +{ + xReq *req; + WORD64ALIGN + if (dpy->bufptr + len > dpy->bufmax) + _XFlush(dpy); + dpy->last_req = dpy->bufptr; + req = (xReq*)dpy->bufptr; + req->reqType = type; + req->length = len / 4; + dpy->bufptr += len; + dpy->request++; + return req; +} + static int x11_load_refcount = 0; void SDL_X11_UnloadSymbols(void) @@ -168,6 +183,15 @@ int SDL_X11_LoadSymbols(void) X11_GetSym("XGetICValues",&SDL_X11_HAVE_UTF8,(void **)&pXGetICValues); #endif + /* + * In case we're built with newer Xlib headers, we need to make sure + * that _XGetRequest() is available, even on older systems. + * Otherwise, various Xlib macros we use will call a NULL pointer. + */ + if (!SDL_X11_HAVE_XGETREQUEST) { + p_XGetRequest = SDL_XGetRequest_workaround; + } + if (SDL_X11_HAVE_BASEXLIB) { /* all required symbols loaded. */ SDL_ClearError(); XInitThreads(); diff --git a/distrib/sdl-1.2.12/src/video/x11/SDL_x11sym.h b/distrib/sdl-1.2.12/src/video/x11/SDL_x11sym.h index b8d90e4..610982c 100644 --- a/distrib/sdl-1.2.12/src/video/x11/SDL_x11sym.h +++ b/distrib/sdl-1.2.12/src/video/x11/SDL_x11sym.h @@ -181,6 +181,12 @@ SDL_X11_SYM(void,_XRead32,(Display *dpy,register long *data,long len),(dpy,data, #endif /* + * libX11 1.4.99.1 added _XGetRequest, and macros use it behind the scenes. + */ +SDL_X11_MODULE(XGETREQUEST) +SDL_X11_SYM(void *,_XGetRequest,(Display* a,CARD8 b,size_t c),(a,b,c),return) + +/* * These only show up on some variants of Unix. */ #if defined(__osf__) diff --git a/hw/goldfish_pipe.c b/hw/goldfish_pipe.c index 97daefb..276ac9a 100644 --- a/hw/goldfish_pipe.c +++ b/hw/goldfish_pipe.c @@ -55,7 +55,7 @@ /* Maximum length of pipe service name, in characters (excluding final 0) */ #define MAX_PIPE_SERVICE_NAME_SIZE 255 -#define GOLDFISH_PIPE_SAVE_VERSION 1 +#define GOLDFISH_PIPE_SAVE_VERSION 2 /*********************************************************************** *********************************************************************** @@ -960,9 +960,9 @@ struct PipeDevice { uint32_t status; uint32_t channel; uint32_t wakes; + uint64_t params_addr; }; - static void pipeDevice_doCommand( PipeDevice* dev, uint32_t command ) { @@ -1097,6 +1097,42 @@ static void pipe_dev_write(void *opaque, target_phys_addr_t offset, uint32_t val s->channel = value; break; + case PIPE_REG_PARAMS_ADDR_HIGH: + s->params_addr = (s->params_addr & ~(0xFFFFFFFFULL << 32) ) | + ((uint64_t)value << 32); + break; + + case PIPE_REG_PARAMS_ADDR_LOW: + s->params_addr = (s->params_addr & ~(0xFFFFFFFFULL) ) | value; + break; + + case PIPE_REG_ACCESS_PARAMS: + { + struct access_params aps; + uint32_t cmd; + + /* Don't touch aps.result if anything wrong */ + if (s->params_addr == 0) + break; + + cpu_physical_memory_read(s->params_addr, (void*)&aps, + sizeof(struct access_params)); + + /* sync pipe device state from batch buffer */ + s->channel = aps.channel; + s->size = aps.size; + s->address = aps.address; + cmd = aps.cmd; + if ((cmd != PIPE_CMD_READ_BUFFER) && (cmd != PIPE_CMD_WRITE_BUFFER)) + break; + + pipeDevice_doCommand(s, cmd); + aps.result = s->status; + cpu_physical_memory_write(s->params_addr, (void*)&aps, + sizeof(struct access_params)); + } + break; + default: D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset, offset, value, value); @@ -1136,6 +1172,12 @@ static uint32_t pipe_dev_read(void *opaque, target_phys_addr_t offset) DR("%s: wakes %d", __FUNCTION__, dev->wakes); return dev->wakes; + case PIPE_REG_PARAMS_ADDR_HIGH: + return dev->params_addr >> 32; + + case PIPE_REG_PARAMS_ADDR_LOW: + return dev->params_addr & 0xFFFFFFFFUL; + default: D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset); } @@ -1165,6 +1207,7 @@ goldfish_pipe_save( QEMUFile* file, void* opaque ) qemu_put_be32(file, dev->status); qemu_put_be32(file, dev->channel); qemu_put_be32(file, dev->wakes); + qemu_put_be64(file, dev->params_addr); /* Count the number of pipe connections */ int count = 0; @@ -1193,6 +1236,7 @@ goldfish_pipe_load( QEMUFile* file, void* opaque, int version_id ) dev->status = qemu_get_be32(file); dev->channel = qemu_get_be32(file); dev->wakes = qemu_get_be32(file); + dev->params_addr = qemu_get_be64(file); /* Count the number of pipe connections */ int count = qemu_get_sbe32(file); diff --git a/hw/goldfish_pipe.h b/hw/goldfish_pipe.h index f08cef8..10efa96 100644 --- a/hw/goldfish_pipe.h +++ b/hw/goldfish_pipe.h @@ -153,6 +153,11 @@ extern void goldfish_pipe_wake( void* hwpipe, unsigned flags ); #define PIPE_REG_SIZE 0x0c /* read/write: buffer size */ #define PIPE_REG_ADDRESS 0x10 /* write: physical address */ #define PIPE_REG_WAKES 0x14 /* read: wake flags */ +/* read/write: parameter buffer address */ +#define PIPE_REG_PARAMS_ADDR_LOW 0x18 +#define PIPE_REG_PARAMS_ADDR_HIGH 0x1c +/* write: access with paremeter buffer */ +#define PIPE_REG_ACCESS_PARAMS 0x20 /* list of commands for PIPE_REG_COMMAND */ #define PIPE_CMD_OPEN 1 /* open new channel */ @@ -189,4 +194,14 @@ extern void goldfish_pipe_wake( void* hwpipe, unsigned flags ); void pipe_dev_init(void); +struct access_params{ + uint32_t channel; + uint32_t size; + uint32_t address; + uint32_t cmd; + uint32_t result; + /* reserved for future extension */ + uint32_t flags; +}; + #endif /* _HW_GOLDFISH_PIPE_H */ @@ -186,10 +186,10 @@ int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) } #ifdef WIN32 -int asprintf( char **, char *, ... ); -int vasprintf( char **, char *, va_list ); +int asprintf( char **, const char *, ... ); +int vasprintf( char **, const char *, va_list ); -int vasprintf( char **sptr, char *fmt, va_list argv ) +int vasprintf( char **sptr, const char *fmt, va_list argv ) { int wanted = vsnprintf( *sptr = NULL, 0, fmt, argv ); if( (wanted > 0) && ((*sptr = malloc( 1 + wanted )) != NULL) ) @@ -198,7 +198,7 @@ int vasprintf( char **sptr, char *fmt, va_list argv ) return wanted; } -int asprintf( char **sptr, char *fmt, ... ) +int asprintf( char **sptr, const char *fmt, ... ) { int retval; va_list argv; @@ -125,7 +125,7 @@ static const WinsockError _winsock_errors[] = { * errno. */ static int -_fix_errno( void ) +fix_errno( void ) { const WinsockError* werr = _winsock_errors; int unix = EINVAL; /* generic error code */ @@ -143,7 +143,7 @@ _fix_errno( void ) } static int -_set_errno( int code ) +set_errno( int code ) { winsock_error = -1; errno = code; @@ -173,13 +173,13 @@ _errno_str(void) } #else static int -_fix_errno( void ) +fix_errno( void ) { return -1; } static int -_set_errno( int code ) +set_errno( int code ) { errno = code; return -1; @@ -560,7 +560,7 @@ sock_address_to_bsd( const SockAddress* a, sockaddr_storage* paddress, socklen #endif /* HAVE_UNIX_SOCKETS */ default: - return _set_errno(EINVAL); + return set_errno(EINVAL); } return 0; @@ -575,7 +575,7 @@ sock_address_from_bsd( SockAddress* a, const void* from, size_t fromlen ) const struct sockaddr_in* src = from; if (fromlen < sizeof(*src)) - return _set_errno(EINVAL); + return set_errno(EINVAL); a->family = SOCKET_INET; a->u.inet.port = ntohs(src->sin_port); @@ -589,7 +589,7 @@ sock_address_from_bsd( SockAddress* a, const void* from, size_t fromlen ) const struct sockaddr_in6* src = from; if (fromlen < sizeof(*src)) - return _set_errno(EINVAL); + return set_errno(EINVAL); a->family = SOCKET_IN6; a->u.in6.port = ntohs(src->sin6_port); @@ -605,12 +605,12 @@ sock_address_from_bsd( SockAddress* a, const void* from, size_t fromlen ) char* end; if (fromlen < sizeof(*src)) - return _set_errno(EINVAL); + return set_errno(EINVAL); /* check that the path is zero-terminated */ end = memchr(src->sun_path, 0, UNIX_PATH_MAX); if (end == NULL) - return _set_errno(EINVAL); + return set_errno(EINVAL); a->family = SOCKET_UNIX; a->u._unix.owner = 1; @@ -620,7 +620,7 @@ sock_address_from_bsd( SockAddress* a, const void* from, size_t fromlen ) #endif default: - return _set_errno(EINVAL); + return set_errno(EINVAL); } return 0; } @@ -646,7 +646,8 @@ sock_address_init_resolve( SockAddress* a, const char* hostname, uint16_t por err = EHOSTDOWN; break; -#ifdef EAI_NODATA +/* NOTE that in x86_64-w64-mingw32 both EAI_NODATA and EAI_NONAME are the same */ +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) case EAI_NODATA: #endif case EAI_NONAME: @@ -660,7 +661,7 @@ sock_address_init_resolve( SockAddress* a, const char* hostname, uint16_t por default: err = EINVAL; } - return _set_errno(err); + return set_errno(err); } /* Parse the returned list of addresses. */ @@ -699,7 +700,7 @@ sock_address_init_resolve( SockAddress* a, const char* hostname, uint16_t por } if (r == NULL) { - ret = _set_errno(ENOENT); + ret = set_errno(ENOENT); goto Exit; } @@ -765,13 +766,13 @@ sock_address_list_create( const char* hostname, case EAI_ADDRFAMILY: #endif case EAI_NODATA: - _set_errno(ENOENT); + set_errno(ENOENT); break; case EAI_FAMILY: - _set_errno(EAFNOSUPPORT); + set_errno(EAFNOSUPPORT); break; case EAI_AGAIN: - _set_errno(EAGAIN); + set_errno(EAGAIN); break; #ifdef EAI_SYSTEM case EAI_SYSTEM: @@ -780,7 +781,7 @@ sock_address_list_create( const char* hostname, break; #endif default: - _set_errno(EINVAL); + set_errno(EINVAL); } return NULL; } @@ -874,7 +875,7 @@ sock_address_get_numeric_info( SockAddress* a, break; #endif default: - return _set_errno(EINVAL); + return set_errno(EINVAL); } ret = getnameinfo( saddr, slen, host, hostlen, serv, servlen, @@ -900,12 +901,12 @@ socket_create( SocketFamily family, SocketType type ) int stype = socket_type_to_bsd(type); if (sfamily < 0 || stype < 0) { - return _set_errno(EINVAL); + return set_errno(EINVAL); } QSOCKET_CALL(ret, socket(sfamily, stype, 0)); if (ret < 0) - return _fix_errno(); + return fix_errno(); return ret; } @@ -956,7 +957,7 @@ int socket_can_read(int fd) int ret; \ QSOCKET_CALL(ret, (cmd)); \ if (ret < 0) \ - return _fix_errno(); \ + return fix_errno(); \ return ret; \ int @@ -998,7 +999,7 @@ socket_recvfrom(int fd, void* buf, int len, SockAddress* from) QSOCKET_CALL(ret,recvfrom(fd,buf,len,0,sa.sa,&salen)); if (ret < 0) - return _fix_errno(); + return fix_errno(); if (sock_address_from_bsd(from, &sa, salen) < 0) return -1; @@ -1039,7 +1040,7 @@ socket_get_address( int fd, SockAddress* address ) QSOCKET_CALL(ret, getsockname(fd, addr.sa, &addrlen)); if (ret < 0) - return _fix_errno(); + return fix_errno(); return sock_address_from_bsd(address, &addr, addrlen); } @@ -1053,7 +1054,7 @@ socket_get_peer_address( int fd, SockAddress* address ) QSOCKET_CALL(ret, getpeername(fd, addr.sa, &addrlen)); if (ret < 0) - return _fix_errno(); + return fix_errno(); return sock_address_from_bsd(address, &addr, addrlen); } @@ -1073,7 +1074,7 @@ socket_accept( int fd, SockAddress* address ) QSOCKET_CALL(ret, accept(fd, addr.sa, &addrlen)); if (ret < 0) - return _fix_errno(); + return fix_errno(); if (address) { if (sock_address_from_bsd(address, &addr, addrlen) < 0) { @@ -1517,7 +1518,7 @@ socket_mcast_inet_add_membership( int s, uint32_t ip ) (const char *)&imr, sizeof(struct ip_mreq)) < 0 ) { - return _fix_errno(); + return fix_errno(); } return 0; } @@ -1534,7 +1535,7 @@ socket_mcast_inet_drop_membership( int s, uint32_t ip ) (const char *)&imr, sizeof(struct ip_mreq)) < 0 ) { - return _fix_errno(); + return fix_errno(); } return 0; } diff --git a/tap-win32.c b/tap-win32.c index ba93355..ce30a50 100644 --- a/tap-win32.c +++ b/tap-win32.c @@ -31,13 +31,7 @@ #include "sysemu.h" #include <stdio.h> #include <windows.h> - -/* NOTE: PCIBus is redefined in winddk.h */ -#define PCIBus _PCIBus -#include <ddk/ntapi.h> -#include <ddk/winddk.h> -#include <ddk/ntddk.h> -#undef PCIBus +#include <winioctl.h> //============= // TAP IOCTLs diff --git a/target-arm/exec.h b/target-arm/exec.h index 07bfd57..83571e6 100644 --- a/target-arm/exec.h +++ b/target-arm/exec.h @@ -19,7 +19,11 @@ #include "config.h" #include "dyngen-exec.h" -register struct CPUARMState *env asm(AREG0); +/* Xcode 4.3 doesn't support global register variables */ +#if !defined(__APPLE_CC__) || __APPLE_CC__ < 5621 + register +#endif +struct CPUARMState *env asm(AREG0); #include "cpu.h" #include "exec-all.h" diff --git a/target-i386/exec.h b/target-i386/exec.h index 42b471a..d194f89 100644 --- a/target-i386/exec.h +++ b/target-i386/exec.h @@ -29,7 +29,11 @@ #include "cpu-defs.h" -register struct CPUX86State *env asm(AREG0); +/* Xcode 4.3 doesn't support global register variables */ +#if !defined(__APPLE_CC__) || __APPLE_CC__ < 5621 + register +#endif +struct CPUX86State *env asm(AREG0); #include "qemu-common.h" #include "qemu-log.h" diff --git a/vl-android.c b/vl-android.c index 5a41e2d..7b661b6 100644 --- a/vl-android.c +++ b/vl-android.c @@ -2271,7 +2271,7 @@ char *qemu_find_file(int type, const char *name) buf = qemu_find_file_with_subdir(data_dir, "../usr/share/pc-bios/", name); /* Finally, try this for standalone builds under external/qemu */ if (buf == NULL) - buf = qemu_find_file_with_subdir(data_dir, "../../../prebuilt/common/pc-bios/", name); + buf = qemu_find_file_with_subdir(data_dir, "../../../prebuilts/qemu-kernel/x86/pc-bios/", name); } #endif return buf; @@ -3867,28 +3867,33 @@ int main(int argc, char **argv, char **envp) nand_add_dev(tmp); } - /* qemu.gles will be read by the OpenGLES emulation libraries. - * If set to 0, the software GLES renderer will be used as a fallback. - * If the parameter is undefined, this means the system image runs - * inside an emulator that doesn't support GPU emulation at all. - */ + /* qemu.gles will be read by the OpenGL ES emulation libraries. + * If set to 0, the software GL ES renderer will be used as a fallback. + * If the parameter is undefined, this means the system image runs + * inside an emulator that doesn't support GPU emulation at all. + * + * We always start the GL ES renderer so we can gather stats on the + * underlying GL implementation. If GL ES acceleration is disabled, + * we just shut it down again once we have the strings. */ { - int gles_emul = 0; - - if (android_hw->hw_gpu_enabled) { - if (android_initOpenglesEmulation() == 0) { - gles_emul = 1; - /* Set framebuffer change notification callback when starting - * GLES emulation. Currently only multi-touch emulation is - * interested in FB changes (to transmit them to the device), so - * the callback is set within MT emulation.*/ - android_startOpenglesRenderer(android_hw->hw_lcd_width, android_hw->hw_lcd_height, - multitouch_opengles_fb_update, NULL); + int qemu_gles = 0; + if (android_initOpenglesEmulation() == 0 && + android_startOpenglesRenderer(android_hw->hw_lcd_width, android_hw->hw_lcd_height) == 0) + { + android_getOpenglesHardwareStrings( + android_gl_vendor, sizeof(android_gl_vendor), + android_gl_renderer, sizeof(android_gl_renderer), + android_gl_version, sizeof(android_gl_version)); + if (android_hw->hw_gpu_enabled) { + qemu_gles = 1; } else { - dwarning("Could not initialize OpenglES emulation, using software renderer."); + android_stopOpenglesRenderer(); + qemu_gles = 0; } + } else { + dwarning("Could not initialize OpenglES emulation, using software renderer."); } - if (gles_emul) { + if (qemu_gles) { stralloc_add_str(kernel_params, " qemu.gles=1"); } else { stralloc_add_str(kernel_params, " qemu.gles=0"); @@ -4041,6 +4046,32 @@ int main(int argc, char **argv, char **envp) } } + /* Quite often (especially on older XP machines) attempts to allocate large + * VM RAM is going to fail, and crash the emulator. Since it's failing deep + * inside QEMU, it's not really possible to provide the user with a + * meaningful explanation for the crash. So, lets see if QEMU is going to be + * able to allocate requested amount of RAM, and if not, lets try to come up + * with a recomendation. */ + { + ram_addr_t r_ram = ram_size; + void* alloc_check = malloc(r_ram); + while (alloc_check == NULL && r_ram > 1024 * 1024) { + /* Make it 25% less */ + r_ram -= r_ram / 4; + alloc_check = malloc(r_ram); + } + if (alloc_check != NULL) { + free(alloc_check); + } + if (r_ram != ram_size) { + /* Requested RAM is too large. Report this, as well as calculated + * recomendation. */ + dwarning("Requested RAM size of %dMB is too large for your environment, and is reduced to %dMB.", + (int)(ram_size / 1024 / 1024), (int)(r_ram / 1024 / 1024)); + ram_size = r_ram; + } + } + #ifdef CONFIG_KQEMU /* FIXME: This is a nasty hack because kqemu can't cope with dynamic guest ram allocation. It needs to go away. */ |