hw/goldfish_pipe.c: New support code for QEMUD fast-pipes.

Change-Id: I338334d53fa9bc52c87e9da18341d0cb94fd4269
diff --git a/Makefile.target b/Makefile.target
index fa76ed7..77f08c3 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -69,6 +69,7 @@
     goldfish_mmc.c   \
     goldfish_memlog.c \
     goldfish_nand.c \
+    goldfish_pipe.c \
     goldfish_tty.c \
     msmouse.c \
     pci.c \
diff --git a/hw/goldfish_pipe.c b/hw/goldfish_pipe.c
new file mode 100644
index 0000000..6c24208
--- /dev/null
+++ b/hw/goldfish_pipe.c
@@ -0,0 +1,740 @@
+/* Copyright (C) 2011 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU 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"
+
+#define  DEBUG 0
+
+#if DEBUG >= 1
+#  define D(...)  fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
+#else
+#  define D(...)  (void)0
+#endif
+
+#if DEBUG >= 2
+#  define DD(...)  fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
+#else
+#  define DD(...)  (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
+
+#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
+
+#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 */
+
+/**********************************************************************
+ **********************************************************************
+ **
+ **  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
+ *****
+ *****/
+
+typedef struct {
+    const char*                   pipeName;
+    void*                         pipeOpaque;
+    const QemudPipeHandlerFuncs*  pipeFuncs;
+} PipeType;
+
+#define MAX_PIPE_TYPES  4
+
+static PipeType  sPipeTypes[MAX_PIPE_TYPES];
+static int       sPipeTypeCount;
+
+void
+goldfish_pipe_add_type( const char*                   pipeName,
+                        void*                         pipeOpaque,
+                        const QemudPipeHandlerFuncs*  pipeFuncs )
+{
+    int count = sPipeTypeCount;
+
+    if (count >= MAX_PIPE_TYPES) {
+        APANIC("%s: Too many qemud pipe types!", __FUNCTION__);
+    }
+
+    sPipeTypes[count].pipeName   = pipeName;
+    sPipeTypes[count].pipeOpaque = pipeOpaque;
+    sPipeTypes[count].pipeFuncs  = pipeFuncs;
+    sPipeTypeCount = ++count;
+}
+
+static const PipeType*
+goldfish_pipe_find_type( const char*  pipeName )
+{
+    const PipeType* ptype = sPipeTypes;
+    const PipeType* limit = ptype + sPipeTypeCount;
+
+    for ( ; ptype < limit; ptype++ ) {
+        if (!strcmp(pipeName, ptype->pipeName)) {
+            return ptype;
+        }
+    }
+    return NULL;
+}
+
+
+/**********************************************************************
+ **********************************************************************
+ *****
+ *****  THREAD-SPECIFIC STATE
+ *****
+ *****/
+
+static void
+pipeClient_closeFromThread( PipeClient*  pcl );
+
+static uint32_t
+pipeClient_doCommand( PipeClient* pcl,
+                      uint32_t   command,
+                      uint32_t   address,
+                      uint32_t*  pSize );
+
+
+/* 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 )
+{
+    /* Get rid of the localId -> PipeClient map */
+    AINTMAP_FOREACH_VALUE(ts->pipes, pcl, pipeClient_closeFromThread(pcl));
+    aintMap_free(ts->pipes);
+
+    AFREE(ts);
+}
+
+static ThreadState*
+threadState_new( void )
+{
+    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);
+            }
+            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;
+    }
+    return count;
+}
+
+/* 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)
+{
+    /* Get rid of the tid -> ThreadState map */
+    AINTMAP_FOREACH_VALUE(pipeSvc->threadMap, ts, threadState_free(ts));
+    aintMap_free(pipeSvc->threadMap);
+    pipeSvc->threadMap = NULL;
+}
+#endif
+
+static void
+pipeService_init(PipeService*  pipeSvc)
+{
+    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);
+    }
+
+    return threadState_addPipe(ts, localId, pcl);
+}
+
+static void
+pipeService_removePipe(PipeService* pipeSvc, int tid, int localId)
+{
+    ThreadState* ts = pipeService_getState(pipeSvc, tid);
+
+    if (ts == NULL)
+        return;
+
+    threadState_delPipe(ts, localId);
+}
+
+/**********************************************************************
+ **********************************************************************
+ *****
+ *****  HARDWARE API - AS SEEN FROM hw/goldfish_trace.c
+ *****
+ *****/
+
+static PipeService  _globalState[1];
+
+void  init_qemud_pipes(void)
+{
+    pipeService_init(_globalState);
+}
+
+void
+goldfish_pipe_thread_death(int  tid)
+{
+    PipeService*  pipeSvc = _globalState;
+    pipeService_removeState(pipeSvc, tid);
+}
+
+void
+goldfish_pipe_write(int tid, int offset, uint32_t value)
+{
+    PipeService*  pipeSvc = _globalState;
+    DD("%s: tid=%d offset=%d value=%d (0x%x)", __FUNCTION__, tid,
+      offset, value, value);
+
+    ThreadState*  ts      = pipeService_getState(pipeSvc, tid);
+
+    if (ts == NULL) {
+        D("%s: no thread state for tid=%d", __FUNCTION__, tid);
+        return;
+    }
+    threadState_write(ts, offset, value);
+}
+
+uint32_t
+goldfish_pipe_read(int tid, int offset)
+{
+    PipeService*  pipeSvc = _globalState;
+    uint32_t      ret;
+
+    DD("%s: tid=%d offset=%d", __FUNCTION__, tid, offset);
+
+    ThreadState*  ts      = pipeService_getState(pipeSvc, tid);
+
+    if (ts == NULL) {
+        D("%s: no thread state for tid=%d", __FUNCTION__, tid);
+        return 0;
+    }
+    ret = threadState_read(ts, offset);
+    DD("%s:    result=%d (0x%d)", __FUNCTION__, ret, ret);
+    return ret;
+}
diff --git a/hw/goldfish_pipe.h b/hw/goldfish_pipe.h
new file mode 100644
index 0000000..03bc443
--- /dev/null
+++ b/hw/goldfish_pipe.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2011 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _HW_GOLDFISH_PIPE_H
+#define _HW_GOLDFISH_PIPE_H
+
+#include <stdint.h>
+#include "android/hw-qemud.h"
+
+/* The following functions should called from hw/goldfish_trace.c and are
+ * used to implement QEMUD 'fast-pipes' in the Android emulator.
+ */
+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 {
+    uint8_t*  data;
+    size_t    size;
+} GoldfishPipeBuffer;
+
+/* 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 );
+    void         (*wakeOn)( void* opaque, int flags );
+} QemudPipeHandlerFuncs;
+
+/* 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 );
+
+#endif /* _HW_GOLDFISH_PIPE_H */