diff options
Diffstat (limited to 'android/hw-qemud-pipe-net.c')
-rw-r--r-- | android/hw-qemud-pipe-net.c | 788 |
1 files changed, 0 insertions, 788 deletions
diff --git a/android/hw-qemud-pipe-net.c b/android/hw-qemud-pipe-net.c deleted file mode 100644 index 874bc69..0000000 --- a/android/hw-qemud-pipe-net.c +++ /dev/null @@ -1,788 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "sockets.h" -#include "android/utils/assert.h" -#include "android/utils/panic.h" -#include "android/utils/system.h" -#include "android/async-utils.h" -#include "android/looper.h" -#include "android/hw-qemud-pipe.h" -#include "hw/goldfish_pipe.h" - -/* Implement the OpenGL fast-pipe */ - -/* Set to 1 or 2 for debug traces */ -#define DEBUG 0 - -#if DEBUG >= 1 -# define D(...) printf(__VA_ARGS__), printf("\n") -#else -# define D(...) ((void)0) -#endif - -#if DEBUG >= 2 -# define DD(...) printf(__VA_ARGS__), printf("\n") -# define DDASSERT(cond) _ANDROID_ASSERT(cond, "Assertion failure: ", #cond) -# define DDASSERT_INT_OP(cond,val,op) _ANDROID_ASSERT_INT_OP(cond,val,op) -#else -# define DD(...) ((void)0) -# define DDASSERT(cond) ((void)0) -# define DDASSERT_INT_OP(cond,val,op) ((void)0) -#endif - -#define DDASSERT_INT_LT(cond,val) DDASSERT_INT_OP(cond,val,<) -#define DDASSERT_INT_LTE(cond,val) DDASSERT_INT_OP(cond,val,<=) -#define DDASSERT_INT_GT(cond,val) DDASSERT_INT_OP(cond,val,>) -#define DDASSERT_INT_GTE(cond,val) DDASSERT_INT_OP(cond,val,>=) -#define DDASSERT_INT_EQ(cond,val) DDASSERT_INT_OP(cond,val,==) -#define DDASSERT_INT_NEQ(cond,val) DDASSERT_INT_OP(cond,val,!=) - - -/* Forward declarations */ - -typedef struct NetPipeInit { - Looper* looper; - SockAddress serverAddress[1]; -} NetPipeInit; - -/********************************************************************** - ********************************************************************** - ***** - ***** P I P E M E S S A G E S - ***** - *****/ - -typedef struct PipeMsg { - struct PipeMsg* next; - size_t size; - uint8_t data[1]; -} PipeMsg; - -static PipeMsg* -pipeMsg_alloc( size_t size ) -{ - PipeMsg* msg = android_alloc(sizeof(*msg) + size); - - msg->next = NULL; - msg->size = size; - return msg; -} - -static void -pipeMsg_free( PipeMsg* msg ) -{ - AFREE(msg); -} - -/********************************************************************** - ********************************************************************** - ***** - ***** M E S S A G E L I S T - ***** - *****/ - -typedef struct { - PipeMsg* firstMsg; /* first message in list */ - PipeMsg* lastMsg; /* last message in list */ - size_t firstBytes; /* bytes in firstMsg that were already sent */ - size_t lastBytes; /* bytes in lastMsg that were already received */ - size_t totalBytes; /* total bytes in list */ -} MsgList; - -/* Default receiver buffer size to accept incoming data */ -#define DEFAULT_RECEIVER_SIZE 8180 - -/* Initialize a message list - appropriate for sending them out */ -static void -msgList_initSend( MsgList* list ) -{ - list->firstMsg = NULL; - list->lastMsg = NULL; - list->firstBytes = 0; - list->lastBytes = 0; - list->totalBytes = 0; -} - -/* Initialize a message list for receiving data */ -static void -msgList_initReceive( MsgList* list ) -{ - msgList_initSend(list); - list->firstMsg = list->lastMsg = pipeMsg_alloc( DEFAULT_RECEIVER_SIZE ); -} - -/* Finalize a message list */ -static void -msgList_done( MsgList* list ) -{ - PipeMsg* msg; - - while ((msg = list->firstMsg) != NULL) { - list->firstMsg = msg->next; - pipeMsg_free(msg); - } - list->lastMsg = NULL; - - list->firstBytes = 0; - list->lastBytes = 0; - list->totalBytes = 0; -} - -static int -msgList_hasData( MsgList* list ) -{ - return list->totalBytes > 0; -} - -/* Append a list of buffers to a message list. - * - * This is a very simple implementation that simply mallocs a single - * new message containing all of the buffer's data, and append it to - * our link list. This also makes the implementation of msgList_send() - * quite simple, since there is no need to deal with the 'lastBytes' - * pointer (it is always assumed to be 'lastMsg->size'). - */ -static int -msgList_sendBuffers( MsgList* list, - const GoldfishPipeBuffer* buffers, - int numBuffers ) -{ - const GoldfishPipeBuffer* buff = buffers; - const GoldfishPipeBuffer* buffEnd = buff + numBuffers; - PipeMsg* msg; - size_t msgSize = 0; - size_t pos; - - /* Count the total number of bytes */ - for ( ; buff < buffEnd; buff++ ) { - msgSize += buff[0].size; - } - - /* Allocate a new message */ - msg = pipeMsg_alloc(msgSize); - if (msg == NULL) { - errno = ENOMEM; - return -1; - } - - /* Copy data from buffers to message */ - for ( pos = 0, buff = buffers; buff < buffEnd; buff++ ) { - memcpy(msg->data + pos, buff->data, buff->size); - pos += buff->size; - } - - /* Append message to current list */ - if (list->lastMsg != NULL) { - list->lastMsg->next = msg; - } else { - list->firstMsg = msg; - list->firstBytes = 0; - } - list->lastMsg = msg; - - list->totalBytes += msgSize; - - /* We are done */ - return 0; -} - -/* Try to send outgoing messages in the list through non-blocking socket 'fd'. - * Return 0 on success, and -1 on failure, where errno will be: - * - * ECONNRESET - connection reset by peer - * - * Note that 0 will be returned if socket_send() returns EAGAIN/EWOULDBLOCK. - */ -static int -msgList_send( MsgList* list, int fd ) -{ - int ret = 0; - - for (;;) { - PipeMsg* msg = list->firstMsg; - size_t sentBytes = list->firstBytes; - size_t availBytes; - - if (msg == NULL) { - /* We sent everything */ - return 0; - } - DDASSERT(sentBytes < msg->size); - availBytes = msg->size - sentBytes; - - ret = socket_send(fd, msg->data + sentBytes, availBytes); - if (ret <= 0) { - goto ERROR; - } - - list->totalBytes -= ret; - list->firstBytes += ret; - - if (list->firstBytes < msg->size) { - continue; - } - - /* We sent the full first packet - remove it from the head */ - list->firstBytes = 0; - list->firstMsg = msg->next; - if (list->firstMsg == NULL) { - list->lastMsg = NULL; - } - pipeMsg_free(msg); - } - -ERROR: - if (ret < 0) { /* EAGAIN/EWOULDBLOCK or disconnection */ - if (errno == EAGAIN || errno == EWOULDBLOCK) { - ret = 0; /* clear error - this is normal */ - } else { - DD("%s: socket_send() returned %d: %s\n", __FUNCTION__, errno, errno_str); - errno = ECONNRESET; - } - } else { -#if DEBUG >= 2 - int err = socket_get_error(fd); - DD("%s: socket_send() returned 0 (error=%d: %s)", __FUNCTION__, - err, strerror(err)); -#endif - errno = ECONNRESET; - ret = -1; - } - return ret; -} - - -/* Try to receive data into caller-provided buffers, and return the total - * size of bytes that were read. Returns -1 on error, with errno: - * - * ECONNRESET: Connection reset by peer - * EAGAIN: No incoming data, wait for it to arrive. - */ -static int -msgList_recvBuffers( MsgList* list, - GoldfishPipeBuffer* buffers, - int numBuffers ) -{ - GoldfishPipeBuffer* buff = buffers; - GoldfishPipeBuffer* buffEnd = buff + numBuffers; - size_t buffStart = 0; - PipeMsg* msg = list->firstMsg; - size_t msgStart = list->firstBytes; - size_t totalSize = 0; - - DDASSERT(msg != NULL); - - D("%s: ENTER list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d", - __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes, - list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg); - - /* If there is no incoming data, return EAGAIN */ - if (list->totalBytes == 0) { - errno = EAGAIN; - return -1; - } - - /* Now try to transfer as much from the list of incoming messages - * into the buffers. - */ - while (msg != NULL && buff < buffEnd) { - DDASSERT(msgStart < msg->size); - DDASSERT(buffStart < buff->size); - - /* Copy data from current message into next buffer. - * For a given message, first determine the start and end - * of available data. Then try to see how much of these - * we can copy to the current buffer. - */ - size_t msgEnd = msg->size; - - if (msg == list->lastMsg) { - msgEnd = list->lastBytes; - } - - size_t msgAvail = msgEnd - msgStart; - size_t buffAvail = buff->size - buffStart; - - if (msgAvail > buffAvail) { - msgAvail = buffAvail; - } - - DDASSERT(msgAvail > 0); - - D("%s: transfer %d bytes (msgStart=%d msgSize=%d buffStart=%d buffSize=%d)", - __FUNCTION__, msgAvail, msgStart, msg->size, buffStart, buff->size); - memcpy(buff->data + buffStart, msg->data + msgStart, msgAvail); - - /* Advance cursors */ - msgStart += msgAvail; - buffStart += msgAvail; - totalSize += msgAvail; - - /* Did we fill up the current buffer? */ - if (buffStart >= buff->size) { - buffStart = 0; - buff++; - } - - /* Did we empty the current message? */ - if (msgStart >= msgEnd) { - msgStart = 0; - /* If this is the last message, reset the 'first' and 'last' - * pointers to reuse it for the next recv(). */ - if (msg == list->lastMsg) { - list->lastBytes = 0; - msg = NULL; - } else { - /* Otherwise, delete the message, and jump to the next one */ - list->firstMsg = msg->next; - pipeMsg_free(msg); - msg = list->firstMsg; - } - } - - } - list->firstBytes = msgStart; - list->totalBytes -= totalSize; - - D("%s: EXIT list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d", - __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes, - list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg); - - return (int)totalSize; -} - - -/* Try to receive data from non-blocking socket 'fd'. - * Return 0 on success, or -1 on error, where errno can be: - * - * ECONNRESET - connection reset by peer - * ENOMEM - full message list, no room to receive more data - */ -static int -msgList_recv( MsgList* list, int fd ) -{ - int ret = 0; - - D("%s: ENTER list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d", - __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes, - list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg); - - for (;;) { - PipeMsg* last = list->lastMsg; - size_t lastBytes = list->lastBytes; - size_t availBytes; - - /* Compute how many bytes we can receive in the last buffer*/ - DDASSERT(last != NULL); - DDASSERT(last->size > 0); - DDASSERT(lastBytes < last->size); - - availBytes = last->size - lastBytes; - - /* Try to receive the data, act on errors */ - ret = socket_recv(fd, last->data + lastBytes, availBytes); - if (ret <= 0) { - goto ERROR; - } - - /* Acknowledge received data */ - list->lastBytes += ret; - list->totalBytes += ret; - - if (list->lastBytes < last->size) { - continue; - } - - /* We filled-up the last message buffer, allocate a new one */ - last = pipeMsg_alloc( DEFAULT_RECEIVER_SIZE ); - list->lastMsg->next = last; - list->lastMsg = last; - list->lastBytes = 0; - } - -ERROR: - if (ret < 0) { /* EAGAIN/EWOULDBLOCK or disconnection */ - if (errno == EAGAIN || errno == EWOULDBLOCK) { - ret = 0; /* clear error - this is normal */ - } else { - DD("%s: socket_send() returned %d: %s\n", __FUNCTION__, errno, errno_str); - errno = ECONNRESET; - } - } else /* ret == 0 */ { -#if DEBUG >= 2 - int err = socket_get_error(fd); - DD("%s: socket_send() returned 0 (error=%d: %s)", __FUNCTION__, - err, strerror(err)); -#endif - errno = ECONNRESET; - ret = -1; - } - D("%s: EXIT list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d", - __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes, - list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg); - - return ret; -} - - - -/********************************************************************** - ********************************************************************** - ***** - ***** P I P E H A N D L E R S - ***** - *****/ - -/* Technical Note: - * - * Each NetPipe object is connected to the following: - * - * - a remote rendering process through a normal TCP socket. - * - a Goldfish pipe (see hw/goldfish_pipe.h) to exchange messages with the guest. - * - a Qemud client (see android/hw-qemud.h) to signal state changes to the guest. - * - * REMOTE <---socket---> PIPE <------> GOLDFISH PIPE - * PROCESS <--+ - * | - * +---> QEMUD CHANNEL (android/hw-qemud.h) - * - */ -enum { - STATE_INIT, - STATE_CONNECTING, - STATE_CONNECTED, - STATE_CLOSING_GUEST, - STATE_CLOSING_SOCKET -}; - -#define DEFAULT_INCOMING_SIZE 4000 - -#define MAX_IN_BUFFERS 4 - -typedef struct { - QemudClient* client; - int state; - int wakeWanted; - - MsgList outList[1]; - MsgList inList[1]; - - LoopIo io[1]; - AsyncConnector connector[1]; - - GoldfishPipeBuffer outBuffer[1]; - GoldfishPipeBuffer inBuffers[MAX_IN_BUFFERS]; - int inBufferCount; - -} NetPipe; - -static void -netPipe_free( NetPipe* pipe ) -{ - int fd; - - /* Removing any pending incoming packet */ - msgList_done(pipe->outList); - msgList_done(pipe->inList); - - /* Close the socket */ - fd = pipe->io->fd; - loopIo_done(pipe->io); - socket_close(fd); - - /* Release the pipe object */ - AFREE(pipe); -} - - -static void -netPipe_resetState( NetPipe* pipe ) -{ - /* If there is a pending outgoing packet, open the valve */ - if (msgList_hasData(pipe->outList)) { - loopIo_wantWrite(pipe->io); - } else { - loopIo_dontWantWrite(pipe->io); - } - - /* Accept incoming data if we are not closing, and our input list isn't full */ - if (pipe->state == STATE_CONNECTED) { - loopIo_wantRead(pipe->io); - } else { - loopIo_dontWantRead(pipe->io); - } -} - - -/* This function is only called when the socket is disconnected. - * See netPipe_closeFromGuest() for the case when the guest requires - * the disconnection. */ -static void -netPipe_closeFromSocket( void* opaque ) -{ - NetPipe* pipe = opaque; - - /* If the guest already ordered the pipe to be closed, delete immediately */ - if (pipe->state == STATE_CLOSING_GUEST) { - netPipe_free(pipe); - return; - } - - /* Force the closure of the QEMUD channel - if a guest is blocked - * waiting for a wake signal, it will receive an error. */ - if (pipe->client != NULL) { - qemud_client_close(pipe->client); - pipe->client = NULL; - } - - /* Remove any outgoing packets - they won't go anywhere */ - msgList_done(pipe->outList); - - pipe->state = STATE_CLOSING_SOCKET; - netPipe_resetState(pipe); -} - - -/* This is the function that gets called each time there is an asynchronous - * event on the network pipe. - */ -static void -netPipe_io_func( void* opaque, int fd, unsigned events ) -{ - NetPipe* pipe = opaque; - int wakeFlags = 0; - - /* Run the connector if we are in the CONNECTING state */ - /* TODO: Add some sort of time-out, to deal with the case */ - /* where the renderer process is wedged. */ - if (pipe->state == STATE_CONNECTING) { - AsyncStatus status = asyncConnector_run(pipe->connector); - if (status == ASYNC_NEED_MORE) { - return; - } - else if (status == ASYNC_ERROR) { - /* Could not connect, tell our client by closing the channel. */ - - netPipe_closeFromSocket(pipe); - return; - } - pipe->state = STATE_CONNECTED; - netPipe_resetState(pipe); - return; - } - - /* Otherwise, accept incoming data */ - if ((events & LOOP_IO_READ) != 0) { - int ret; - - if ((pipe->wakeWanted & QEMUD_PIPE_WAKE_ON_RECV) != 0) { - wakeFlags |= QEMUD_PIPE_WAKE_ON_RECV; - } - - ret = msgList_recv(pipe->inList, fd); - if (ret < 0) { - wakeFlags &= ~QEMUD_PIPE_WAKE_ON_RECV; - - if (errno == ENOMEM) { /* shouldn't happen */ - DD("%s: msgList_recv() return ENOMEM!?\n", __FUNCTION__); - } else { - /* errno == ECONNRESET */ - DD("%s: msgList_recv() error, closing pipe\n", __FUNCTION__); - netPipe_closeFromSocket(pipe); - return; - } - } - } - - if ((events & LOOP_IO_WRITE) != 0) { - int ret; - - DDASSERT(msgList_hasData(pipe->outList)); - - ret = msgList_send(pipe->outList, fd); - if (ret < 0) { - DD("%s: msgList_send() error, closing pipe\n", __FUNCTION__); - netPipe_closeFromSocket(pipe); - return; - } - - if ((pipe->wakeWanted & QEMUD_PIPE_WAKE_ON_SEND) != 0) { - wakeFlags |= QEMUD_PIPE_WAKE_ON_SEND; - } - } - - /* Send wake signal to the guest if needed */ - if (wakeFlags != 0) { - uint8_t byte = (uint8_t) wakeFlags; - DD("%s: Sending wake flags %d (wanted=%d)", __FUNCTION__, byte, pipe->wakeWanted); - qemud_client_send(pipe->client, &byte, 1); - pipe->wakeWanted &= ~wakeFlags; - } - - /* Reset state */ - netPipe_resetState(pipe); -} - - -void* -netPipe_init( QemudClient* qcl, void* pipeOpaque ) -{ - NetPipe* pipe; - NetPipeInit* pipeSvc = pipeOpaque; - - ANEW0(pipe); - - pipe->client = qcl; - pipe->state = STATE_INIT; - - msgList_initSend(pipe->outList); - msgList_initReceive(pipe->inList); - -#define DEFAULT_OPENGLES_PORT 22468 - - { - AsyncStatus status; - - int fd = socket_create_inet( SOCKET_STREAM ); - if (fd < 0) { - netPipe_free(pipe); - return NULL; - } - - loopIo_init(pipe->io, pipeSvc->looper, fd, netPipe_io_func, pipe); - asyncConnector_init(pipe->connector, pipeSvc->serverAddress, pipe->io); - pipe->state = STATE_CONNECTING; - - status = asyncConnector_run(pipe->connector); - if (status == ASYNC_ERROR) { - D("%s: Could not create to renderer process: %s", - __FUNCTION__, errno_str); - netPipe_free(pipe); - return NULL; - } - if (status == ASYNC_COMPLETE) { - pipe->state = STATE_CONNECTED; - netPipe_resetState(pipe); - } - } - - return pipe; -} - -/* Called when the guest wants to close the channel. This is different - * from netPipe_closeFromSocket() which is called when the socket is - * disconnected. */ -static void -netPipe_closeFromGuest( void* opaque ) -{ - NetPipe* pipe = opaque; - - /* The qemud client is gone when we reach this code */ - pipe->client = NULL; - - /* Remove input messages */ - msgList_done(pipe->inList); - - /* If the socket is already closed, or if there are no - * outgoing messages, delete immediately */ - if (pipe->state == STATE_CLOSING_SOCKET || - !msgList_hasData(pipe->outList)) { - netPipe_free(pipe); - return; - } - - /* Otherwise, mark our pipe as closing, and wait until everything is - * sent before deleting the object. */ - pipe->state = STATE_CLOSING_GUEST; - netPipe_resetState(pipe); -} - - -static int -netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers ) -{ - NetPipe* pipe = opaque; - int ret; - - ret = msgList_sendBuffers(pipe->outList, buffers, numBuffers); - netPipe_resetState(pipe); - return ret; -} - -static int -netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers ) -{ - NetPipe* pipe = opaque; - int ret; - - ret = msgList_recvBuffers(pipe->inList, buffers, numBuffers); - netPipe_resetState(pipe); - return ret; -} - -static void -netPipe_wakeOn( void* opaque, int flags ) -{ - NetPipe* pipe = opaque; - - pipe->wakeWanted |= flags; -} - -/********************************************************************** - ********************************************************************** - ***** - ***** N E T W O R K P I P E M E S S A G E S - ***** - *****/ - -static const QemudPipeHandlerFuncs net_pipe_handler_funcs = { - netPipe_init, - netPipe_closeFromGuest, - netPipe_sendBuffers, - netPipe_recvBuffers, - netPipe_wakeOn, -}; - - -static NetPipeInit _netPipeService[1]; - -/********************************************************************** - ********************************************************************** - ***** - ***** O P E N G L E S P I P E S E R V I C E - ***** - *****/ - -void -android_hw_opengles_init(void) -{ - NetPipeInit* svc = _netPipeService; - int ret; - - DD("%s: Registering service\n", __FUNCTION__); - - svc->looper = looper_newCore(); - - ret = sock_address_init_resolve(svc->serverAddress, - "127.0.0.1", - DEFAULT_OPENGLES_PORT, - 0); - if (ret < 0) { - APANIC("Could not resolve renderer process address!"); - } - - goldfish_pipe_add_type( "opengles", svc, &net_pipe_handler_funcs ); -} |