/* Copyright (C) 2007-2008 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "sockets.h"
#include "sysdeps.h"
#include "qemu-common.h"
#include "qemu-timer.h"
#include "qemu-char.h"
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#endif
#define DEBUG 0
#define D_ACTIVE DEBUG
#if DEBUG
#define D(...) do { if (D_ACTIVE) fprintf(stderr, __VA_ARGS__); } while (0)
#else
#define D(...) ((void)0)
#endif
/** TIME
**/
SysTime
sys_time_ms( void )
{
return qemu_get_clock( rt_clock );
}
/** TIMERS
**/
typedef struct SysTimerRec_ {
QEMUTimer* timer;
QEMUTimerCB* callback;
void* opaque;
SysTimer next;
} SysTimerRec;
#define MAX_TIMERS 32
static SysTimerRec _s_timers0[ MAX_TIMERS ];
static SysTimer _s_free_timers;
static void
sys_init_timers( void )
{
int nn;
for (nn = 0; nn < MAX_TIMERS-1; nn++)
_s_timers0[nn].next = _s_timers0 + (nn+1);
_s_free_timers = _s_timers0;
}
static SysTimer
sys_timer_alloc( void )
{
SysTimer timer = _s_free_timers;
if (timer != NULL) {
_s_free_timers = timer->next;
timer->next = NULL;
timer->timer = NULL;
}
return timer;
}
static void
sys_timer_free( SysTimer timer )
{
if (timer->timer) {
qemu_del_timer( timer->timer );
qemu_free_timer( timer->timer );
timer->timer = NULL;
}
timer->next = _s_free_timers;
_s_free_timers = timer;
}
SysTimer sys_timer_create( void )
{
SysTimer timer = sys_timer_alloc();
return timer;
}
void
sys_timer_set( SysTimer timer, SysTime when, SysCallback _callback, void* opaque )
{
QEMUTimerCB* callback = (QEMUTimerCB*)_callback;
if (callback == NULL) { /* unsetting the timer */
if (timer->timer) {
qemu_del_timer( timer->timer );
qemu_free_timer( timer->timer );
timer->timer = NULL;
}
timer->callback = callback;
timer->opaque = NULL;
return;
}
if ( timer->timer ) {
if ( timer->callback == callback && timer->opaque == opaque )
goto ReuseTimer;
/* need to replace the timer */
qemu_free_timer( timer->timer );
}
timer->timer = qemu_new_timer( rt_clock, callback, opaque );
timer->callback = callback;
timer->opaque = opaque;
ReuseTimer:
qemu_mod_timer( timer->timer, when );
}
void
sys_timer_unset( SysTimer timer )
{
if (timer->timer) {
qemu_del_timer( timer->timer );
}
}
void
sys_timer_destroy( SysTimer timer )
{
sys_timer_free( timer );
}
/** CHANNELS
**/
typedef struct SysChannelRec_ {
int fd;
SysChannelCallback callback;
void* opaque;
SysChannel next;
} SysChannelRec;
#define MAX_CHANNELS 16
static SysChannelRec _s_channels0[ MAX_CHANNELS ];
static SysChannel _s_free_channels;
static void
sys_init_channels( void )
{
int nn;
for ( nn = 0; nn < MAX_CHANNELS-1; nn++ ) {
_s_channels0[nn].next = _s_channels0 + (nn+1);
}
_s_free_channels = _s_channels0;
}
static SysChannel
sys_channel_alloc( )
{
SysChannel channel = _s_free_channels;
if (channel != NULL) {
_s_free_channels = channel->next;
channel->next = NULL;
channel->fd = -1;
channel->callback = NULL;
channel->opaque = NULL;
}
return channel;
}
static void
sys_channel_free( SysChannel channel )
{
if (channel->fd >= 0) {
socket_close( channel->fd );
channel->fd = -1;
}
channel->next = _s_free_channels;
_s_free_channels = channel;
}
static void
sys_channel_read_handler( void* _channel )
{
SysChannel channel = _channel;
D( "%s: read event for channel %p:%d\n", __FUNCTION__,
channel, channel->fd );
channel->callback( channel->opaque, SYS_EVENT_READ );
}
static void
sys_channel_write_handler( void* _channel )
{
SysChannel channel = _channel;
D( "%s: write event for channel %p:%d\n", __FUNCTION__, channel, channel->fd );
channel->callback( channel->opaque, SYS_EVENT_WRITE );
}
void
sys_channel_on( SysChannel channel,
int events,
SysChannelCallback event_callback,
void* event_opaque )
{
IOHandler* read_handler = NULL;
IOHandler* write_handler = NULL;
if (events & SYS_EVENT_READ) {
read_handler = sys_channel_read_handler;
}
if (events & SYS_EVENT_WRITE) {
write_handler = sys_channel_write_handler;
}
channel->callback = event_callback;
channel->opaque = event_opaque;
qemu_set_fd_handler( channel->fd, read_handler, write_handler, channel );
}
int
sys_channel_read( SysChannel channel, void* buffer, int size )
{
int len = size;
char* buf = (char*) buffer;
while (len > 0) {
int ret = socket_recv(channel->fd, buf, len);
if (ret < 0) {
if (errno == EINTR)
continue;
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
D( "%s: after reading %d bytes, recv() returned error %d: %s\n",
__FUNCTION__, size - len, errno, errno_str);
return -1;
} else if (ret == 0) {
break;
} else {
buf += ret;
len -= ret;
}
}
return size - len;
}
int
sys_channel_write( SysChannel channel, const void* buffer, int size )
{
int len = size;
const char* buf = (const char*) buffer;
while (len > 0) {
int ret = socket_send(channel->fd, buf, len);
if (ret < 0) {
if (errno == EINTR)
continue;
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
D( "%s: send() returned error %d: %s\n",
__FUNCTION__, errno, errno_str);
return -1;
} else if (ret == 0) {
break;
} else {
buf += ret;
len -= ret;
}
}
return size - len;
}
void sys_channel_close( SysChannel channel )
{
qemu_set_fd_handler( channel->fd, NULL, NULL, NULL );
sys_channel_free( channel );
}
void sys_main_init( void )
{
sys_init_channels();
sys_init_timers();
}
int sys_main_loop( void )
{
/* no looping, qemu has its own event loop */
return 0;
}
SysChannel
sys_channel_create_tcp_server( int port )
{
SysChannel channel = sys_channel_alloc();
channel->fd = socket_anyaddr_server( port, SOCKET_STREAM );
if (channel->fd < 0) {
D( "%s: failed to created network socket on TCP:%d\n",
__FUNCTION__, port );
sys_channel_free( channel );
return NULL;
}
D( "%s: server channel %p:%d now listening on port %d\n",
__FUNCTION__, channel, channel->fd, port );
return channel;
}
SysChannel
sys_channel_create_tcp_handler( SysChannel server_channel )
{
SysChannel channel = sys_channel_alloc();
D( "%s: creating handler from server channel %p:%d\n", __FUNCTION__,
server_channel, server_channel->fd );
channel->fd = socket_accept_any( server_channel->fd );
if (channel->fd < 0) {
perror( "accept" );
sys_channel_free( channel );
return NULL;
}
/* disable Nagle algorithm */
socket_set_nodelay( channel->fd );
D( "%s: handler %p:%d created from server %p:%d\n", __FUNCTION__,
server_channel, server_channel->fd, channel, channel->fd );
return channel;
}
SysChannel
sys_channel_create_tcp_client( const char* hostname, int port )
{
SysChannel channel = sys_channel_alloc();
channel->fd = socket_network_client( hostname, port, SOCKET_STREAM );
if (channel->fd < 0) {
sys_channel_free(channel);
return NULL;
};
/* set to non-blocking and disable Nagle algorithm */
socket_set_nonblock( channel->fd );
socket_set_nodelay( channel->fd );
return channel;
}