OSDN Git Service

[goldfish] update goldfish for O
authorLingfeng Yang <lfy@google.com>
Thu, 19 Jan 2017 23:53:05 +0000 (15:53 -0800)
committerLingfeng Yang <lfy@google.com>
Fri, 20 Jan 2017 03:49:54 +0000 (19:49 -0800)
1. use latest qemu_pipe / qemd header combos
2. self contain them in goldfish/ so we don't break
anything that doesn't use goldfish but uses qemu pipe
3. stahp building the "vibrator" device for now since it uses
a lotof legacy stuff that is delted in O
4. upgrade sensor HAL impl to version 1.3

bug: 34465648

after this, rotation and camera work at least

Change-Id: I6157ede893d720acf4f8fcbe42db68a911cad531

24 files changed:
camera/Android.mk
camera/QemuClient.cpp
fingerprint/Android.mk
fingerprint/fingerprint.c
gps/Android.mk
gps/gps_qemu.c
include/qemu.h [new file with mode: 0644]
include/qemu_pipe.h [new file with mode: 0644]
include/qemud.h [new file with mode: 0644]
init.goldfish.rc
libqemu/test_util.h
lights/Android.mk
lights/lights_qemu.c
power/Android.mk
power/power_qemu.c
qemu-props/Android.mk
qemu-props/qemu-props.c
qemud/Android.mk [new file with mode: 0644]
qemud/qemud.c [new file with mode: 0644]
sensors/Android.mk
sensors/sensors_qemu.c
vibrator/Android.mk
vibrator/qemu.c [new file with mode: 0644]
vibrator/vibrator_qemu.c

index cb24d2b..bd33131 100644 (file)
@@ -42,6 +42,7 @@ emulator_camera_c_includes := external/libjpeg-turbo \
        external/libexif \
        external/libyuv/files/include \
        frameworks/native/include/media/hardware \
+       $(LOCAL_PATH)/../include \
        $(LOCAL_PATH)/../../goldfish-opengl/system/OpenglSystemCommon \
        $(call include-path-for, camera)
 
index 15c9d00..5ad5c4f 100755 (executable)
@@ -34,7 +34,7 @@
 #endif  // LOG_QUERIES
 
 #define QEMU_PIPE_DEBUG  LOGQ
