aboutsummaryrefslogtreecommitdiffstats
path: root/android
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@android.com>2011-04-05 12:48:01 +0200
committerDavid 'Digit' Turner <digit@android.com>2011-04-11 18:19:16 +0200
commit9b4029807c91ea42ff5e84e3616c90e2ce42d6f8 (patch)
tree3108c57f8ebd03b8dfa571c88761ca3c48052532 /android
parent335d2c1342bb887ac67f1f60cff795f0c06beaca (diff)
downloadexternal_qemu-9b4029807c91ea42ff5e84e3616c90e2ce42d6f8.zip
external_qemu-9b4029807c91ea42ff5e84e3616c90e2ce42d6f8.tar.gz
external_qemu-9b4029807c91ea42ff5e84e3616c90e2ce42d6f8.tar.bz2
Add OpenGLES pipe implementation.
Change-Id: I3ad498380de8438dbf430316af2d4391876057cc
Diffstat (limited to 'android')
-rw-r--r--android/hw-qemud-pipe-net.c788
1 files changed, 788 insertions, 0 deletions
diff --git a/android/hw-qemud-pipe-net.c b/android/hw-qemud-pipe-net.c
new file mode 100644
index 0000000..874bc69
--- /dev/null
+++ b/android/hw-qemud-pipe-net.c
@@ -0,0 +1,788 @@
+/*
+ * 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 );
+}