merge from tools_r11

Change-Id: I4c7d3f7880d10ca6d79378921a3f8811288770fa
diff --git a/Makefile.common b/Makefile.common
index 98c2325..9c6d40a 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -376,8 +376,8 @@
     android/hw-control.c \
     android/hw-sensors.c \
     android/hw-qemud.c \
-    android/hw-qemud-pipe-net.c \
     android/looper-qemu.c \
+    android/hw-pipe-net.c \
     android/qemu-setup.c \
     android/snapshot.c \
     android/utils/timezone.c \
diff --git a/android/console.c b/android/console.c
index de32ddb..0affb46 100644
--- a/android/console.c
+++ b/android/console.c
@@ -2094,6 +2094,12 @@
 do_snapshot_save( ControlClient  client, char*  args )
 {
     int ret;
+
+    if (args == NULL) {
+        control_write(client, "KO: argument missing, try 'avd snapshot save <name>'\r\n");
+        return -1;
+    }
+
     OutputChannel *err = output_channel_alloc(client, control_write_err_cb);
     do_savevm_oc(err, args);
     ret = output_channel_written(err);
@@ -2106,6 +2112,12 @@
 do_snapshot_load( ControlClient  client, char*  args )
 {
     int ret;
+
+    if (args == NULL) {
+        control_write(client, "KO: argument missing, try 'avd snapshot load <name>'\r\n");
+        return -1;
+    }
+
     OutputChannel *err = output_channel_alloc(client, control_write_err_cb);
     do_loadvm_oc(err, args);
     ret = output_channel_written(err);
@@ -2118,6 +2130,12 @@
 do_snapshot_del( ControlClient  client, char*  args )
 {
     int ret;
+
+    if (args == NULL) {
+        control_write(client, "KO: argument missing, try 'avd snapshot del <name>'\r\n");
+        return -1;
+    }
+
     OutputChannel *err = output_channel_alloc(client, control_write_err_cb);
     do_delvm_oc(err, args);
     ret = output_channel_written(err);
diff --git a/android/hw-control.c b/android/hw-control.c
index 9180383..8164bc0 100644
--- a/android/hw-control.c
+++ b/android/hw-control.c
@@ -128,11 +128,23 @@
                                                     NULL, NULL);
 }
 
-void
-android_hw_control_init( void*  opaque, const AndroidHwControlFuncs*  funcs )
-{
-    static HwControl   hwstate[1];
+const AndroidHwControlFuncs  defaultControls = {
+    NULL,
+};
 
-    hw_control_init(hwstate, opaque, funcs);
+static HwControl   hwstate[1];
+
+void
+android_hw_control_init( void )
+{
+    hw_control_init(hwstate, NULL, &defaultControls);
     D("%s: hw-control qemud handler initialized", __FUNCTION__);
 }
+
+void
+android_hw_control_set( void*  opaque, const AndroidHwControlFuncs*  funcs )
+{
+    hwstate->client       = opaque;
+    hwstate->client_funcs = funcs[0];
+}
+
diff --git a/android/hw-control.h b/android/hw-control.h
index 6e9874e..67a9bc7 100644
--- a/android/hw-control.h
+++ b/android/hw-control.h
@@ -32,7 +32,10 @@
 } AndroidHwControlFuncs;
 
 /* used to initialize the hardware control support */
-extern void  android_hw_control_init( void*                         opaque,
+extern void  android_hw_control_init( void );
+
+/* used to register a new hw-control back-end */
+extern void  android_hw_control_set( void*                         opaque,
                                       const AndroidHwControlFuncs*  funcs );
 
 #endif /* _android_hw_control_h */
diff --git a/android/hw-pipe-net.c b/android/hw-pipe-net.c
new file mode 100644
index 0000000..d83d8b1
--- /dev/null
+++ b/android/hw-pipe-net.c
@@ -0,0 +1,520 @@
+/*
+ * 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.
+ */
+
+/* This file implements the 'tcp:' goldfish pipe type which allows
+ * guest clients to directly connect to a TCP port through /dev/qemu_pipe.
+ */
+
+#include "sockets.h"
+#include "android/utils/assert.h"
+#include "android/utils/panic.h"
+#include "android/utils/system.h"
+#include "android/async-utils.h"
+#include "android/looper.h"
+#include "hw/goldfish_pipe.h"
+
+/* Implement the OpenGL fast-pipe */
+
+/* Set to 1 or 2 for debug traces */
+#define  DEBUG  1
+
+#if DEBUG >= 1
+#  define D(...)   printf(__VA_ARGS__), printf("\n")
+#else
+#  define D(...)   ((void)0)
+#endif
+
+#if DEBUG >= 2
+#  define DD(...)                       printf(__VA_ARGS__), printf("\n")
+#  define DDASSERT(cond)                _ANDROID_ASSERT(cond, "Assertion failure: ", #cond)
+#  define DDASSERT_INT_OP(cond,val,op)  _ANDROID_ASSERT_INT_OP(cond,val,op)
+#else
+#  define DD(...)                       ((void)0)
+#  define DDASSERT(cond)                ((void)0)
+#  define DDASSERT_INT_OP(cond,val,op)  ((void)0)
+#endif
+
+#define DDASSERT_INT_LT(cond,val)  DDASSERT_INT_OP(cond,val,<)
+#define DDASSERT_INT_LTE(cond,val)  DDASSERT_INT_OP(cond,val,<=)
+#define DDASSERT_INT_GT(cond,val)  DDASSERT_INT_OP(cond,val,>)
+#define DDASSERT_INT_GTE(cond,val)  DDASSERT_INT_OP(cond,val,>=)
+#define DDASSERT_INT_EQ(cond,val)  DDASSERT_INT_OP(cond,val,==)
+#define DDASSERT_INT_NEQ(cond,val)  DDASSERT_INT_OP(cond,val,!=)
+
+enum {
+    STATE_INIT,
+    STATE_CONNECTING,
+    STATE_CONNECTED,
+    STATE_CLOSING_GUEST,
+    STATE_CLOSING_SOCKET
+};
+
+typedef struct {
+    void*           hwpipe;
+    int             state;
+    int             wakeWanted;
+    LoopIo          io[1];
+    AsyncConnector  connector[1];
+
+} NetPipe;
+
+static void
+netPipe_free( NetPipe*  pipe )
+{
+    int  fd;
+
+    /* Close the socket */
+    fd = pipe->io->fd;
+    loopIo_done(pipe->io);
+    socket_close(fd);
+
+    /* Release the pipe object */
+    AFREE(pipe);
+}
+
+
+static void
+netPipe_resetState( NetPipe* pipe )
+{
+    if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) {
+        loopIo_wantWrite(pipe->io);
+    } else {
+        loopIo_dontWantWrite(pipe->io);
+    }
+
+   if (pipe->state == STATE_CONNECTED && (pipe->wakeWanted & PIPE_WAKE_READ) != 0) {
+        loopIo_wantRead(pipe->io);
+    } else {
+        loopIo_dontWantRead(pipe->io);
+    }
+}
+
+
+/* This function is only called when the socket is disconnected.
+ * See netPipe_closeFromGuest() for the case when the guest requires
+ * the disconnection. */
+static void
+netPipe_closeFromSocket( void* opaque )
+{
+    NetPipe*  pipe = opaque;
+
+    D("%s", __FUNCTION__);
+
+    /* If the guest already ordered the pipe to be closed, delete immediately */
+    if (pipe->state == STATE_CLOSING_GUEST) {
+        netPipe_free(pipe);
+        return;
+    }
+
+    /* Force the closure of the QEMUD channel - if a guest is blocked
+     * waiting for a wake signal, it will receive an error. */
+    if (pipe->hwpipe != NULL) {
+        goldfish_pipe_close(pipe->hwpipe);
+        pipe->hwpipe = NULL;
+    }
+
+    pipe->state = STATE_CLOSING_SOCKET;
+    netPipe_resetState(pipe);
+}
+
+
+/* This is the function that gets called each time there is an asynchronous
+ * event on the network pipe.
+ */
+static void
+netPipe_io_func( void* opaque, int fd, unsigned events )
+{
+    NetPipe*  pipe = opaque;
+    int         wakeFlags = 0;
+
+    /* Run the connector if we are in the CONNECTING state     */
+    /* TODO: Add some sort of time-out, to deal with the case */
+    /*        when the server is wedged.                      */
+    if (pipe->state == STATE_CONNECTING) {
+        AsyncStatus  status = asyncConnector_run(pipe->connector);
+        if (status == ASYNC_NEED_MORE) {
+            return;
+        }
+        else if (status == ASYNC_ERROR) {
+            /* Could not connect, tell our client by closing the channel. */
+
+            netPipe_closeFromSocket(pipe);
+            return;
+        }
+        pipe->state = STATE_CONNECTED;
+        netPipe_resetState(pipe);
+        return;
+    }
+
+    /* Otherwise, accept incoming data */
+    if ((events & LOOP_IO_READ) != 0) {
+        if ((pipe->wakeWanted & PIPE_WAKE_READ) != 0) {
+            wakeFlags |= PIPE_WAKE_READ;
+        }
+    }
+
+    if ((events & LOOP_IO_WRITE) != 0) {
+        if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) {
+            wakeFlags |= PIPE_WAKE_WRITE;
+        }
+    }
+
+    /* Send wake signal to the guest if needed */
+    if (wakeFlags != 0) {
+        goldfish_pipe_wake(pipe->hwpipe, wakeFlags);
+        pipe->wakeWanted &= ~wakeFlags;
+    }
+
+    /* Reset state */
+    netPipe_resetState(pipe);
+}
+
+
+void*
+netPipe_initFromAddress( void* hwpipe, const SockAddress*  address, Looper* looper )
+{
+    NetPipe*     pipe;
+
+    ANEW0(pipe);
+
+    pipe->hwpipe = hwpipe;
+    pipe->state  = STATE_INIT;
+
+    {
+        AsyncStatus  status;
+
+        int  fd = socket_create( sock_address_get_family(address), SOCKET_STREAM );
+        if (fd < 0) {
+            D("%s: Could create socket from address family!", __FUNCTION__);
+            netPipe_free(pipe);
+            return NULL;
+        }
+
+        loopIo_init(pipe->io, looper, fd, netPipe_io_func, pipe);
+        asyncConnector_init(pipe->connector, address, pipe->io);
+        pipe->state = STATE_CONNECTING;
+
+        status = asyncConnector_run(pipe->connector);
+        if (status == ASYNC_ERROR) {
+            D("%s: Could not connect to socket: %s",
+              __FUNCTION__, errno_str);
+            netPipe_free(pipe);
+            return NULL;
+        }
+        if (status == ASYNC_COMPLETE) {
+            pipe->state = STATE_CONNECTED;
+            netPipe_resetState(pipe);
+        }
+    }
+
+    return pipe;
+}
+
+
+/* Called when the guest wants to close the channel. This is different
+ * from netPipe_closeFromSocket() which is called when the socket is
+ * disconnected. */
+static void
+netPipe_closeFromGuest( void* opaque )
+{
+    NetPipe*  pipe = opaque;
+    netPipe_free(pipe);
+}
+
+
+static int
+netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
+{
+    NetPipe*  pipe = opaque;
+    int       count = 0;
+    int       ret   = 0;
+    int       buffStart = 0;
+    const GoldfishPipeBuffer* buff = buffers;
+    const GoldfishPipeBuffer* buffEnd = buff + numBuffers;
+
+    for (; buff < buffEnd; buff++)
+        count += buff->size;
+
+    buff = buffers;
+    while (count > 0) {
+        int  avail = buff->size - buffStart;
+        int  len = write(pipe->io->fd, buff->data + buffStart, avail);
+
+        /* the write succeeded */
+        if (len > 0) {
+            buffStart += len;
+            if (buffStart >= buff->size) {
+                buff++;
+                buffStart = 0;
+            }
+            count -= len;
+            ret   += len;
+            continue;
+        }
+
+        /* we reached the end of stream? */
+        if (len == 0) {
+            if (ret == 0)
+                ret = PIPE_ERROR_IO;
+            break;
+        }
+
+        /* loop on EINTR */
+        if (errno == EINTR)
+            continue;
+
+        /* if we already wrote some stuff, simply return */
+        if (ret > 0) {
+            break;
+        }
+
+        /* need to return an appropriate error code */
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            ret = PIPE_ERROR_AGAIN;
+        } else {
+            ret = PIPE_ERROR_IO;
+        }
+        break;
+    }
+
+    return ret;
+}
+
+static int
+netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer*  buffers, int  numBuffers )
+{
+    NetPipe*  pipe = opaque;
+    int       count = 0;
+    int       ret   = 0;
+    int       buffStart = 0;
+    GoldfishPipeBuffer* buff = buffers;
+    GoldfishPipeBuffer* buffEnd = buff + numBuffers;
+
+    for (; buff < buffEnd; buff++)
+        count += buff->size;
+
+    buff = buffers;
+    while (count > 0) {
+        int  avail = buff->size - buffStart;
+        int  len = read(pipe->io->fd, buff->data + buffStart, avail);
+
+        /* the read succeeded */
+        if (len > 0) {
+            buffStart += len;
+            if (buffStart >= buff->size) {
+                buff++;
+                buffStart = 0;
+            }
+            count -= len;
+            ret   += len;
+            continue;
+        }
+
+        /* we reached the end of stream? */
+        if (len == 0) {
+            if (ret == 0)
+                ret = PIPE_ERROR_IO;
+            break;
+        }
+
+        /* loop on EINTR */
+        if (errno == EINTR)
+            continue;
+
+        /* if we already read some stuff, simply return */
+        if (ret > 0) {
+            break;
+        }
+
+        /* need to return an appropriate error code */
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            ret = PIPE_ERROR_AGAIN;
+        } else {
+            ret = PIPE_ERROR_IO;
+        }
+        break;
+    }
+    return ret;
+}
+
+static unsigned
+netPipe_poll( void* opaque )
+{
+    NetPipe*  pipe = opaque;
+    unsigned  mask = loopIo_poll(pipe->io);
+    unsigned  ret  = 0;
+
+    if (mask & LOOP_IO_READ)
+        ret |= PIPE_WAKE_READ;
+    if (mask & LOOP_IO_WRITE)
+        ret |= PIPE_WAKE_WRITE;
+
+    return ret;
+}
+
+static void
+netPipe_wakeOn( void* opaque, int flags )
+{
+    NetPipe*  pipe = opaque;
+
+    DD("%s: flags=%d", __FUNCTION__, flags);
+
+    pipe->wakeWanted |= flags;
+    netPipe_resetState(pipe);
+}
+
+
+void*
+netPipe_initTcp( void* hwpipe, void* _looper, const char* args )
+{
+    /* Build SockAddress from arguments. Acceptable formats are:
+     *
+     *   <port>
+     *   <host>:<port>
+     */
+    SockAddress  address;
+    void*        ret;
+
+    if (args == NULL) {
+        D("%s: Missing address!", __FUNCTION__);
+        return NULL;
+    }
+    D("%s: Address is '%s'", __FUNCTION__, args);
+
+    char        host[256];  /* max size of regular FDQN+1 */
+    int         hostlen = 0;
+    int         port;
+    const char* p;
+
+    /* Assume that anything after the last ':' is a port number
+        * And that what is before it is a port number. Should handle IPv6
+        * notation. */
+    p = strrchr(args, ':');
+    if (p != NULL) {
+        hostlen = p - args;
+        if (hostlen >= sizeof(host)) {
+            D("%s: Address too long!", __FUNCTION__);
+            return NULL;
+        }
+        memcpy(host, args, hostlen);
+        host[hostlen] = '\0';
+        args = p + 1;
+    } else {
+        snprintf(host, sizeof host, "127.0.0.1");
+    }
+
+    /* Now, look at the port number */
+    {
+        char* end;
+        long  val = strtol(args, &end, 10);
+        if (end == NULL || *end != '\0' || val <= 0 || val > 65535) {
+            D("%s: Invalid port number: '%s'", __FUNCTION__, args);
+        }
+        port = (int)val;
+    }
+    if (sock_address_init_resolve(&address, host, port, 0) < 0) {
+        D("%s: Could not resolve address", __FUNCTION__);
+        return NULL;
+    }
+
+    ret = netPipe_initFromAddress(hwpipe, &address, _looper);
+
+    sock_address_done(&address);
+    return ret;
+}
+
+void*
+netPipe_initUnix( void* hwpipe, void* _looper, const char* args )
+{
+    /* Build SockAddress from arguments. Acceptable formats are:
+     *
+     *   <path>
+     */
+    SockAddress  address;
+    void*        ret;
+
+    if (args == NULL || args[0] == '\0') {
+        D("%s: Missing address!", __FUNCTION__);
+        return NULL;
+    }
+    D("%s: Address is '%s'", __FUNCTION__, args);
+
+    sock_address_init_unix(&address, args);
+
+    ret = netPipe_initFromAddress(hwpipe, &address, _looper);
+
+    sock_address_done(&address);
+    return ret;
+}
+
+
+/**********************************************************************
+ **********************************************************************
+ *****
+ *****  N E T W O R K   P I P E   M E S S A G E S
+ *****
+ *****/
+
+static const GoldfishPipeFuncs  netPipeTcp_funcs = {
+    netPipe_initTcp,
+    netPipe_closeFromGuest,
+    netPipe_sendBuffers,
+    netPipe_recvBuffers,
+    netPipe_poll,
+    netPipe_wakeOn,
+};
+
+static const GoldfishPipeFuncs  netPipeUnix_funcs = {
+    netPipe_initUnix,
+    netPipe_closeFromGuest,
+    netPipe_sendBuffers,
+    netPipe_recvBuffers,
+    netPipe_poll,
+    netPipe_wakeOn,
+};
+
+
+#define DEFAULT_OPENGLES_PORT  22468
+
+static void*
+openglesPipe_init( void* hwpipe, void* _looper, const char* args )
+{
+    char temp[32];
+
+    /* For now, simply connect through tcp */
+    snprintf(temp, sizeof temp, "%d", DEFAULT_OPENGLES_PORT);
+    return netPipe_initTcp(hwpipe, _looper, temp);
+}
+
+static const GoldfishPipeFuncs  openglesPipe_funcs = {
+    openglesPipe_init,
+    netPipe_closeFromGuest,
+    netPipe_sendBuffers,
+    netPipe_recvBuffers,
+    netPipe_poll,
+    netPipe_wakeOn,
+};
+
+
+void
+android_net_pipes_init(void)
+{
+    Looper*  looper = looper_newCore();
+
+    goldfish_pipe_add_type( "tcp", looper, &netPipeTcp_funcs );
+    goldfish_pipe_add_type( "unix", looper, &netPipeUnix_funcs );
+    goldfish_pipe_add_type( "opengles", looper, &openglesPipe_funcs );
+}
diff --git a/android/hw-qemud-pipe.h b/android/hw-pipe-net.h
similarity index 78%
rename from android/hw-qemud-pipe.h
rename to android/hw-pipe-net.h
index 540e02b..08db031 100644
--- a/android/hw-qemud-pipe.h
+++ b/android/hw-pipe-net.h
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef _ANDROID_HW_QEMUD_PIPE_H
-#define _ANDROID_HW_QEMUD_PIPE_H
 
