diff options
Diffstat (limited to 'android')
-rw-r--r-- | android/hw-qemud-pipe-net.c | 788 | ||||
-rw-r--r-- | android/pipe-net.c | 520 | ||||
-rw-r--r-- | android/pipe-net.h (renamed from android/hw-qemud-pipe.h) | 10 |
3 files changed, 525 insertions, 793 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 ); -} diff --git a/android/pipe-net.c b/android/pipe-net.c new file mode 100644 index 0000000..d83d8b1 --- /dev/null +++ b/android/pipe-net.c @@ -0,0 +1,520 @@ +/* + * 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. + */ + +/* This file implements the 'tcp:' goldfish pipe type which allows + * guest clients to directly connect to a TCP port through /dev/qemu_pipe. + */ + +#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 "hw/goldfish_pipe.h" + +/* Implement the OpenGL fast-pipe */ + +/* Set to 1 or 2 for debug traces */ +#define DEBUG 1 + +#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,!=) + +enum { + STATE_INIT, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_CLOSING_GUEST, + STATE_CLOSING_SOCKET +}; + +typedef struct { + void* hwpipe; + int state; + int wakeWanted; + LoopIo io[1]; + AsyncConnector connector[1]; + +} NetPipe; + +static void +netPipe_free( NetPipe* pipe ) +{ + int fd; + + /* 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 ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) { + loopIo_wantWrite(pipe->io); + } else { + loopIo_dontWantWrite(pipe->io); + } + + if (pipe->state == STATE_CONNECTED && (pipe->wakeWanted & PIPE_WAKE_READ) != 0) { + 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; + + D("%s", __FUNCTION__); + + /* 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->hwpipe != NULL) { + goldfish_pipe_close(pipe->hwpipe); + pipe->hwpipe = NULL; + } + + 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 */ + /* when the server 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) { + if ((pipe->wakeWanted & PIPE_WAKE_READ) != 0) { + wakeFlags |= PIPE_WAKE_READ; + } + } + + if ((events & LOOP_IO_WRITE) != 0) { + if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) { + wakeFlags |= PIPE_WAKE_WRITE; + } + } + + /* Send wake signal to the guest if needed */ + if (wakeFlags != 0) { + goldfish_pipe_wake(pipe->hwpipe, wakeFlags); + pipe->wakeWanted &= ~wakeFlags; + } + + /* Reset state */ + netPipe_resetState(pipe); +} + + +void* +netPipe_initFromAddress( void* hwpipe, const SockAddress* address, Looper* looper ) +{ + NetPipe* pipe; + + ANEW0(pipe); + + pipe->hwpipe = hwpipe; + pipe->state = STATE_INIT; + + { + AsyncStatus status; + + int fd = socket_create( sock_address_get_family(address), SOCKET_STREAM ); + if (fd < 0) { + D("%s: Could create socket from address family!", __FUNCTION__); + netPipe_free(pipe); + return NULL; + } + + loopIo_init(pipe->io, looper, fd, netPipe_io_func, pipe); + asyncConnector_init(pipe->connector, address, pipe->io); + pipe->state = STATE_CONNECTING; + + status = asyncConnector_run(pipe->connector); + if (status == ASYNC_ERROR) { + D("%s: Could not connect to socket: %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; + netPipe_free(pipe); +} + + +static int +netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers ) +{ + NetPipe* pipe = opaque; + int count = 0; + int ret = 0; + int buffStart = 0; + const GoldfishPipeBuffer* buff = buffers; + const GoldfishPipeBuffer* buffEnd = buff + numBuffers; + + for (; buff < buffEnd; buff++) + count += buff->size; + + buff = buffers; + while (count > 0) { + int avail = buff->size - buffStart; + int len = write(pipe->io->fd, buff->data + buffStart, avail); + + /* the write succeeded */ + if (len > 0) { + buffStart += len; + if (buffStart >= buff->size) { + buff++; + buffStart = 0; + } + count -= len; + ret += len; + continue; + } + + /* we reached the end of stream? */ + if (len == 0) { + if (ret == 0) + ret = PIPE_ERROR_IO; + break; + } + + /* loop on EINTR */ + if (errno == EINTR) + continue; + + /* if we already wrote some stuff, simply return */ + if (ret > 0) { + break; + } + + /* need to return an appropriate error code */ + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ret = PIPE_ERROR_AGAIN; + } else { + ret = PIPE_ERROR_IO; + } + break; + } + + return ret; +} + +static int +netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers ) +{ + NetPipe* pipe = opaque; + int count = 0; + int ret = 0; + int buffStart = 0; + GoldfishPipeBuffer* buff = buffers; + GoldfishPipeBuffer* buffEnd = buff + numBuffers; + + for (; buff < buffEnd; buff++) + count += buff->size; + + buff = buffers; + while (count > 0) { + int avail = buff->size - buffStart; + int len = read(pipe->io->fd, buff->data + buffStart, avail); + + /* the read succeeded */ + if (len > 0) { + buffStart += len; + if (buffStart >= buff->size) { + buff++; + buffStart = 0; + } + count -= len; + ret += len; + continue; + } + + /* we reached the end of stream? */ + if (len == 0) { + if (ret == 0) + ret = PIPE_ERROR_IO; + break; + } + + /* loop on EINTR */ + if (errno == EINTR) + continue; + + /* if we already read some stuff, simply return */ + if (ret > 0) { + break; + } + + /* need to return an appropriate error code */ + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ret = PIPE_ERROR_AGAIN; + } else { + ret = PIPE_ERROR_IO; + } + break; + } + return ret; +} + +static unsigned +netPipe_poll( void* opaque ) +{ + NetPipe* pipe = opaque; + unsigned mask = loopIo_poll(pipe->io); + unsigned ret = 0; + + if (mask & LOOP_IO_READ) + ret |= PIPE_WAKE_READ; + if (mask & LOOP_IO_WRITE) + ret |= PIPE_WAKE_WRITE; + + return ret; +} + +static void +netPipe_wakeOn( void* opaque, int flags ) +{ + NetPipe* pipe = opaque; + + DD("%s: flags=%d", __FUNCTION__, flags); + + pipe->wakeWanted |= flags; + netPipe_resetState(pipe); +} + + +void* +netPipe_initTcp( void* hwpipe, void* _looper, const char* args ) +{ + /* Build SockAddress from arguments. Acceptable formats are: + * + * <port> + * <host>:<port> + */ + SockAddress address; + void* ret; + + if (args == NULL) { + D("%s: Missing address!", __FUNCTION__); + return NULL; + } + D("%s: Address is '%s'", __FUNCTION__, args); + + char host[256]; /* max size of regular FDQN+1 */ + int hostlen = 0; + int port; + const char* p; + + /* Assume that anything after the last ':' is a port number + * And that what is before it is a port number. Should handle IPv6 + * notation. */ + p = strrchr(args, ':'); + if (p != NULL) { + hostlen = p - args; + if (hostlen >= sizeof(host)) { + D("%s: Address too long!", __FUNCTION__); + return NULL; + } + memcpy(host, args, hostlen); + host[hostlen] = '\0'; + args = p + 1; + } else { + snprintf(host, sizeof host, "127.0.0.1"); + } + + /* Now, look at the port number */ + { + char* end; + long val = strtol(args, &end, 10); + if (end == NULL || *end != '\0' || val <= 0 || val > 65535) { + D("%s: Invalid port number: '%s'", __FUNCTION__, args); + } + port = (int)val; + } + if (sock_address_init_resolve(&address, host, port, 0) < 0) { + D("%s: Could not resolve address", __FUNCTION__); + return NULL; + } + + ret = netPipe_initFromAddress(hwpipe, &address, _looper); + + sock_address_done(&address); + return ret; +} + +void* +netPipe_initUnix( void* hwpipe, void* _looper, const char* args ) +{ + /* Build SockAddress from arguments. Acceptable formats are: + * + * <path> + */ + SockAddress address; + void* ret; + + if (args == NULL || args[0] == '\0') { + D("%s: Missing address!", __FUNCTION__); + return NULL; + } + D("%s: Address is '%s'", __FUNCTION__, args); + + sock_address_init_unix(&address, args); + + ret = netPipe_initFromAddress(hwpipe, &address, _looper); + + sock_address_done(&address); + return ret; +} + + +/********************************************************************** + ********************************************************************** + ***** + ***** N E T W O R K P I P E M E S S A G E S + ***** + *****/ + +static const GoldfishPipeFuncs netPipeTcp_funcs = { + netPipe_initTcp, + netPipe_closeFromGuest, + netPipe_sendBuffers, + netPipe_recvBuffers, + netPipe_poll, + netPipe_wakeOn, +}; + +static const GoldfishPipeFuncs netPipeUnix_funcs = { + netPipe_initUnix, + netPipe_closeFromGuest, + netPipe_sendBuffers, + netPipe_recvBuffers, + netPipe_poll, + netPipe_wakeOn, +}; + + +#define DEFAULT_OPENGLES_PORT 22468 + +static void* +openglesPipe_init( void* hwpipe, void* _looper, const char* args ) +{ + char temp[32]; + + /* For now, simply connect through tcp */ + snprintf(temp, sizeof temp, "%d", DEFAULT_OPENGLES_PORT); + return netPipe_initTcp(hwpipe, _looper, temp); +} + +static const GoldfishPipeFuncs openglesPipe_funcs = { + openglesPipe_init, + netPipe_closeFromGuest, + netPipe_sendBuffers, + netPipe_recvBuffers, + netPipe_poll, + netPipe_wakeOn, +}; + + +void +android_net_pipes_init(void) +{ + Looper* looper = looper_newCore(); + + goldfish_pipe_add_type( "tcp", looper, &netPipeTcp_funcs ); + goldfish_pipe_add_type( "unix", looper, &netPipeUnix_funcs ); + goldfish_pipe_add_type( "opengles", looper, &openglesPipe_funcs ); +} diff --git a/android/hw-qemud-pipe.h b/android/pipe-net.h index 540e02b..08db031 100644 --- a/android/hw-qemud-pipe.h +++ b/android/pipe-net.h @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef _ANDROID_HW_QEMUD_PIPE_H -#define _ANDROID_HW_QEMUD_PIPE_H -void init_qemud_pipes(void); -void android_hw_opengles_init(void); +#ifndef ANDROID_PIPE_NET_H +#define ANDROID_PIPE_NET_H -#endif /* _ANDROID_HW_QEMUD_PIPE_H */ +void android_net_pipes_init(void); + +#endif /* ANDROID_PIPE_NET_H */ |