| /* |
| * 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 ); |
| } |