-void init_qemud_pipes(void);
-void android_hw_opengles_init(void);
+#ifndef ANDROID_PIPE_NET_H
+#define ANDROID_PIPE_NET_H
 
-#endif /* _ANDROID_HW_QEMUD_PIPE_H */
+void android_net_pipes_init(void);
+
+#endif /* ANDROID_PIPE_NET_H */
diff --git a/android/hw-qemud-pipe-net.c b/android/hw-qemud-pipe-net.c
deleted file mode 100644
index 874bc69..0000000
--- a/android/hw-qemud-pipe-net.c
+++ /dev/null
@@ -1,788 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "sockets.h"
-#include "android/utils/assert.h"
-#include "android/utils/panic.h"
-#include "android/utils/system.h"
-#include "android/async-utils.h"
-#include "android/looper.h"
-#include "android/hw-qemud-pipe.h"
-#include "hw/goldfish_pipe.h"
-
-/* Implement the OpenGL fast-pipe */
-
-/* Set to 1 or 2 for debug traces */
-#define  DEBUG  0
-
-#if DEBUG >= 1
-#  define D(...)   printf(__VA_ARGS__), printf("\n")
-#else
-#  define D(...)   ((void)0)
-#endif
-
-#if DEBUG >= 2
-#  define DD(...)                       printf(__VA_ARGS__), printf("\n")
-#  define DDASSERT(cond)                _ANDROID_ASSERT(cond, "Assertion failure: ", #cond)
-#  define DDASSERT_INT_OP(cond,val,op)  _ANDROID_ASSERT_INT_OP(cond,val,op)
-#else
-#  define DD(...)                       ((void)0)
-#  define DDASSERT(cond)                ((void)0)
-#  define DDASSERT_INT_OP(cond,val,op)  ((void)0)
-#endif
-
-#define DDASSERT_INT_LT(cond,val)  DDASSERT_INT_OP(cond,val,<)
-#define DDASSERT_INT_LTE(cond,val)  DDASSERT_INT_OP(cond,val,<=)
-#define DDASSERT_INT_GT(cond,val)  DDASSERT_INT_OP(cond,val,>)
-#define DDASSERT_INT_GTE(cond,val)  DDASSERT_INT_OP(cond,val,>=)
-#define DDASSERT_INT_EQ(cond,val)  DDASSERT_INT_OP(cond,val,==)
-#define DDASSERT_INT_NEQ(cond,val)  DDASSERT_INT_OP(cond,val,!=)
-
-
-/* Forward declarations */
-
-typedef struct NetPipeInit {
-    Looper*      looper;
-    SockAddress  serverAddress[1];
-} NetPipeInit;
-
-/**********************************************************************
- **********************************************************************
- *****
- *****  P I P E   M E S S A G E S
- *****
- *****/
-
-typedef struct PipeMsg {
-    struct PipeMsg* next;
-    size_t           size;
-    uint8_t          data[1];
-} PipeMsg;
-
-static PipeMsg*
-pipeMsg_alloc( size_t  size )
-{
-    PipeMsg*  msg = android_alloc(sizeof(*msg) + size);
-
-    msg->next = NULL;
-    msg->size = size;
-    return msg;
-}
-
-static void
-pipeMsg_free( PipeMsg*  msg )
-{
-    AFREE(msg);
-}
-
-/**********************************************************************
- **********************************************************************
- *****
- *****  M E S S A G E   L I S T
- *****
- *****/
-
-typedef struct {
-    PipeMsg*  firstMsg;    /* first message in list */
-    PipeMsg*  lastMsg;     /* last message in list */
-    size_t    firstBytes;  /* bytes in firstMsg that were already sent */
-    size_t    lastBytes;   /* bytes in lastMsg that were already received */
-    size_t    totalBytes;  /* total bytes in list */
-} MsgList;
-
-/* Default receiver buffer size to accept incoming data */
-#define  DEFAULT_RECEIVER_SIZE  8180
-
-/* Initialize a message list - appropriate for sending them out */
-static void
-msgList_initSend( MsgList*  list )
-{
-    list->firstMsg   = NULL;
-    list->lastMsg    = NULL;
-    list->firstBytes = 0;
-    list->lastBytes  = 0;
-    list->totalBytes = 0;
-}
-
-/* Initialize a message list for receiving data */
-static void
-msgList_initReceive( MsgList*  list )
-{
-    msgList_initSend(list);
-    list->firstMsg  = list->lastMsg = pipeMsg_alloc( DEFAULT_RECEIVER_SIZE );
-}
-
-/* Finalize a message list */
-static void
-msgList_done( MsgList*  list )
-{
-    PipeMsg*  msg;
-
-    while ((msg = list->firstMsg) != NULL) {
-        list->firstMsg = msg->next;
-        pipeMsg_free(msg);
-    }
-    list->lastMsg = NULL;
-
-    list->firstBytes  = 0;
-    list->lastBytes   = 0;
-    list->totalBytes  = 0;
-}
-
-static int
-msgList_hasData( MsgList*  list )
-{
-    return list->totalBytes > 0;
-}
-
-/* Append a list of buffers to a message list.
- *
- * This is a very simple implementation that simply mallocs a single
- * new message containing all of the buffer's data, and append it to
- * our link list. This also makes the implementation of msgList_send()
- * quite simple, since there is no need to deal with the 'lastBytes'
- * pointer (it is always assumed to be 'lastMsg->size').
- */
-static int
-msgList_sendBuffers( MsgList*                   list,
-                     const GoldfishPipeBuffer*  buffers,
-                     int                        numBuffers )
-{
-    const GoldfishPipeBuffer*  buff    = buffers;
-    const GoldfishPipeBuffer*  buffEnd = buff + numBuffers;
-    PipeMsg*                   msg;
-    size_t                     msgSize = 0;
-    size_t                     pos;
-
-    /* Count the total number of bytes */
-    for ( ; buff < buffEnd; buff++ ) {
-        msgSize += buff[0].size;
-    }
-
-    /* Allocate a new message */
-    msg = pipeMsg_alloc(msgSize);
-    if (msg == NULL) {
-        errno = ENOMEM;
-        return -1;
-    }
-
-    /* Copy data from buffers to message */
-    for ( pos = 0, buff = buffers; buff < buffEnd; buff++ ) {
-        memcpy(msg->data + pos, buff->data, buff->size);
-        pos += buff->size;
-    }
-
-    /* Append message to current list */
-    if (list->lastMsg != NULL) {
-        list->lastMsg->next = msg;
-    } else {
-        list->firstMsg   = msg;
-        list->firstBytes = 0;
-    }
-    list->lastMsg = msg;
-
-    list->totalBytes += msgSize;
-
-    /* We are done */
-    return 0;
-}
-
-/* Try to send outgoing messages in the list through non-blocking socket 'fd'.
- * Return 0 on success, and -1 on failure, where errno will be:
- *
- *    ECONNRESET - connection reset by peer
- *
- * Note that 0 will be returned if socket_send() returns EAGAIN/EWOULDBLOCK.
- */
-static int
-msgList_send( MsgList*  list, int  fd )
-{
-    int  ret  = 0;
-
-    for (;;) {
-        PipeMsg*  msg        = list->firstMsg;
-        size_t    sentBytes  = list->firstBytes;
-        size_t    availBytes;
-
-        if (msg == NULL) {
-            /* We sent everything */
-            return 0;
-        }
-        DDASSERT(sentBytes < msg->size);
-        availBytes = msg->size - sentBytes;
-
-        ret = socket_send(fd, msg->data + sentBytes, availBytes);
-        if (ret <= 0) {
-            goto ERROR;
-        }
-
-        list->totalBytes -= ret;
-        list->firstBytes += ret;
-
-        if (list->firstBytes < msg->size) {
-            continue;
-        }
-
-        /* We sent the full first packet - remove it from the head */
-        list->firstBytes = 0;
-        list->firstMsg   = msg->next;
-        if (list->firstMsg == NULL) {
-            list->lastMsg = NULL;
-        }
-        pipeMsg_free(msg);
-    }
-
-ERROR:
-    if (ret < 0) { /* EAGAIN/EWOULDBLOCK or disconnection */
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-            ret = 0; /* clear error - this is normal */
-        } else {
-            DD("%s: socket_send() returned %d: %s\n", __FUNCTION__, errno, errno_str);
-            errno = ECONNRESET;
-        }
-    } else {
-#if DEBUG >= 2
-        int  err = socket_get_error(fd);
-        DD("%s: socket_send() returned 0 (error=%d: %s)", __FUNCTION__,
-        err, strerror(err));
-#endif
-        errno = ECONNRESET;
-        ret   = -1;
-    }
-    return ret;
-}
-
-
-/* Try to receive data into caller-provided buffers, and return the total
- * size of bytes that were read. Returns -1 on error, with errno:
- *
- *    ECONNRESET: Connection reset by peer
- *    EAGAIN: No incoming data, wait for it to arrive.
- */
-static int
-msgList_recvBuffers( MsgList*             list,
-                     GoldfishPipeBuffer*  buffers,
-                     int                  numBuffers )
-{
-    GoldfishPipeBuffer*  buff = buffers;
-    GoldfishPipeBuffer*  buffEnd = buff + numBuffers;
-    size_t               buffStart = 0;
-    PipeMsg*             msg   = list->firstMsg;
-    size_t               msgStart = list->firstBytes;
-    size_t               totalSize = 0;
-
-    DDASSERT(msg != NULL);
-
-    D("%s: ENTER list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d",
-      __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes,
-      list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg);
-
-    /* If there is no incoming data, return EAGAIN */
-    if (list->totalBytes == 0) {
-        errno = EAGAIN;
-        return -1;
-    }
-
-    /* Now try to transfer as much from the list of incoming messages
-     * into the buffers.
-     */
-    while (msg != NULL && buff < buffEnd) {
-        DDASSERT(msgStart < msg->size);
-        DDASSERT(buffStart < buff->size);
-
-        /* Copy data from current message into next buffer.
-         * For a given message, first determine the start and end
-         * of available data. Then try to see how much of these
-         * we can copy to the current buffer.
-         */
-        size_t  msgEnd = msg->size;
-
-        if (msg == list->lastMsg) {
-            msgEnd = list->lastBytes;
-        }
-
-        size_t  msgAvail = msgEnd - msgStart;
-        size_t  buffAvail = buff->size - buffStart;
-
-        if (msgAvail > buffAvail) {
-            msgAvail = buffAvail;
-        }
-
-        DDASSERT(msgAvail > 0);
-
-        D("%s: transfer %d bytes (msgStart=%d msgSize=%d buffStart=%d buffSize=%d)",
-          __FUNCTION__, msgAvail, msgStart, msg->size, buffStart, buff->size);
-        memcpy(buff->data + buffStart, msg->data + msgStart, msgAvail);
-
-        /* Advance cursors */
-        msgStart   += msgAvail;
-        buffStart  += msgAvail;
-        totalSize  += msgAvail;
-
-        /* Did we fill up the current buffer? */
-        if (buffStart >= buff->size) {
-            buffStart = 0;
-            buff++;
-        }
-
-        /* Did we empty the current message? */
-        if (msgStart >= msgEnd) {
-            msgStart = 0;
-            /* If this is the last message, reset the 'first' and 'last'
-             * pointers to reuse it for the next recv(). */
-            if (msg == list->lastMsg) {
-                list->lastBytes = 0;
-                msg = NULL;
-            } else {
-                /* Otherwise, delete the message, and jump to the next one */
-                list->firstMsg = msg->next;
-                pipeMsg_free(msg);
-                msg = list->firstMsg;
-            }
-        }
-
-    }
-    list->firstBytes  = msgStart;
-    list->totalBytes -= totalSize;
-
-    D("%s: EXIT list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d",
-      __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes,
-      list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg);
-
-    return (int)totalSize;
-}
-
-
-/* Try to receive data from non-blocking socket 'fd'.
- * Return 0 on success, or -1 on error, where errno can be:
- *
- *    ECONNRESET         - connection reset by peer
- *    ENOMEM             - full message list, no room to receive more data
- */
-static int
-msgList_recv( MsgList*  list, int  fd )
-{
-    int  ret = 0;
-
-    D("%s: ENTER list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d",
-      __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes,
-      list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg);
-
-    for (;;) {
-        PipeMsg*  last = list->lastMsg;
-        size_t    lastBytes = list->lastBytes;
-        size_t    availBytes;
-
-        /* Compute how many bytes we can receive in the last buffer*/
-        DDASSERT(last != NULL);
-        DDASSERT(last->size > 0);
-        DDASSERT(lastBytes < last->size);
-
-        availBytes = last->size - lastBytes;
-
-        /* Try to receive the data, act on errors */
-        ret = socket_recv(fd, last->data + lastBytes, availBytes);
-        if (ret <= 0) {
-            goto ERROR;
-        }
-
-        /* Acknowledge received data */
-        list->lastBytes  += ret;
-        list->totalBytes += ret;
-
-        if (list->lastBytes < last->size) {
-            continue;
-        }
-
-        /* We filled-up the last message buffer, allocate a new one */
-        last = pipeMsg_alloc( DEFAULT_RECEIVER_SIZE );
-        list->lastMsg->next = last;
-        list->lastMsg       = last;
-        list->lastBytes     = 0;
-    }
-
-ERROR:
-    if (ret < 0) { /* EAGAIN/EWOULDBLOCK or disconnection */
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-            ret = 0;  /* clear error - this is normal */
-        } else {
-            DD("%s: socket_send() returned %d: %s\n", __FUNCTION__, errno, errno_str);
-            errno = ECONNRESET;
-        }
-    } else /* ret == 0 */ {
-#if DEBUG >= 2
-        int  err = socket_get_error(fd);
-        DD("%s: socket_send() returned 0 (error=%d: %s)", __FUNCTION__,
-        err, strerror(err));
-#endif
-        errno = ECONNRESET;
-        ret   = -1;
-    }
-    D("%s: EXIT list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d",
-      __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes,
-      list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg);
-
-    return ret;
-}
-
-
-
-/**********************************************************************
- **********************************************************************
- *****
- *****  P I P E   H A N D L E R S
- *****
- *****/
-
-/* Technical Note:
- *
- * Each NetPipe object is connected to the following:
- *
- *  - a remote rendering process through a normal TCP socket.
- *  - a Goldfish pipe (see hw/goldfish_pipe.h) to exchange messages with the guest.
- *  - a Qemud client  (see android/hw-qemud.h) to signal state changes to the guest.
- *
- *      REMOTE  <---socket---> PIPE <------> GOLDFISH PIPE
- *     PROCESS                      <--+
- *                                     |
- *                                     +---> QEMUD CHANNEL (android/hw-qemud.h)
- *
- */
-enum {
-    STATE_INIT,
-    STATE_CONNECTING,
-    STATE_CONNECTED,
-    STATE_CLOSING_GUEST,
-    STATE_CLOSING_SOCKET
-};
-
-#define  DEFAULT_INCOMING_SIZE  4000
-
-#define  MAX_IN_BUFFERS  4
-
-typedef struct {
-    QemudClient*  client;
-    int           state;
-    int           wakeWanted;
-
-    MsgList       outList[1];
-    MsgList       inList[1];
-
-    LoopIo          io[1];
-    AsyncConnector  connector[1];
-
-    GoldfishPipeBuffer  outBuffer[1];
-    GoldfishPipeBuffer  inBuffers[MAX_IN_BUFFERS];
-    int                 inBufferCount;
-
-} NetPipe;
-
-static void
-netPipe_free( NetPipe*  pipe )
-{
-    int  fd;
-
-    /* Removing any pending incoming packet */
-    msgList_done(pipe->outList);
-    msgList_done(pipe->inList);
-
-    /* Close the socket */
-    fd = pipe->io->fd;
-    loopIo_done(pipe->io);
-    socket_close(fd);
-
-    /* Release the pipe object */
-    AFREE(pipe);
-}
-
-
-static void
-netPipe_resetState( NetPipe* pipe )
-{
-    /* If there is a pending outgoing packet, open the valve */
-    if (msgList_hasData(pipe->outList)) {
-        loopIo_wantWrite(pipe->io);
-    } else {
-        loopIo_dontWantWrite(pipe->io);
-    }
-
-    /* Accept incoming data if we are not closing, and our input list isn't full */
-    if (pipe->state == STATE_CONNECTED) {
-        loopIo_wantRead(pipe->io);
-    } else {
-        loopIo_dontWantRead(pipe->io);
-    }
-}
-
-
-/* This function is only called when the socket is disconnected.
- * See netPipe_closeFromGuest() for the case when the guest requires
- * the disconnection. */
-static void
-netPipe_closeFromSocket( void* opaque )
-{
-    NetPipe*  pipe = opaque;
-
-    /* If the guest already ordered the pipe to be closed, delete immediately */
-    if (pipe->state == STATE_CLOSING_GUEST) {
-        netPipe_free(pipe);
-        return;
-    }
-
-    /* Force the closure of the QEMUD channel - if a guest is blocked
-     * waiting for a wake signal, it will receive an error. */
-    if (pipe->client != NULL) {
-        qemud_client_close(pipe->client);
-        pipe->client = NULL;
-    }
-
-    /* Remove any outgoing packets - they won't go anywhere */
-    msgList_done(pipe->outList);
-
-    pipe->state = STATE_CLOSING_SOCKET;
-    netPipe_resetState(pipe);
-}
-
-
-/* This is the function that gets called each time there is an asynchronous
- * event on the network pipe.
- */
-static void
-netPipe_io_func( void* opaque, int fd, unsigned events )
-{
-    NetPipe*  pipe = opaque;
-    int         wakeFlags = 0;
-
-    /* Run the connector if we are in the CONNECTING state     */
-    /* TODO: Add some sort of time-out, to deal with the case */
-    /*        where the renderer process is wedged.            */
-    if (pipe->state == STATE_CONNECTING) {
-        AsyncStatus  status = asyncConnector_run(pipe->connector);
-        if (status == ASYNC_NEED_MORE) {
-            return;
-        }
-        else if (status == ASYNC_ERROR) {
-            /* Could not connect, tell our client by closing the channel. */
-
-            netPipe_closeFromSocket(pipe);
-            return;
-        }
-        pipe->state = STATE_CONNECTED;
-        netPipe_resetState(pipe);
-        return;
-    }
-
-    /* Otherwise, accept incoming data */
-    if ((events & LOOP_IO_READ) != 0) {
-        int ret;
-
-        if ((pipe->wakeWanted & QEMUD_PIPE_WAKE_ON_RECV) != 0) {
-            wakeFlags |= QEMUD_PIPE_WAKE_ON_RECV;
-        }
-
-        ret = msgList_recv(pipe->inList, fd);
-        if (ret < 0) {
-            wakeFlags &= ~QEMUD_PIPE_WAKE_ON_RECV;
-
-            if (errno == ENOMEM) { /* shouldn't happen */
-                DD("%s: msgList_recv() return ENOMEM!?\n", __FUNCTION__);
-            } else {
-                /* errno == ECONNRESET */
-                DD("%s: msgList_recv() error, closing pipe\n", __FUNCTION__);
-                netPipe_closeFromSocket(pipe);
-                return;
-            }
-        }
-    }
-
-    if ((events & LOOP_IO_WRITE) != 0) {
-        int  ret;
-
-        DDASSERT(msgList_hasData(pipe->outList));
-
-        ret = msgList_send(pipe->outList, fd);
-        if (ret < 0) {
-            DD("%s: msgList_send() error, closing pipe\n", __FUNCTION__);
-            netPipe_closeFromSocket(pipe);
-            return;
-        }
-
-        if ((pipe->wakeWanted & QEMUD_PIPE_WAKE_ON_SEND) != 0) {
-            wakeFlags |= QEMUD_PIPE_WAKE_ON_SEND;
-        }
-    }
-
-    /* Send wake signal to the guest if needed */
-    if (wakeFlags != 0) {
-        uint8_t  byte = (uint8_t) wakeFlags;
-        DD("%s: Sending wake flags %d (wanted=%d)", __FUNCTION__, byte, pipe->wakeWanted);
-        qemud_client_send(pipe->client, &byte, 1);
-        pipe->wakeWanted &= ~wakeFlags;
-    }
-
-    /* Reset state */
-    netPipe_resetState(pipe);
-}
-
-
-void*
-netPipe_init( QemudClient*  qcl, void*  pipeOpaque )
-{
-    NetPipe*      pipe;
-    NetPipeInit*  pipeSvc = pipeOpaque;
-
-    ANEW0(pipe);
-
-    pipe->client = qcl;
-    pipe->state  = STATE_INIT;
-
-    msgList_initSend(pipe->outList);
-    msgList_initReceive(pipe->inList);
-
-#define DEFAULT_OPENGLES_PORT  22468
-
-    {
-        AsyncStatus  status;
-
-        int  fd = socket_create_inet( SOCKET_STREAM );
-        if (fd < 0) {
-            netPipe_free(pipe);
-            return NULL;
-        }
-
-        loopIo_init(pipe->io, pipeSvc->looper, fd, netPipe_io_func, pipe);
-        asyncConnector_init(pipe->connector, pipeSvc->serverAddress, pipe->io);
-        pipe->state = STATE_CONNECTING;
-
-        status = asyncConnector_run(pipe->connector);
-        if (status == ASYNC_ERROR) {
-            D("%s: Could not create to renderer process: %s",
-              __FUNCTION__, errno_str);
-            netPipe_free(pipe);
-            return NULL;
-        }
-        if (status == ASYNC_COMPLETE) {
-            pipe->state = STATE_CONNECTED;
-            netPipe_resetState(pipe);
-        }
-    }
-
-    return pipe;
-}
-
-/* Called when the guest wants to close the channel. This is different
- * from netPipe_closeFromSocket() which is called when the socket is
- * disconnected. */
-static void
-netPipe_closeFromGuest( void* opaque )
-{
-    NetPipe*  pipe = opaque;
-
-    /* The qemud client is gone when we reach this code */
-    pipe->client = NULL;
-
-    /* Remove input messages */
-    msgList_done(pipe->inList);
-
-    /* If the socket is already closed, or if there are no
-     * outgoing messages, delete immediately */
-    if (pipe->state == STATE_CLOSING_SOCKET ||
-        !msgList_hasData(pipe->outList)) {
-        netPipe_free(pipe);
-        return;
-    }
-
-    /* Otherwise, mark our pipe as closing, and wait until everything is
-     * sent before deleting the object. */
-    pipe->state = STATE_CLOSING_GUEST;
-    netPipe_resetState(pipe);
-}
-
-
-static int
-netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
-{
-    NetPipe*  pipe = opaque;
-    int         ret;
-
-    ret = msgList_sendBuffers(pipe->outList, buffers, numBuffers);
-    netPipe_resetState(pipe);
-    return ret;
-}
-
-static int
-netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer*  buffers, int  numBuffers )
-{
-    NetPipe*  pipe = opaque;
-    int         ret;
-
-    ret = msgList_recvBuffers(pipe->inList, buffers, numBuffers);
-    netPipe_resetState(pipe);
-    return ret;
-}
-
-static void
-netPipe_wakeOn( void* opaque, int flags )
-{
-    NetPipe*  pipe = opaque;
-
-    pipe->wakeWanted |= flags;
-}
-
-/**********************************************************************
- **********************************************************************
- *****
- *****  N E T W O R K   P I P E   M E S S A G E S
- *****
- *****/
-
-static const QemudPipeHandlerFuncs  net_pipe_handler_funcs = {
-    netPipe_init,
-    netPipe_closeFromGuest,
-    netPipe_sendBuffers,
-    netPipe_recvBuffers,
-    netPipe_wakeOn,
-};
-
-
-static NetPipeInit  _netPipeService[1];
-
-/**********************************************************************
- **********************************************************************
- *****
- *****  O P E N G L E S   P I P E   S E R V I C E
- *****
- *****/
-
-void
-android_hw_opengles_init(void)
-{
-    NetPipeInit*  svc = _netPipeService;
-    int           ret;
-
-    DD("%s: Registering service\n", __FUNCTION__);
-
-    svc->looper = looper_newCore();
-
-    ret = sock_address_init_resolve(svc->serverAddress,
-                                    "127.0.0.1",
-                                    DEFAULT_OPENGLES_PORT,
-                                    0);
-    if (ret < 0) {
-        APANIC("Could not resolve renderer process address!");
-    }
-
-    goldfish_pipe_add_type( "opengles", svc, &net_pipe_handler_funcs );
-}
diff --git a/android/looper-generic.c b/android/looper-generic.c
index d75fbff..ddc0824 100644
--- a/android/looper-generic.c
+++ b/android/looper-generic.c
@@ -203,6 +203,13 @@
     gloopio_modify(io, io->wanted & ~LOOP_IO_WRITE);
 }
 
