blob: 5da391852a74a5f555061eadf8143c7a16f76a6d [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.
*/
/* This file implements the 'tcp:' goldfish pipe type which allows
* guest clients to directly connect to a TCP port through /dev/qemu_pipe.
*/
#include "android/sockets.h"
#include "android/utils/assert.h"
#include "android/utils/panic.h"
#include "android/utils/system.h"
#include "android/async-utils.h"
#include "android/opengles.h"
#include "android/looper.h"
#include "hw/android/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);
status = asyncConnector_init(pipe->connector, address, pipe->io);
pipe->state = STATE_CONNECTING;
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 netPipeReadySend(NetPipe *pipe)
{
if (pipe->state == STATE_CONNECTED)
return 0;
else if (pipe->state == STATE_CONNECTING)
return PIPE_ERROR_AGAIN;
else if (pipe->hwpipe == NULL)
return PIPE_ERROR_INVAL;
else
return PIPE_ERROR_IO;
}
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;
ret = netPipeReadySend(pipe);
if (ret != 0)
return ret;
for (; buff < buffEnd; buff++)
count += buff->size;
buff = buffers;
while (count > 0) {
int avail = buff->size - buffStart;
int len = socket_send(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;
}
/* 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 = socket_recv(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;
}
/* 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_POLL_IN;
if (mask & LOOP_IO_WRITE)
ret |= PIPE_POLL_OUT;
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>
*/
SockAddress address;
uint16_t port;
void* ret;
if (args == NULL) {
D("%s: Missing address!", __FUNCTION__);
return NULL;
}
D("%s: Port is '%s'", __FUNCTION__, args);
/* 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 = (uint16_t)val;
}
sock_address_init_inet(&address, SOCK_ADDRESS_INET_LOOPBACK, port);
ret = netPipe_initFromAddress(hwpipe, &address, _looper);
sock_address_done(&address);
return ret;
}
#ifndef _WIN32
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;
}
#endif
/**********************************************************************
**********************************************************************
*****
***** 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,
NULL, /* we can't save these */
NULL, /* we can't load these */
};
#ifndef _WIN32
static const GoldfishPipeFuncs netPipeUnix_funcs = {
netPipe_initUnix,
netPipe_closeFromGuest,
netPipe_sendBuffers,
netPipe_recvBuffers,
netPipe_poll,
netPipe_wakeOn,
NULL, /* we can't save these */
NULL, /* we can't load these */
};
#endif
/* This is set to 1 in android_init_opengles() below, and tested
* by openglesPipe_init() to refuse a pipe connection if the function
* was never called.
*/
static int _opengles_init;
static void*
openglesPipe_init( void* hwpipe, void* _looper, const char* args )
{
NetPipe *pipe;
if (!_opengles_init) {
/* This should never happen, unless there is a bug in the
* emulator's initialization, or the system image. */
D("Trying to open the OpenGLES pipe without GPU emulation!");
return NULL;
}
char server_addr[PATH_MAX];
android_gles_server_path(server_addr, sizeof(server_addr));
#ifndef _WIN32
if (android_gles_fast_pipes) {
pipe = (NetPipe *)netPipe_initUnix(hwpipe, _looper, server_addr);
D("Creating Unix OpenGLES pipe for GPU emulation: %s", server_addr);
} else {
#else /* _WIN32 */
{
#endif
/* Connect through TCP as a fallback */
pipe = (NetPipe *)netPipe_initTcp(hwpipe, _looper, server_addr);
D("Creating TCP OpenGLES pipe for GPU emulation!");
}
if (pipe != NULL) {
// Disable TCP nagle algorithm to improve throughput of small packets
socket_set_nodelay(pipe->io->fd);
// On Win32, adjust buffer sizes
#ifdef _WIN32
{
int sndbuf = 128 * 1024;
int len = sizeof(sndbuf);
if (setsockopt(pipe->io->fd, SOL_SOCKET, SO_SNDBUF,
(char*)&sndbuf, len) == SOCKET_ERROR) {
D("Failed to set SO_SNDBUF to %d error=0x%x\n",
sndbuf, WSAGetLastError());
}
}
#endif /* _WIN32 */
}
return pipe;
}
static const GoldfishPipeFuncs openglesPipe_funcs = {
openglesPipe_init,
netPipe_closeFromGuest,
netPipe_sendBuffers,
netPipe_recvBuffers,
netPipe_poll,
netPipe_wakeOn,
NULL, /* we can't save these */
NULL, /* we can't load these */
};
void
android_net_pipes_init(void)
{
Looper* looper = looper_newCore();
goldfish_pipe_add_type( "tcp", looper, &netPipeTcp_funcs );
#ifndef _WIN32
goldfish_pipe_add_type( "unix", looper, &netPipeUnix_funcs );
#endif
goldfish_pipe_add_type( "opengles", looper, &openglesPipe_funcs );
}
int
android_init_opengles_pipes(void)
{
/* TODO: Check that we can load and initialize the host emulation
* libraries, and return -1 in case of error.
*/
_opengles_init = 1;
return 0;
}