blob: 290e69a54e2124d9a4094db4424494c9c526e8d6 [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/opengles.h"
#include "hw/misc/android_pipe.h"
#include "qemu-common.h"
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
/* 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")
#else
# define DD(...) ((void)0)
#endif
/* Network pipe implementation */
enum {
STATE_INIT,
STATE_CONNECTING,
STATE_CONNECTED,
STATE_CLOSING_GUEST,
STATE_CLOSING_SOCKET
};
typedef struct {
void* hwpipe;
int fd;
int state;
int wakeWanted;
int wakeActual;
} NetPipe;
static void net_pipe_free(NetPipe* pipe) {
DD("%s: fd=%d\n", __FUNCTION__, pipe->fd);
/* Close the socket */
int fd = pipe->fd;
qemu_set_fd_handler(fd, NULL, NULL, NULL);
closesocket(fd);
/* Release the pipe object */
g_free(pipe);
}
static void net_pipe_handle_read(void* opaque);
static void net_pipe_handle_write(void* opaque);
static void net_pipe_reset_state(NetPipe* pipe) {
IOHandler* read_handler = NULL;
IOHandler* write_handler = NULL;
if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) {
write_handler = net_pipe_handle_write;
}
if (pipe->state == STATE_CONNECTED && (pipe->wakeWanted & PIPE_WAKE_READ) != 0) {
read_handler = net_pipe_handle_read;
}
qemu_set_fd_handler(pipe->fd, read_handler, write_handler, pipe);
}
/* This function is only called when the socket is disconnected.
* See net_pipe_close_from_guest() for the case when the guest requires
* the disconnection. */
static void net_pipe_close_from_socket(void* opaque) {
NetPipe* pipe = opaque;
D("%s: fd=%d", __FUNCTION__, pipe->fd);
/* If the guest already ordered the pipe to be closed, delete immediately */
if (pipe->state == STATE_CLOSING_GUEST) {
net_pipe_free(pipe);
return;
}
/* Force the closure of the pipe channel - if a guest is blocked
* waiting for a wake signal, it will receive an error. */
if (pipe->hwpipe != NULL) {
android_pipe_close(pipe->hwpipe);
pipe->hwpipe = NULL;
}
pipe->state = STATE_CLOSING_SOCKET;
net_pipe_reset_state(pipe);
DD("%s: fd=%d: waiting for guest to close pipe", __FUNCTION__, pipe->fd);
}
/* Called whenever data is available from the pipe socket. */
static void net_pipe_handle_read(void* opaque) {
NetPipe* pipe = opaque;
DD("%s: fd=%d", __FUNCTION__, pipe->fd);
pipe->wakeActual |= PIPE_WAKE_READ;
/* Send wake signal to guest if needed */
if ((pipe->wakeWanted & PIPE_WAKE_READ) != 0) {
android_pipe_wake(pipe->hwpipe, PIPE_WAKE_READ);
pipe->wakeWanted &= ~PIPE_WAKE_READ;
}
/* Reset state */
net_pipe_reset_state(pipe);
}
/* Called whenever the pipe socket is ready to receive data. */
static void net_pipe_handle_write(void* opaque) {
NetPipe* pipe = opaque;
DD("%s: fd=%d", __FUNCTION__, pipe->fd);
pipe->wakeActual |= PIPE_WAKE_WRITE;
/* Send wake signal to guest if needed */
if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) {
android_pipe_wake(pipe->hwpipe, PIPE_WAKE_WRITE);
pipe->wakeWanted &= ~PIPE_WAKE_WRITE;
}
/* Reset state */
net_pipe_reset_state(pipe);
}
/* Called when async connection to the pipe destination completes (fd >= 0)
* or fails (fd < 0). */
static void net_pipe_handle_connect(int fd, Error *errp, void* opaque) {
NetPipe *pipe = opaque;
DD("%s: fd=%d result=%d", __FUNCTION__, pipe->fd, fd);
if (fd < 0) {
/* Could not connect, tell our client by closing the channel. */
net_pipe_close_from_socket(pipe);
return;
}
pipe->state = STATE_CONNECTED;
net_pipe_reset_state(pipe);
return;
}
static void* net_pipe_init_from_address(void* hwpipe, const char* address) {
NetPipe* pipe = g_new0(NetPipe, 1);
DD("%s: address=[%s]", __FUNCTION__, address);
pipe->hwpipe = hwpipe;
pipe->state = STATE_CONNECTING;
if (address[0] == ':') {
char full_address[64];
snprintf(full_address, sizeof(full_address), "127.0.0.1%s", address);
pipe->fd = inet_nonblocking_connect(full_address, net_pipe_handle_connect,
pipe, NULL);
} else {
pipe->fd = unix_nonblocking_connect(address, net_pipe_handle_connect,
pipe, NULL);
}
return pipe;
}
/* Called when the guest wants to close the channel. This is different
* from net_pipe_close_from_socket() which is called when the socket is
* disconnected. */
static void net_pipe_close_from_guest(void* opaque) {
NetPipe *pipe = opaque;
DD("%s: fd=%d", __FUNCTION__, pipe->fd);
net_pipe_free(pipe);
}
static int net_pipe_ready_send(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 net_pipe_send_buffers(void* opaque,
const AndroidPipeBuffer* buffers,
int numBuffers) {
NetPipe *pipe = opaque;
int count = 0;
int ret = 0;
size_t buffStart = 0;
const AndroidPipeBuffer* buff = buffers;
const AndroidPipeBuffer* buffEnd = buff + numBuffers;
ret = net_pipe_ready_send(pipe);
if (ret != 0) {
DD("%s: fd=%d error=%d", __FUNCTION__, pipe->fd, errno);
return ret;
}
for (; buff < buffEnd; buff++) {
count += buff->size;
}
DD("%s: fd=%d count=%d num_buffers=%d", __FUNCTION__, pipe->fd,
count, numBuffers);
buff = buffers;
while (count > 0) {
int avail = buff->size - buffStart;
#ifdef _WIN32
int len = send_all(pipe->fd, buff->data + buffStart, avail);
#else
int len = opengl_send_all(pipe->fd, buff->data + buffStart, avail);
#endif
/* 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) {
DD("%s: fd=%d EOF", __FUNCTION__, pipe->fd);
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 {
DD("%s: fd=%d I/O error [%s]", __FUNCTION__, pipe->fd,
strerror(errno));
ret = PIPE_ERROR_IO;
}
break;
}
if (count > 0) {
pipe->wakeActual &= ~PIPE_WAKE_WRITE;
}
return ret;
}
static int net_pipe_recv_buffers(void *opaque,
AndroidPipeBuffer* buffers,
int numBuffers) {
NetPipe *pipe = opaque;
int count = 0;
int ret = 0;
size_t buffStart = 0;
AndroidPipeBuffer* buff = buffers;
AndroidPipeBuffer* buffEnd = buff + numBuffers;
for (; buff < buffEnd; buff++) {
count += buff->size;
}
DD("%s: fd=%d count=%d num_buffers=%d", __FUNCTION__, pipe->fd,
count, numBuffers);
buff = buffers;
while (count > 0) {
int avail = buff->size - buffStart;
#ifdef _WIN32
int len = recv_all(pipe->fd, buff->data + buffStart, avail, true);
#else
int len = opengl_recv_all(pipe->fd, buff->data + buffStart, avail, true);
#endif
/* 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) {
DD("%s: fd=%d EOS", __FUNCTION__, pipe->fd);
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 {
DD("%s: fd=%d I/O error [%s]", __FUNCTION__, pipe->fd,
strerror(errno));
ret = PIPE_ERROR_IO;
}
break;
}
if (count > 0) {
pipe->wakeActual &= ~PIPE_WAKE_READ;
}
return ret;
}
static unsigned net_pipe_poll(void *opaque) {
NetPipe* pipe = opaque;
unsigned mask = pipe->wakeActual;
unsigned ret = 0;
if (mask & PIPE_WAKE_READ)
ret |= PIPE_POLL_IN;
if (mask & PIPE_WAKE_WRITE)
ret |= PIPE_POLL_OUT;
DD("%s: fd=%d result=%d", __FUNCTION__, pipe->fd, ret);
return ret;
}
static void net_pipe_wake_on(void* opaque, int flags) {
NetPipe *pipe = opaque;
DD("%s: fd=%d flags=%d", __FUNCTION__, pipe->fd, flags);
pipe->wakeWanted |= flags;
net_pipe_reset_state(pipe);
}
static void* net_pipe_init_tcp(void* hwpipe, void* _looper, const char* args) {
if (args == NULL) {
D("%s: Missing address!", __FUNCTION__);
return NULL;
}
D("%s: Port is '%s'", __FUNCTION__, args);
/* Now, look at the port number */
uint16_t port = 0;
{
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;
}
char address[32];
snprintf(address, sizeof(address), ":%d", port);
void *ret = net_pipe_init_from_address(hwpipe, address);
return ret;
}
#ifndef _WIN32
static void* net_pipe_init_unix(void* hwpipe, void* _looper, const char* args) {
return net_pipe_init_from_address(hwpipe, args);
}
#endif
/**********************************************************************
**********************************************************************
*****
***** N E T W O R K P I P E M E S S A G E S
*****
*****/
static const AndroidPipeFuncs netPipeTcp_funcs = {
net_pipe_init_tcp,
net_pipe_close_from_guest,
net_pipe_send_buffers,
net_pipe_recv_buffers,
net_pipe_poll,
net_pipe_wake_on,
NULL, /* we can't save these */
NULL, /* we can't load these */
};
#ifndef _WIN32
static const AndroidPipeFuncs netPipeUnix_funcs = {
net_pipe_init_unix,
net_pipe_close_from_guest,
net_pipe_send_buffers,
net_pipe_recv_buffers,
net_pipe_poll,
net_pipe_wake_on,
NULL, /* we can't save these */
NULL, /* we can't load these */
};
#endif
static void*
openglesPipe_init( void* hwpipe, void* _looper, const char* args )
{
NetPipe *pipe;
char server_addr[PATH_MAX];
android_gles_server_path(server_addr, sizeof(server_addr));
#ifndef _WIN32
if (true) {
pipe = (NetPipe *)net_pipe_init_unix(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 *)net_pipe_init_tcp(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->fd);
// Adjust buffer sizes
#ifdef _WIN32
{
int sndbuf = 128 * 1024;
int len = sizeof(sndbuf);
if (setsockopt(pipe->fd, SOL_SOCKET, SO_SNDBUF,
(char*)&sndbuf, len) == SOCKET_ERROR) {
D("Failed to set SO_SNDBUF to %d error=0x%x\n",
sndbuf, WSAGetLastError());
}
}
#else
{
int sndbuf = 128 * 8192;
socklen_t len = sizeof(sndbuf);
if (setsockopt(pipe->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, len) == -1) {
D("opengles pipe: failed to set SO_SNDBUF to %d error=0x%x\n",sndbuf, errno);
exit(1);
}
}
#endif /* _WIN32 */
}
return pipe;
}
static const AndroidPipeFuncs openglesPipe_funcs = {
openglesPipe_init,
net_pipe_close_from_guest,
net_pipe_send_buffers,
net_pipe_recv_buffers,
net_pipe_poll,
net_pipe_wake_on,
NULL, /* we can't save these */
NULL, /* we can't load these */
};
void android_net_pipes_init(void) {
android_pipe_add_type("opengles", NULL, &openglesPipe_funcs);
}