aboutsummaryrefslogtreecommitdiffstats
path: root/telephony/sysdeps_posix.c
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit55f4e4a5ec657a017e3bf75299ad71fd1c968dd3 (patch)
tree550ce922ea0e125ac6a9738210ce2939bf2fe901 /telephony/sysdeps_posix.c
parent413f05aaf54fa08c0ae7e997327a4f4a473c0a8d (diff)
downloadexternal_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.zip
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.gz
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.bz2
Initial Contribution
Diffstat (limited to 'telephony/sysdeps_posix.c')
-rw-r--r--telephony/sysdeps_posix.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/telephony/sysdeps_posix.c b/telephony/sysdeps_posix.c
new file mode 100644
index 0000000..8c5eb12
--- /dev/null
+++ b/telephony/sysdeps_posix.c
@@ -0,0 +1,645 @@
+/* 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 "sysdeps.h"
+#include <assert.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <memory.h>
+#include <stdio.h>
+#ifndef HAVE_WINSOCK
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#endif
+
+/** QUEUE
+ **/
+#define SYS_MAX_QUEUE 16
+
+typedef struct {
+ int start;
+ int end;
+ void* pending[ SYS_MAX_QUEUE ];
+}
+SysQueueRec, *SysQueue;
+
+static void
+sys_queue_reset( SysQueue queue )
+{
+ queue->start = queue->end = 0;
+}
+
+static void
+sys_queue_add( SysQueue queue, void* item )
+{
+ assert( queue->end - queue->start < SYS_MAX_QUEUE );
+ assert( queue->start == 0 );
+ assert( item != NULL );
+ queue->pending[ queue->end++ ] = item;
+}
+
+#if 0
+static void
+sys_queue_remove( SysQueue queue, void* item )
+{
+ int nn, count;
+ assert( queue->end > queue->start );
+ assert( item != NULL );
+ count = queue->end - queue->start;
+ for ( nn = queue->start; count > 0; ++nn, --count ) {
+ if ( queue->pending[nn] == item ) {
+ queue->pending[nn] = queue->pending[nn+count-1];
+ queue->end -= 1;
+ break;
+ }
+ }
+ assert( 0 && "sys_queue_remove: item not found" );
+}
+#endif
+
+static void*
+sys_queue_get( SysQueue queue )
+{
+ if (queue->end > queue->start) {
+ return queue->pending[ queue->start++ ];
+ }
+ return NULL;
+}
+
+/** CHANNELS
+ **/
+typedef struct SysChannelRec_ {
+ SysChannel next;
+ int fd;
+ char active;
+ char pending;
+ char closed;
+ int wanted;
+ int ready;
+ SysChannelCallback callback;
+ void* opaque;
+} SysChannelRec;
+
+
+/*** channel allocation ***/
+#define SYS_EVENT_MAX 3
+#define SYS_MAX_CHANNELS 16
+
+static SysChannelRec _s_channels0[ SYS_MAX_CHANNELS ];
+static SysChannel _s_free_channels;
+
+static SysChannel
+sys_channel_alloc( void )
+{
+ SysChannel channel = _s_free_channels;
+ assert( channel != NULL && "out of free channels" );
+ _s_free_channels = channel->next;
+ channel->next = NULL;
+ channel->active = 0;
+ channel->closed = 0;
+ channel->pending = 0;
+ channel->wanted = 0;
+ return channel;
+}
+
+static void
+sys_channel_free( SysChannel channel )
+{
+ if (channel->fd >= 0) {
+#ifdef _WIN32
+ shutdown( channel->fd, SD_BOTH );
+#else
+ shutdown( channel->fd, SHUT_RDWR );
+#endif
+ close(channel->fd);
+ channel->fd = -1;
+ }
+ channel->wanted = 0;
+ channel->ready = 0;
+ channel->callback = NULL;
+
+ channel->next = _s_free_channels;
+ _s_free_channels = channel;
+}
+
+
+/* list of active channels */
+static SysChannel _s_channels;
+
+/* used by select to wait on channel events */
+static fd_set _s_fdsets[SYS_EVENT_MAX];
+static int _s_maxfd;
+
+static void
+sys_channel_deactivate( SysChannel channel )
+{
+ assert( channel->active != 0 );
+ SysChannel *pnode = &_s_channels;
+ for (;;) {
+ SysChannel node = *pnode;
+ assert( node != NULL );
+ if (node == channel)
+ break;
+ pnode = &node->next;
+ }
+ *pnode = channel->next;
+ channel->next = NULL;
+ channel->active = 0;
+}
+
+static void
+sys_channel_activate( SysChannel channel )
+{
+ assert( channel->active == 0 );
+ channel->next = _s_channels;
+ _s_channels = channel;
+ channel->active = 1;
+ if (channel->fd > _s_maxfd)
+ _s_maxfd = channel->fd;
+}
+
+
+/* queue of pending channels */
+static SysQueueRec _s_pending_channels[1];
+
+
+static void
+sys_init_channels( void )
+{
+ int nn;
+
+ for (nn = 0; nn < SYS_MAX_CHANNELS-1; nn++)
+ _s_channels0[nn].next = &_s_channels0[nn+1];
+ _s_free_channels = &_s_channels0[0];
+
+ for (nn = 0; nn < SYS_EVENT_MAX; nn++)
+ FD_ZERO( &_s_fdsets[nn] );
+
+ _s_maxfd = -1;
+
+ sys_queue_reset( _s_pending_channels );
+}
+
+
+void
+sys_channel_on( SysChannel channel,
+ int events,
+ SysChannelCallback callback,
+ void* opaque )
+{
+ int adds = events & ~channel->wanted;
+ int removes = channel->wanted & ~events;
+
+ channel->wanted = events;
+ channel->callback = callback;
+ channel->opaque = opaque;
+
+ /* update global fdsets */
+ if (adds) {
+ int ee;
+ for (ee = 0; ee < SYS_EVENT_MAX; ee++)
+ if (adds & (1 << ee))
+ FD_SET( channel->fd, &_s_fdsets[ee] );
+ }
+ if (removes) {
+ int ee;
+ for (ee = 0; ee < SYS_EVENT_MAX; ee++)
+ if (removes & (1 << ee))
+ FD_CLR( channel->fd, &_s_fdsets[ee] );
+ }
+ if (events && !channel->active) {
+ sys_channel_activate( channel );
+ }
+ else if (!events && channel->active) {
+ sys_channel_deactivate( channel );
+ }
+}
+
+int
+sys_channel_read( SysChannel channel, void* buffer, int size )
+{
+ char* buff = buffer;
+ int count = 0;
+
+ assert( !channel->closed );
+
+ while (size > 0) {
+ int len = read(channel->fd, buff, size);
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ if (count == 0)
+ count = -1;
+ break;
+ }
+ buff += len;
+ size -= len;
+ count += len;
+ }
+ return count;
+}
+
+
+int
+sys_channel_write( SysChannel channel, const void* buffer, int size )
+{
+ const char* buff = buffer;
+ int count = 0;
+
+ assert( !channel->closed );
+
+ while (size > 0) {
+ int len = write(channel->fd, buff, size);
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ if (count == 0)
+ count = -1;
+ break;
+ }
+ buff += len;
+ size -= len;
+ count += len;
+ }
+ return count;
+}
+
+
+void
+sys_channel_close( SysChannel channel )
+{
+ if (channel->active) {
+ sys_channel_on( channel, 0, NULL, NULL );
+ }
+
+ if (channel->pending) {
+ /* we can't free the channel right now because it */
+ /* is in the pending list, set a flag */
+ channel->closed = 1;
+ return;
+ }
+
+ if (!channel->closed) {
+ channel->closed = 1;
+ }
+
+ sys_channel_free( channel );
+}
+
+/** time measurement
+ **/
+SysTime sys_time_ms( void )
+{
+ struct timeval tv;
+ gettimeofday( &tv, NULL );
+ return (SysTime)(tv.tv_usec / 1000) + (SysTime)tv.tv_sec * 1000;
+}
+
+/** timers
+ **/
+typedef struct SysTimerRec_
+{
+ SysTimer next;
+ SysTime when;
+ SysCallback callback;
+ void* opaque;
+} SysTimerRec;
+
+#define SYS_MAX_TIMERS 16
+
+static SysTimerRec _s_timers0[ SYS_MAX_TIMERS ];
+static SysTimer _s_free_timers;
+static SysTimer _s_timers;
+
+static SysQueueRec _s_pending_timers[1];
+
+
+static void
+sys_init_timers( void )
+{
+ int nn;
+ for (nn = 0; nn < SYS_MAX_TIMERS-1; nn++) {
+ _s_timers0[nn].next = & _s_timers0[nn+1];
+ }
+ _s_free_timers = &_s_timers0[0];
+
+ sys_queue_reset( _s_pending_timers );
+}
+
+
+SysTimer sys_timer_create( void )
+{
+ SysTimer timer = _s_free_timers;
+ assert( timer != NULL && "too many timers allocated" );
+ _s_free_timers = timer->next;
+ timer->next = NULL;
+ return timer;
+}
+
+
+void sys_timer_unset( SysTimer timer )
+{
+ if (timer->callback != NULL) {
+ SysTimer *pnode, node;
+ pnode = &_s_timers;
+ for (;;) {
+ node = *pnode;
+ if (node == NULL)
+ break;
+ if (node == timer) {
+ *pnode = node->next;
+ break;
+ }
+ pnode = &node->next;
+ }
+ timer->next = NULL;
+ timer->callback = NULL;
+ timer->opaque = NULL;
+ }
+}
+
+
+void sys_timer_set( SysTimer timer,
+ SysTime when,
+ SysCallback callback,
+ void* opaque )
+{
+ if (timer->callback != NULL)
+ sys_timer_unset(timer);
+
+ if (callback != NULL) {
+ SysTime now = sys_time_ms();
+
+ if (now >= when) {
+ callback( opaque );
+ } else {
+ SysTimer *pnode, node;
+ pnode = &_s_timers;
+ for (;;) {
+ node = *pnode;
+ if (node == NULL || node->when >= when) {
+ break;
+ }
+ pnode = &node->next;
+ }
+ timer->next = *pnode;
+ *pnode = timer;
+ timer->when = when;
+ timer->callback = callback;
+ timer->opaque = opaque;
+ }
+ }
+}
+
+
+void sys_timer_destroy( SysTimer timer )
+{
+ assert( timer != NULL && "sys_timer_destroy: bad argument" );
+ if (timer->callback != NULL)
+ sys_timer_unset(timer);
+
+ timer->next = _s_free_timers;
+ _s_free_timers = timer;
+}
+
+
+static void
+sys_single_loop( void )
+{
+ fd_set rfd, wfd, efd;
+ struct timeval timeout_tv, *timeout = NULL;
+ int n;
+
+ memcpy(&rfd, &_s_fdsets[0], sizeof(fd_set));
+ memcpy(&wfd, &_s_fdsets[1], sizeof(fd_set));
+ memcpy(&efd, &_s_fdsets[2], sizeof(fd_set));
+
+ if ( _s_timers != NULL ) {
+ SysTime now = sys_time_ms();
+ SysTimer first = _s_timers;
+
+ timeout = &timeout_tv;
+ if (first->when <= now) {
+ timeout->tv_sec = 0;
+ timeout->tv_usec = 0;
+ } else {
+ SysTime diff = first->when - now;
+ timeout->tv_sec = diff / 1000;
+ timeout->tv_usec = (diff - timeout->tv_sec*1000) * 1000;
+ }
+ }
+
+ n = select( _s_maxfd+1, &rfd, &wfd, &efd, timeout);
+ if(n < 0) {
+ if(errno == EINTR) return;
+ perror("select");
+ return;
+ }
+
+ /* enqueue pending channels */
+ {
+ int i;
+
+ sys_queue_reset( _s_pending_channels );
+ for(i = 0; (i <= _s_maxfd) && (n > 0); i++)
+ {
+ int events = 0;
+
+ if(FD_ISSET(i, &rfd)) events |= SYS_EVENT_READ;
+ if(FD_ISSET(i, &wfd)) events |= SYS_EVENT_WRITE;
+ if(FD_ISSET(i, &efd)) events |= SYS_EVENT_ERROR;
+
+ if (events) {
+ SysChannel channel;
+
+ n--;
+ for (channel = _s_channels; channel; channel = channel->next)
+ {
+ if (channel->fd != i)
+ continue;
+
+ channel->ready = events;
+ channel->pending = 1;
+ sys_queue_add( _s_pending_channels, channel );
+ break;
+ }
+ }
+ }
+ }
+
+ /* enqueue pending timers */
+ {
+ SysTimer timer = _s_timers;
+ SysTime now = sys_time_ms();
+
+ sys_queue_reset( _s_pending_timers );
+ while (timer != NULL)
+ {
+ if (timer->when > now)
+ break;
+
+ sys_queue_add( _s_pending_timers, timer );
+ _s_timers = timer = timer->next;
+ }
+ }
+}
+
+void sys_main_init( void )
+{
+ sys_init_channels();
+ sys_init_timers();
+}
+
+
+int sys_main_loop( void )
+{
+ for (;;) {
+ SysTimer timer;
+ SysChannel channel;
+
+ /* exit if we have nothing to do */
+ if (_s_channels == NULL && _s_timers == NULL)
+ break;
+
+ sys_single_loop();
+
+ while ((timer = sys_queue_get( _s_pending_timers )) != NULL) {
+ timer->callback( timer->opaque );
+ }
+
+ while ((channel = sys_queue_get( _s_pending_channels )) != NULL) {
+ int events;
+
+ channel->pending = 0;
+ if (channel->closed) {
+ /* the channel was closed by a previous callback */
+ sys_channel_close(channel);
+ }
+ events = channel->ready;
+ channel->ready = 0;
+ channel->callback( channel->opaque, events );
+ }
+ }
+ return 0;
+}
+
+
+
+
+SysChannel
+sys_channel_create_tcp_server( int port )
+{
+ SysChannel channel;
+ int on = 1;
+ const int BACKLOG = 4;
+
+ channel = sys_channel_alloc();
+ if (-1==(channel->fd=socket(AF_INET, SOCK_STREAM, 0))) {
+ perror("socket");
+ sys_channel_free( channel );
+ return NULL;
+ }
+
+ /* Enable address re-use for server mode */
+ if ( -1==setsockopt( channel->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) )) {
+ perror("setsockopt(SO_REUSEADDR)");
+ }
+
+ {
+ struct sockaddr_in servname;
+ long in_addr = INADDR_ANY;
+
+ servname.sin_family = AF_INET;
+ servname.sin_port = htons(port);
+
+ servname.sin_addr.s_addr=in_addr;
+
+ if (-1==bind(channel->fd, (struct sockaddr*)&servname, sizeof(servname))) {
+ perror("bind");
+ sys_channel_close(channel);
+ return NULL;
+ }
+
+ /* Listen but don't accept */
+ if ( listen(channel->fd, BACKLOG) < 0 ) {
+ perror("listen");
+ sys_channel_close(channel);
+ return NULL;
+ }
+ }
+ return channel;
+}
+
+
+SysChannel
+sys_channel_create_tcp_handler( SysChannel server_channel )
+{
+ int on = 1;
+ SysChannel channel = sys_channel_alloc();
+
+ channel->fd = accept( server_channel->fd, NULL, 0 );
+ if (channel->fd < 0) {
+ perror( "accept" );
+ sys_channel_free( channel );
+ return NULL;
+ }
+
+ /* set to non-blocking and disable TCP Nagle algorithm */
+ fcntl(channel->fd, F_SETFL, O_NONBLOCK);
+ setsockopt(channel->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+ return channel;
+}
+
+
+SysChannel
+sys_channel_create_tcp_client( const char* hostname, int port )
+{
+ struct hostent* hp;
+ struct sockaddr_in addr;
+ SysChannel channel = sys_channel_alloc();
+ int on = 1;
+
+ hp = gethostbyname(hostname);
+ if(hp == 0) {
+ fprintf(stderr, "unknown host: %s\n", hostname);
+ sys_channel_free(channel);
+ return NULL;
+ };
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+ channel->fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+ if(channel->fd < 0) {
+ sys_channel_free(channel);
+ return NULL;
+ }
+
+ if(connect( channel->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror( "connect" );
+ sys_channel_free(channel);
+ return NULL;
+ }
+
+ /* set to non-blocking and disable Nagle algorithm */
+ fcntl(channel->fd, F_SETFL, O_NONBLOCK);
+ setsockopt( channel->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on) );
+ return channel;
+}
+