+static unsigned
+gloopio_poll(void* impl)
+{
+    GLoopIo* io = impl;
+    return io->ready;
+}
+
 static void
 gloopio_free(void* impl)
 {
@@ -219,6 +226,7 @@
     gloopio_wantWrite,
     gloopio_dontWantRead,
     gloopio_dontWantWrite,
+    gloopio_poll,
     gloopio_free
 };
 
diff --git a/android/looper-qemu.c b/android/looper-qemu.c
index cce0bfa..5526f5b 100644
--- a/android/looper-qemu.c
+++ b/android/looper-qemu.c
@@ -254,11 +254,19 @@
     qemu_free(io);
 }
 
+static unsigned
+qloopio_poll(void* impl)
+{
+    QLoopIo* io = impl;
+    return io->ready;
+}
+
 static const LoopIoClass  qlooper_io_class = {
     qloopio_wantRead,
     qloopio_wantWrite,
     qloopio_dontWantRead,
     qloopio_dontWantWrite,
+    qloopio_poll,
     qloopio_free
 };
 
diff --git a/android/looper.h b/android/looper.h
index d67fdad..923ef09 100644
--- a/android/looper.h
+++ b/android/looper.h
@@ -215,6 +215,7 @@
     void (*wantWrite)(void* impl);
     void (*dontWantRead)(void* impl);
     void (*dontWantWrite)(void* impl);
+    unsigned (*poll)(void* impl);
     void (*done)(void* impl);
 };
 
@@ -255,6 +256,11 @@
 {
     io->clazz->dontWantWrite(io->impl);
 }