-#include <system/qemu_pipe.h>
+#include "qemu_pipe.h"
 
 namespace android {
 
index f4b76c9..be21c97 100644 (file)
@@ -18,6 +18,7 @@ include $(CLEAR_VARS)
 
 LOCAL_MODULE := fingerprint.goldfish
 LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := fingerprint.c
 LOCAL_SHARED_LIBRARIES := liblog
 
@@ -27,6 +28,7 @@ include $(CLEAR_VARS)
 
 LOCAL_MODULE := fingerprint.ranchu
 LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := fingerprint.c
 LOCAL_SHARED_LIBRARIES := liblog
 
index fbe5d0f..4112d85 100644 (file)
  */
 #define LOG_TAG "FingerprintHal"
 
-#include <cutils/log.h>
-#include <hardware/hardware.h>
-#include <hardware/fingerprint.h>
-#include <system/qemu_pipe.h>
-
 #include <errno.h>
 #include <endian.h>
 #include <inttypes.h>
 #include <malloc.h>
-#include <poll.h>
-#include <stdbool.h>
-#include <stdlib.h>
 #include <string.h>
+#include <cutils/log.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
+#include "qemud.h"
 
+#include <poll.h>
 
-#define FINGERPRINT_LISTEN_SERVICE_NAME "pipe:qemud:fingerprintlisten"
+#define FINGERPRINT_LISTEN_SERVICE_NAME "fingerprintlisten"
 #define FINGERPRINT_FILENAME "emufp.bin"
 #define AUTHENTICATOR_ID_FILENAME "emuauthid.bin"
 #define MAX_COMM_CHARS 128
@@ -661,7 +658,7 @@ static void* listenerFunction(void* data) {
     ALOGD("----------------> %s ----------------->", __FUNCTION__);
     qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)data;
 
-    int fd = qemu_pipe_open(FINGERPRINT_LISTEN_SERVICE_NAME);
+    int fd = qemud_channel_open(FINGERPRINT_LISTEN_SERVICE_NAME);
     pthread_mutex_lock(&qdev->lock);
     qdev->qchanfd = fd;
     if (qdev->qchanfd < 0) {
@@ -672,9 +669,8 @@ static void* listenerFunction(void* data) {
     qdev->listener.state = STATE_IDLE;
     pthread_mutex_unlock(&qdev->lock);
 
-    static const char kListenCmd[] = "listen";
-    size_t kListenCmdSize = sizeof(kListenCmd) - 1U;
-    if (qemu_pipe_frame_send(qdev->qchanfd, kListenCmd, kListenCmdSize) < 0) {
+    const char* cmd = "listen";
+    if (qemud_channel_send(qdev->qchanfd, cmd, strlen(cmd)) < 0) {
         ALOGE("cannot write fingerprint 'listen' to host");
         goto done_quiet;
     }
@@ -729,8 +725,8 @@ static void* listenerFunction(void* data) {
         }
 
         // Shouldn't block since we were just notified of a POLLIN event
-        if ((size = qemu_pipe_frame_recv(qdev->qchanfd, buffer,
-                                         sizeof(buffer) - 1)) > 0) {
+        if ((size = qemud_channel_recv(qdev->qchanfd, buffer,
+                                       sizeof(buffer) - 1)) > 0) {
             buffer[size] = '\0';
             if (sscanf(buffer, "on:%d", &fid) == 1) {
                 if (fid > 0 && fid <= MAX_FID_VALUE) {
index 0abf1fb..c143981 100644 (file)
@@ -27,6 +27,7 @@ include $(CLEAR_VARS)
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_CFLAGS += -DQEMU_HARDWARE
 LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := gps_qemu.c
 ifeq ($(TARGET_PRODUCT),vbox_x86)
 LOCAL_MODULE := gps.vbox_x86
@@ -41,6 +42,7 @@ include $(CLEAR_VARS)
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_CFLAGS += -DQEMU_HARDWARE
 LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := gps_qemu.c
 LOCAL_MODULE := gps.ranchu
 
index 6f077d3..9f04152 100644 (file)
@@ -34,7 +34,7 @@
 #include <cutils/log.h>
 #include <cutils/sockets.h>
 #include <hardware/gps.h>
-#include <system/qemu_pipe.h>
+#include "qemu_pipe.h"
 
 /* the name of the qemu-controlled pipe */
 #define  QEMU_CHANNEL_NAME  "pipe:qemud:gps"
diff --git a/include/qemu.h b/include/qemu.h
new file mode 100644 (file)
index 0000000..b5ba463
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 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 _libs_hardware_qemu_h
+#define _libs_hardware_qemu_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* returns 1 iff we're running in the emulator */
+extern int  qemu_check(void);
+
+/* a structure used to hold enough state to connect to a given
+ * QEMU communication channel, either through a qemud socket or
+ * a serial port.
+ *
+ * initialize the structure by zero-ing it out
+ */
+typedef struct {
+    char   is_inited;
+    char   is_available;
+    char   is_qemud;
+    char   is_qemud_old;
+    char   is_tty;
+    int    fd;
+    char   device[32];
+} QemuChannel;
+
+/* try to open a qemu communication channel.
+ * returns a file descriptor on success, or -1 in case of
+ * error.
+ *
+ * 'channel' must be a QemuChannel structure that is empty
+ * on the first call. You can call this function several
+ * time to re-open the channel using the same 'channel'
+ * object to speed things a bit.
+ */
+extern int  qemu_channel_open( QemuChannel*  channel,
+                               const char*   name,
+                               int           mode );
+
+/* create a command made of a 4-hexchar prefix followed
+ * by the content. the prefix contains the content's length
+ * in hexadecimal coding.
+ *
+ * 'buffer' must be at last 6 bytes
+ * returns -1 in case of overflow, or the command's total length
+ * otherwise (i.e. content length + 4)
+ */
+extern int  qemu_command_format( char*        buffer, 
+                                 int          buffer_size,
+                                 const char*  format,
+                                 ... );
+
+/* directly sends a command through the 'hw-control' channel.
+ * this will open the channel, send the formatted command, then
+ * close the channel automatically.
+ * returns 0 on success, or -1 on error.
+ */
+extern int  qemu_control_command( const char*  fmt, ... );
+
+/* sends a question to the hw-control channel, then receive an answer in
+ * a user-allocated buffer. returns the length of the answer, or -1
+ * in case of error.
+ *
+ * 'question' *must* have been formatted through qemu_command_format
+ */
+extern int  qemu_control_query( const char*  question, int  questionlen,
+                                char*        answer,   int  answersize );
+
+/* use QEMU_FALLBACK(call) to call a QEMU-specific callback  */
+/* use QEMU_FALLBACK_VOID(call) if the function returns void */
+#  define  QEMU_FALLBACK(x)  \
+    do { \
+        if (qemu_check()) \
+            return qemu_ ## x ; \
+    } while (0)
+#  define  QEMU_FALLBACK_VOID(x)  \
+    do { \
+        if (qemu_check()) { \
+            qemu_ ## x ; \
+            return; \
+        } \
+    } while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _libs_hardware_qemu_h */
diff --git a/include/qemu_pipe.h b/include/qemu_pipe.h
new file mode 100644 (file)
index 0000000..53aec97
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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_INCLUDE_HARDWARE_QEMU_PIPE_H
+#define ANDROID_INCLUDE_HARDWARE_QEMU_PIPE_H
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <pthread.h>  /* for pthread_once() */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef D
+#  define  D(...)   do{}while(0)
+#endif
+
+/* Try to open a new Qemu fast-pipe. This function returns a file descriptor
+ * that can be used to communicate with a named service managed by the
+ * emulator.
+ *
+ * This file descriptor can be used as a standard pipe/socket descriptor.
+ *
+ * 'pipeName' is the name of the emulator service you want to connect to.
+ * E.g. 'opengles' or 'camera'.
+ *
+ * On success, return a valid file descriptor
+ * Returns -1 on error, and errno gives the error code, e.g.:
+ *
+ *    EINVAL  -> unknown/unsupported pipeName
+ *    ENOSYS  -> fast pipes not available in this system.
+ *
+ * ENOSYS should never happen, except if you're trying to run within a
+ * misconfigured emulator.
+ *
+ * You should be able to open several pipes to the same pipe service,
+ * except for a few special cases (e.g. GSM modem), where EBUSY will be
+ * returned if more than one client tries to connect to it.
+ */
+static __inline__ int
+qemu_pipe_open(const char*  pipeName)
+{
+    char  buff[256];
+    int   buffLen;
+    int   fd, ret;
+
+    if (pipeName == NULL || pipeName[0] == '\0') {
+        errno = EINVAL;
+        return -1;
+    }
+
+    snprintf(buff, sizeof buff, "pipe:%s", pipeName);
+
+    fd = open("/dev/qemu_pipe", O_RDWR);
+    if (fd < 0 && errno == ENOENT)
+        fd = open("/dev/goldfish_pipe", O_RDWR);
+    if (fd < 0) {
+        D("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__, strerror(errno));
+        //errno = ENOSYS;
+        return -1;
+    }
+
+    buffLen = strlen(buff);
+
+    ret = TEMP_FAILURE_RETRY(write(fd, buff, buffLen+1));
+    if (ret != buffLen+1) {
+        D("%s: Could not connect to %s pipe service: %s", __FUNCTION__, pipeName, strerror(errno));
+        if (ret == 0) {
+            errno = ECONNRESET;
+        } else if (ret > 0) {
+            errno = EINVAL;
+        }
+        return -1;
+    }
+
+    return fd;
+}
+
+#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/include/qemud.h b/include/qemud.h
new file mode 100644 (file)
index 0000000..ccc7b1e
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2008 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_INCLUDE_HARDWARE_QEMUD_H
+#define ANDROID_INCLUDE_HARDWARE_QEMUD_H
+
+#include <cutils/sockets.h>
+#include "qemu_pipe.h"
+
+/* the following is helper code that is used by the QEMU-specific
+ * hardware HAL modules to communicate with the emulator program
+ * through the 'qemud' multiplexing daemon, or through the qemud
+ * pipe.
+ *
+ * see the documentation comments for details in
+ * development/emulator/qemud/qemud.c
+ *
+ * all definitions here are built into the HAL module to avoid
+ * having to write a tiny shared library for this.
+ */
+
+/* we expect the D macro to be defined to a function macro
+ * that sends its formatted string argument(s) to the log.
+ * If not, ignore the traces.
+ */
+#  define  D(...) ((void)0)
+
+static __inline__ int
+qemud_fd_write(int  fd, const void*  buff, int  len)
+{
+    int  len2;
+    do {
+        len2 = write(fd, buff, len);
+    } while (len2 < 0 && errno == EINTR);
+    return len2;
+}
+
+static __inline__ int
+qemud_fd_read(int  fd, void*  buff, int  len)
+{
+    int  len2;
+    do {
+        len2 = read(fd, buff, len);
+    } while (len2 < 0 && errno == EINTR);
+    return len2;
+}
+
+static __inline__ int
+qemud_channel_open(const char*  name)
+{
+    int  fd;
+    int  namelen = strlen(name);
+    char answer[2];
+    char pipe_name[256];
+
+    /* First, try to connect to the pipe. */
+    snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", name);
+    fd = qemu_pipe_open(pipe_name);
+    D("%s: pipe name %s (name %s) fd %d", __FUNCTION__, pipe_name, name, fd);
+    if (fd < 0) {
+        D("QEMUD pipe is not available for %s: %s", name, strerror(errno));
+        /* If pipe is not available, connect to qemud control socket */
+        fd = socket_local_client( "qemud",
+                                  ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                  SOCK_STREAM );
+        if (fd < 0) {
+            D("no qemud control socket: %s", strerror(errno));
+            return -1;
+        }
+
+        /* send service name to connect */
+        if (qemud_fd_write(fd, name, namelen) != namelen) {
+            D("can't send service name to qemud: %s",
+               strerror(errno));
+            close(fd);
+            return -1;
+        }
+
+        /* read answer from daemon */
+        if (qemud_fd_read(fd, answer, 2) != 2 ||
+            answer[0] != 'O' || answer[1] != 'K') {
+            D("cant' connect to %s service through qemud", name);
+            close(fd);
+            return -1;
+        }
+    }
+    return fd;
+}
+
+static __inline__ int
+qemud_channel_send(int  fd, const void*  msg, int  msglen)
+{
+    char  header[5];
+
+    if (msglen < 0)
+        msglen = strlen((const char*)msg);
+
+    if (msglen == 0)
+        return 0;
+
+    snprintf(header, sizeof header, "%04x", msglen);
+    if (qemud_fd_write(fd, header, 4) != 4) {
+        D("can't write qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+
+    if (qemud_fd_write(fd, msg, msglen) != msglen) {
+        D("can4t write qemud frame payload: %s", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+static __inline__ int
+qemud_channel_recv(int  fd, void*  msg, int  msgsize)
+{
+    char  header[5];
+    int   size, avail;
+
+    if (qemud_fd_read(fd, header, 4) != 4) {
+        D("can't read qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    header[4] = 0;
+    if (sscanf(header, "%04x", &size) != 1) {
+        D("malformed qemud frame header: '%.*s'", 4, header);
+        return -1;
+    }
+    if (size > msgsize)
+        return -1;
+
+    if (qemud_fd_read(fd, msg, size) != size) {
+        D("can't read qemud frame payload: %s", strerror(errno));
+        return -1;
+    }
+    return size;
+}
+
+#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_H */
index 233548d..c8e7698 100644 (file)
@@ -1,18 +1,19 @@
 
 on early-init
-    mount debugfs debugfs /sys/kernel/debug
+    mount debugfs debugfs /sys/kernel/debug mode=755
 
 on init
 
-    symlink /dev/goldfish_pipe /dev/android_pipe
-    symlink /dev/goldfish_pipe /dev/qemu_pipe
-
 on boot
     setprop ARGH ARGH
     setprop net.eth0.gw 10.0.2.2
     setprop net.eth0.dns1 10.0.2.3
     setprop net.dns1 10.0.2.3
     setprop net.gprs.local-ip 10.0.2.15
+    setprop persist.adb.notify 1
+    setprop persist.sys.usb.config adb
+    setprop qemu.adb.secure 0
+    setprop ro.adb.secure 1
     setprop ro.radio.use-ppp no
     setprop ro.build.product generic
     setprop ro.product.device generic
@@ -41,7 +42,7 @@ on boot
     stop akmd
 
 # start essential services
-    setprop rild.libpath libreference-ril.so
+    start qemud
     start goldfish-logcat
     start goldfish-setup
 
@@ -69,7 +70,7 @@ on fs
 
 service goldfish-setup /system/etc/init.goldfish.sh
     user root
-    group root wakelock
+    group root
     oneshot
 
 # The qemu-props program is used to set various system
@@ -84,6 +85,10 @@ service qemu-props /system/bin/qemu-props
     group root
     oneshot
 
+service qemud /system/bin/qemud
+    socket qemud    stream 666
+    oneshot
+
 # -Q is a special logcat option that forces the
 # program to check wether it runs on the emulator
 # if it does, it redirects its output to the device
index 28e5115..60caf88 100644 (file)
@@ -17,7 +17,7 @@
 #define TEST_UTIL_H
 
 #include <stddef.h>
-#include <hardware/qemu_pipe.h>
+#include "qemu_pipe.h"
 
 
 double now_secs(void);
index fa783a9..dadcabc 100644 (file)
@@ -19,6 +19,8 @@ LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_C_INC := lights_qemu.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := lights_qemu.c
 LOCAL_MODULE := lights.goldfish
 LOCAL_CFLAGS += -DLIGHT_BACKLIGHT
index 3628588..1c408c1 100644 (file)
 #define LOG_TAG "Lights"
 #endif
 
-/* Set to 1 to enable debug messages to the log */
-#define DEBUG 0
-#if DEBUG
-# define D(...) ALOGD(__VA_ARGS__)
-#else
-# define D(...) do{}while(0)
-#endif
-
-#define  E(...)  ALOGE(__VA_ARGS__)
-
 /* we connect with the emulator through the "hw-control" qemud service */
-#define  LIGHTS_SERVICE_NAME "pipe:qemud:hw-control"
+#define  LIGHTS_SERVICE_NAME "hw-control"
 
 #include <cutils/log.h>
-
-#define DEBUG_QEMU_PIPE D
-#include <system/qemu_pipe.h>
-
-#include <hardware/lights.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
-#include <stdint.h>
-#include <string.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
-#include <unistd.h>
+#include <hardware/lights.h>
+#include "qemud.h"
+
+/* Set to 1 to enable debug messages to the log */
+#define DEBUG 0
+#if DEBUG
+# define D(...) ALOGD(__VA_ARGS__)
+#else
+# define D(...) do{}while(0)
+#endif
 
-#include <stdlib.h>
+#define  E(...)  ALOGE(__VA_ARGS__)
 
 /* Get brightness(0~255) from state. */
 static int
@@ -67,13 +62,13 @@ rgb_to_brightness( struct light_state_t const* state )
 
 /* set backlight brightness by LIGHTS_SERVICE_NAME service. */
 static int
-set_light_backlight( struct light_device_t* __unused dev, struct light_state_t const* state )
+set_light_backlight( struct light_device_t* dev, struct light_state_t const* state )
 {
     /* Get Lights service. */
-    int  fd = qemu_pipe_open(LIGHTS_SERVICE_NAME);
+    int  fd = qemud_channel_open( LIGHTS_SERVICE_NAME );
 
     if (fd < 0) {
-        E( "%s: no qemu pipe connection", __FUNCTION__ );
+        E( "%s: no qemud connection", __FUNCTION__ );
         return -1;
     }
 
@@ -92,7 +87,7 @@ set_light_backlight( struct light_device_t* __unused dev, struct light_state_t c
     D( "%s: lcd_backlight command: %s", __FUNCTION__, buffer );
 
     /* send backlight command to perform the backlight setting. */
-    if (qemu_pipe_frame_send(fd, buffer, strlen(buffer)) < 0) {
+    if (qemud_channel_send( fd, buffer, -1 ) < 0) {
         E( "%s: could not query lcd_backlight: %s", __FUNCTION__, strerror(errno) );
         close( fd );
         return -1;
@@ -103,8 +98,7 @@ set_light_backlight( struct light_device_t* __unused dev, struct light_state_t c
 }
 
 static int
-set_light_buttons( struct light_device_t* __unused dev,
-                   struct light_state_t const* __unused state )
+set_light_buttons( struct light_device_t* dev, struct light_state_t const* state )
 {
     /* @Waiting for later implementation. */
     D( "%s: Not implemented.", __FUNCTION__ );
@@ -113,8 +107,7 @@ set_light_buttons( struct light_device_t* __unused dev,
 }
 
 static int
-set_light_battery( struct light_device_t* __unused dev,
-                   struct light_state_t const* __unused state )
+set_light_battery( struct light_device_t* dev, struct light_state_t const* state )
 {
     /* @Waiting for later implementation. */
     D( "%s: Not implemented.", __FUNCTION__ );
@@ -123,8 +116,7 @@ set_light_battery( struct light_device_t* __unused dev,
 }
 
 static int
-set_light_keyboard( struct light_device_t* __unused dev,
-                    struct light_state_t const* __unused state )
+set_light_keyboard( struct light_device_t* dev, struct light_state_t const* state )
 {
     /* @Waiting for later implementation. */
     D( "%s: Not implemented.", __FUNCTION__ );
@@ -133,8 +125,7 @@ set_light_keyboard( struct light_device_t* __unused dev,
 }
 
 static int
-set_light_notifications( struct light_device_t* __unused dev,
-                         struct light_state_t const* __unused state )
+set_light_notifications( struct light_device_t* dev, struct light_state_t const* state )
 {
     /* @Waiting for later implementation. */
     D( "%s: Not implemented.", __FUNCTION__ );
@@ -143,8 +134,7 @@ set_light_notifications( struct light_device_t* __unused dev,
 }
 
 static int
-set_light_attention( struct light_device_t* __unused dev,
-                     struct light_state_t const* __unused state )
+set_light_attention( struct light_device_t* dev, struct light_state_t const* state )
 {
     /* @Waiting for later implementation. */
     D( "%s: Not implemented.", __FUNCTION__ );
index 0273f04..ffbc457 100644 (file)
@@ -22,6 +22,7 @@ include $(CLEAR_VARS)
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_CFLAGS += -DQEMU_HARDWARE
 LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := power_qemu.c
 LOCAL_MODULE := power.goldfish
 LOCAL_MODULE_TAGS := optional
index 86c3ffd..808589a 100644 (file)
 
 #include <hardware/hardware.h>
 #include <hardware/power.h>
-#include <system/qemu_pipe.h>
+#include "qemud.h"
 #include <fcntl.h>
 #include <errno.h>
 
 static int qemud_fd;
 
-static void power_qemu_init(struct power_module * __unused module)
+static void power_qemu_init(struct power_module *module)
 {
-    qemud_fd = qemu_pipe_open("pipe:qemud:hw-control");
+    qemud_fd = qemud_channel_open("hw-control");
 
     if (qemud_fd < 0)
         ALOGE("Error connecting to qemud hw-control service\n");
 }
 
-static void power_qemu_set_interactive(struct power_module * __unused module,
-                                       int on)
+static void power_qemu_set_interactive(struct power_module *module, int on)
 {
-    const char* command = on ? "power:screen_state:wake" :
-            "power:screen_state:standby";
+    int r;
+
+    r = qemud_channel_send(qemud_fd, on ? "power:screen_state:wake"
+                           : "power:screen_state:standby", -1);
 
-    int r = qemu_pipe_frame_send(qemud_fd, command, strlen(command));
     if (r < 0)
         ALOGE("Error sending power command to qemud hw-control service\n");
 }
index 78ab1d5..d2af64d 100644 (file)
@@ -23,6 +23,7 @@ LOCAL_PATH := $(call my-dir)
 #
 include $(CLEAR_VARS)
 LOCAL_MODULE    := qemu-props
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := qemu-props.c
 LOCAL_SHARED_LIBRARIES := libcutils liblog
 include $(BUILD_EXECUTABLE)
index 0b3ea11..a5dc819 100644 (file)
@@ -34,8 +34,8 @@
 #endif
 
 #include <cutils/properties.h>
-#include <system/qemu_pipe.h>
 #include <unistd.h>
+#include "qemud.h"
 
 /* Name of the qemud service we want to connect to.
  */
@@ -52,7 +52,7 @@ int  main(void)
         int  tries = MAX_TRIES;
 
         while (1) {
-            qemud_fd = qemu_pipe_open( "pipe:qemud:boot-properties" );
+            qemud_fd = qemud_channel_open( "boot-properties" );
             if (qemud_fd >= 0)
                 break;
 
@@ -69,7 +69,7 @@ int  main(void)
     DD("connected to '%s' qemud service.", QEMUD_SERVICE);
 
     /* send the 'list' command to the service */
-    if (qemu_pipe_frame_send(qemud_fd, "list", 4) < 0) {
+    if (qemud_channel_send(qemud_fd, "list", -1) < 0) {
         DD("could not send command to '%s' service", QEMUD_SERVICE);
         return 1;
     }
@@ -83,7 +83,7 @@ int  main(void)
         DD("receiving..");
         char* q;
         char  temp[BUFF_SIZE];
-        int   len = qemu_pipe_frame_recv(qemud_fd, temp, sizeof temp - 1);
+        int   len = qemud_channel_recv(qemud_fd, temp, sizeof temp - 1);
 
         /* lone NUL-byte signals end of properties */
         if (len < 0 || len > BUFF_SIZE-1 || temp[0] == '\0')
diff --git a/qemud/Android.mk b/qemud/Android.mk
new file mode 100644 (file)
index 0000000..30ee5eb
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright 2008 The Android Open Source Project
+
+# We're moving the emulator-specific platform libs to
+# development.git/tools/emulator/. The following test is to ensure
+# smooth builds even if the tree contains both versions.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+       qemud.c
+
+
+LOCAL_SHARED_LIBRARIES := \
+       libcutils liblog
+
+LOCAL_MODULE:= qemud
+
+include $(BUILD_EXECUTABLE)
diff --git a/qemud/qemud.c b/qemud/qemud.c
new file mode 100644 (file)
index 0000000..3d18893
--- /dev/null
@@ -0,0 +1,1720 @@
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <termios.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+
+/*
+ *  the qemud daemon program is only used within Android as a bridge
+ *  between the emulator program and the emulated system. it really works as
+ *  a simple stream multiplexer that works as follows:
+ *
+ *    - qemud is started by init following instructions in
+ *      /system/etc/init.goldfish.rc (i.e. it is never started on real devices)
+ *
+ *    - qemud communicates with the emulator program through a single serial
+ *      port, whose name is passed through a kernel boot parameter
+ *      (e.g. android.qemud=ttyS1)
+ *
+ *    - qemud binds one unix local stream socket (/dev/socket/qemud, created
+ *      by init through /system/etc/init.goldfish.rc).
+ *
+ *
+ *      emulator <==serial==> qemud <---> /dev/socket/qemud <-+--> client1
+ *                                                            |
+ *                                                            +--> client2
+ *
+ *   - the special channel index 0 is used by the emulator and qemud only.
+ *     other channel numbers correspond to clients. More specifically,
+ *     connection are created like this:
+ *
+ *     * the client connects to /dev/socket/qemud
+ *
+ *     * the client sends the service name through the socket, as
+ *            <service-name>
+ *
+ *     * qemud creates a "Client" object internally, assigns it an
+ *       internal unique channel number > 0, then sends a connection
+ *       initiation request to the emulator (i.e. through channel 0):
+ *
+ *           connect:<id>:<name>
+ *
+ *       where <name> is the service name, and <id> is a 2-hexchar
+ *       number corresponding to the channel number.
+ *
+ *     * in case of success, the emulator responds through channel 0
+ *       with:
+ *
+ *           ok:connect:<id>
+ *
+ *       after this, all messages between the client and the emulator
+ *       are passed in pass-through mode.
+ *
+ *     * if the emulator refuses the service connection, it will
+ *       send the following through channel 0:
+ *
+ *           ko:connect:<id>:reason-for-failure
+ *
+ *     * If the client closes the connection, qemud sends the following
+ *       to the emulator:
+ *
+ *           disconnect:<id>
+ *
+ *       The same message is the opposite direction if the emulator
+ *       chooses to close the connection.
+ *
+ *     * any command sent through channel 0 to the emulator that is
+ *       not properly recognized will be answered by:
+ *
+ *           ko:unknown command
+ *
+ *
+ *  Internally, the daemon maintains a "Client" object for each client
+ *  connection (i.e. accepting socket connection).
+ */
+
+/* name of the single control socket used by the daemon */
+#define CONTROL_SOCKET_NAME  "qemud"
+
+#define  DEBUG     0
+#define  T_ACTIVE  0  /* set to 1 to dump traffic */
+
+#if DEBUG
+#  define LOG_TAG  "qemud"
+#  include <cutils/log.h>
+#  define  D(...)   ALOGD(__VA_ARGS__)
+#else
+#  define  D(...)  ((void)0)
+#  define  T(...)  ((void)0)
+#endif
+
+#if T_ACTIVE
+#  define  T(...)   D(__VA_ARGS__)
+#else
+#  define  T(...)   ((void)0)
+#endif
+
+/** UTILITIES
+ **/
+
+static void
+fatal( const char*  fmt, ... )
+{
+    va_list  args;
+    va_start(args, fmt);
+    fprintf(stderr, "PANIC: ");
+    vfprintf(stderr, fmt, args);
+    fprintf(stderr, "\n" );
+    va_end(args);
+    exit(1);
+}
+
+static void*
+xalloc( size_t   sz )
+{
+    void*  p;
+
+    if (sz == 0)
+        return NULL;
+
+    p = malloc(sz);
+    if (p == NULL)
+        fatal( "not enough memory" );
+
+    return p;
+}
+
+#define  xnew(p)   (p) = xalloc(sizeof(*(p)))
+
+static void*
+xalloc0( size_t  sz )
+{
+    void*  p = xalloc(sz);
+    memset( p, 0, sz );
+    return p;
+}
+
+#define  xnew0(p)   (p) = xalloc0(sizeof(*(p)))
+
+#define  xfree(p)    (free((p)), (p) = NULL)
+
+static void*
+xrealloc( void*  block, size_t  size )
+{
+    void*  p = realloc( block, size );
+
+    if (p == NULL && size > 0)
+        fatal( "not enough memory" );
+
+    return p;
+}
+
+#define  xrenew(p,count)  (p) = xrealloc((p),sizeof(*(p))*(count))
+
+static int
+hex2int( const uint8_t*  data, int  len )
+{
+    int  result = 0;
+    while (len > 0) {
+        int       c = *data++;
+        unsigned  d;
+
+        result <<= 4;
+        do {
+            d = (unsigned)(c - '0');
+            if (d < 10)
+                break;
+
+            d = (unsigned)(c - 'a');
+            if (d < 6) {
+                d += 10;
+                break;
+            }
+
+            d = (unsigned)(c - 'A');
+            if (d < 6) {
+                d += 10;
+                break;
+            }
+
+            return -1;
+        }
+        while (0);
+
+        result |= d;
+        len    -= 1;
+    }
+    return  result;
+}
+
+
+static void
+int2hex( int  value, uint8_t*  to, int  width )
+{
+    int  nn = 0;
+    static const char hexchars[16] = "0123456789abcdef";
+
+    for ( --width; width >= 0; width--, nn++ ) {
+        to[nn] = hexchars[(value >> (width*4)) & 15];
+    }
+}
+
+static int
+fd_read(int  fd, void*  to, int  len)
+{
+    int  ret;
+
+    do {
+        ret = read(fd, to, len);
+    } while (ret < 0 && errno == EINTR);
+
+    return ret;
+}
+
+static int
+fd_write(int  fd, const void*  from, int  len)
+{
+    int  ret;
+
+    do {
+        ret = write(fd, from, len);
+    } while (ret < 0 && errno == EINTR);
+
+    return ret;
+}
+
+static void
+fd_setnonblock(int  fd)
+{
+    int  ret, flags;
+
+    do {
+        flags = fcntl(fd, F_GETFD);
+    } while (flags < 0 && errno == EINTR);
+
+    if (flags < 0) {
+        fatal( "%s: could not get flags for fd %d: %s",
+               __FUNCTION__, fd, strerror(errno) );
+    }
+
+    do {
+        ret = fcntl(fd, F_SETFD, flags | O_NONBLOCK);
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret < 0) {
+        fatal( "%s: could not set fd %d to non-blocking: %s",
+               __FUNCTION__, fd, strerror(errno) );
+    }
+}
+
+
+static int
+fd_accept(int  fd)
+{
+    struct sockaddr  from;
+    socklen_t        fromlen = sizeof(from);
+    int              ret;
+
+    do {
+        ret = accept(fd, &from, &fromlen);
+    } while (ret < 0 && errno == EINTR);
+
+    return ret;
+}
+
+/** FD EVENT LOOP
+ **/
+
+/* A Looper object is used to monitor activity on one or more
+ * file descriptors (e.g sockets).
+ *
+ * - call looper_add() to register a function that will be
+ *   called when events happen on the file descriptor.
+ *
+ * - call looper_enable() or looper_disable() to enable/disable
+ *   the set of monitored events for a given file descriptor.
+ *
+ * - call looper_del() to unregister a file descriptor.
+ *   this does *not* close the file descriptor.
+ *
+ * Note that you can only provide a single function to handle
+ * all events related to a given file descriptor.
+
+ * You can call looper_enable/_disable/_del within a function
+ * callback.
+ */
+
+/* the current implementation uses Linux's epoll facility
+ * the event mask we use are simply combinations of EPOLLIN
+ * EPOLLOUT, EPOLLHUP and EPOLLERR
+ */
+#include <sys/epoll.h>
+
+#define  MAX_CHANNELS  16
+#define  MAX_EVENTS    (MAX_CHANNELS+1)  /* each channel + the serial fd */
+
+/* the event handler function type, 'user' is a user-specific
+ * opaque pointer passed to looper_add().
+ */
+typedef void (*EventFunc)( void*  user, int  events );
+
+/* bit flags for the LoopHook structure.
+ *
+ * HOOK_PENDING means that an event happened on the
+ * corresponding file descriptor.
+ *
+ * HOOK_CLOSING is used to delay-close monitored
+ * file descriptors.
+ */
+enum {
+    HOOK_PENDING = (1 << 0),
+    HOOK_CLOSING = (1 << 1),
+};
+
+/* A LoopHook structure is used to monitor a given
+ * file descriptor and record its event handler.
+ */
+typedef struct {
+    int        fd;
+    int        wanted;  /* events we are monitoring */
+    int        events;  /* events that occured */
+    int        state;   /* see HOOK_XXX constants */
+    void*      ev_user; /* user-provided handler parameter */
+    EventFunc  ev_func; /* event handler callback */
+} LoopHook;
+
+/* Looper is the main object modeling a looper object
+ */
+typedef struct {
+    int                  epoll_fd;
+    int                  num_fds;
+    int                  max_fds;
+    struct epoll_event*  events;
+    LoopHook*            hooks;
+} Looper;
+
+/* initialize a looper object */
+static void
+looper_init( Looper*  l )
+{
+    l->epoll_fd = epoll_create(4);
+    l->num_fds  = 0;
+    l->max_fds  = 0;
+    l->events   = NULL;
+    l->hooks    = NULL;
+}
+
+/* finalize a looper object */
+static void
+looper_done( Looper*  l )
+{
+    xfree(l->events);
+    xfree(l->hooks);
+    l->max_fds = 0;
+    l->num_fds = 0;
+
+    close(l->epoll_fd);
+    l->epoll_fd  = -1;
+}
+
+/* return the LoopHook corresponding to a given
+ * monitored file descriptor, or NULL if not found
+ */
+static LoopHook*
+looper_find( Looper*  l, int  fd )
+{
+    LoopHook*  hook = l->hooks;
+    LoopHook*  end  = hook + l->num_fds;
+
+    for ( ; hook < end; hook++ ) {
+        if (hook->fd == fd)
+            return hook;
+    }
+    return NULL;
+}
+
+/* grow the arrays in the looper object */
+static void
+looper_grow( Looper*  l )
+{
+    int  old_max = l->max_fds;
+    int  new_max = old_max + (old_max >> 1) + 4;
+    int  n;
+
+    xrenew( l->events, new_max );
+    xrenew( l->hooks,  new_max );
+    l->max_fds = new_max;
+
+    /* now change the handles to all events */
+    for (n = 0; n < l->num_fds; n++) {
+        struct epoll_event ev;
+        LoopHook*          hook = l->hooks + n;
+
+        ev.events   = hook->wanted;
+        ev.data.ptr = hook;
+        epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, hook->fd, &ev );
+    }
+}
+
+/* register a file descriptor and its event handler.
+ * no event mask will be enabled
+ */
+static void
+looper_add( Looper*  l, int  fd, EventFunc  func, void*  user )
+{
+    struct epoll_event  ev;
+    LoopHook*           hook;
+
+    if (l->num_fds >= l->max_fds)
+        looper_grow(l);
+
+    hook = l->hooks + l->num_fds;
+
+    hook->fd      = fd;
+    hook->ev_user = user;
+    hook->ev_func = func;
+    hook->state   = 0;
+    hook->wanted  = 0;
+    hook->events  = 0;
+
+    fd_setnonblock(fd);
+
+    ev.events   = 0;
+    ev.data.ptr = hook;
+    epoll_ctl( l->epoll_fd, EPOLL_CTL_ADD, fd, &ev );
+
+    l->num_fds += 1;
+}
+
+/* unregister a file descriptor and its event handler
+ */
+static void
+looper_del( Looper*  l, int  fd )
+{
+    LoopHook*  hook = looper_find( l, fd );
+
+    if (!hook) {
+        D( "%s: invalid fd: %d", __FUNCTION__, fd );
+        return;
+    }
+    /* don't remove the hook yet */
+    hook->state |= HOOK_CLOSING;
+
+    epoll_ctl( l->epoll_fd, EPOLL_CTL_DEL, fd, NULL );
+}
+
+/* enable monitoring of certain events for a file
+ * descriptor. This adds 'events' to the current
+ * event mask
+ */
+static void
+looper_enable( Looper*  l, int  fd, int  events )
+{
+    LoopHook*  hook = looper_find( l, fd );
+
+    if (!hook) {
+        D("%s: invalid fd: %d", __FUNCTION__, fd );
+        return;
+    }
+
+    if (events & ~hook->wanted) {
+        struct epoll_event  ev;
+
+        hook->wanted |= events;
+        ev.events   = hook->wanted;
+        ev.data.ptr = hook;
+
+        epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, fd, &ev );
+    }
+}
+
+/* disable monitoring of certain events for a file
+ * descriptor. This ignores events that are not
+ * currently enabled.
+ */
+static void
+looper_disable( Looper*  l, int  fd, int  events )
+{
+    LoopHook*  hook = looper_find( l, fd );
+
+    if (!hook) {
+        D("%s: invalid fd: %d", __FUNCTION__, fd );
+        return;
+    }
+
+    if (events & hook->wanted) {
+        struct epoll_event  ev;
+
+        hook->wanted &= ~events;
+        ev.events   = hook->wanted;
+        ev.data.ptr = hook;
+
+        epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, fd, &ev );
+    }
+}
+
+/* wait until an event occurs on one of the registered file
+ * descriptors. Only returns in case of error !!
+ */
+static void
+looper_loop( Looper*  l )
+{
+    for (;;) {
+        int  n, count;
+
+        do {
+            count = epoll_wait( l->epoll_fd, l->events, l->num_fds, -1 );
+        } while (count < 0 && errno == EINTR);
+
+        if (count < 0) {
+            D("%s: error: %s", __FUNCTION__, strerror(errno) );
+            return;
+        }
+
+        if (count == 0) {
+            D("%s: huh ? epoll returned count=0", __FUNCTION__);
+            continue;
+        }
+
+        /* mark all pending hooks */
+        for (n = 0; n < count; n++) {
+            LoopHook*  hook = l->events[n].data.ptr;
+            hook->state  = HOOK_PENDING;
+            hook->events = l->events[n].events;
+        }
+
+        /* execute hook callbacks. this may change the 'hooks'
+         * and 'events' array, as well as l->num_fds, so be careful */
+        for (n = 0; n < l->num_fds; n++) {
+            LoopHook*  hook = l->hooks + n;
+            if (hook->state & HOOK_PENDING) {
+                hook->state &= ~HOOK_PENDING;
+                hook->ev_func( hook->ev_user, hook->events );
+            }
+        }
+
+        /* now remove all the hooks that were closed by
+         * the callbacks */
+        for (n = 0; n < l->num_fds;) {
+            struct epoll_event ev;
+            LoopHook*  hook = l->hooks + n;
+
+            if (!(hook->state & HOOK_CLOSING)) {
+                n++;
+                continue;
+            }
+
+            hook[0]     = l->hooks[l->num_fds-1];
+            l->num_fds -= 1;
+            ev.events   = hook->wanted;
+            ev.data.ptr = hook;
+            epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, hook->fd, &ev );
+        }
+    }
+}
+
+#if T_ACTIVE
+char*
+quote( const void*  data, int  len )
+{
+    const char*  p   = data;
+    const char*  end = p + len;
+    int          count = 0;
+    int          phase = 0;
+    static char*  buff = NULL;
+
+    for (phase = 0; phase < 2; phase++) {
+        if (phase != 0) {
+            xfree(buff);
+            buff = xalloc(count+1);
+        }
+        count = 0;
+        for (p = data; p < end; p++) {
+            int  c = *p;
+
+            if (c == '\\') {
+                if (phase != 0) {
+                    buff[count] = buff[count+1] = '\\';
+                }
+                count += 2;
+                continue;
+            }
+
+            if (c >= 32 && c < 127) {
+                if (phase != 0)
+                    buff[count] = c;
+                count += 1;
+                continue;
+            }
+
+
+            if (c == '\t') {
+                if (phase != 0) {
+                    memcpy(buff+count, "<TAB>", 5);
+                }
+                count += 5;
+                continue;
+            }
+            if (c == '\n') {
+                if (phase != 0) {
+                    memcpy(buff+count, "<LN>", 4);
+                }
+                count += 4;
+                continue;
+            }
+            if (c == '\r') {
+                if (phase != 0) {
+                    memcpy(buff+count, "<CR>", 4);
+                }
+                count += 4;
+                continue;
+            }
+
+            if (phase != 0) {
+                buff[count+0] = '\\';
+                buff[count+1] = 'x';
+                buff[count+2] = "0123456789abcdef"[(c >> 4) & 15];
+                buff[count+3] = "0123456789abcdef"[     (c) & 15];
+            }
+            count += 4;
+        }
+    }
+    buff[count] = 0;
+    return buff;
+}
+#endif /* T_ACTIVE */
+
+/** PACKETS
+ **
+ ** We need a way to buffer data before it can be sent to the
+ ** corresponding file descriptor. We use linked list of Packet
+ ** objects to do this.
+ **/
+
+typedef struct Packet   Packet;
+
+#define  MAX_PAYLOAD  4000
+
+struct Packet {
+    Packet*   next;
+    int       len;
+    int       channel;
+    uint8_t   data[ MAX_PAYLOAD ];
+};
+
+/* we expect to alloc/free a lot of packets during
+ * operations so use a single linked list of free packets
+ * to keep things speedy and simple.
+ */
+static Packet*   _free_packets;
+
+/* Allocate a packet */
+static Packet*
+packet_alloc(void)
+{
+    Packet*  p = _free_packets;
+    if (p != NULL) {
+        _free_packets = p->next;
+    } else {
+        xnew(p);
+    }
+    p->next    = NULL;
+    p->len     = 0;
+    p->channel = -1;
+    return p;
+}
+
+/* Release a packet. This takes the address of a packet
+ * pointer that will be set to NULL on exit (avoids
+ * referencing dangling pointers in case of bugs)
+ */
+static void
+packet_free( Packet*  *ppacket )
+{
+    Packet*  p = *ppacket;
+    if (p) {
+        p->next       = _free_packets;
+        _free_packets = p;
+        *ppacket = NULL;
+    }
+}
+
+/** PACKET RECEIVER
+ **
+ ** Simple abstraction for something that can receive a packet
+ ** from a FDHandler (see below) or something else.
+ **
+ ** Send a packet to it with 'receiver_post'
+ **
+ ** Call 'receiver_close' to indicate that the corresponding
+ ** packet source was closed.
+ **/
+
+typedef void (*PostFunc) ( void*  user, Packet*  p );
+typedef void (*CloseFunc)( void*  user );
+
+typedef struct {
+    PostFunc   post;
+    CloseFunc  close;
+    void*      user;
+} Receiver;
+
+/* post a packet to a receiver. Note that this transfers
+ * ownership of the packet to the receiver.
+ */
+static __inline__ void
+receiver_post( Receiver*  r, Packet*  p )
+{
+    if (r->post)
+        r->post( r->user, p );
+    else
+        packet_free(&p);
+}
+
+/* tell a receiver the packet source was closed.
+ * this will also prevent further posting to the
+ * receiver.
+ */
+static __inline__ void
+receiver_close( Receiver*  r )
+{
+    if (r->close) {
+        r->close( r->user );
+        r->close = NULL;
+    }
+    r->post  = NULL;
+}
+
+
+/** FD HANDLERS
+ **
+ ** these are smart listeners that send incoming packets to a receiver
+ ** and can queue one or more outgoing packets and send them when
+ ** possible to the FD.
+ **
+ ** note that we support clean shutdown of file descriptors,
+ ** i.e. we try to send all outgoing packets before destroying
+ ** the FDHandler.
+ **/
+
+typedef struct FDHandler      FDHandler;
+typedef struct FDHandlerList  FDHandlerList;
+
+struct FDHandler {
+    int             fd;
+    FDHandlerList*  list;
+    char            closing;
+    Receiver        receiver[1];
+
+    /* queue of outgoing packets */
+    int             out_pos;
+    Packet*         out_first;
+    Packet**        out_ptail;
+
+    FDHandler*      next;
+    FDHandler**     pref;
+
+};
+
+struct FDHandlerList {
+    /* the looper that manages the fds */
+    Looper*      looper;
+
+    /* list of active FDHandler objects */
+    FDHandler*   active;
+
+    /* list of closing FDHandler objects.
+     * these are waiting to push their
+     * queued packets to the fd before
+     * freeing themselves.
+     */
+    FDHandler*   closing;
+
+};
+
+/* remove a FDHandler from its current list */
+static void
+fdhandler_remove( FDHandler*  f )
+{
+    f->pref[0] = f->next;
+    if (f->next)
+        f->next->pref = f->pref;
+}
+
+/* add a FDHandler to a given list */
+static void
+fdhandler_prepend( FDHandler*  f, FDHandler**  list )
+{
+    f->next = list[0];
+    f->pref = list;
+    list[0] = f;
+    if (f->next)
+        f->next->pref = &f->next;
+}
+
+/* initialize a FDHandler list */
+static void
+fdhandler_list_init( FDHandlerList*  list, Looper*  looper )
+{
+    list->looper  = looper;
+    list->active  = NULL;
+    list->closing = NULL;
+}
+
+
+/* close a FDHandler (and free it). Note that this will not
+ * perform a graceful shutdown, i.e. all packets in the
+ * outgoing queue will be immediately free.
+ *
+ * this *will* notify the receiver that the file descriptor
+ * was closed.
+ *
+ * you should call fdhandler_shutdown() if you want to
+ * notify the FDHandler that its packet source is closed.
+ */
+static void
+fdhandler_close( FDHandler*  f )
+{
+    /* notify receiver */
+    receiver_close(f->receiver);
+
+    /* remove the handler from its list */
+    fdhandler_remove(f);
+
+    /* get rid of outgoing packet queue */
+    if (f->out_first != NULL) {
+        Packet*  p;
+        while ((p = f->out_first) != NULL) {
+            f->out_first = p->next;
+            packet_free(&p);
+        }
+    }
+
+    /* get rid of file descriptor */
+    if (f->fd >= 0) {
+        looper_del( f->list->looper, f->fd );
+        close(f->fd);
+        f->fd = -1;
+    }
+
+    f->list = NULL;
+    xfree(f);
+}
+
+/* Ask the FDHandler to cleanly shutdown the connection,
+ * i.e. send any pending outgoing packets then auto-free
+ * itself.
+ */
+static void
+fdhandler_shutdown( FDHandler*  f )
+{
+    /* prevent later fdhandler_close() to
+     * call the receiver's close.
+     */
+    f->receiver->close = NULL;
+
+    if (f->out_first != NULL && !f->closing)
+    {
+        /* move the handler to the 'closing' list */
+        f->closing = 1;
+        fdhandler_remove(f);
+        fdhandler_prepend(f, &f->list->closing);
+        return;
+    }
+
+    fdhandler_close(f);
+}
+
+/* Enqueue a new packet that the FDHandler will
+ * send through its file descriptor.
+ */
+static void
+fdhandler_enqueue( FDHandler*  f, Packet*  p )
+{
+    Packet*  first = f->out_first;
+
+    p->next         = NULL;
+    f->out_ptail[0] = p;
+    f->out_ptail    = &p->next;
+
+    if (first == NULL) {
+        f->out_pos = 0;
+        looper_enable( f->list->looper, f->fd, EPOLLOUT );
+    }
+}
+
+
+/* FDHandler file descriptor event callback for read/write ops */
+static void
+fdhandler_event( FDHandler*  f, int  events )
+{
+   int  len;
+
+    /* in certain cases, it's possible to have both EPOLLIN and
+     * EPOLLHUP at the same time. This indicates that there is incoming
+     * data to read, but that the connection was nonetheless closed
+     * by the sender. Be sure to read the data before closing
+     * the receiver to avoid packet loss.
+     */
+
+    if (events & EPOLLIN) {
+        Packet*  p = packet_alloc();
+        int      len;
+
+        if ((len = fd_read(f->fd, p->data, MAX_PAYLOAD)) < 0) {
+            D("%s: can't recv: %s", __FUNCTION__, strerror(errno));
+            packet_free(&p);
+        } else if (len > 0) {
+            p->len     = len;
+            p->channel = -101;  /* special debug value, not used */
+            receiver_post( f->receiver, p );
+        }
+    }
+
+    if (events & (EPOLLHUP|EPOLLERR)) {
+        /* disconnection */
+        D("%s: disconnect on fd %d", __FUNCTION__, f->fd);
+        fdhandler_close(f);
+        return;
+    }
+
+    if (events & EPOLLOUT && f->out_first) {
+        Packet*  p = f->out_first;
+        int      avail, len;
+
+        avail = p->len - f->out_pos;
+        if ((len = fd_write(f->fd, p->data + f->out_pos, avail)) < 0) {
+            D("%s: can't send: %s", __FUNCTION__, strerror(errno));
+        } else {
+            f->out_pos += len;
+            if (f->out_pos >= p->len) {
+                f->out_pos   = 0;
+                f->out_first = p->next;
+                packet_free(&p);
+                if (f->out_first == NULL) {
+                    f->out_ptail = &f->out_first;
+                    looper_disable( f->list->looper, f->fd, EPOLLOUT );
+                }
+            }
+        }
+    }
+}
+
+
+/* Create a new FDHandler that monitors read/writes */
+static FDHandler*
+fdhandler_new( int             fd,
+               FDHandlerList*  list,
+               Receiver*       receiver )
+{
+    FDHandler*  f = xalloc0(sizeof(*f));
+
+    f->fd          = fd;
+    f->list        = list;
+    f->receiver[0] = receiver[0];
+    f->out_first   = NULL;
+    f->out_ptail   = &f->out_first;
+    f->out_pos     = 0;
+
+    fdhandler_prepend(f, &list->active);
+
+    looper_add( list->looper, fd, (EventFunc) fdhandler_event, f );
+    looper_enable( list->looper, fd, EPOLLIN );
+
+    return f;
+}
+
+
+/* event callback function to monitor accepts() on server sockets.
+ * the convention used here is that the receiver will receive a
+ * dummy packet with the new client socket in p->channel
+ */
+static void
+fdhandler_accept_event( FDHandler*  f, int  events )
+{
+    if (events & EPOLLIN) {
+        /* this is an accept - send a dummy packet to the receiver */
+        Packet*  p = packet_alloc();
+
+        D("%s: accepting on fd %d", __FUNCTION__, f->fd);
+        p->data[0] = 1;
+        p->len     = 1;
+        p->channel = fd_accept(f->fd);
+        if (p->channel < 0) {
+            D("%s: accept failed ?: %s", __FUNCTION__, strerror(errno));
+            packet_free(&p);
+            return;
+        }
+        receiver_post( f->receiver, p );
+    }
+
+    if (events & (EPOLLHUP|EPOLLERR)) {
+        /* disconnecting !! */
+        D("%s: closing accept fd %d", __FUNCTION__, f->fd);
+        fdhandler_close(f);
+        return;
+    }
+}
+
+
+/* Create a new FDHandler used to monitor new connections on a
+ * server socket. The receiver must expect the new connection
+ * fd in the 'channel' field of a dummy packet.
+ */
+static FDHandler*
+fdhandler_new_accept( int             fd,
+                      FDHandlerList*  list,
+                      Receiver*       receiver )
+{
+    FDHandler*  f = xalloc0(sizeof(*f));
+
+    f->fd          = fd;
+    f->list        = list;
+    f->receiver[0] = receiver[0];
+
+    fdhandler_prepend(f, &list->active);
+
+    looper_add( list->looper, fd, (EventFunc) fdhandler_accept_event, f );
+    looper_enable( list->looper, fd, EPOLLIN );
+    listen( fd, 5 );
+
+    return f;
+}
+
+/** SERIAL CONNECTION STATE
+ **
+ ** The following is used to handle the framing protocol
+ ** used on the serial port connection.
+ **/
+
+/* each packet is made of a 6 byte header followed by a payload
+ * the header looks like:
+ *
+ *   offset   size    description
+ *       0       2    a 2-byte hex string for the channel number
+ *       4       4    a 4-char hex string for the size of the payload
+ *       6       n    the payload itself
+ */
+#define  HEADER_SIZE    6
+#define  CHANNEL_OFFSET 0
+#define  LENGTH_OFFSET  2
+#define  CHANNEL_SIZE   2
+#define  LENGTH_SIZE    4
+
+#define  CHANNEL_CONTROL  0
+
+/* The Serial object receives data from the serial port,
+ * extracts the payload size and channel index, then sends
+ * the resulting messages as a packet to a generic receiver.
+ *
+ * You can also use serial_send to send a packet through
+ * the serial port.
+ */
+typedef struct Serial {
+    FDHandler*  fdhandler;   /* used to monitor serial port fd */
+    Receiver    receiver[1]; /* send payload there */
+    int         in_len;      /* current bytes in input packet */
+    int         in_datalen;  /* payload size, or 0 when reading header */
+    int         in_channel;  /* extracted channel number */
+    Packet*     in_packet;   /* used to read incoming packets */
+} Serial;
+
+
+/* a callback called when the serial port's fd is closed */
+static void
+serial_fd_close( Serial*  s )
+{
+    fatal("unexpected serial port close !!");
+}
+
+static void
+serial_dump( Packet*  p, const char*  funcname )
+{
+    T("%s: %03d bytes: '%s'",
+      funcname, p->len, quote(p->data, p->len));
+}
+
+/* a callback called when a packet arrives from the serial port's FDHandler.
+ *
+ * This will essentially parse the header, extract the channel number and
+ * the payload size and store them in 'in_datalen' and 'in_channel'.
+ *
+ * After that, the payload is sent to the receiver once completed.
+ */
+static void
+serial_fd_receive( Serial*  s, Packet*  p )
+{
+    int      rpos  = 0, rcount = p->len;
+    Packet*  inp   = s->in_packet;
+    int      inpos = s->in_len;
+
+    serial_dump( p, __FUNCTION__ );
+
+    while (rpos < rcount)
+    {
+        int  avail = rcount - rpos;
+
+        /* first, try to read the header */
+        if (s->in_datalen == 0) {
+            int  wanted = HEADER_SIZE - inpos;
+            if (avail > wanted)
+                avail = wanted;
+
+            memcpy( inp->data + inpos, p->data + rpos, avail );
+            inpos += avail;
+            rpos  += avail;
+
+            if (inpos == HEADER_SIZE) {
+                s->in_datalen = hex2int( inp->data + LENGTH_OFFSET,  LENGTH_SIZE );
+                s->in_channel = hex2int( inp->data + CHANNEL_OFFSET, CHANNEL_SIZE );
+
+                if (s->in_datalen <= 0) {
+                    D("ignoring %s packet from serial port",
+                      s->in_datalen ? "empty" : "malformed");
+                    s->in_datalen = 0;
+                }
+
+                //D("received %d bytes packet for channel %d", s->in_datalen, s->in_channel);
+                inpos = 0;
+            }
+        }
+        else /* then, populate the packet itself */
+        {
+            int   wanted = s->in_datalen - inpos;
+
+            if (avail > wanted)
+                avail = wanted;
+
+            memcpy( inp->data + inpos, p->data + rpos, avail );
+            inpos += avail;
+            rpos  += avail;
+
+            if (inpos == s->in_datalen) {
+                if (s->in_channel < 0) {
+                    D("ignoring %d bytes addressed to channel %d",
+                       inpos, s->in_channel);
+                } else {
+                    inp->len     = inpos;
+                    inp->channel = s->in_channel;
+                    receiver_post( s->receiver, inp );
+                    s->in_packet  = inp = packet_alloc();
+                }
+                s->in_datalen = 0;
+                inpos         = 0;
+            }
+        }
+    }
+    s->in_len = inpos;
+    packet_free(&p);
+}
+
+
+/* send a packet to the serial port.
+ * this assumes that p->len and p->channel contain the payload's
+ * size and channel and will add the appropriate header.
+ */
+static void
+serial_send( Serial*  s, Packet*  p )
+{
+    Packet*  h = packet_alloc();
+
+    //D("sending to serial %d bytes from channel %d: '%.*s'", p->len, p->channel, p->len, p->data);
+
+    /* insert a small header before this packet */
+    h->len = HEADER_SIZE;
+    int2hex( p->len,     h->data + LENGTH_OFFSET,  LENGTH_SIZE );
+    int2hex( p->channel, h->data + CHANNEL_OFFSET, CHANNEL_SIZE );
+
+    serial_dump( h, __FUNCTION__ );
+    serial_dump( p, __FUNCTION__ );
+
+    fdhandler_enqueue( s->fdhandler, h );
+    fdhandler_enqueue( s->fdhandler, p );
+}
+
+
+/* initialize serial reader */
+static void
+serial_init( Serial*         s,
+             int             fd,
+             FDHandlerList*  list,
+             Receiver*       receiver )
+{
+    Receiver  recv;
+
+    recv.user  = s;
+    recv.post  = (PostFunc)  serial_fd_receive;
+    recv.close = (CloseFunc) serial_fd_close;
+
+    s->receiver[0] = receiver[0];
+
+    s->fdhandler = fdhandler_new( fd, list, &recv );
+    s->in_len     = 0;
+    s->in_datalen = 0;
+    s->in_channel = 0;
+    s->in_packet  = packet_alloc();
+}
+
+
+/** CLIENTS
+ **/
+
+typedef struct Client       Client;
+typedef struct Multiplexer  Multiplexer;
+
+/* A Client object models a single qemud client socket
+ * connection in the emulated system.
+ *
+ * the client first sends the name of the system service
+ * it wants to contact (no framing), then waits for a 2
+ * byte answer from qemud.
+ *
+ * the answer is either "OK" or "KO" to indicate
+ * success or failure.
+ *
+ * In case of success, the client can send messages
+ * to the service.
+ *
+ * In case of failure, it can disconnect or try sending
+ * the name of another service.
+ */
+struct Client {
+    Client*       next;
+    Client**      pref;
+    int           channel;
+    char          registered;
+    FDHandler*    fdhandler;
+    Multiplexer*  multiplexer;
+};
+
+struct Multiplexer {
+    Client*        clients;
+    int            last_channel;
+    Serial         serial[1];
+    Looper         looper[1];
+    FDHandlerList  fdhandlers[1];
+};
+
+
+static int   multiplexer_open_channel( Multiplexer*  mult, Packet*  p );
+static void  multiplexer_close_channel( Multiplexer*  mult, int  channel );
+static void  multiplexer_serial_send( Multiplexer* mult, int  channel, Packet*  p );
+
+static void
+client_dump( Client*  c, Packet*  p, const char*  funcname )
+{
+    T("%s: client %p (%d): %3d bytes: '%s'",
+      funcname, c, c->fdhandler->fd,
+      p->len, quote(p->data, p->len));
+}
+
+/* destroy a client */
+static void
+client_free( Client*  c )
+{
+    /* remove from list */
+    c->pref[0] = c->next;
+    if (c->next)
+        c->next->pref = c->pref;
+
+    c->channel    = -1;
+    c->registered = 0;
+
+    /* gently ask the FDHandler to shutdown to
+     * avoid losing queued outgoing packets */
+    if (c->fdhandler != NULL) {
+        fdhandler_shutdown(c->fdhandler);
+        c->fdhandler = NULL;
+    }
+
+    xfree(c);
+}
+
+
+/* a function called when a client socket receives data */
+static void
+client_fd_receive( Client*  c, Packet*  p )
+{
+    client_dump(c, p, __FUNCTION__);
+
+    if (c->registered) {
+        /* the client is registered, just send the
+         * data through the serial port
+         */
+        multiplexer_serial_send(c->multiplexer, c->channel, p);
+        return;
+    }
+
+    if (c->channel > 0) {
+        /* the client is waiting registration results.
+         * this should not happen because the client
+         * should wait for our 'ok' or 'ko'.
+         * close the connection.
+         */
+         D("%s: bad client sending data before end of registration",
+           __FUNCTION__);
+     BAD_CLIENT:
+         packet_free(&p);
+         client_free(c);
+         return;
+    }
+
+    /* the client hasn't registered a service yet,
+     * so this must be the name of a service, call
+     * the multiplexer to start registration for
+     * it.
+     */
+    D("%s: attempting registration for service '%.*s'",
+      __FUNCTION__, p->len, p->data);
+    c->channel = multiplexer_open_channel(c->multiplexer, p);
+    if (c->channel < 0) {
+        D("%s: service name too long", __FUNCTION__);
+        goto BAD_CLIENT;
+    }
+    D("%s:    -> received channel id %d", __FUNCTION__, c->channel);
+    packet_free(&p);
+}
+
+
+/* a function called when the client socket is closed. */
+static void
+client_fd_close( Client*  c )
+{
+    T("%s: client %p (%d)", __FUNCTION__, c, c->fdhandler->fd);
+
+    /* no need to shutdown the FDHandler */
+    c->fdhandler = NULL;
+
+    /* tell the emulator we're out */
+    if (c->channel > 0)
+        multiplexer_close_channel(c->multiplexer, c->channel);
+
+    /* free the client */
+    client_free(c);
+}
+
+/* a function called when the multiplexer received a registration
+ * response from the emulator for a given client.
+ */
+static void
+client_registration( Client*  c, int  registered )
+{
+    Packet*  p = packet_alloc();
+
+    /* sends registration status to client */
+    if (!registered) {
+        D("%s: registration failed for client %d", __FUNCTION__, c->channel);
+        memcpy( p->data, "KO", 2 );
+        p->len = 2;
+    } else {
+        D("%s: registration succeeded for client %d", __FUNCTION__, c->channel);
+        memcpy( p->data, "OK", 2 );
+        p->len = 2;
+    }
+    client_dump(c, p, __FUNCTION__);
+    fdhandler_enqueue(c->fdhandler, p);
+
+    /* now save registration state
+     */
+    c->registered = registered;
+    if (!registered) {
+        /* allow the client to try registering another service */
+        c->channel = -1;
+    }
+}
+
+/* send data to a client */
+static void
+client_send( Client*  c, Packet*  p )
+{
+    client_dump(c, p, __FUNCTION__);
+    fdhandler_enqueue(c->fdhandler, p);
+}
+
+
+/* Create new client socket handler */
+static Client*
+client_new( Multiplexer*    mult,
+            int             fd,
+            FDHandlerList*  pfdhandlers,
+            Client**        pclients )
+{
+    Client*   c;
+    Receiver  recv;
+
+    xnew(c);
+
+    c->multiplexer = mult;
+    c->next        = NULL;
+    c->pref        = &c->next;
+    c->channel     = -1;
+    c->registered  = 0;
+
+    recv.user  = c;
+    recv.post  = (PostFunc)  client_fd_receive;
+    recv.close = (CloseFunc) client_fd_close;
+
+    c->fdhandler = fdhandler_new( fd, pfdhandlers, &recv );
+
+    /* add to client list */
+    c->next   = *pclients;
+    c->pref   = pclients;
+    *pclients = c;
+    if (c->next)
+        c->next->pref = &c->next;
+
+    return c;
+}
+
+/**  GLOBAL MULTIPLEXER
+ **/
+
+/* find a client by its channel */
+static Client*
+multiplexer_find_client( Multiplexer*  mult, int  channel )
+{
+    Client* c = mult->clients;
+
+    for ( ; c != NULL; c = c->next ) {
+        if (c->channel == channel)
+            return c;
+    }
+    return NULL;
+}
+
+/* handle control messages coming from the serial port
+ * on CONTROL_CHANNEL.
+ */
+static void
+multiplexer_handle_control( Multiplexer*  mult, Packet*  p )
+{
+    /* connection registration success */
+    if (p->len == 13 && !memcmp(p->data, "ok:connect:", 11)) {
+        int      channel = hex2int(p->data+11, 2);
+        Client*  client  = multiplexer_find_client(mult, channel);
+
+        /* note that 'client' can be NULL if the corresponding
+         * socket was closed before the emulator response arrived.
+         */
+        if (client != NULL) {
+            client_registration(client, 1);
+        } else {
+            D("%s: NULL client: '%.*s'", __FUNCTION__, p->len, p->data+11);
+        }
+        goto EXIT;
+    }
+
+    /* connection registration failure */
+    if (p->len == 13 && !memcmp(p->data, "ko:connect:",11)) {
+        int     channel = hex2int(p->data+11, 2);
+        Client* client  = multiplexer_find_client(mult, channel);
+
+        if (client != NULL)
+            client_registration(client, 0);
+
+        goto EXIT;
+    }
+
+    /* emulator-induced client disconnection */
+    if (p->len == 13 && !memcmp(p->data, "disconnect:",11)) {
+        int      channel = hex2int(p->data+11, 2);
+        Client*  client  = multiplexer_find_client(mult, channel);
+
+        if (client != NULL)
+            client_free(client);
+
+        goto EXIT;
+    }
+
+    /* A message that begins with "X00" is a probe sent by
+     * the emulator used to detect which version of qemud it runs
+     * against (in order to detect 1.0/1.1 system images. Just
+     * silently ignore it there instead of printing an error
+     * message.
+     */
+    if (p->len >= 3 && !memcmp(p->data,"X00",3)) {
+        goto EXIT;
+    }
+
+    D("%s: unknown control message (%d bytes): '%.*s'",
+      __FUNCTION__, p->len, p->len, p->data);
+
+EXIT:
+    packet_free(&p);
+}
+
+/* a function called when an incoming packet comes from the serial port */
+static void
+multiplexer_serial_receive( Multiplexer*  mult, Packet*  p )
+{
+    Client*  client;
+
+    T("%s: channel=%d '%.*s'", __FUNCTION__, p->channel, p->len, p->data);
+
+    if (p->channel == CHANNEL_CONTROL) {
+        multiplexer_handle_control(mult, p);
+        return;
+    }
+
+    client = multiplexer_find_client(mult, p->channel);
+    if (client != NULL) {
+        client_send(client, p);
+        return;
+    }
+
+    D("%s: discarding packet for unknown channel %d", __FUNCTION__, p->channel);
+    packet_free(&p);
+}
+
+/* a function called when the serial reader closes */
+static void
+multiplexer_serial_close( Multiplexer*  mult )
+{
+    fatal("unexpected close of serial reader");
+}
+
+/* a function called to send a packet to the serial port */
+static void
+multiplexer_serial_send( Multiplexer*  mult, int  channel, Packet*  p )
+{
+    p->channel = channel;
+    serial_send( mult->serial, p );
+}
+
+
+
+/* a function used by a client to allocate a new channel id and
+ * ask the emulator to open it. 'service' must be a packet containing
+ * the name of the service in its payload.
+ *
+ * returns -1 if the service name is too long.
+ *
+ * notice that client_registration() will be called later when
+ * the answer arrives.
+ */
+static int
+multiplexer_open_channel( Multiplexer*  mult, Packet*  service )
+{
+    Packet*   p = packet_alloc();
+    int       len, channel;
+
+    /* find a free channel number, assume we don't have many
+     * clients here. */
+    {
+        Client*  c;
+    TRY_AGAIN:
+        channel = (++mult->last_channel) & 0xff;
+
+        for (c = mult->clients; c != NULL; c = c->next)
+            if (c->channel == channel)
+                goto TRY_AGAIN;
+    }
+
+    len = snprintf((char*)p->data, sizeof p->data, "connect:%.*s:%02x", service->len, service->data, channel);
+    if (len >= (int)sizeof(p->data)) {
+        D("%s: weird, service name too long (%d > %d)", __FUNCTION__, len, sizeof(p->data));
+        packet_free(&p);
+        return -1;
+    }
+    p->channel = CHANNEL_CONTROL;
+    p->len     = len;
+
+    serial_send(mult->serial, p);
+    return channel;
+}
+
+/* used to tell the emulator a channel was closed by a client */
+static void
+multiplexer_close_channel( Multiplexer*  mult, int  channel )
+{
+    Packet*  p   = packet_alloc();
+    int      len = snprintf((char*)p->data, sizeof(p->data), "disconnect:%02x", channel);
+
+    if (len > (int)sizeof(p->data)) {
+        /* should not happen */
+        return;
+    }
+
+    p->channel = CHANNEL_CONTROL;
+    p->len     = len;
+
+    serial_send(mult->serial, p);
+}
+
+/* this function is used when a new connection happens on the control
+ * socket.
+ */
+static void
+multiplexer_control_accept( Multiplexer*  m, Packet*  p )
+{
+    /* the file descriptor for the new socket connection is
+     * in p->channel. See fdhandler_accept_event() */
+    int      fd     = p->channel;
+    Client*  client = client_new( m, fd, m->fdhandlers, &m->clients );
+
+    D("created client %p listening on fd %d", client, fd);
+
+    /* free dummy packet */
+    packet_free(&p);
+}
+
+static void
+multiplexer_control_close( Multiplexer*  m )
+{
+    fatal("unexpected multiplexer control close");
+}
+
+static void
+multiplexer_init( Multiplexer*  m, const char*  serial_dev )
+{
+    int       fd, control_fd;
+    Receiver  recv;
+
+    /* initialize looper and fdhandlers list */
+    looper_init( m->looper );
+    fdhandler_list_init( m->fdhandlers, m->looper );
+
+    /* open the serial port */
+    do {
+        fd = open(serial_dev, O_RDWR);
+    } while (fd < 0 && errno == EINTR);
+
+    if (fd < 0) {
+        fatal( "%s: could not open '%s': %s", __FUNCTION__, serial_dev,
+               strerror(errno) );
+    }
+    // disable echo on serial lines
+    if ( !memcmp( serial_dev, "/dev/tty", 8 ) ) {
+        struct termios  ios;
+        tcgetattr( fd, &ios );
+        ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
+        tcsetattr( fd, TCSANOW, &ios );
+    }
+
+    /* initialize the serial reader/writer */
+    recv.user  = m;
+    recv.post  = (PostFunc)  multiplexer_serial_receive;
+    recv.close = (CloseFunc) multiplexer_serial_close;
+
+    serial_init( m->serial, fd, m->fdhandlers, &recv );
+
+    /* open the qemud control socket */
+    recv.user  = m;
+    recv.post  = (PostFunc)  multiplexer_control_accept;
+    recv.close = (CloseFunc) multiplexer_control_close;
+
+    fd = android_get_control_socket(CONTROL_SOCKET_NAME);
+    if (fd < 0) {
+        fatal("couldn't get fd for control socket '%s'", CONTROL_SOCKET_NAME);
+    }
+
+    fdhandler_new_accept( fd, m->fdhandlers, &recv );
+
+    /* initialize clients list */
+    m->clients = NULL;
+}
+
+/** MAIN LOOP
+ **/
+
+static Multiplexer  _multiplexer[1];
+
+int  main( void )
+{
+    Multiplexer*  m = _multiplexer;
+
+   /* extract the name of our serial device from the kernel
+    * boot options that are stored in /proc/cmdline
+    */
+#define  KERNEL_OPTION  "android.qemud="
+
+    {
+        char          buff[1024];
+        int           fd, len;
+        char*         p;
+        char*         q;
+
+        fd = open( "/proc/cmdline", O_RDONLY );
+        if (fd < 0) {
+            D("%s: can't open /proc/cmdline !!: %s", __FUNCTION__,
+            strerror(errno));
+            exit(1);
+        }
+
+        len = fd_read( fd, buff, sizeof(buff)-1 );
+        close(fd);
+        if (len < 0) {
+            D("%s: can't read /proc/cmdline: %s", __FUNCTION__,
+            strerror(errno));
+            exit(1);
+        }
+        buff[len] = 0;
+
+        p = strstr( buff, KERNEL_OPTION );
+        if (p == NULL) {
+            D("%s: can't find '%s' in /proc/cmdline",
+            __FUNCTION__, KERNEL_OPTION );
+            exit(1);
+        }
+
+        p += sizeof(KERNEL_OPTION)-1;  /* skip option */
+        q  = p;
+        while ( *q && *q != ' ' && *q != '\t' )
+            q += 1;
+
+        snprintf( buff, sizeof(buff), "/dev/%.*s", q-p, p );
+
+        multiplexer_init( m, buff );
+    }
+
+    D( "entering main loop");
+    looper_loop( m->looper );
+    D( "unexpected termination !!" );
+    return 0;
+}
index 4b935a7..e3df473 100644 (file)
@@ -21,6 +21,7 @@ include $(CLEAR_VARS)
 
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := sensors_qemu.c
 ifeq ($(TARGET_PRODUCT),vbox_x86)
 LOCAL_MODULE := sensors.vbox_x86
@@ -30,10 +31,12 @@ endif
 include $(BUILD_SHARED_LIBRARY)
 
 
+
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := sensors_qemu.c
 LOCAL_MODULE := sensors.ranchu
 
index be6d882..bca3570 100644 (file)
@@ -23,9 +23,9 @@
  */
 
 
-/* we connect with the emulator through the "sensors" qemu pipe service
+/* we connect with the emulator through the "sensors" qemud service
  */
-#define  SENSORS_SERVICE_NAME "pipe:sensors"
+#define  SENSORS_SERVICE_NAME "sensors"
 
 #define LOG_TAG "QemuSensors"
 
@@ -45,7 +45,7 @@
 
 #define  E(...)  ALOGE(__VA_ARGS__)
 
-#include <system/qemu_pipe.h>
+#include "qemud.h"
 
 /** SENSOR IDS AND NAMES
  **/
@@ -152,9 +152,9 @@ typedef struct SensorDevice {
  * from different threads, and poll() is blocking.
  *
  * Note that the emulator's sensors service creates a new client for each
- * connection through qemu_pipe_open(), where each client has its own
+ * connection through qemud_channel_open(), where each client has its own
  * delay and set of activated sensors. This precludes calling
- * qemu_pipe_open() on each request, because a typical emulated system
+ * qemud_channel_open() on each request, because a typical emulated system
  * will do something like:
  *
  * 1) On a first thread, de-activate() all sensors first, then call poll(),
@@ -174,7 +174,7 @@ typedef struct SensorDevice {
 static int sensor_device_get_fd_locked(SensorDevice* dev) {
     /* Create connection to service on first call */
     if (dev->fd < 0) {
-        dev->fd = qemu_pipe_open(SENSORS_SERVICE_NAME);
+        dev->fd = qemud_channel_open(SENSORS_SERVICE_NAME);
         if (dev->fd < 0) {
             int ret = -errno;
             E("%s: Could not open connection to service: %s", __FUNCTION__,
@@ -196,7 +196,7 @@ static int sensor_device_send_command_locked(SensorDevice* dev,
     }
 
     int ret = 0;
-    if (qemu_pipe_frame_send(fd, cmd, strlen(cmd)) < 0) {
+    if (qemud_channel_send(fd, cmd, strlen(cmd)) < 0) {
         ret = -errno;
         E("%s(fd=%d): ERROR: %s", __FUNCTION__, fd, strerror(errno));
     }
@@ -267,7 +267,7 @@ static int sensor_device_poll_event_locked(SensorDevice* dev)
 
         /* read the next event */
         char buff[256];
-        int len = qemu_pipe_frame_recv(fd, buff, sizeof(buff) - 1U);
+        int len = qemud_channel_recv(fd, buff, sizeof(buff) - 1U);
         /* re-acquire the lock to modify the device state. */
         pthread_mutex_lock(&dev->lock);
 
@@ -333,7 +333,7 @@ static int sensor_device_poll_event_locked(SensorDevice* dev)
         if (sscanf(buff, "temperature:%g", params+0) == 1) {
             new_sensors |= SENSORS_TEMPERATURE;
             events[ID_TEMPERATURE].temperature = params[0];
-            events[ID_TEMPERATURE].type = SENSOR_TYPE_TEMPERATURE;
+            events[ID_TEMPERATURE].type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
             continue;
         }
  
@@ -540,6 +540,33 @@ static int sensor_device_activate(struct sensors_poll_device_t *dev0,
     return ret;
 }
 
+static int sensor_device_default_flush(
+        struct sensors_poll_device_1* dev0,
+        int handle) {
+
+    SensorDevice* dev = (void*)dev0;
+
+    D("%s: handle=%s (%d)", __FUNCTION__,
+        _sensorIdToName(handle), handle);
+
+    /* Sanity check */
+    if (!ID_CHECK(handle)) {
+        E("%s: bad handle ID", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    pthread_mutex_lock(&dev->lock);
+    dev->sensors[handle].version = META_DATA_VERSION;
+    dev->sensors[handle].type = SENSOR_TYPE_META_DATA;
+    dev->sensors[handle].sensor = 0;
+    dev->sensors[handle].timestamp = 0;
+    dev->sensors[handle].meta_data.what = META_DATA_FLUSH_COMPLETE;
+    dev->pendingSensors |= (1U << handle);
+    pthread_mutex_unlock(&dev->lock);
+
+    return 0;
+}
+
 static int sensor_device_set_delay(struct sensors_poll_device_t *dev0,
                                    int handle __unused,
                                    int64_t ns)
@@ -561,6 +588,15 @@ static int sensor_device_set_delay(struct sensors_poll_device_t *dev0,
     return ret;
 }
 
+static int sensor_device_default_batch(
+     struct sensors_poll_device_1* dev,
+     int sensor_handle,
+     int flags,
+     int64_t sampling_period_ns,
+     int64_t max_report_latency_ns) {
+    return sensor_device_set_delay(dev, sensor_handle, sampling_period_ns);
+}
+
 /** MODULE REGISTRATION SUPPORT
  **
  ** This is required so that hardware/libhardware/hardware.c
@@ -588,6 +624,12 @@ static const struct sensor_t sSensorListInit[] = {
           .power      = 3.0f,
           .minDelay   = 10000,
           .maxDelay   = 60 * 1000 * 1000,
+          .fifoReservedEventCount = 0,
+          .fifoMaxEventCount =   0,
+          .stringType =         0,
+          .requiredPermission = 0,
+          .maxDelay =      200000,
+          .flags = SENSOR_FLAG_CONTINUOUS_MODE,
           .reserved   = {}
         },
 
@@ -601,6 +643,12 @@ static const struct sensor_t sSensorListInit[] = {
           .power      = 6.7f,
           .minDelay   = 10000,
           .maxDelay   = 60 * 1000 * 1000,
+          .fifoReservedEventCount = 0,
+          .fifoMaxEventCount =   0,
+          .stringType =         0,
+          .requiredPermission = 0,
+          .maxDelay =      200000,
+          .flags = SENSOR_FLAG_CONTINUOUS_MODE,
           .reserved   = {}
         },
 
@@ -614,6 +662,12 @@ static const struct sensor_t sSensorListInit[] = {
           .power      = 9.7f,
           .minDelay   = 10000,
           .maxDelay   = 60 * 1000 * 1000,
+          .fifoReservedEventCount = 0,
+          .fifoMaxEventCount =   0,
+          .stringType =         0,
+          .requiredPermission = 0,
+          .maxDelay =      200000,
+          .flags = SENSOR_FLAG_CONTINUOUS_MODE,
           .reserved   = {}
         },
 
@@ -621,12 +675,18 @@ static const struct sensor_t sSensorListInit[] = {
           .vendor     = "The Android Open Source Project",
           .version    = 1,
           .handle     = ID_TEMPERATURE,
-          .type       = SENSOR_TYPE_TEMPERATURE,
+          .type       = SENSOR_TYPE_AMBIENT_TEMPERATURE,
           .maxRange   = 80.0f,
           .resolution = 1.0f,
           .power      = 0.0f,
           .minDelay   = 10000,
           .maxDelay   = 60 * 1000 * 1000,
+          .fifoReservedEventCount = 0,
+          .fifoMaxEventCount =   0,
+          .stringType =         0,
+          .requiredPermission = 0,
+          .maxDelay =      200000,
+          .flags = SENSOR_FLAG_CONTINUOUS_MODE,
           .reserved   = {}
         },
 
@@ -640,6 +700,12 @@ static const struct sensor_t sSensorListInit[] = {
           .power      = 20.0f,
           .minDelay   = 10000,
           .maxDelay   = 60 * 1000 * 1000,
+          .fifoReservedEventCount = 0,
+          .fifoMaxEventCount =   0,
+          .stringType =         0,
+          .requiredPermission = 0,
+          .maxDelay =      200000,
+          .flags = SENSOR_FLAG_CONTINUOUS_MODE,
           .reserved   = {}
         },
 
@@ -653,6 +719,12 @@ static const struct sensor_t sSensorListInit[] = {
           .power      = 20.0f,
           .minDelay   = 10000,
           .maxDelay   = 60 * 1000 * 1000,
+          .fifoReservedEventCount = 0,
+          .fifoMaxEventCount =   0,
+          .stringType =         0,
+          .requiredPermission = 0,
+          .maxDelay =      200000,
+          .flags = SENSOR_FLAG_CONTINUOUS_MODE,
           .reserved   = {}
         },
 
@@ -666,6 +738,12 @@ static const struct sensor_t sSensorListInit[] = {
           .power      = 20.0f,
           .minDelay   = 10000,
           .maxDelay   = 60 * 1000 * 1000,
+          .fifoReservedEventCount = 0,
+          .fifoMaxEventCount =   0,
+          .stringType =         0,
+          .requiredPermission = 0,
+          .maxDelay =      200000,
+          .flags = SENSOR_FLAG_CONTINUOUS_MODE,
           .reserved   = {}
         },
 
@@ -679,6 +757,12 @@ static const struct sensor_t sSensorListInit[] = {
           .power      = 20.0f,
           .minDelay   = 10000,
           .maxDelay   = 60 * 1000 * 1000,
+          .fifoReservedEventCount = 0,
+          .fifoMaxEventCount =   0,
+          .stringType =         0,
+          .requiredPermission = 0,
+          .maxDelay =      200000,
+          .flags = SENSOR_FLAG_CONTINUOUS_MODE,
           .reserved   = {}
         }
 };
@@ -688,23 +772,22 @@ static struct sensor_t  sSensorList[MAX_NUM_SENSORS];
 static int sensors__get_sensors_list(struct sensors_module_t* module __unused,
         struct sensor_t const** list)
 {
-    int  fd = qemu_pipe_open(SENSORS_SERVICE_NAME);
+    int  fd = qemud_channel_open(SENSORS_SERVICE_NAME);
     char buffer[12];
     int  mask, nn, count;
     int  ret = 0;
 
     if (fd < 0) {
-        E("%s: no qemu pipe connection", __FUNCTION__);
+        E("%s: no qemud connection", __FUNCTION__);
         goto out;
     }
-    static const char kListSensors[] = "list-sensors";
-    ret = qemu_pipe_frame_send(fd, kListSensors, sizeof(kListSensors) - 1);
+    ret = qemud_channel_send(fd, "list-sensors", -1);
     if (ret < 0) {
         E("%s: could not query sensor list: %s", __FUNCTION__,
           strerror(errno));
         goto out;
     }
-    ret = qemu_pipe_frame_recv(fd, buffer, sizeof buffer-1);
+    ret = qemud_channel_recv(fd, buffer, sizeof buffer-1);
     if (ret < 0) {
         E("%s: could not receive sensor list: %s", __FUNCTION__,
           strerror(errno));
@@ -718,7 +801,6 @@ static int sensors__get_sensors_list(struct sensors_module_t* module __unused,
     for (nn = 0; nn < MAX_NUM_SENSORS; nn++) {
         if (((1 << nn) & mask) == 0)
             continue;
-
         sSensorList[count++] = sSensorListInit[nn];
     }
     D("%s: returned %d sensors (mask=%d)", __FUNCTION__, count, mask);
@@ -748,13 +830,17 @@ open_sensors(const struct hw_module_t* module,
         memset(dev, 0, sizeof(*dev));
 
         dev->device.common.tag     = HARDWARE_DEVICE_TAG;
-        dev->device.common.version = SENSORS_DEVICE_API_VERSION_1_0;
+        dev->device.common.version = SENSORS_DEVICE_API_VERSION_1_3;
         dev->device.common.module  = (struct hw_module_t*) module;
         dev->device.common.close   = sensor_device_close;
         dev->device.poll           = sensor_device_poll;
         dev->device.activate       = sensor_device_activate;
         dev->device.setDelay       = sensor_device_set_delay;
 
+        // Version 1.3-specific functions
+        dev->device.batch       = sensor_device_default_batch;
+        dev->device.flush       = sensor_device_default_flush;
+
         dev->fd = -1;
         pthread_mutex_init(&dev->lock, NULL);
 
@@ -773,7 +859,7 @@ struct sensors_module_t HAL_MODULE_INFO_SYM = {
     .common = {
         .tag = HARDWARE_MODULE_TAG,
         .version_major = 1,
-        .version_minor = 0,
+        .version_minor = 3,
         .id = SENSORS_HARDWARE_MODULE_ID,
         .name = "Goldfish SENSORS Module",
         .author = "The Android Open Source Project",
index 7253cbc..dba243f 100644 (file)
@@ -21,8 +21,9 @@ LOCAL_MODULE := vibrator.goldfish
 # HAL module implemenation stored in
 # hw/<VIBRATOR_HARDWARE_MODULE_ID>.goldfish.so
 LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := vibrator_qemu.c
-LOCAL_SHARED_LIBRARIES := liblog libhardware
+LOCAL_C_INCLUDES := hardware/libhardware hardware/libhardware_legacy $(LOCAL_PATH)/../include
+LOCAL_SRC_FILES := qemu.c vibrator_qemu.c
+LOCAL_SHARED_LIBRARIES := liblog libhardware libhardware_legacy
 LOCAL_MODULE_TAGS := optional
 
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/vibrator/qemu.c b/vibrator/qemu.c
new file mode 100644 (file)
index 0000000..11d3f19
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* this file contains various functions used by all libhardware modules
+ * that support QEMU emulation
+ */
+#include "qemu.h"
+#define  LOG_TAG  "hardware-qemu"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#define  QEMU_DEBUG  0
+
+#if QEMU_DEBUG
+#  define  D(...)   ALOGD(__VA_ARGS__)
+#else
+#  define  D(...)   ((void)0)
+#endif
+
+#include <system/qemu_pipe.h>
+
+int
+qemu_check(void)
+{
+    static int  in_qemu = -1;
+
+    if (__builtin_expect(in_qemu < 0,0)) {
+        char  propBuf[PROPERTY_VALUE_MAX];
+        property_get("ro.kernel.qemu", propBuf, "");
+        in_qemu = (propBuf[0] == '1');
+    }
+    return in_qemu;
+}
+
+static int
+qemu_fd_write( int  fd, const char*  cmd, int  len )
+{
+    int  len2;
+    do {
+        len2 = write(fd, cmd, len);
+    } while (len2 < 0 && errno == EINTR);
+    return len2;
+}
+
+static int
+qemu_fd_read( int  fd, char*  buff, int  len )
+{
+    int  len2;
+    do {
+        len2 = read(fd, buff, len);
+    } while (len2 < 0 && errno == EINTR);
+    return len2;
+}
+
+static int
+qemu_channel_open_qemud_pipe( QemuChannel*  channel,
+                              const char*   name )
+{
+    int   fd;
+    char  pipe_name[512];
+
+    snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", name);
+    fd = qemu_pipe_open(pipe_name);
+    if (fd < 0) {
+        D("no qemud pipe: %s", strerror(errno));
+        return -1;
+    }
+
+    channel->is_qemud = 1;
+    channel->fd       = fd;
+    return 0;
+}
+
+static int
+qemu_channel_open_qemud( QemuChannel*  channel,
+                         const char*   name )
+{
+    int   fd, ret, namelen = strlen(name);
+    char  answer[2];
+
+    fd = socket_local_client( "qemud",
+                              ANDROID_SOCKET_NAMESPACE_RESERVED,
+                              SOCK_STREAM );
+    if (fd < 0) {
+        D("no qemud control socket: %s", strerror(errno));
+        return -1;
+    }
+
+    /* send service name to connect */
+    if (qemu_fd_write(fd, name, namelen) != namelen) {
+        D("can't send service name to qemud: %s",
+           strerror(errno));
+        close(fd);
+        return -1;
+    }
+
+    /* read answer from daemon */
+    if (qemu_fd_read(fd, answer, 2) != 2 ||
+        answer[0] != 'O' || answer[1] != 'K') {
+        D("cant' connect to %s service through qemud", name);
+        close(fd);
+        return -1;
+    }
+
+    channel->is_qemud = 1;
+    channel->fd       = fd;
+    return 0;
+}
+
+
+static int
+qemu_channel_open_qemud_old( QemuChannel*  channel,
+                             const char*   name )
+{
+    int  fd;
+
+    snprintf(channel->device, sizeof channel->device,
+                "qemud_%s", name);
+
+    fd = socket_local_client( channel->device,
+                              ANDROID_SOCKET_NAMESPACE_RESERVED,
+                              SOCK_STREAM );
+    if (fd < 0) {
+        D("no '%s' control socket available: %s",
+            channel->device, strerror(errno));
+        return -1;
+    }
+
+    close(fd);
+    channel->is_qemud_old = 1;
+    return 0;
+}
+
+
+static int
+qemu_channel_open_tty( QemuChannel*  channel,
+                       const char*   name,
+                       int           mode )
+{
+    char   key[PROPERTY_KEY_MAX];
+    char   prop[PROPERTY_VALUE_MAX];
+    int    ret;
+
+    ret = snprintf(key, sizeof key, "ro.kernel.android.%s", name);
+    if (ret >= (int)sizeof key)
+        return -1;
+
+    if (property_get(key, prop, "") == 0) {
+        D("no kernel-provided %s device name", name);
+        return -1;
+    }
+
+    ret = snprintf(channel->device, sizeof channel->device,
+                    "/dev/%s", prop);
+    if (ret >= (int)sizeof channel->device) {
+        D("%s device name too long: '%s'", name, prop);
+        return -1;
+    }
+
+    channel->is_tty = !memcmp("/dev/tty", channel->device, 8);
+    return 0;
+}
+
+int
+qemu_channel_open( QemuChannel*  channel,
+                   const char*   name,
+                   int           mode )
+{
+    int  fd = -1;
+
+    /* initialize the channel is needed */
+    if (!channel->is_inited)
+    {
+        channel->is_inited = 1;
+
+        do {
+            if (qemu_channel_open_qemud_pipe(channel, name) == 0)
+                break;
+
+            if (qemu_channel_open_qemud(channel, name) == 0)
+                break;
+
+            if (qemu_channel_open_qemud_old(channel, name) == 0)
+                break;
+
+            if (qemu_channel_open_tty(channel, name, mode) == 0)
+                break;
+
+            channel->is_available = 0;
+            return -1;
+        } while (0);
+
+        channel->is_available = 1;
+    }
+
+    /* try to open the file */
+    if (!channel->is_available) {
+        errno = ENOENT;
+        return -1;
+    }
+
+    if (channel->is_qemud) {
+        return dup(channel->fd);
+    }
+
+    if (channel->is_qemud_old) {
+        do {
+            fd = socket_local_client( channel->device,
+                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                      SOCK_STREAM );
+        } while (fd < 0 && errno == EINTR);
+    }
+    else /* /dev/ttySn ? */
+    {
+        do {
+            fd = open(channel->device, mode);
+        } while (fd < 0 && errno == EINTR);
+
+        /* disable ECHO on serial lines */
+        if (fd >= 0 && channel->is_tty) {
+            struct termios  ios;
+            tcgetattr( fd, &ios );
+            ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
+            tcsetattr( fd, TCSANOW, &ios );
+        }
+    }
+    return fd;
+}
+
+
+static int
+qemu_command_vformat( char*        buffer,
+                      int          buffer_size,
+                      const char*  format,
+                      va_list      args )
+{
+    char     header[5];
+    int      len;
+
+    if (buffer_size < 6)
+        return -1;
+
+    len = vsnprintf(buffer+4, buffer_size-4, format, args);
+    if (len >= buffer_size-4)
+        return -1;
+
+    snprintf(header, sizeof header, "%04x", len);
+    memcpy(buffer, header, 4);
+    return len + 4;
+}
+
+extern int
+qemu_command_format( char*        buffer,
+                     int          buffer_size,
+                     const char*  format,
+                     ... )
+{
+    va_list  args;
+    int      ret;
+
+    va_start(args, format);
+    ret = qemu_command_vformat(buffer, buffer_size, format, args);
+    va_end(args);
+    return ret;
+}
+
+
+static int
+qemu_control_fd(void)
+{
+    static QemuChannel  channel[1];
+    int                 fd;
+
+    fd = qemu_channel_open( channel, "hw-control", O_RDWR );
+    if (fd < 0) {
+        D("%s: could not open control channel: %s", __FUNCTION__,
+          strerror(errno));
+    }
+    return fd;
+}
+
+static int
+qemu_control_send(const char*  cmd, int  len)
+{
+    int  fd, len2;
+
+    if (len < 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    fd = qemu_control_fd();
+    if (fd < 0)
+        return -1;
+
+    len2 = qemu_fd_write(fd, cmd, len);
+    close(fd);
+    if (len2 != len) {
+        D("%s: could not send everything %d < %d",
+          __FUNCTION__, len2, len);
+        return -1;
+    }
+    return 0;
+}
+
+
+int
+qemu_control_command( const char*  fmt, ... )
+{
+    va_list  args;
+    char     command[256];
+    int      len, fd;
+
+    va_start(args, fmt);
+    len = qemu_command_vformat( command, sizeof command, fmt, args );
+    va_end(args);
+
+    if (len < 0 || len >= (int)sizeof command) {
+        if (len < 0) {
+            D("%s: could not send: %s", __FUNCTION__, strerror(errno));
+        } else {
+            D("%s: too large %d > %d", __FUNCTION__, len, (int)(sizeof command));
+        }
+        errno = EINVAL;
+        return -1;
+    }
+
+    return qemu_control_send( command, len );
+}
+
+extern int  qemu_control_query( const char*  question, int  questionlen,
+                                char*        answer,   int  answersize )
+{
+    int   ret, fd, len, result = -1;
+    char  header[5], *end;
+
+    if (questionlen <= 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    fd = qemu_control_fd();
+    if (fd < 0)
+        return -1;
+
+    ret = qemu_fd_write( fd, question, questionlen );
+    if (ret != questionlen) {
+        D("%s: could not write all: %d < %d", __FUNCTION__,
+          ret, questionlen);
+        goto Exit;
+    }
+
+    /* read a 4-byte header giving the length of the following content */
+    ret = qemu_fd_read( fd, header, 4 );
+    if (ret != 4) {
+        D("%s: could not read header (%d != 4)",
+          __FUNCTION__, ret);
+        goto Exit;
+    }
+
+    header[4] = 0;
+    len = strtol( header, &end,  16 );
+    if ( len < 0 || end == NULL || end != header+4 || len > answersize ) {
+        D("%s: could not parse header: '%s'",
+          __FUNCTION__, header);
+        goto Exit;
+    }
+
+    /* read the answer */
+    ret = qemu_fd_read( fd, answer, len );
+    if (ret != len) {
+        D("%s: could not read all of answer %d < %d",
+          __FUNCTION__, ret, len);
+        goto Exit;
+    }
+
+    result = len;
+
+Exit:
+    close(fd);
+    return result;
+}
index 59085f7..da28484 100644 (file)
 #include <cutils/log.h>
 
 #define QEMU_HARDWARE
+#include "qemu.h"
 #include <hardware/hardware.h>
 #include <hardware/vibrator.h>
-#include <system/qemu_pipe.h>
 
 static int sendit(unsigned int timeout_ms)
 {
-    static int pipe_fd = -2;
-    if (pipe_fd < -1) {
-        pipe_fd = qemu_pipe_open("pipe:qemud:hw-control");
+    if (qemu_check()) {
+        if (qemu_control_command("vibrator:%u", timeout_ms) < 0) {
+            return -errno;
+        }
+        return 0;
     }
-    if (pipe_fd < 0) {
-        return -ENOSYS;
-    }
-    char buff[16];
-    snprintf(buff, sizeof(buff), "vibrator:%u", timeout_ms);
-    if (qemu_pipe_frame_send(pipe_fd, buff, strlen(buff)) < 0) {
-        return -errno;
-    }
-    return 0;
+
+    return -ENOSYS;
 }
 
-static int qemu_vibra_on(vibrator_device_t* vibradev __unused,
-                         unsigned int timeout_ms)
+static int qemu_vibra_on(vibrator_device_t* vibradev __unused, unsigned int timeout_ms)
 {
     return sendit(timeout_ms);
 }