blob: 79ab2f25a9339daf28a8a9d9c08a8789d419a12d [file] [log] [blame]
/* 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 "sysemu/char.h"
#include "android/cbuffer.h"
#include "android/qemu-debug.h"
#define xxDEBUG
#ifdef DEBUG
# include <stdio.h>
# define D(...) ( fprintf( stderr, __VA_ARGS__ ), fprintf(stderr, "\n") )
#else
# define D(...) ((void)0)
#endif
/* we want to implement a bi-directionnal communication channel
* between two QEMU character drivers that merge well into the
* QEMU event loop.
*
* each half of the channel has its own object and buffer, and
* we implement communication through charpipe_poll() which
* must be called by the main event loop after its call to select()
*
*/
#define BIP_BUFFER_SIZE 512
typedef struct BipBuffer {
struct BipBuffer* next;
CBuffer cb[1];
char buff[ BIP_BUFFER_SIZE ];
} BipBuffer;
static BipBuffer* _free_bip_buffers;
static BipBuffer*
bip_buffer_alloc( void )
{
BipBuffer* bip = _free_bip_buffers;
if (bip != NULL) {
_free_bip_buffers = bip->next;
} else {
bip = malloc( sizeof(*bip) );
if (bip == NULL) {
derror( "%s: not enough memory", __FUNCTION__ );
exit(1);
}
}
bip->next = NULL;
cbuffer_reset( bip->cb, bip->buff, sizeof(bip->buff) );
return bip;
}
static void
bip_buffer_free( BipBuffer* bip )
{
bip->next = _free_bip_buffers;
_free_bip_buffers = bip;
}
/* this models each half of the charpipe */
typedef struct CharPipeHalf {
CharDriverState cs[1];
BipBuffer* bip_first;
BipBuffer* bip_last;
struct CharPipeHalf* peer; /* NULL if closed */
} CharPipeHalf;
static void
charpipehalf_close( CharDriverState* cs )
{
CharPipeHalf* ph = cs->opaque;
while (ph->bip_first) {
BipBuffer* bip = ph->bip_first;
ph->bip_first = bip->next;
bip_buffer_free(bip);
}
ph->bip_last = NULL;
ph->peer = NULL;
}
static int
charpipehalf_write( CharDriverState* cs, const uint8_t* buf, int len )
{
CharPipeHalf* ph = cs->opaque;
CharPipeHalf* peer = ph->peer;
BipBuffer* bip = ph->bip_last;
int ret = 0;
D("%s: writing %d bytes to %p: '%s'", __FUNCTION__,
len, ph, quote_bytes( buf, len ));
if (bip == NULL && peer != NULL && peer->cs->chr_read != NULL) {
/* no buffered data, try to write directly to the peer */
while (len > 0) {
int size;
if (peer->cs->chr_can_read) {
size = qemu_chr_can_read( peer->cs );
if (size == 0)
break;
if (size > len)
size = len;
} else
size = len;
qemu_chr_read( peer->cs, (uint8_t*)buf, size );
buf += size;
len -= size;
ret += size;
}
}
if (len == 0)
return ret;
/* buffer the remaining data */
if (bip == NULL) {
bip = bip_buffer_alloc();
ph->bip_first = ph->bip_last = bip;
}
while (len > 0) {
int len2 = cbuffer_write( bip->cb, buf, len );
buf += len2;
ret += len2;
len -= len2;
if (len == 0)
break;
/* ok, we need another buffer */
ph->bip_last = bip_buffer_alloc();
bip->next = ph->bip_last;
bip = ph->bip_last;
}
return ret;
}
static void
charpipehalf_poll( CharPipeHalf* ph )
{
CharPipeHalf* peer = ph->peer;
int size;
if (peer == NULL || peer->cs->chr_read == NULL)
return;
while (1) {
BipBuffer* bip = ph->bip_first;
uint8_t* base;
int avail;
if (bip == NULL)
break;
size = cbuffer_read_avail(bip->cb);
if (size == 0) {
ph->bip_first = bip->next;
if (ph->bip_first == NULL)
ph->bip_last = NULL;
bip_buffer_free(bip);
continue;
}
if (ph->cs->chr_can_read) {
int size2 = qemu_chr_can_read(peer->cs);
if (size2 == 0)
break;
if (size > size2)
size = size2;
}
avail = cbuffer_read_peek( bip->cb, &base );
if (avail > size)
avail = size;
D("%s: sending %d bytes from %p: '%s'", __FUNCTION__,
avail, ph, quote_bytes( base, avail ));
qemu_chr_read( peer->cs, base, avail );
cbuffer_read_step( bip->cb, avail );
}
}
static void
charpipehalf_init( CharPipeHalf* ph, CharPipeHalf* peer )
{
CharDriverState* cs = ph->cs;
ph->bip_first = NULL;
ph->bip_last = NULL;
ph->peer = peer;
cs->chr_write = charpipehalf_write;
cs->chr_ioctl = NULL;
cs->chr_send_event = NULL;
cs->chr_close = charpipehalf_close;
cs->opaque = ph;
}
typedef struct CharPipeState {
CharPipeHalf a[1];
CharPipeHalf b[1];
} CharPipeState;
#define MAX_CHAR_PIPES 8
static CharPipeState _s_charpipes[ MAX_CHAR_PIPES ];
int
qemu_chr_open_charpipe( CharDriverState* *pfirst, CharDriverState* *psecond )
{
CharPipeState* cp = _s_charpipes;
CharPipeState* cp_end = cp + MAX_CHAR_PIPES;
for ( ; cp < cp_end; cp++ ) {
if ( cp->a->peer == NULL && cp->b->peer == NULL )
break;
}
if (cp == cp_end) { /* can't allocate one */
*pfirst = NULL;
*psecond = NULL;
return -1;
}
charpipehalf_init( cp->a, cp->b );
charpipehalf_init( cp->b, cp->a );
*pfirst = cp->a->cs;
*psecond = cp->b->cs;
return 0;
}
/** This models a charbuffer, an object used to buffer
** the data that is sent to a given endpoint CharDriverState
** object.
**
** On the other hand, any can_read() / read() request performed
** by the endpoint will be passed to the CharBuffer's corresponding
** handlers.
**/
typedef struct CharBuffer {
CharDriverState cs[1];
BipBuffer* bip_first;
BipBuffer* bip_last;
CharDriverState* endpoint; /* NULL if closed */
char closing;
} CharBuffer;
static void
charbuffer_close( CharDriverState* cs )
{
CharBuffer* cbuf = cs->opaque;
while (cbuf->bip_first) {
BipBuffer* bip = cbuf->bip_first;
cbuf->bip_first = bip->next;
bip_buffer_free(bip);
}
cbuf->bip_last = NULL;
cbuf->endpoint = NULL;
if (cbuf->endpoint != NULL) {
qemu_chr_close(cbuf->endpoint);
cbuf->endpoint = NULL;
}
}
static int
charbuffer_write( CharDriverState* cs, const uint8_t* buf, int len )
{
CharBuffer* cbuf = cs->opaque;
CharDriverState* peer = cbuf->endpoint;
BipBuffer* bip = cbuf->bip_last;
int ret = 0;
D("%s: writing %d bytes to %p: '%s'", __FUNCTION__,
len, cbuf, quote_bytes( buf, len ));
if (bip == NULL && peer != NULL) {
/* no buffered data, try to write directly to the peer */
int size = qemu_chr_write(peer, buf, len);
if (size < 0) /* just to be safe */
size = 0;
else if (size > len)
size = len;
buf += size;
ret += size;
len -= size;
}
if (len == 0)
return ret;
/* buffer the remaining data */
if (bip == NULL) {
bip = bip_buffer_alloc();
cbuf->bip_first = cbuf->bip_last = bip;
}
while (len > 0) {
int len2 = cbuffer_write( bip->cb, buf, len );
buf += len2;
ret += len2;
len -= len2;
if (len == 0)
break;
/* ok, we need another buffer */
cbuf->bip_last = bip_buffer_alloc();
bip->next = cbuf->bip_last;
bip = cbuf->bip_last;
}
return ret;
}
static void
charbuffer_poll( CharBuffer* cbuf )
{
CharDriverState* peer = cbuf->endpoint;
if (peer == NULL)
return;
while (1) {
BipBuffer* bip = cbuf->bip_first;
uint8_t* base;
int avail;
int size;
if (bip == NULL)
break;
avail = cbuffer_read_peek( bip->cb, &base );
if (avail == 0) {
cbuf->bip_first = bip->next;
if (cbuf->bip_first == NULL)
cbuf->bip_last = NULL;
bip_buffer_free(bip);
continue;
}
size = qemu_chr_write( peer, base, avail );
if (size < 0) /* just to be safe */
size = 0;
else if (size > avail)
size = avail;
cbuffer_read_step( bip->cb, size );
if (size < avail)
break;
}
}
static void
charbuffer_update_handlers( CharDriverState* cs )
{
CharBuffer* cbuf = cs->opaque;
qemu_chr_add_handlers( cbuf->endpoint,
cs->chr_can_read,
cs->chr_read,
cs->chr_event,
cs->handler_opaque );
}
static void
charbuffer_init( CharBuffer* cbuf, CharDriverState* endpoint )
{
CharDriverState* cs = cbuf->cs;
cbuf->bip_first = NULL;
cbuf->bip_last = NULL;
cbuf->endpoint = endpoint;
cs->chr_write = charbuffer_write;
cs->chr_ioctl = NULL;
cs->chr_send_event = NULL;
cs->chr_close = charbuffer_close;
cs->chr_update_read_handler = charbuffer_update_handlers;
cs->opaque = cbuf;
}
#define MAX_CHAR_BUFFERS 8
static CharBuffer _s_charbuffers[ MAX_CHAR_BUFFERS ];
CharDriverState*
qemu_chr_open_buffer( CharDriverState* endpoint )
{
CharBuffer* cbuf = _s_charbuffers;
CharBuffer* cbuf_end = cbuf + MAX_CHAR_BUFFERS;
if (endpoint == NULL)
return NULL;
for ( ; cbuf < cbuf_end; cbuf++ ) {
if (cbuf->endpoint == NULL)
break;
}
if (cbuf == cbuf_end)
return NULL;
charbuffer_init(cbuf, endpoint);
return cbuf->cs;
}
void
charpipe_poll( void )
{
CharPipeState* cp = _s_charpipes;
CharPipeState* cp_end = cp + MAX_CHAR_PIPES;
CharBuffer* cb = _s_charbuffers;
CharBuffer* cb_end = cb + MAX_CHAR_BUFFERS;
/* poll the charpipes */
for ( ; cp < cp_end; cp++ ) {
CharPipeHalf* half;
half = cp->a;
if (half->peer != NULL)
charpipehalf_poll(half);
half = cp->b;
if (half->peer != NULL)
charpipehalf_poll(half);
}
/* poll the charbuffers */
for ( ; cb < cb_end; cb++ ) {
if (cb->endpoint != NULL)
charbuffer_poll(cb);
}
}