blob: 874bc694a8893cd03b50168eb7cecd8edb684b0b [file] [log] [blame]
/*
* 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 );
}