aboutsummaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@android.com>2011-03-21 17:51:03 +0100
committerDavid 'Digit' Turner <digit@android.com>2011-04-11 18:19:02 +0200
commit89217f57a256796b67b4d3f319e0f18f6225666a (patch)
tree92d21483086414b53778cf58942b2040dfb45ada /hw
parentbb1f432421288dae208c7189356643023ccbffbc (diff)
downloadexternal_qemu-89217f57a256796b67b4d3f319e0f18f6225666a.zip
external_qemu-89217f57a256796b67b4d3f319e0f18f6225666a.tar.gz
external_qemu-89217f57a256796b67b4d3f319e0f18f6225666a.tar.bz2
hw/goldfish_pipe.c: New support code for QEMUD fast-pipes.
Change-Id: I338334d53fa9bc52c87e9da18341d0cb94fd4269
Diffstat (limited to 'hw')
-rw-r--r--hw/goldfish_pipe.c740
-rw-r--r--hw/goldfish_pipe.h98
2 files changed, 838 insertions, 0 deletions
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 */