+AINLINED unsigned
+loopIo_poll(LoopIo* io)
+{
+    return io->clazz->poll(io->impl);
+}
 
 /**********************************************************************
  **********************************************************************
diff --git a/android/protocol/ui-commands-proxy.c b/android/protocol/ui-commands-proxy.c
index 1aedc62..d16092a 100644
--- a/android/protocol/ui-commands-proxy.c
+++ b/android/protocol/ui-commands-proxy.c
@@ -177,7 +177,7 @@
         // the UI about the event.
         AndroidHwControlFuncs  funcs;
         funcs.light_brightness = _uiCmdProxy_brightness_change_callback;
-        android_hw_control_init(&_uiCmdProxy, &funcs);
+        android_hw_control_set(&_uiCmdProxy, &funcs);
     }
     return 0;
 }
diff --git a/android/protocol/ui-commands-qemu.c b/android/protocol/ui-commands-qemu.c
index 3dbed31..2bb22c9 100644
--- a/android/protocol/ui-commands-qemu.c
+++ b/android/protocol/ui-commands-qemu.c
@@ -35,6 +35,6 @@
 {
     AndroidHwControlFuncs  funcs;
     funcs.light_brightness = callback;
-    android_hw_control_init(opaque, &funcs);
+    android_hw_control_set(opaque, &funcs);
     return 0;
 }
diff --git a/android/utils/bufprint.c b/android/utils/bufprint.c
index e3aa63c..4626a0f 100644
--- a/android/utils/bufprint.c
+++ b/android/utils/bufprint.c
@@ -147,7 +147,7 @@
 		len = sizeof(appDir)-1;
 	    appDir[len] = 0;
     }
-	
+
 	sep = strrchr(appDir, '\\');
 	if (sep)
 	  *sep = 0;
@@ -215,9 +215,15 @@
 
     return  bufprint(buff, end, "%s", path);
 #else
+    char path[MAX_PATH];
     const char*  tmppath = getenv("ANDROID_TMP");
     if (!tmppath) {
-        tmppath = "/tmp/android";
+        const char* user = getenv("USER");
+        if (user == NULL || user[0] == '\0')
+            user = "unknown";
+
+        snprintf(path, sizeof path, "/tmp/android-%s", user);
+        tmppath = path;
     }
     mkdir(tmppath, 0744);
     return  bufprint(buff, end, "%s", tmppath );
diff --git a/docs/ANDROID-QEMU-PIPE.TXT b/docs/ANDROID-QEMU-PIPE.TXT
new file mode 100644
index 0000000..c12dea0
--- /dev/null
+++ b/docs/ANDROID-QEMU-PIPE.TXT
@@ -0,0 +1,224 @@
+Android QEMU FAST PIPES
+=======================
+
+Introduction:
+-------------
+
+The Android emulator implements a special virtual device used to provide
+_very_ fast communication channels between the guest system and the
+emulator itself.
+
+From the guest, usage is simply as follows:
+
+  1/ Open the /dev/qemu_pipe device for read+write
+
+  2/ Write a zero-terminated string describing which service you want to
+     connect.
+
+  3/ Simply use read() and write() to communicate with the service.
+
+In other words:
+
+   fd = open("/dev/qemu_pipe", O_RDWR);
+   const char* pipeName = "<pipename>";
+   ret = write(fd, pipeName, strlen(pipeName)+1);
+   if (ret < 0) {
+       // error
+   }
+   ... ready to go
+
+Where <pipename> is the name of a specific emulator service you want to use.
+Supported service names are listed later in this document.
+
+
+Implementation details:
+-----------------------
+
+In the emulator source tree:
+
+    ./hw/goldfish_pipe.c implements the virtual driver.
+
+    ./hw/goldfish_pipe.h provides the interface that must be
+    implemented by any emulator pipe service.
+
+    ./android/hw-pipe-net.c contains the implementation of the network pipe
+    services (i.e. 'tcp' and 'unix'). See below for details.
+
+In the kernel source tree:
+
+    drivers/misc/qemupipe/qemu_pipe.c contains the driver source code
+    that will be accessible as /dev/qemu_pipe within the guest.
+
+
+Device / Driver Protocol details:
+---------------------------------
+
+The device and driver use an I/O memory page and an IRQ to communicate.
+
+  - The driver writes to various I/O registers to send commands to the
+    device.
+
+  - The device raises an IRQ to instruct the driver that certain events
+    occured.
+
+  - The driver reads I/O registers to get the status of its latest command,
+    or the list of events that occured in case of interrupt.
+
+Each opened file descriptor to /dev/qemu_pipe in the guest corresponds to a
+32-bit 'channel' value allocated by the driver.
+
+The following is a description of the various commands sent by the driver
+to the device. Variable names beginning with REG_ correspond to 32-bit I/O
+registers:
+
+  1/ Creating a new channel:
+
+     Used by the driver to indicate that the guest just opened /dev/qemu_pipe
+     that will be identified by a 32-bit value named '<channel>' here:
+
+        REG_CHANNEL = <channel>
+        REG_CMD     = CMD_OPEN
+
+     IMPORTANT: <channel> should never be 0
+
+  2/ Closing a channel:
+
+     Used by the driver to indicate that the guest called 'close' on the
+     channel file descriptor.
+
+        REG_CHANNEL = <channel>
+        REG_CMD     = CMD_CLOSE
+
+  3/ Writing data to the channel:
+
+     Corresponds to when the guest does a write() or writev() on the
+     channel's file descriptor. This command is used to send a single
+     memory buffer:
+
+        REG_CHANNEL = <channel>
+        REG_ADDRESS = <buffer-address>
+        REG_SIZE    = <buffer-size>
+        REG_CMD     = CMD_WRITE_BUFFER
+
+        status = REG_STATUS
+
+    NOTE: The <buffer-address> is the *GUEST* buffer address, not the
+          physical/kernel one.
+
+    IMPORTANT: The buffer sent through this command SHALL ALWAYS be entirely
+               contained inside a single page of guest memory. This is
+               enforced to simplify both the driver and the device.
+
+               When a write() spans several pages of guest memory, the
+               driver will issue several CMD_WRITE_BUFFER commands in
+               succession, transparently to the client.
+
+    The value returned by REG_STATUS should be:
+
+       > 0    The number of bytes that were written to the pipe
+       0      To indicate end-of-stream status
+       < 0    A negative error code (see below).
+
+    On important error code is PIPE_ERROR_AGAIN, used to indicate that
+    writes can't be performed yet. See CMD_WAKE_ON_WRITE for more.
+
+  4/ Reading data from the channel:
+
+    Corresponds to when the guest does a read() or readv() on the
+    channel's file descriptor.
+
+        REG_CHANNEL = <channel>
+        REG_ADDRESS = <buffer-address>
+        REG_SIZE    = <buffer-size>
+        REG_CMD     = CMD_READ_BUFFER
+
+        status = REG_STATUS
+
+    Same restrictions on buffer addresses/lengths and same set of error
+    codes.
+
+  5/ Waiting for write ability:
+
+    If CMD_WRITE_BUFFER returns PIPE_ERROR_AGAIN, and the file descriptor
+    is not in non-blocking mode, the driver must put the client task on a
+    wait queue until the pipe service can accept data again.
+
+    Before this, the driver will do:
+
+        REG_CHANNEL = <channel>
+        REG_CMD     = CMD_WAKE_ON_WRITE
+
+    To indicate to the virtual device that it is waiting and should be woken
+    up when the pipe becomes writable again. How this is done is explained
+    later.
+
+  6/ Waiting for read ability:
+
+    This is the same than CMD_WAKE_ON_WRITE, but for readability instead.
+
+        REG_CHANNEL = <channel>
+        REG_CMD     = CMD_WAKE_ON_WRITE
+
+  7/ Polling for write-able/read-able state:
+
+    The following command is used by the driver to implement the select(),
+    poll() and epoll() system calls where a pipe channel is involved.
+
+        REG_CHANNEL = <channel>
+        REG_CMD     = CMD_POLL
+        mask = REG_STATUS
+
+    The mask value returned by REG_STATUS is a mix of bit-flags for
+    which events are available / have occured since the last call.
+    See PIPE_POLL_READ / PIPE_POLL_WRITE / PIPE_POLL_CLOSED.
+
+  8/ Signaling events to the driver:
+
+    The device can signal events to the driver by raising its IRQ.
+    The driver's interrupt handler will then have to read a list of
+    (channel,mask) pairs, terminated by a single 0 value for the channel.
+
+    In other words, the driver's interrupt handler will do:
+
+        for (;;) {
+            channel = REG_CHANNEL
+            if (channel == 0)  // END OF LIST
+                break;
+
+            mask = REG_WAKES  // BIT FLAGS OF EVENTS
+            ... process events
+        }
+
+    The events reported through this list are simply:
+
+       PIPE_WAKE_READ   :: the channel is now readable.
+       PIPE_WAKE_WRITE  :: the channel is now writable.
+       PIPE_WAKE_CLOSED :: the pipe service closed the connection.
+
+    The PIPE_WAKE_READ and PIPE_WAKE_WRITE are only reported for a given
+    channel if CMD_WAKE_ON_READ or CMD_WAKE_ON_WRITE (respectively) were
+    issued for it.
+
+    PIPE_WAKE_CLOSED can be signaled at any time.
+
+
+Available services:
+-------------------
+
+  tcp:<port>
+  tcp:<hostname>:<port>
+
+     Open a TCP socket to a given port. This provides a very fast
+     pass-through that doesn't depend on the very slow internal emulator
+     NAT router. Note that you can only use the file descriptor with read()
+     and write() though, send() and recv() will return an ENOTSOCK error,
+     as well as any socket ioctl().
+
+  unix:<path>
+
+     Open a Unix-domain socket on the host.
+
+  opengles
+
+     Connects to the OpenGL ES emulation process. For now, the implementation
+     is equivalent to tcp:22468, but this may change in the future.
diff --git a/hw/android_arm.c b/hw/android_arm.c
index 806d9bd..3b9dc6d 100644
--- a/hw/android_arm.c
+++ b/hw/android_arm.c
@@ -27,7 +27,6 @@
 #endif  // CONFIG_MEMCHECK
 
 #include "android/utils/debug.h"
-#include "android/hw-qemud-pipe.h"
 
 #define  D(...)  VERBOSE_PRINT(init,__VA_ARGS__)
 
@@ -148,7 +147,6 @@
 #ifdef CONFIG_MEMCHECK
         || memcheck_enabled
 #endif  // CONFIG_MEMCHECK
-        || 1  /* XXX: ALWAYS AVAILABLE FOR QEMUD PIPES */
        ) {
         trace_dev_init();
     }
@@ -159,7 +157,7 @@
     }
 #endif
 
-    init_qemud_pipes();
+    pipe_dev_init();
 
 #if TEST_SWITCH
     {
diff --git a/hw/goldfish_pipe.c b/hw/goldfish_pipe.c
index 6c24208..998ba49 100644
--- a/hw/goldfish_pipe.c
+++ b/hw/goldfish_pipe.c
@@ -9,14 +9,16 @@
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU General Public License for more details.
 */
-#include "android/utils/intmap.h"
 #include "android/utils/panic.h"
-#include "android/utils/reflist.h"
 #include "android/utils/system.h"
-#include "android/hw-qemud.h"
 #include "hw/goldfish_pipe.h"
+#include "hw/goldfish_device.h"
+#include "qemu-timer.h"
 
-#define  DEBUG 0
+#define  DEBUG 1
+
+/* Set to 1 to debug i/o register reads/writes */
+#define DEBUG_REGS  0
 
 #if DEBUG >= 1
 #  define D(...)  fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
@@ -30,711 +32,1035 @@
 #  define DD(...)  (void)0
 #endif
 
+#if DEBUG_REGS >= 1
+#  define DR(...)   D(__VA_ARGS__)
+#else
+#  define DR(...)   (void)0
+#endif
+
 #define E(...)  fprintf(stderr, "ERROR:" __VA_ARGS__), fprintf(stderr, "\n")
 
-/* Must match hw/goldfish_trace.h */
-#define  SLOT_COMMAND   0
-#define  SLOT_STATUS    0
-#define  SLOT_ADDRESS   1
-#define  SLOT_SIZE      2
-#define  SLOT_CHANNEL   3
+/* Set to 1 to enable the 'zero' pipe type, useful for debugging */
+#define DEBUG_ZERO_PIPE  1
 
-#define   QEMUD_PIPE_CMD_CLOSE              1
-#define   QEMUD_PIPE_CMD_SEND               2
-#define   QEMUD_PIPE_CMD_RECV               3
-#define   QEMUD_PIPE_CMD_WAKE_ON_SEND       4
-#define   QEMUD_PIPE_CMD_WAKE_ON_RECV       5
+/* Set to 1 to enable the 'pingpong' pipe type, useful for debugging */
+#define DEBUG_PINGPONG_PIPE 1
 
-#define   QEMUD_PIPE_ERROR_INVAL            22   /* EINVAL */
-#define   QEMUD_PIPE_ERROR_AGAIN            11   /* EAGAIN */
-#define   QEMUD_PIPE_ERROR_CONNRESET       104   /* ECONNRESET */
-#define   QEMUD_PIPE_ERROR_NOMEM            12   /* ENOMEM */
+/* Set to 1 to enable the 'throttle' pipe type, useful for debugging */
+#define DEBUG_THROTTLE_PIPE 1
 
-/**********************************************************************
- **********************************************************************
- **
- **  TECHNICAL NOTE ON THE FOLLOWING IMPLEMENTATION:
- **
- **    PipeService ::
- **       The global state for the QEMUD fast pipes service.
- **       Holds a tid -> ThreadState map. Registers as a Qemud
- **       service named "fast-pipes" which creates PipeClient
- **       objects on connection.
- **
- **    PipeClient ::
- **       The state of each QEMUD pipe. This handles the initial
- **       connection, message exchanges and signalling.
- **
- **    ThreadState ::
- **       Hold the thread-specific state corresponding to each guest
- **       thread that has created a least one qemud pipe. Stores the
- **       state of our 4 I/O Registers, and a map of localId -> PipeState
- **
- **
- **  The following graphics is an example corresponding to the following
- **  situation:
- **
- **     - two guest threads have opened pipe connections
- **     - the first thread has opened two different pipes
- **     - the second thread has opened only one pipe
- **
- **
- **     QEMUD-SERVICE
- **       |     |
- **       |     |________________________________________
- **       |          |                |                 |
- **       |          v                v                 v
- **       |      QEMUD-CLIENT#1    QEMUD-CLIENT#2    QEMUD-CLIENT#3
- **       |         ^                ^                  ^
- **       |         |                |                  |
- **       |         |                |                  |
- **       |         v                v                  v
- **       |      PIPE-CLIENT#1     PIPE-CLIENT#2     PIPE-CLIENT#3
- **       |         ^                ^                  ^
- **       |         |                |                  |
- **       |         |________________|                  |
- **       |         |                                   +
- **       |         |                                   |
- **       |      THREAD-STATE#1                   THREAD-STATE#2
- **       |         ^                                   ^
- **       |     ____|___________________________________|
- **       |    |
- **       |    |
- **     PIPE-SERVICE
- **
- **  Note that the QemudService and QemudClient objects are created by
- **  hw-qemud.c and not defined here.
- **
- **/
-
-typedef struct PipeService  PipeService;
-typedef struct PipeClient   PipeClient;
-
-static void pipeService_removeState(PipeService* pipeSvc, int tid);
-
-/**********************************************************************
- **********************************************************************
+/***********************************************************************
+ ***********************************************************************
  *****
- *****  REGISTRY OF SUPPORTED PIPE SERVICES
+ *****   P I P E   S E R V I C E   R E G I S T R A T I O N
  *****
  *****/
 
+#define MAX_PIPE_SERVICES  8
 typedef struct {
-    const char*                   pipeName;
-    void*                         pipeOpaque;
-    const QemudPipeHandlerFuncs*  pipeFuncs;
-} PipeType;
+    const char*        name;
+    void*              opaque;
+    GoldfishPipeFuncs  funcs;
+} PipeService;
 
-#define MAX_PIPE_TYPES  4
+typedef struct {
+    int          count;
+    PipeService  services[MAX_PIPE_SERVICES];
+} PipeServices;
 
-static PipeType  sPipeTypes[MAX_PIPE_TYPES];
-static int       sPipeTypeCount;
+static PipeServices  _pipeServices[1];
 
 void
