/* 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 "qemu-char.h" #include "cbuffer.h" #include "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); } }