/* 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 "android_qemud.h" #include "android_debug.h" #include "charpipe.h" #include "cbuffer.h" #include "android_utils.h" #define D(...) VERBOSE_PRINT(qemud,__VA_ARGS__) #define D_ACTIVE VERBOSE_CHECK(qemud) /* the T(...) macro is used to dump traffic */ #define T_ACTIVE 0 #if T_ACTIVE #define T(...) VERBOSE_PRINT(qemud,__VA_ARGS__) #else #define T(...) ((void)0) #endif #define MAX_PAYLOAD 4000 #define MAX_CHANNELS 8 #define CHANNEL_CONTROL_INDEX 0 /** packets **/ #define HEADER_SIZE 6 typedef struct Packet { struct Packet* next; int len; uint8_t header[HEADER_SIZE]; uint8_t data[MAX_PAYLOAD]; } Packet; static Packet* _free_packets; static void packet_free( Packet* p ) { p->next = _free_packets; _free_packets = p; } static Packet* packet_alloc( void ) { Packet* p = _free_packets; if (p != NULL) { _free_packets = p->next; } else { p = malloc(sizeof(*p)); if (p == NULL) { derror("%s: not enough memory", __FUNCTION__); exit(1); } } p->next = NULL; p->len = 0; return p; } /** channels **/ typedef void (*EnqueueFunc)( void* user, Packet* p ); typedef struct { const char* name; int index; CharDriverState* cs; EnqueueFunc enq_func; void* enq_user; } Channel; static int channel_can_read( void* opaque ) { Channel* c = opaque; return c->index < 0 ? 0 : MAX_PAYLOAD; } /* here, the data comes from the emulated device (e.g. GSM modem) through * a charpipe, we simply need to send it through the multiplexer */ static void channel_read( void* opaque, const uint8_t* from, int len ) { Channel* c = opaque; if (c->enq_func != NULL) { Packet* p = packet_alloc(); if (len > MAX_PAYLOAD) len = MAX_PAYLOAD; memcpy( p->data, from, len ); p->len = len + HEADER_SIZE; int2hex( p->header+0, 4, len ); int2hex( p->header+4, 2, c->index ); c->enq_func( c->enq_user, p ); } else { D("%s: discarding %d bytes for channel '%s'", __FUNCTION__, len, c->name); } } static void channel_init( Channel* c, const char* name, CharDriverState* peer_cs ) { c->name = name; c->index = -1; c->enq_func = NULL; c->enq_user = NULL; c->cs = peer_cs; } static void channel_set_peer( Channel* c, int index, EnqueueFunc enq_func, void* enq_user ) { c->index = index; qemu_chr_add_read_handler( c->cs, channel_can_read, channel_read, c ); c->enq_func = enq_func; c->enq_user = enq_user; } static int channel_write( Channel*c , const uint8_t* buf, int len ) { return qemu_chr_write( c->cs, buf, len ); } /** multiplexer **/ #define IN_BUFF_SIZE (2*MAX_PAYLOAD) typedef struct { CharDriverState* cs; CBuffer in_cbuffer[1]; int in_datalen; int in_channel; int count; Channel channels[MAX_CHANNELS]; uint8_t in_buff[ IN_BUFF_SIZE + HEADER_SIZE ]; } Multiplexer; /* called by channel_read when data comes from an emulated * device, and needs to be multiplexed through the serial * port */ static void multiplexer_enqueue( Multiplexer* m, Packet* p ) { T("%s: sending %d bytes: '%s'", __FUNCTION__, p->len - HEADER_SIZE, quote_bytes( p->data, p->len - HEADER_SIZE ) ); qemu_chr_write( m->cs, p->header, HEADER_SIZE ); qemu_chr_write( m->cs, p->data, p->len - HEADER_SIZE ); packet_free(p); } /* called when we received a channel registration from the * qemud daemon */ static void multiplexer_register_channel( Multiplexer* m, const char* name, int index ) { Channel* c = m->channels; Channel* c_end = c + m->count; for ( ; c < c_end; c++ ) { if ( !strcmp(c->name, name) ) break; } if (c >= c_end) { D( "%s: unknown channel name '%s'", __FUNCTION__, name ); return; } if (c->index >= 0) { D( "%s: channel '%s' re-assigned index %d", __FUNCTION__, name, index ); c->index = index; return; } channel_set_peer( c, index, (EnqueueFunc) multiplexer_enqueue, m ); D( "%s: channel '%s' registered as index %d", __FUNCTION__, c->name, c->index ); } /* handle answers from the control channel */ static void multiplexer_handle_control( Multiplexer* m, Packet* p ) { int len = p->len - HEADER_SIZE; /* for now, the only supported answer is 'ok:connect::' where * is a hexdecimal channel numner */ D( "%s: received '%s'", __FUNCTION__, quote_bytes( (const void*)p->data, (unsigned)len ) ); if ( !memcmp( p->data, "ok:connect:", 11 ) ) do { char* name = (char*)p->data + 11; char* q = strchr( name, ':' ); int index; if (q == NULL) break; q[0] = 0; if (q + 3 > (char*)p->data + len) break; index = hex2int( (uint8_t*)q+1, 2 ); if (index < 0) break; multiplexer_register_channel( m, name, index ); goto Exit; } while(0); D( "%s: unsupported message !!", __FUNCTION__ ); Exit: packet_free(p); } static int multiplexer_can_read( void* opaque ) { Multiplexer* m = opaque; return cbuffer_write_avail( m->in_cbuffer ); } /* the data comes from the serial port, we need to reconstruct packets then * dispatch them to the appropriate channel */ static void multiplexer_read( void* opaque, const uint8_t* from, int len ) { Multiplexer* m = opaque; CBuffer* cb = m->in_cbuffer; int ret = 0; T("%s: received %d bytes from serial: '%s'", __FUNCTION__, len, quote_bytes( from, len )); ret = cbuffer_write( cb, from, len ); if (ret == 0) return; for (;;) { int len = cbuffer_read_avail( cb ); if (m->in_datalen == 0) { uint8_t header[HEADER_SIZE]; if (len < HEADER_SIZE) break; cbuffer_read( cb, header, HEADER_SIZE ); m->in_datalen = hex2int( header+0, 4 ); m->in_channel = hex2int( header+4, 2 ); } else { Packet* p; if (len < m->in_datalen) break; /* a full packet was received */ p = packet_alloc(); cbuffer_read( cb, p->data, m->in_datalen ); p->len = HEADER_SIZE + m->in_datalen; /* find the channel for this packet */ if (m->in_channel == CHANNEL_CONTROL_INDEX) multiplexer_handle_control( m, p ); else { Channel* c = m->channels; Channel* c_end = c + m->count; for ( ; c < c_end; c++ ) { if (c->index == m->in_channel) { channel_write( c, p->data, m->in_datalen ); break; } } packet_free(p); } m->in_datalen = 0; } } return; } static void multiplexer_query_channel( Multiplexer* m, const char* name ) { Packet* p = packet_alloc(); int len; len = snprintf( (char*)p->data, MAX_PAYLOAD, "connect:%s", name ); int2hex( p->header+0, 4, len ); int2hex( p->header+4, 2, CHANNEL_CONTROL_INDEX ); p->len = HEADER_SIZE + len; multiplexer_enqueue( m, p ); } static Channel* multiplexer_find_channel( Multiplexer* m, const char* name ) { int n; for (n = 0; n < m->count; n++) if ( !strcmp(m->channels[n].name, name) ) return m->channels + n; return NULL; } static Multiplexer _multiplexer[1]; static CharDriverState* android_qemud_cs; extern void android_qemud_init( void ) { Multiplexer* m = _multiplexer; if (android_qemud_cs != NULL) return; m->count = 0; cbuffer_reset( m->in_cbuffer, m->in_buff, sizeof(m->in_buff) ); m->in_datalen = 0; m->in_channel = 0; if (qemu_chr_open_charpipe( &android_qemud_cs, &m->cs ) < 0) { derror( "%s: can't create charpipe to serial port", __FUNCTION__ ); exit(1); } qemu_chr_add_read_handler( m->cs, multiplexer_can_read, multiplexer_read, m ); } CharDriverState* android_qemud_get_cs( void ) { if (android_qemud_cs == NULL) android_qemud_init(); return android_qemud_cs; } extern int android_qemud_get_channel( const char* name, CharDriverState** pcs ) { Multiplexer* m = _multiplexer; Channel* c; CharDriverState* peer_cs; int ret; if (m->cs == NULL) android_qemud_init(); c = multiplexer_find_channel( m, name ); if (c) { derror( "%s: trying to get already-opened qemud channel '%s'", __FUNCTION__, name ); return -1; } if (m->count >= MAX_CHANNELS) { derror( "%s: too many registered channels (%d)", __FUNCTION__, m->count ); return -1; } c = m->channels + m->count; ret = qemu_chr_open_charpipe( &peer_cs, pcs ); if (ret == 0) { channel_init(c, name, peer_cs); m->count += 1; multiplexer_query_channel( m, c->name ); } return ret; } extern int android_qemud_set_channel( const char* name, CharDriverState* peer_cs ) { Multiplexer* m = _multiplexer; Channel* c; int ret; if (m->cs == NULL) android_qemud_init(); c = multiplexer_find_channel(m, name); if (c != NULL) { derror( "%s: trying to set opened qemud channel '%s'", __FUNCTION__, name ); return -1; } if (m->count >= MAX_CHANNELS) { derror( "%s: too many registered channels (%d)", __FUNCTION__, m->count ); return -1; } c = m->channels + m->count; channel_init(c, name, peer_cs); m->count += 1; multiplexer_query_channel( m, c->name ); return ret; }