-goldfish_pipe_add_type( const char*                   pipeName,
-                        void*                         pipeOpaque,
-                        const QemudPipeHandlerFuncs*  pipeFuncs )
+goldfish_pipe_add_type(const char*               pipeName,
+                       void*                     pipeOpaque,
+                       const GoldfishPipeFuncs*  pipeFuncs )
 {
-    int count = sPipeTypeCount;
+    PipeServices* list = _pipeServices;
+    int           count = list->count;
 
-    if (count >= MAX_PIPE_TYPES) {
-        APANIC("%s: Too many qemud pipe types!", __FUNCTION__);
+    if (count >= MAX_PIPE_SERVICES) {
+        APANIC("Too many goldfish pipe services (%d)", count);
     }
 
-    sPipeTypes[count].pipeName   = pipeName;
-    sPipeTypes[count].pipeOpaque = pipeOpaque;
-    sPipeTypes[count].pipeFuncs  = pipeFuncs;
-    sPipeTypeCount = ++count;
+    list->services[count].name   = pipeName;
+    list->services[count].opaque = pipeOpaque;
+    list->services[count].funcs  = pipeFuncs[0];
+
+    list->count++;
 }
 
-static const PipeType*
-goldfish_pipe_find_type( const char*  pipeName )
+static const PipeService*
+goldfish_pipe_find_type(const char*  pipeName)
 {
-    const PipeType* ptype = sPipeTypes;
-    const PipeType* limit = ptype + sPipeTypeCount;
+    PipeServices* list = _pipeServices;
+    int           count = list->count;
+    int           nn;
 
-    for ( ; ptype < limit; ptype++ ) {
-        if (!strcmp(pipeName, ptype->pipeName)) {
-            return ptype;
+    for (nn = 0; nn < count; nn++) {
+        if (!strcmp(list->services[nn].name, pipeName)) {
+            return &list->services[nn];
         }
     }
     return NULL;
 }
 
 
-/**********************************************************************
- **********************************************************************
+/***********************************************************************
+ ***********************************************************************
  *****
- *****  THREAD-SPECIFIC STATE
+ *****    P I P E   C O N N E C T I O N S
  *****
  *****/
 
-static void
-pipeClient_closeFromThread( PipeClient*  pcl );
+typedef struct PipeDevice  PipeDevice;
 
-static uint32_t
-pipeClient_doCommand( PipeClient* pcl,
-                      uint32_t   command,
-                      uint32_t   address,
-                      uint32_t*  pSize );
+typedef struct Pipe {
+    struct Pipe*              next;
+    struct Pipe*              next_waked;
+    PipeDevice*               device;
+    uint32_t                  channel;
+    void*                     opaque;
+    const GoldfishPipeFuncs*  funcs;
+    unsigned char             wanted;
+    char                      closed;
+} Pipe;
 
+/* Forward */
+static void*  pipeConnector_new(Pipe*  pipe);
 
-/* For each guest thread, we will store the following state:
- *
- * - The current state of the 'address', 'size', 'localId' and 'status'
- *   I/O slots provided through the magic page by hw/goldfish_trace.c
- *
- * - A list of PipeClient objects, corresponding to all the pipes in
- *   this thread, identified by localId.
- */
-typedef struct {
-    uint32_t   address;
-    uint32_t   size;
-    uint32_t   localId;
-    uint32_t   status;
-    AIntMap*   pipes;
-} ThreadState;
-
-static void
-threadState_free( ThreadState*  ts )
+Pipe*
+pipe_new(uint32_t channel, PipeDevice* dev)
 {
-    /* Get rid of the localId -> PipeClient map */
-    AINTMAP_FOREACH_VALUE(ts->pipes, pcl, pipeClient_closeFromThread(pcl));
-    aintMap_free(ts->pipes);
-
-    AFREE(ts);
+    Pipe*  pipe;
+    ANEW0(pipe);
+    pipe->channel = channel;
+    pipe->device  = dev;
+    pipe->opaque  = pipeConnector_new(pipe);
+    return pipe;
 }
 
-static ThreadState*
-threadState_new( void )
+static Pipe**
+pipe_list_findp_channel( Pipe** list, uint32_t channel )
 {
-    ThreadState*  ts;
-    ANEW0(ts);
-    ts->pipes = aintMap_new();
-    return ts;
-}
-
-static int
-threadState_addPipe( ThreadState* ts, int  localId, PipeClient* pcl )
-{
-    /* We shouldn't already have a pipe for this localId */
-    if (aintMap_get(ts->pipes, localId) != NULL) {
-        errno = EBADF;
-        return -1;
-    }
-    aintMap_set(ts->pipes, localId, pcl);
-    return 0;
-}
-
-static void
-threadState_delPipe( ThreadState* ts, int localId )
-{
-    aintMap_del(ts->pipes, localId);
-}
-
-static void
-threadState_write( ThreadState*  ts, int  offset, uint32_t value )
-{
-    PipeClient*  pcl;
-
-    switch (offset) {
-        case SLOT_COMMAND:
-            pcl = aintMap_get(ts->pipes, (int)ts->localId);
-            if (pcl == NULL) {
-                D("%s: Invalid localId (%d)",
-                  __FUNCTION__, ts->localId);
-                ts->status = QEMUD_PIPE_ERROR_INVAL;
-            } else {
-                ts->status = pipeClient_doCommand(pcl,
-                                                  value,
-                                                  ts->address,
-                                                  &ts->size);
-            }
+    Pipe** pnode = list;
+    for (;;) {
+        Pipe* node = *pnode;
+        if (node == NULL || node->channel == channel) {
             break;
-        case SLOT_ADDRESS:
-            ts->address = value;
-            break;
-        case SLOT_SIZE:
-            ts->size = value;
-            break;
-        case SLOT_CHANNEL:
-            ts->localId = value;
-            break;
-        default:
-            /* XXX: PRINT ERROR? */
-            ;
-    }
-}
-
-static uint32_t
-threadState_read( ThreadState*  ts, int offset )
-{
-    switch (offset) {
-        case SLOT_STATUS:       return ts->status;
-        case SLOT_ADDRESS:      return ts->address;
-        case SLOT_SIZE:         return ts->size;
-        case SLOT_CHANNEL:      return ts->localId;
-        default:                return 0;
-    }
-}
-
-/**********************************************************************
- **********************************************************************
- *****
- *****  PIPE CLIENT STATE
- *****
- *****/
-
-/* Each client object points to a PipeState after it has received the
- * initial request from the guest, which shall look like:
- * <tid>:<localId>:<name>
- */
-struct PipeClient {
-    char*         pipeName;
-    QemudClient*  client;
-    PipeService*  pipeSvc;
-
-    int                           tid;
-    uint32_t                      localId;
-    void*                         handler;
-    const QemudPipeHandlerFuncs*  handlerFuncs;
-};
-
-static int  pipeService_addPipe(PipeService* pipeSvc, int tid, int localId, PipeClient* pcl);
-static void pipeService_removePipe(PipeService* pipeSvc, int tid, int localId);
-
-static void
-pipeClient_closeFromThread( PipeClient*  pcl )
-{
-    qemud_client_close(pcl->client);
-}
-
-/* This function should only be invoked through qemud_client_close().
- * Never call it explicitely. */
-static void
-pipeClient_close( void* opaque )
-{
-    PipeClient*  pcl = opaque;
-
-    if (pcl->handler && pcl->handlerFuncs && pcl->handlerFuncs->close) {
-        pcl->handlerFuncs->close(pcl->handler);
-    }
-
-    D("qemud:pipe: closing client (%d,%x)", pcl->tid, pcl->localId);
-    qemud_client_close(pcl->client);
-    pcl->client = NULL;
-
-    /* The guest is closing the connection, so remove it from our state */
-    pipeService_removePipe(pcl->pipeSvc, pcl->tid, pcl->localId);
-
-    AFREE(pcl->pipeName);
-}
-
-static void
-pipeClient_recv( void* opaque, uint8_t* msg, int msgLen, QemudClient* client )
-{
-    PipeClient*      pcl = opaque;
-    const PipeType*  ptype;
-
-    const char* p;
-    int         failure = 1;
-    char        answer[64];
-
-    if (pcl->pipeName != NULL) {
-        /* Should never happen, the only time we'll receive something
-         * is when the connection is created! Simply ignore any incoming
-         * message.
-         */
-        return;
-    }
-
-    /* The message format is <tid>:<localIdHex>:<name> */
-    if (sscanf((char*)msg, "%d:%x:", &pcl->tid, &pcl->localId) != 2) {
-        goto BAD_FORMAT;
-    }
-    p = strchr((const char*)msg, ':');
-    if (p != NULL) {
-        p = strchr(p+1, ':');
-        if (p != NULL)
-            p += 1;
-    }
-    if (p == NULL || *p == '\0') {
-        goto BAD_FORMAT;
-    }
-
-    pcl->pipeName = ASTRDUP(p);
-
-    ptype = goldfish_pipe_find_type(pcl->pipeName);
-    if (ptype == NULL) {
-        goto UNKNOWN_PIPE_TYPE;
-    }
-
-    pcl->handlerFuncs = ptype->pipeFuncs;
-    pcl->handler      = ptype->pipeFuncs->init( client, ptype->pipeOpaque );
-    if (pcl->handler == NULL) {
-        goto BAD_INIT;
-    }
-
-    if (pipeService_addPipe(pcl->pipeSvc, pcl->tid, pcl->localId, pcl) < 0) {
-        goto DUPLICATE_REQUEST;
-    }
-
-    D("qemud:pipe: Added new client: %s", msg);
-    failure = 0;
-    snprintf(answer, sizeof answer, "OK");
-    goto SEND_ANSWER;
-
-BAD_INIT:
-    /* Initialization failed for some reason! */
-    E("qemud:pipe: Could not initialize pipe: '%s'", pcl->pipeName);
-    snprintf(answer, sizeof answer, "KO:%d:Could not initialize pipe",
-                QEMUD_PIPE_ERROR_INVAL);
-    goto SEND_ANSWER;
-
-UNKNOWN_PIPE_TYPE:
-    E("qemud:pipe: Unknown pipe type: '%s'", p);
-    snprintf(answer, sizeof answer, "KO:%d:Unknown pipe type name",
-             QEMUD_PIPE_ERROR_INVAL);
-    goto SEND_ANSWER;
-
-BAD_FORMAT:
-    E("qemud:pipe: Invalid connection request: '%s'", msg);
-    snprintf(answer, sizeof answer, "KO:%d:Invalid connection request",
-             QEMUD_PIPE_ERROR_INVAL);
-    goto SEND_ANSWER;
-
-DUPLICATE_REQUEST:
-    E("qemud:pipe: Duplicate connection request: '%s'", msg);
-    snprintf(answer, sizeof answer, "KO:%d:Duplicate connection request",
-             QEMUD_PIPE_ERROR_INVAL);
-    goto SEND_ANSWER;
-
-SEND_ANSWER:
-    qemud_client_send(client, (uint8_t*)answer, strlen(answer));
-    if (failure) {
-        qemud_client_close(client);
-    } else {
-        /* Disable framing for the rest of signalling */
-        qemud_client_set_framing(client, 0);
-    }
-}
-
-/* Count the number of GoldfishPipeBuffers we will need to transfer
- * memory from/to [address...address+size)
- */
-static int
-_countPipeBuffers( uint32_t  address, uint32_t  size )
-{
-    CPUState*  env   = cpu_single_env;
-    int        count = 0;
-
-    while (size > 0) {
-        uint32_t   vstart = address & TARGET_PAGE_MASK;
-        uint32_t   vend   = vstart + TARGET_PAGE_SIZE;
-        uint32_t   next   = address + size;
-
-        DD("%s: trying to map (0x%x - 0x%0x, %d bytes) -> page=0x%x - 0x%x",
-          __FUNCTION__, address, address+size, size, vstart, vend);
-
-        if (next > vend) {
-            next = vend;
         }
-
-        /* Check that the address is valid */
-        if (cpu_get_phys_page_debug(env, vstart) == -1) {
-            DD("%s: bad guest address!", __FUNCTION__);
-            return -1;
-        }
-
-        count++;
-
-        size   -= (next - address);
-        address = next;
+        pnode = &node->next;
     }
-    return count;
+    return pnode;
 }
 
-/* Fill the pipe buffers to prepare memory transfer from/to
- * [address...address+size). This assumes 'buffers' points to an array
- * which size corresponds to _countPipeBuffers(address, size)
- */
-static void
-_fillPipeBuffers( uint32_t  address, uint32_t  size, GoldfishPipeBuffer*  buffers )
-{
-    CPUState*  env = cpu_single_env;
-
-    while (size > 0) {
-        uint32_t   vstart = address & TARGET_PAGE_MASK;
-        uint32_t   vend   = vstart + TARGET_PAGE_SIZE;
-        uint32_t   next   = address + size;
-
-        if (next > vend) {
-            next = vend;
-        }
-
-        /* Translate virtual address into physical one, into emulator
-         * memory. */
-        target_phys_addr_t  phys = cpu_get_phys_page_debug(env, vstart);
-
-        buffers[0].data = qemu_get_ram_ptr(phys) + (address - vstart);
-        buffers[0].size = next - address;
-        buffers++;
-
-        size   -= (next - address);
-        address = next;
-    }
-}
-
-static uint32_t
-pipeClient_doCommand( PipeClient* pcl,
-                      uint32_t   command,
-                      uint32_t   address,
-                      uint32_t*  pSize )
-{
-    uint32_t  size = *pSize;
-
-    D("%s: TID=%4d CHANNEL=%08x COMMAND=%d ADDRESS=%08x SIZE=%d\n",
-           __FUNCTION__, pcl->tid, pcl->localId, command, address, size);
-
-    /* XXX: TODO */
-    switch (command) {
-        case QEMUD_PIPE_CMD_CLOSE:
-            /* The client is asking us to close the connection, so
-             * just do that. */
-            qemud_client_close(pcl->client);
-            return 0;
-
-        case QEMUD_PIPE_CMD_SEND: {
-            /* First, try to allocate a buffer from the handler */
-            void*                opaque = pcl->handler;
-            GoldfishPipeBuffer*  buffers;
-            int                  numBuffers = 0;
-
-            /* Count the number of buffers we need, allocate them,
-             * then fill them. */
-            numBuffers = _countPipeBuffers(address, size);
-            if (numBuffers < 0) {
-                D("%s: Invalid guest address range 0x%x - 0x%x (%d bytes)",
-                  __FUNCTION__, address, address+size, size);
-                  return QEMUD_PIPE_ERROR_NOMEM;
-            }
-            buffers = alloca( sizeof(*buffers) * numBuffers );
-            _fillPipeBuffers(address, size, buffers);
-
-            D("%s: Sending %d bytes using %d buffers", __FUNCTION__,
-              size, numBuffers);
-
-            /* Send the data */
-            if (pcl->handlerFuncs->sendBuffers(opaque, buffers, numBuffers) < 0) {
-                /* When .sendBuffers() returns -1, it usually means
-                * that the handler isn't ready to accept a new message. There
-                * is however once exception: when the message is too large
-                * and it wasn't possible to allocate the buffer.
-                *
-                * Differentiate between these two cases by looking at errno.
-                */
-                if (errno == ENOMEM)
-                    return QEMUD_PIPE_ERROR_NOMEM;
-                else
-                    return QEMUD_PIPE_ERROR_AGAIN;
-            }
-            return 0;
-        }
-
-        case QEMUD_PIPE_CMD_RECV: {
-            void*     opaque = pcl->handler;
-            GoldfishPipeBuffer* buffers;
-            int                 numBuffers, ret;
-
-            /* Count the number of buffers we have */
-            numBuffers = _countPipeBuffers(address, size);
-            if (numBuffers < 0) {
-                D("%s: Invalid guest address range 0x%x - 0x%x (%d bytes)",
-                  __FUNCTION__, address, address+size, size);
-                  return QEMUD_PIPE_ERROR_NOMEM;
-            }
-            buffers    = alloca(sizeof(*buffers)*numBuffers);
-            _fillPipeBuffers(address, size, buffers);
-
-            /* Receive data */
-            ret = pcl->handlerFuncs->recvBuffers(opaque, buffers, numBuffers);
-            if (ret < 0) {
-                if (errno == ENOMEM) {
-                    // XXXX: TODO *pSize = msgSize;
-                    return QEMUD_PIPE_ERROR_NOMEM;
-                } else {
-                    return QEMUD_PIPE_ERROR_AGAIN;
-                }
-            }
-            *pSize = ret;
-            return 0;
-        }
-
-        case QEMUD_PIPE_CMD_WAKE_ON_SEND: {
-            pcl->handlerFuncs->wakeOn(pcl->handler, QEMUD_PIPE_WAKE_ON_SEND);
-            return 0;
-        }
-
-        case QEMUD_PIPE_CMD_WAKE_ON_RECV: {
-            pcl->handlerFuncs->wakeOn(pcl->handler, QEMUD_PIPE_WAKE_ON_RECV);
-            return 0;
-        }
-
-        default:
-            return QEMUD_PIPE_ERROR_CONNRESET;
-    }
-}
-
-
-
-QemudClient*
-pipeClient_connect( void* opaque, QemudService*  svc, int channel )
-{
-    PipeClient*   pcl;
-
-    ANEW0(pcl);
-    pcl->pipeSvc = opaque;
-    pcl->client  = qemud_client_new( svc, channel, pcl,
-                                     pipeClient_recv,
-                                     pipeClient_close,
-                                     NULL,   /* TODO: NO SNAPSHOT SAVE */
-                                     NULL ); /* TODO: NO SNAPHOT LOAD */
-
-    /* Only for the initial connection message */
-    qemud_client_set_framing(pcl->client, 1);
-    return pcl->client;
-}
-
-/**********************************************************************
- **********************************************************************
- *****
- *****  GLOBAL PIPE STATE
- *****
- *****/
-
-struct PipeService {
-    AIntMap*  threadMap;  /* maps tid to ThreadState */
-};
-
 #if 0
-static void
-pipeService_done(PipeService*  pipeSvc)
+static Pipe**
+pipe_list_findp_opaque( Pipe** list, void* opaque )
 {
-    /* Get rid of the tid -> ThreadState map */
-    AINTMAP_FOREACH_VALUE(pipeSvc->threadMap, ts, threadState_free(ts));
-    aintMap_free(pipeSvc->threadMap);
-    pipeSvc->threadMap = NULL;
+    Pipe** pnode = list;
+    for (;;) {
+        Pipe* node = *pnode;
+        if (node == NULL || node->opaque == opaque) {
+            break;
+        }
+        pnode = &node->next;
+    }
+    return pnode;
 }
 #endif
 
-static void
-pipeService_init(PipeService*  pipeSvc)
+static Pipe**
+pipe_list_findp_waked( Pipe** list, Pipe* pipe )
 {
-    pipeSvc->threadMap = aintMap_new();
-
-    qemud_service_register( "fast-pipe", 0, pipeSvc,
-                            pipeClient_connect,
-                            NULL,  /* TODO: NO SNAPSHOT SAVE SUPPORT */
-                            NULL   /* TODO: NO SNAPSHOT LOAD SUPPORT */ );
-}
-
-static ThreadState*
-pipeService_getState(PipeService* pipeSvc, int tid)
-{
-    if (pipeSvc->threadMap == NULL)
-        pipeSvc->threadMap = aintMap_new();
-
-    return (ThreadState*) aintMap_get(pipeSvc->threadMap, tid);
-}
-
-static void
-pipeService_removeState(PipeService* pipeSvc, int tid)
-{
-    ThreadState* ts = pipeService_getState(pipeSvc, tid);
-
-    if (ts == NULL)
-        return;
-
-    aintMap_del(pipeSvc->threadMap,tid);
-    threadState_free(ts);
-}
-
-static int
-pipeService_addPipe(PipeService* pipeSvc, int  tid, int  localId, PipeClient*  pcl)
-{
-    ThreadState*  ts = pipeService_getState(pipeSvc, tid);
-
-    if (ts == NULL) {
-        ts = threadState_new();
-        aintMap_set(pipeSvc->threadMap, tid, ts);
+    Pipe** pnode = list;
+    for (;;) {
+        Pipe* node = *pnode;
+        if (node == NULL || node == pipe) {
+            break;
+        }
+        pnode = &node->next_waked;
     }
-
-    return threadState_addPipe(ts, localId, pcl);
+    return pnode;
 }
 
+
 static void
-pipeService_removePipe(PipeService* pipeSvc, int tid, int localId)
+pipe_list_remove_waked( Pipe** list, Pipe*  pipe )
 {
-    ThreadState* ts = pipeService_getState(pipeSvc, tid);
+    Pipe** lookup = pipe_list_findp_waked(list, pipe);
+    Pipe*  node   = *lookup;
 
-    if (ts == NULL)
-        return;
-
-    threadState_delPipe(ts, localId);
+    if (node != NULL) {
+        (*lookup) = node->next_waked;
+        node->next_waked = NULL;
+    }
 }
 
-/**********************************************************************
- **********************************************************************
+/***********************************************************************
+ ***********************************************************************
  *****
- *****  HARDWARE API - AS SEEN FROM hw/goldfish_trace.c
+ *****    P I P E   C O N N E C T O R S
  *****
  *****/
 
-static PipeService  _globalState[1];
+/* These are used to handle the initial connection attempt, where the
+ * client is going to write the name of the pipe service it wants to
+ * connect to, followed by a terminating zero.
+ */
+typedef struct {
+    Pipe*  pipe;
+    char   buffer[128];
+    int    buffpos;
+} PipeConnector;
 
-void  init_qemud_pipes(void)
+static const GoldfishPipeFuncs  pipeConnector_funcs;  // forward
+
+void*
+pipeConnector_new(Pipe*  pipe)
 {
-    pipeService_init(_globalState);
+    PipeConnector*  pcon;
+
+    ANEW0(pcon);
+    pcon->pipe  = pipe;
+    pipe->funcs = &pipeConnector_funcs;
+    return pcon;
 }
 
-void
-goldfish_pipe_thread_death(int  tid)
+static void
+pipeConnector_close( void* opaque )
 {
-    PipeService*  pipeSvc = _globalState;
-    pipeService_removeState(pipeSvc, tid);
+    PipeConnector*  pcon = opaque;
+    AFREE(pcon);
 }
 
-void
-goldfish_pipe_write(int tid, int offset, uint32_t value)
+static int
+pipeConnector_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
 {
-    PipeService*  pipeSvc = _globalState;
-    DD("%s: tid=%d offset=%d value=%d (0x%x)", __FUNCTION__, tid,
-      offset, value, value);
+    PipeConnector* pcon = opaque;
+    const GoldfishPipeBuffer*  buffers_limit = buffers + numBuffers;
+    int ret = 0;
 
-    ThreadState*  ts      = pipeService_getState(pipeSvc, tid);
+    DD("%s: channel=0x%x numBuffers=%d", __FUNCTION__,
+       pcon->pipe->channel,
+       numBuffers);
 
-    if (ts == NULL) {
-        D("%s: no thread state for tid=%d", __FUNCTION__, tid);
+    while (buffers < buffers_limit) {
+        int  avail;
+
+        DD("%s: buffer data (%3d bytes): '%.*s'", __FUNCTION__,
+           buffers[0].size, buffers[0].size, buffers[0].data);
+
+        if (buffers[0].size == 0) {
+            buffers++;
+            continue;
+        }
+
+        avail = sizeof(pcon->buffer) - pcon->buffpos;
+        if (avail > buffers[0].size)
+            avail = buffers[0].size;
+
+        if (avail > 0) {
+            memcpy(pcon->buffer + pcon->buffpos, buffers[0].data, avail);
+            pcon->buffpos += avail;
+            ret += avail;
+        }
+        buffers++;
+    }
+
+    /* Now check that our buffer contains a zero-terminated string */
+    if (memchr(pcon->buffer, '\0', pcon->buffpos) != NULL) {
+        /* Acceptable formats for the connection string are:
+         *
+         *   pipe:<name>
+         *   pipe:<name>:<arguments>
+         */
+        char* pipeName;
+        char* pipeArgs;
+
+        D("%s: connector: '%s'", __FUNCTION__, pcon->buffer);
+
+        if (memcmp(pcon->buffer, "pipe:", 5) != 0) {
+            /* Nope, we don't handle these for now. */
+            D("%s: Unknown pipe connection: '%s'", __FUNCTION__, pcon->buffer);
+            return PIPE_ERROR_INVAL;
+        }
+
+        pipeName = pcon->buffer + 5;
+        pipeArgs = strchr(pipeName, ':');
+
+        if (pipeArgs != NULL) {
+            *pipeArgs++ = '\0';
+        }
+
+        Pipe* pipe = pcon->pipe;
+        const PipeService* svc = goldfish_pipe_find_type(pipeName);
+        if (svc == NULL) {
+            D("%s: Unknown server!", __FUNCTION__);
+            return PIPE_ERROR_INVAL;
+        }
+
+        void*  peer = svc->funcs.init(pipe, svc->opaque, pipeArgs);
+        if (peer == NULL) {
+            D("%s: Initialization failed!", __FUNCTION__);
+            return PIPE_ERROR_INVAL;
+        }
+
+        /* Do the evil switch now */
+        pipe->opaque = peer;
+        pipe->funcs  = &svc->funcs;
+        AFREE(pcon);
+    }
+
+    return ret;
+}
+
+static int
+pipeConnector_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
+{
+    return PIPE_ERROR_IO;
+}
+
+static unsigned
+pipeConnector_poll( void* opaque )
+{
+    return PIPE_WAKE_WRITE;
+}
+
+static void
+pipeConnector_wakeOn( void* opaque, int flags )
+{
+    /* nothing, really should never happen */
+}
+
+static const GoldfishPipeFuncs  pipeConnector_funcs = {
+    NULL,  /* init */
+    pipeConnector_close,        /* should rarely happen */
+    pipeConnector_sendBuffers,  /* the interesting stuff */
+    pipeConnector_recvBuffers,  /* should not happen */
+    pipeConnector_poll,         /* should not happen */
+    pipeConnector_wakeOn,       /* should not happen */
+};
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ *****    Z E R O   P I P E S
+ *****
+ *****/
+
+/* A simple pipe service that mimics /dev/zero, you can write anything to
+ * it, and you can always read any number of zeros from it. Useful for debugging
+ * the kernel driver.
+ */
+#if DEBUG_ZERO_PIPE
+
+typedef struct {
+    void* hwpipe;
+} ZeroPipe;
+
+static void*
+zeroPipe_init( void* hwpipe, void* svcOpaque, const char* args )
+{
+    ZeroPipe*  zpipe;
+
+    D("%s: hwpipe=%p", __FUNCTION__, hwpipe);
+    ANEW0(zpipe);
+    zpipe->hwpipe = hwpipe;
+    return zpipe;
+}
+
+static void
+zeroPipe_close( void* opaque )
+{
+    ZeroPipe*  zpipe = opaque;
+
+    D("%s: hwpipe=%p", __FUNCTION__, zpipe->hwpipe);
+    AFREE(zpipe);
+}
+
+static int
+zeroPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
+{
+    int  ret = 0;
+    while (numBuffers > 0) {
+        ret += buffers[0].size;
+        buffers++;
+        numBuffers--;
+    }
+    return ret;
+}
+
+static int
+zeroPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
+{
+    int  ret = 0;
+    while (numBuffers > 0) {
+        ret += buffers[0].size;
+        memset(buffers[0].data, 0, buffers[0].size);
+        buffers++;
+        numBuffers--;
+    }
+    return ret;
+}
+
+static unsigned
+zeroPipe_poll( void* opaque )
+{
+    return PIPE_WAKE_READ | PIPE_WAKE_WRITE;
+}
+
+static void
+zeroPipe_wakeOn( void* opaque, int flags )
+{
+    /* nothing to do here */
+}
+
+static const GoldfishPipeFuncs  zeroPipe_funcs = {
+    zeroPipe_init,
+    zeroPipe_close,
+    zeroPipe_sendBuffers,
+    zeroPipe_recvBuffers,
+    zeroPipe_poll,
+    zeroPipe_wakeOn,
+};
+
+#endif /* DEBUG_ZERO */
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ *****    P I N G   P O N G   P I P E S
+ *****
+ *****/
+
+/* Similar debug service that sends back anything it receives */
+/* All data is kept in a circular dynamic buffer */
+
+#if DEBUG_PINGPONG_PIPE
+
+/* Initial buffer size */
+#define PINGPONG_SIZE  1024
+
+typedef struct {
+    void*     hwpipe;
+    uint8_t*  buffer;
+    size_t    size;
+    size_t    pos;
+    size_t    count;
+    unsigned  flags;
+} PingPongPipe;
+
+static void
+pingPongPipe_init0( PingPongPipe* pipe, void* hwpipe, void* svcOpaque )
+{
+    pipe->hwpipe = hwpipe;
+    pipe->size = PINGPONG_SIZE;
+    pipe->buffer = malloc(pipe->size);
+    pipe->pos = 0;
+    pipe->count = 0;
+}
+
+static void*
+pingPongPipe_init( void* hwpipe, void* svcOpaque, const char* args )
+{
+    PingPongPipe*  ppipe;
+
+    D("%s: hwpipe=%p", __FUNCTION__, hwpipe);
+    ANEW0(ppipe);
+    pingPongPipe_init0(ppipe, hwpipe, svcOpaque);
+    return ppipe;
+}
+
+static void
+pingPongPipe_close( void* opaque )
+{
+    PingPongPipe*  ppipe = opaque;
+
+    D("%s: hwpipe=%p (pos=%d count=%d size=%d)", __FUNCTION__,
+      ppipe->hwpipe, ppipe->pos, ppipe->count, ppipe->size);
+    free(ppipe->buffer);
+    AFREE(ppipe);
+}
+
+static int
+pingPongPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
+{
+    PingPongPipe*  pipe = opaque;
+    int  ret = 0;
+    int  count;
+    const GoldfishPipeBuffer* buff = buffers;
+    const GoldfishPipeBuffer* buffEnd = buff + numBuffers;
+
+    count = 0;
+    for ( ; buff < buffEnd; buff++ )
+        count += buff->size;
+
+    /* Do we need to grow the pingpong buffer? */
+    while (count > pipe->size - pipe->count) {
+        size_t    newsize = pipe->size*2;
+        uint8_t*  newbuff = realloc(pipe->buffer, newsize);
+        int       wpos    = pipe->pos + pipe->count;
+        if (newbuff == NULL) {
+            break;
+        }
+        if (wpos > pipe->size) {
+            wpos -= pipe->size;
+            memcpy(newbuff + pipe->size, newbuff, wpos);
+        }
+        pipe->buffer = newbuff;
+        pipe->size   = newsize;
+        D("pingpong buffer is now %d bytes", newsize);
+    }
+
+    for ( buff = buffers; buff < buffEnd; buff++ ) {
+        int avail = pipe->size - pipe->count;
+        if (avail <= 0) {
+            if (ret == 0)
+                ret = PIPE_ERROR_AGAIN;
+            break;
+        }
+        if (avail > buff->size) {
+            avail = buff->size;
+        }
+
+        int wpos = pipe->pos + pipe->count;
+        if (wpos >= pipe->size) {
+            wpos -= pipe->size;
+        }
+        if (wpos + avail <= pipe->size) {
+            memcpy(pipe->buffer + wpos, buff->data, avail);
+        } else {
+            int  avail2 = pipe->size - wpos;
+            memcpy(pipe->buffer + wpos, buff->data, avail2);
+            memcpy(pipe->buffer, buff->data + avail2, avail - avail2);
+        }
+        pipe->count += avail;
+        ret += avail;
+    }
+
+    /* Wake up any waiting readers if we wrote something */
+    if (pipe->count > 0 && (pipe->flags & PIPE_WAKE_READ)) {
+        goldfish_pipe_wake(pipe->hwpipe, PIPE_WAKE_READ);
+    }
+
+    return ret;
+}
+
+static int
+pingPongPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
+{
+    PingPongPipe*  pipe = opaque;
+    int  ret = 0;
+
+    while (numBuffers > 0) {
+        int avail = pipe->count;
+        if (avail <= 0) {
+            if (ret == 0)
+                ret = PIPE_ERROR_AGAIN;
+            break;
+        }
+        if (avail > buffers[0].size) {
+            avail = buffers[0].size;
+        }
+
+        int rpos = pipe->pos;
+
+        if (rpos + avail <= pipe->size) {
+            memcpy(buffers[0].data, pipe->buffer + rpos, avail);
+        } else {
+            int  avail2 = pipe->size - rpos;
+            memcpy(buffers[0].data, pipe->buffer + rpos, avail2);
+            memcpy(buffers[0].data + avail2, pipe->buffer, avail - avail2);
+        }
+        pipe->count -= avail;
+        pipe->pos   += avail;
+        if (pipe->pos >= pipe->size) {
+            pipe->pos -= pipe->size;
+        }
+        ret += avail;
+        numBuffers--;
+        buffers++;
+    }
+
+    /* Wake up any waiting readers if we wrote something */
+    if (pipe->count < PINGPONG_SIZE && (pipe->flags & PIPE_WAKE_WRITE)) {
+        goldfish_pipe_wake(pipe->hwpipe, PIPE_WAKE_WRITE);
+    }
+
+    return ret;
+}
+
+static unsigned
+pingPongPipe_poll( void* opaque )
+{
+    PingPongPipe*  pipe = opaque;
+    unsigned       ret = 0;
+
+    if (pipe->count < pipe->size)
+        ret |= PIPE_WAKE_WRITE;
+
+    if (pipe->count > 0)
+        ret |= PIPE_WAKE_READ;
+
+    return ret;
+}
+
+static void
+pingPongPipe_wakeOn( void* opaque, int flags )
+{
+    PingPongPipe* pipe = opaque;
+    pipe->flags |= (unsigned)flags;
+}
+
+static const GoldfishPipeFuncs  pingPongPipe_funcs = {
+    pingPongPipe_init,
+    pingPongPipe_close,
+    pingPongPipe_sendBuffers,
+    pingPongPipe_recvBuffers,
+    pingPongPipe_poll,
+    pingPongPipe_wakeOn,
+};
+
+#endif /* DEBUG_PINGPONG_PIPE */
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ *****    T H R O T T L E   P I P E S
+ *****
+ *****/
+
+/* Similar to PingPongPipe, but will throttle the bandwidth to test
+ * blocking I/O.
+ */
+
+#ifdef DEBUG_THROTTLE_PIPE
+
+typedef struct {
+    PingPongPipe  pingpong;
+    double        sendRate;
+    int64_t       sendExpiration;
+    double        recvRate;
+    int64_t       recvExpiration;
+    QEMUTimer*    timer;
+} ThrottlePipe;
+
+/* forward declaration */
+static void throttlePipe_timerFunc( void* opaque );
+
+static void*
+throttlePipe_init( void* hwpipe, void* svcOpaque, const char* args )
+{
+    ThrottlePipe* pipe;
+
+    ANEW0(pipe);
+    pingPongPipe_init0(&pipe->pingpong, hwpipe, svcOpaque);
+    pipe->timer = qemu_new_timer(vm_clock, throttlePipe_timerFunc, pipe);
+    /* For now, limit to 500 KB/s in both directions */
+    pipe->sendRate = 1e9 / (500*1024*8);
+    pipe->recvRate = pipe->sendRate;
+    return pipe;
+}
+
+static void
+throttlePipe_close( void* opaque )
+{
+    ThrottlePipe* pipe = opaque;
+
+    qemu_del_timer(pipe->timer);
+    qemu_free_timer(pipe->timer);
+    pingPongPipe_close(&pipe->pingpong);
+}
+
+static void
+throttlePipe_rearm( ThrottlePipe* pipe )
+{
+    int64_t  minExpiration = 0;
+
+    DD("%s: sendExpiration=%lld recvExpiration=%lld\n", __FUNCTION__, pipe->sendExpiration, pipe->recvExpiration);
+
+    if (pipe->sendExpiration) {
+        if (minExpiration == 0 || pipe->sendExpiration < minExpiration)
+            minExpiration = pipe->sendExpiration;
+    }
+
+    if (pipe->recvExpiration) {
+        if (minExpiration == 0 || pipe->recvExpiration < minExpiration)
+            minExpiration = pipe->recvExpiration;
+    }
+
+    if (minExpiration != 0) {
+        DD("%s: Arming for %lld\n", __FUNCTION__, minExpiration);
+        qemu_mod_timer(pipe->timer, minExpiration);
+    }
+}
+
+static void
+throttlePipe_timerFunc( void* opaque )
+{
+    ThrottlePipe* pipe = opaque;
+    int64_t  now = qemu_get_clock_ns(vm_clock);
+
+    DD("%s: TICK! now=%lld sendExpiration=%lld recvExpiration=%lld\n",
+       __FUNCTION__, now, pipe->sendExpiration, pipe->recvExpiration);
+
+    /* Timer has expired, signal wake up if needed */
+    int      flags = 0;
+
+    if (pipe->sendExpiration && now > pipe->sendExpiration) {
+        flags |= PIPE_WAKE_WRITE;
+        pipe->sendExpiration = 0;
+    }
+    if (pipe->recvExpiration && now > pipe->recvExpiration) {
+        flags |= PIPE_WAKE_READ;
+        pipe->recvExpiration = 0;
+    }
+    flags &= pipe->pingpong.flags;
+    if (flags != 0) {
+        DD("%s: WAKE %d\n", __FUNCTION__, flags);
+        goldfish_pipe_wake(pipe->pingpong.hwpipe, flags);
+    }
+
+    throttlePipe_rearm(pipe);
+}
+
+static int
+throttlePipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
+{
+    ThrottlePipe*  pipe = opaque;
+    int            ret;
+
+    if (pipe->sendExpiration > 0) {
+        return PIPE_ERROR_AGAIN;
+    }
+
+    ret = pingPongPipe_sendBuffers(&pipe->pingpong, buffers, numBuffers);
+    if (ret > 0) {
+        /* Compute next send expiration time */
+        pipe->sendExpiration = qemu_get_clock_ns(vm_clock) + ret*pipe->sendRate;
+        throttlePipe_rearm(pipe);
+    }
+    return ret;
+}
+
+static int
+throttlePipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
+{
+    ThrottlePipe* pipe = opaque;
+    int           ret;
+
+    if (pipe->recvExpiration > 0) {
+        return PIPE_ERROR_AGAIN;
+    }
+
+    ret = pingPongPipe_recvBuffers(&pipe->pingpong, buffers, numBuffers);
+    if (ret > 0) {
+        pipe->recvExpiration = qemu_get_clock_ns(vm_clock) + ret*pipe->recvRate;
+        throttlePipe_rearm(pipe);
+    }
+    return ret;
+}
+
+static unsigned
+throttlePipe_poll( void* opaque )
+{
+    ThrottlePipe*  pipe = opaque;
+    unsigned       ret  = pingPongPipe_poll(&pipe->pingpong);
+
+    if (pipe->sendExpiration > 0)
+        ret &= ~PIPE_WAKE_WRITE;
+
+    if (pipe->recvExpiration > 0)
+        ret &= ~PIPE_WAKE_READ;
+
+    return ret;
+}
+
+static void
+throttlePipe_wakeOn( void* opaque, int flags )
+{
+    ThrottlePipe* pipe = opaque;
+    pingPongPipe_wakeOn(&pipe->pingpong, flags);
+}
+
+static const GoldfishPipeFuncs  throttlePipe_funcs = {
+    throttlePipe_init,
+    throttlePipe_close,
+    throttlePipe_sendBuffers,
+    throttlePipe_recvBuffers,
+    throttlePipe_poll,
+    throttlePipe_wakeOn,
+};
+
+#endif /* DEBUG_THROTTLE_PIPE */
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ *****    G O L D F I S H   P I P E   D E V I C E
+ *****
+ *****/
+
+struct PipeDevice {
+    struct goldfish_device dev;
+
+    /* the list of all pipes */
+    Pipe*  pipes;
+
+    /* the list of signalled pipes */
+    Pipe*  signaled_pipes;
+
+    /* i/o registers */
+    uint32_t  address;
+    uint32_t  size;
+    uint32_t  status;
+    uint32_t  channel;
+    uint32_t  wakes;
+};
+
+
+static void
+pipeDevice_doCommand( PipeDevice* dev, uint32_t command )
+{
+    Pipe** lookup = pipe_list_findp_channel(&dev->pipes, dev->channel);
+    Pipe*  pipe   = *lookup;
+    CPUState* env = cpu_single_env;
+
+    /* Check that we're referring a known pipe channel */
+    if (command != PIPE_CMD_OPEN && pipe == NULL) {
+        dev->status = PIPE_ERROR_INVAL;
         return;
     }
-    threadState_write(ts, offset, value);
+
+    /* If the pipe is closed by the host, return an error */
+    if (pipe != NULL && pipe->closed && command != PIPE_CMD_CLOSE) {
+        dev->status = PIPE_ERROR_IO;
+        return;
+    }
+
+    switch (command) {
+    case PIPE_CMD_OPEN:
+        DD("%s: CMD_OPEN channel=0x%x", __FUNCTION__, dev->channel);
+        if (pipe != NULL) {
+            dev->status = PIPE_ERROR_INVAL;
+            break;
+        }
+        pipe = pipe_new(dev->channel, dev);
+        pipe->next = dev->pipes;
+        dev->pipes = pipe;
+        dev->status = 0;
+        break;
+
+    case PIPE_CMD_CLOSE:
+        DD("%s: CMD_CLOSE channel=0x%x", __FUNCTION__, dev->channel);
+        /* Remove from device's lists */
+        *lookup = pipe->next;
+        pipe->next = NULL;
+        pipe_list_remove_waked(&dev->signaled_pipes, pipe);
+        /* Call close callback */
+        if (pipe->funcs->close) {
+            pipe->funcs->close(pipe->opaque);
+        }
+        /* Free stuff */
+        AFREE(pipe);
+        break;
+
+    case PIPE_CMD_POLL:
+        dev->status = pipe->funcs->poll(pipe->opaque);
+        DD("%s: CMD_POLL > status=%d", __FUNCTION__, dev->status);
+        break;
+
+    case PIPE_CMD_READ_BUFFER: {
+        /* Translate virtual address into physical one, into emulator memory. */
+        GoldfishPipeBuffer  buffer;
+        uint32_t            address = dev->address;
+        uint32_t            page    = address & TARGET_PAGE_MASK;
+        target_phys_addr_t  phys = cpu_get_phys_page_debug(env, page);
+        buffer.data = qemu_get_ram_ptr(phys) + (address - page);
+        buffer.size = dev->size;
+        dev->status = pipe->funcs->recvBuffers(pipe->opaque, &buffer, 1);
+        DD("%s: CMD_READ_BUFFER channel=0x%x address=0x%08x size=%d > status=%d",
+           __FUNCTION__, dev->channel, dev->address, dev->size, dev->status);
+        break;
+    }
+
+    case PIPE_CMD_WRITE_BUFFER: {
+        /* Translate virtual address into physical one, into emulator memory. */
+        GoldfishPipeBuffer  buffer;
+        uint32_t            address = dev->address;
+        uint32_t            page    = address & TARGET_PAGE_MASK;
+        target_phys_addr_t  phys = cpu_get_phys_page_debug(env, page);
+        buffer.data = qemu_get_ram_ptr(phys) + (address - page);
+        buffer.size = dev->size;
+        dev->status = pipe->funcs->sendBuffers(pipe->opaque, &buffer, 1);
+        DD("%s: CMD_WRITE_BUFFER channel=0x%x address=0x%08x size=%d > status=%d",
+           __FUNCTION__, dev->channel, dev->address, dev->size, dev->status);
+        break;
+    }
+
+    case PIPE_CMD_WAKE_ON_READ:
+        DD("%s: CMD_WAKE_ON_READ channel=0x%x", __FUNCTION__, dev->channel);
+        if ((pipe->wanted & PIPE_WAKE_READ) == 0) {
+            pipe->wanted |= PIPE_WAKE_READ;
+            pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
+        }
+        dev->status = 0;
+        break;
+
+    case PIPE_CMD_WAKE_ON_WRITE:
+        DD("%s: CMD_WAKE_ON_WRITE channel=0x%x", __FUNCTION__, dev->channel);
+        if ((pipe->wanted & PIPE_WAKE_WRITE) == 0) {
+            pipe->wanted |= PIPE_WAKE_WRITE;
+            pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
+        }
+        dev->status = 0;
+        break;
+
+    default:
+        D("%s: command=%d (0x%x)\n", __FUNCTION__, command, command);
+    }
 }
 
-uint32_t
-goldfish_pipe_read(int tid, int offset)
+static void pipe_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
 {
-    PipeService*  pipeSvc = _globalState;
-    uint32_t      ret;
+    PipeDevice *s = (PipeDevice *)opaque;
 
-    DD("%s: tid=%d offset=%d", __FUNCTION__, tid, offset);
+    switch (offset) {
+    case PIPE_REG_COMMAND:
+        DR("%s: command=%d (0x%x)", __FUNCTION__, value, value);
+        pipeDevice_doCommand(s, value);
+        break;
 
-    ThreadState*  ts      = pipeService_getState(pipeSvc, tid);
+    case PIPE_REG_SIZE:
+        DR("%s: size=%d (0x%x)", __FUNCTION__, value, value);
+        s->size = value;
+        break;
 
-    if (ts == NULL) {
-        D("%s: no thread state for tid=%d", __FUNCTION__, tid);
-        return 0;
+    case PIPE_REG_ADDRESS:
+        DR("%s: address=%d (0x%x)", __FUNCTION__, value, value);
+        s->address = value;
+        break;
+
+    case PIPE_REG_CHANNEL:
+        DR("%s: channel=%d (0x%x)", __FUNCTION__, value, value);
+        s->channel = value;
+        break;
+
+    default:
+        D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset,
+            offset, value, value);
+        break;
     }
-    ret = threadState_read(ts, offset);
-    DD("%s:    result=%d (0x%d)", __FUNCTION__, ret, ret);
-    return ret;
+}
+
+/* I/O read */
+static uint32_t pipe_dev_read(void *opaque, target_phys_addr_t offset)
+{
+    PipeDevice *dev = (PipeDevice *)opaque;
+
+    switch (offset) {
+    case PIPE_REG_STATUS:
+        DR("%s: REG_STATUS status=%d (0x%x)", __FUNCTION__, dev->status, dev->status);
+        return dev->status;
+
+    case PIPE_REG_CHANNEL:
+        if (dev->signaled_pipes != NULL) {
+            Pipe* pipe = dev->signaled_pipes;
+            DR("%s: channel=0x%x wanted=%d", __FUNCTION__,
+               pipe->channel, pipe->wanted);
+            dev->wakes = pipe->wanted;
+            pipe->wanted = 0;
+            dev->signaled_pipes = pipe->next_waked;
+            pipe->next_waked = NULL;
+            if (dev->signaled_pipes == NULL) {
+                goldfish_device_set_irq(&dev->dev, 0, 0);
+                DD("%s: lowering IRQ", __FUNCTION__);
+            }
+            return pipe->channel;
+        }
+        DR("%s: no signaled channels", __FUNCTION__);
+        return 0;
+
+    case PIPE_REG_WAKES:
+        DR("%s: wakes %d", __FUNCTION__, dev->wakes);
+        return dev->wakes;
+
+    default:
+        D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset);
+    }
+    return 0;
+}
+
+static CPUReadMemoryFunc *pipe_dev_readfn[] = {
+   pipe_dev_read,
+   pipe_dev_read,
+   pipe_dev_read
+};
+
+static CPUWriteMemoryFunc *pipe_dev_writefn[] = {
+   pipe_dev_write,
+   pipe_dev_write,
+   pipe_dev_write
+};
+
+/* initialize the trace device */
+void pipe_dev_init()
+{
+    PipeDevice *s;
+
+    s = (PipeDevice *) qemu_mallocz(sizeof(*s));
+
+    s->dev.name = "qemu_pipe";
+    s->dev.id = -1;
+    s->dev.base = 0;       // will be allocated dynamically
+    s->dev.size = 0x2000;
+    s->dev.irq = 0;
+    s->dev.irq_count = 1;
+
+    goldfish_device_add(&s->dev, pipe_dev_readfn, pipe_dev_writefn, s);
+
+#if DEBUG_ZERO_PIPE
+    goldfish_pipe_add_type("zero", NULL, &zeroPipe_funcs);
+#endif
+#if DEBUG_PINGPONG_PIPE
+    goldfish_pipe_add_type("pingpong", NULL, &pingPongPipe_funcs);
+#endif
+#if DEBUG_THROTTLE_PIPE
+    goldfish_pipe_add_type("throttle", NULL, &throttlePipe_funcs);
+#endif
+}
+
+void
+goldfish_pipe_wake( void* hwpipe, unsigned flags )
+{
+    Pipe*  pipe = hwpipe;
+    Pipe** lookup;
+    PipeDevice*  dev = pipe->device;
+
+    DD("%s: channel=0x%x flags=%d", __FUNCTION__, pipe->channel, flags);
+
+    /* If not already there, add to the list of signaled pipes */
+    lookup = pipe_list_findp_waked(&dev->signaled_pipes, pipe);
+    if (!*lookup) {
+        pipe->next_waked = dev->signaled_pipes;
+        dev->signaled_pipes = pipe;
+    }
+    pipe->wanted |= (unsigned)flags;
+
+    /* Raise IRQ to indicate there are items on our list ! */
+    goldfish_device_set_irq(&dev->dev, 0, 1);
+    DD("%s: raising IRQ", __FUNCTION__);
+}
+
+void
+goldfish_pipe_close( void* hwpipe )
+{
+    Pipe* pipe = hwpipe;
+
+    D("%s: channel=0x%x (closed=%d)", __FUNCTION__, pipe->channel, pipe->closed);
+
+    if (!pipe->closed) {
+        pipe->closed = 1;
+        goldfish_pipe_wake( hwpipe, PIPE_WAKE_CLOSED );
+    }
 }
diff --git a/hw/goldfish_pipe.h b/hw/goldfish_pipe.h
index 03bc443..be5c449 100644
--- a/hw/goldfish_pipe.h
+++ b/hw/goldfish_pipe.h
@@ -13,65 +13,44 @@
 #define _HW_GOLDFISH_PIPE_H
 
 #include <stdint.h>
-#include "android/hw-qemud.h"
+#include "hw/hw.h"
 
-/* The following functions should called from hw/goldfish_trace.c and are
- * used to implement QEMUD 'fast-pipes' in the Android emulator.
+/* TECHNICAL NOTE:
+ *
+ * A goldfish pipe is a very fast communication channel between the guest
+ * system and the emulator program.
+ *
+ * To open a new pipe to the emulator, a guest client will do the following:
+ *
+ *     fd = open("/dev/qemu_pipe", O_RDWR);
+ *     char  invite[64];
+ *     snprintf(invite, sizeof invite, "%s", pipeName);
+ *     ret = write(fd, invite, strlen(invite));
+ *
+ *     if (ret < 0) {
+ *         // something bad happened, see errno
+ *     }
+ *
+ *     now read()/write() to communicate with <pipeName> service in the
+ *     emulator.
+ *
+ * This header provides the interface used by pipe services in the emulator
+ * to receive new client connection and deal with them.
+ *
+ *
+ * 1/ Call goldfish_pipe_add_type() to register a new pipe service by name.
+ *    This must provide a pointer to a series of functions that will be called
+ *    during normal pipe operations.
+ *
+ * 2/ When a client connects to the service, the 'init' callback will be called
+ *    to create a new service-specific client identifier (which must returned
+ *    by the function).
+ *
+ * 3/ Call goldfish_pipe_close() to force the closure of a given pipe.
+ *
+ * 4/ Call goldfish_pipe_signal() to signal a change of state to the pipe.
+ *
  */
-extern void      goldfish_pipe_thread_death(int  tid);
-extern void      goldfish_pipe_write(int tid, int offset, uint32_t value);
-extern uint32_t  goldfish_pipe_read(int tid, int offset);
-
-/* The following definitions are used to define a "pipe handler" type.
- * Each pipe handler manages a given, named pipe type, and must provide
- * a few callbacks that will be used at appropriate times:
- *
- * - init ::
- *       is called when a guest client has connected to a given
- *       pipe. The function should return an opaque pointer that
- *       will be passed as the first parameter to other callbacks.
- *
- *       Note: pipeOpaque is the value that was passed to
- *             goldfish_pipe_add_type() to register the pipe handler.
- *
- * - close ::
- *       is called when the pipe is closed.(either from the guest, or
- *       when the handler itself calls qemud_client_close() on the
- *       corresponding QemudClient object passed to init()).
- *
- * - sendBuffers ::
- *       is called when the guest is sending data through the pipe. This
- *       callback receives a list of buffer descriptors that indicate
- *       where the data is located in memory.
- *
- *       Must return 0 on success, or -1 on failure, with errno of:
- *
- *           ENOMEM -> indicates that the message is too large
- *           EAGAIN -> indicates that the handler is not ready
- *                     to accept the message yet.
- *
- * - recvBuffers ::
- *       Is called when the guest wants to receive data from the pipe.
- *       The caller provides a list of memory buffers to put the data into.
- *
- *       Must return the size of the incoming data on success, or -1
- *       on error, with errno of:
- *
- *           ENOMEM -> buffer too small to receive the message
- *           EAGAIN -> no incoming data yet
- *
- * - wakeOn ::
- *       is called to indicate that the guest wants to be waked when the
- *       pipe becomes able to either receive data from the guest, or send it
- *       new incoming data. It is the responsability of the pipe handler to
- *       signal the corresponding events by sending a single byte containing
- *       QEMUD_PIPE_WAKE_XXX bit flags through qemud_client_send() to do so.
- */
-
-enum {
-    QEMUD_PIPE_WAKE_ON_SEND = (1 << 0),
-    QEMUD_PIPE_WAKE_ON_RECV = (1 << 1),
-};
 
 /* Buffer descriptor for sendBuffers() and recvBuffers() callbacks */
 typedef struct GoldfishPipeBuffer {
@@ -81,18 +60,112 @@
 
 /* Pipe handler funcs */
 typedef struct {
-    void*        (*init)( QemudClient*  client, void* pipeOpaque );
-    void         (*close)( void* opaque );
-    int          (*sendBuffers)( void* opaque, const GoldfishPipeBuffer*  buffers, int numBuffers );
-    int          (*recvBuffers)( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers );
+    /* Create new client connection, 'hwpipe' must be passed to other
+     * goldfish_pipe_xxx functions, while the returned value will be passed
+     * to other callbacks (e.g. close). 'pipeOpaque' is the value passed
+     * to goldfish_pipe_add_type() when registering a given pipe service.
+     */
+    void*        (*init)( void* hwpipe, void* pipeOpaque, const char* args );
+
+    /* Called when the guest kernel has finally closed a pipe connection.
+     * This is the only place where you can release/free the client connection.
+     * You should never invoke this callback directly. Call goldfish_pipe_close()
+     * instead.
+     */
+    void         (*close)( void* pipe );
+
+    /* Called when the guest is write()-ing to the pipe. Should return the
+     * number of bytes transfered, 0 for EOF status, or a negative error
+     * value otherwise, including PIPE_ERROR_AGAIN to indicate that the
+     * emulator is not ready to receive data yet.
+     */
+    int          (*sendBuffers)( void* pipe, const GoldfishPipeBuffer*  buffers, int numBuffers );
+
+    /* Same as sendBuffers when the guest is read()-ing from the pipe. */
+    int          (*recvBuffers)( void* pipe, GoldfishPipeBuffer* buffers, int numBuffers );
+
+    /* Called when guest wants to poll the read/write status for the pipe.
+     * Should return a combination of PIPE_WAKE_XXX flags.
+     */
+    unsigned     (*poll)( void* pipe );
+
+    /* Called to signal that the guest wants to be woken when the set of
+     * PIPE_WAKE_XXX bit-flags in 'flags' occur. When the condition occurs,
+     * then the pipe implementation shall call goldfish_pipe_wake().
+     */
     void         (*wakeOn)( void* opaque, int flags );
-} QemudPipeHandlerFuncs;
+} GoldfishPipeFuncs;
 
 /* Register a new pipe handler type. 'pipeOpaque' is passed directly
  * to 'init() when a new pipe is connected to.
  */
-extern void  goldfish_pipe_add_type(const char*                   pipeName,
-                                     void*                         pipeOpaque,
-                                     const QemudPipeHandlerFuncs*  pipeFuncs );
+extern void  goldfish_pipe_add_type(const char*               pipeName,
+                                     void*                     pipeOpaque,
+                                     const GoldfishPipeFuncs*  pipeFuncs );
+
+/* This tells the guest system that we want to close the pipe and that
+ * further attempts to read or write to it will fail. This will not
+ * necessarily call the 'close' callback immediately though.
+ *
+ * This will also wake-up any blocked guest threads waiting for i/o.
+ */
+extern void goldfish_pipe_close( void* hwpipe );
+
+/* Signal that the pipe can be woken up. 'flags' must be a combination of
+ * PIPE_WAKE_READ and PIPE_WAKE_WRITE.
+ */
+extern void goldfish_pipe_wake( void* hwpipe, unsigned flags );
+
+/* The following definitions must match those under:
+ *
+ *    $KERNEL/drivers/misc/qemupipe/qemu_pipe.c
+ *
+ * Where $KERNEL points to the android-goldfish-2.6.xx branch on:
+ *
+ *     android.git.kernel.org/kernel/qemu.git.
+ */
+
+/* pipe device registers */
+#define PIPE_REG_COMMAND            0x00  /* write: value = command */
+#define PIPE_REG_STATUS             0x04  /* read */
+#define PIPE_REG_CHANNEL            0x08  /* read/write: channel id */
+#define PIPE_REG_SIZE               0x0c  /* read/write: buffer size */
+#define PIPE_REG_ADDRESS            0x10  /* write: physical address */
+#define PIPE_REG_WAKES              0x14  /* read: wake flags */
+
+/* list of commands for PIPE_REG_COMMAND */
+#define PIPE_CMD_OPEN               1  /* open new channel */
+#define PIPE_CMD_CLOSE              2  /* close channel (from guest) */
+#define PIPE_CMD_POLL               3  /* poll read/write status */
+
+/* List of bitflags returned in status of CMD_POLL command */
+#define PIPE_POLL_IN   (1 << 0)
+#define PIPE_POLL_OUT  (1 << 1)
+#define PIPE_POLL_HUP  (1 << 2)
+
+/* The following commands are related to write operations */
+#define PIPE_CMD_WRITE_BUFFER       4  /* send a user buffer to the emulator */
+#define PIPE_CMD_WAKE_ON_WRITE      5  /* tell the emulator to wake us when writing is possible */
+
+/* The following commands are related to read operations, they must be
+ * listed in the same order than the corresponding write ones, since we
+ * will use (CMD_READ_BUFFER - CMD_WRITE_BUFFER) as a special offset
+ * in qemu_pipe_read_write() below.
+ */
+#define PIPE_CMD_READ_BUFFER        6  /* receive a page-contained buffer from the emulator */
+#define PIPE_CMD_WAKE_ON_READ       7  /* tell the emulator to wake us when reading is possible */
+
+/* Possible status values used to signal errors - see qemu_pipe_error_convert */
+#define PIPE_ERROR_INVAL       -1
+#define PIPE_ERROR_AGAIN       -2
+#define PIPE_ERROR_NOMEM       -3
+#define PIPE_ERROR_IO          -4
+
+/* Bit-flags used to signal events from the emulator */
+#define PIPE_WAKE_CLOSED       (1 << 0)  /* emulator closed pipe */
+#define PIPE_WAKE_READ         (1 << 1)  /* pipe can now be read from */
+#define PIPE_WAKE_WRITE        (1 << 2)  /* pipe can now be written to */
+
+void pipe_dev_init(void);
 
 #endif /* _HW_GOLDFISH_PIPE_H */
diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c
index ee51a8b..02f9b8d 100644
--- a/hw/goldfish_trace.c
+++ b/hw/goldfish_trace.c
@@ -15,7 +15,6 @@
  */
 #include "qemu_file.h"
 #include "goldfish_trace.h"
-#include "goldfish_pipe.h"
 #include "sysemu.h"
 #include "trace.h"
 #ifdef CONFIG_MEMCHECK
@@ -177,7 +176,6 @@
             memcheck_exit(value);
         }
 #endif  // CONFIG_MEMCHECK
-        goldfish_pipe_thread_death((int)tid);
         break;
     case TRACE_DEV_REG_NAME:            // record thread name
         vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
@@ -350,13 +348,6 @@
         break;
 #endif // CONFIG_MEMCHECK
 
-    case TRACE_DEV_PIPE_COMMAND:
-    case TRACE_DEV_PIPE_ADDRESS:
-    case TRACE_DEV_PIPE_SIZE:
-    case TRACE_DEV_PIPE_CHANNEL:
-        goldfish_pipe_write(tid, ((offset >> 2) - TRACE_DEV_PIPE_BASE), value);
-        break;
-
     default:
         if (offset < 4096) {
             cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset);
@@ -379,12 +370,6 @@
     case TRACE_DEV_REG_ENABLE:          // tracing enable
         return tracing;
 
-    case TRACE_DEV_PIPE_COMMAND:
-    case TRACE_DEV_PIPE_ADDRESS:
-    case TRACE_DEV_PIPE_SIZE:
-    case TRACE_DEV_PIPE_CHANNEL:
-        return goldfish_pipe_read(tid, (offset >> 2) - TRACE_DEV_PIPE_BASE);
-
     default:
         if (offset < 4096) {
             cpu_abort(cpu_single_env, "trace_dev_read: Bad offset %x\n", offset);
diff --git a/vl-android.c b/vl-android.c
index 5d67fc6..a1b88be 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -51,9 +51,9 @@
 #include "charpipe.h"
 #include "modem_driver.h"
 #include "android/gps.h"
-#include "android/hw-qemud.h"
-#include "android/hw-qemud-pipe.h"
 #include "android/hw-kmsg.h"
+#include "android/hw-pipe-net.h"
+#include "android/hw-qemud.h"
 #include "android/charmap.h"
 #include "android/globals.h"
 #include "android/utils/bufprint.h"
@@ -198,6 +198,7 @@
 #include "balloon.h"
 #include "android/hw-lcd.h"
 #include "android/boot-properties.h"
+#include "android/hw-control.h"
 #include "android/core-init-utils.h"
 #include "android/audio-test.h"
 
@@ -4043,6 +4044,8 @@
 
     /* Initialize boot properties. */
     boot_property_init_service();
+    android_hw_control_init();
+    android_net_pipes_init();
 
     optind = 1;
     for(;;) {
@@ -5087,9 +5090,14 @@
                 PANIC("Snapshot storage file does not exist: %s", spath);
             }
             if (filelock_create(spath) == NULL) {
-                PANIC("Snapshot storag already in use: %s", spath);
+                PANIC("Snapshot storage already in use: %s", spath);
             }
             hdb_opts = drive_add(spath, HD_ALIAS, 1);
+            /* VERY IMPORTANT:
+             *    Set this property or the file will be mounted with O_DIRECT,
+             *    which will slow down snapshot saving x100 !
+             */
+            qemu_opt_set(hdb_opts, "cache", "unsafe");
         }
     }
 
@@ -5173,7 +5181,7 @@
     }
 
     /* Initialize OpenGLES emulation */
-    android_hw_opengles_init();
+    //android_hw_opengles_init();
 
     if (android_op_cpu_delay) {
         char*   end;