diff options
Diffstat (limited to 'charpipe.c')
-rw-r--r-- | charpipe.c | 196 |
1 files changed, 196 insertions, 0 deletions
@@ -253,12 +253,202 @@ qemu_chr_open_charpipe( CharDriverState* *pfirst, CharDriverState* *psecond ) 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; @@ -270,4 +460,10 @@ charpipe_poll( void ) if (half->peer != NULL) charpipehalf_poll(half); } + + /* poll the charbuffers */ + for ( ; cb < cb_end; cb++ ) { + if (cb->endpoint != NULL) + charbuffer_poll(cb); + } } |