char: move mux to its own file
A mechanical move, except that qemu_chr_write_all() needs to be declared in char.h header to be used from chardev unit files. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
		
							parent
							
								
									247c92af2b
								
							
						
					
					
						commit
						df85a78bf8
					
				@ -1,2 +1,3 @@
 | 
				
			|||||||
chardev-obj-y += char.o
 | 
					chardev-obj-y += char.o
 | 
				
			||||||
 | 
					chardev-obj-y += char-mux.o
 | 
				
			||||||
chardev-obj-y += char-null.o
 | 
					chardev-obj-y += char-null.o
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										358
									
								
								chardev/char-mux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								chardev/char-mux.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,358 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * QEMU System Emulator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (c) 2003-2008 Fabrice Bellard
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					 * all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					 * THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include "qemu/osdep.h"
 | 
				
			||||||
 | 
					#include "qapi/error.h"
 | 
				
			||||||
 | 
					#include "qemu-common.h"
 | 
				
			||||||
 | 
					#include "sysemu/char.h"
 | 
				
			||||||
 | 
					#include "sysemu/block-backend.h"
 | 
				
			||||||
 | 
					#include "char-mux.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* MUX driver for serial I/O splitting */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called with chr_write_lock held.  */
 | 
				
			||||||
 | 
					static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(chr);
 | 
				
			||||||
 | 
					    int ret;
 | 
				
			||||||
 | 
					    if (!d->timestamps) {
 | 
				
			||||||
 | 
					        ret = qemu_chr_fe_write(&d->chr, buf, len);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ret = 0;
 | 
				
			||||||
 | 
					        for (i = 0; i < len; i++) {
 | 
				
			||||||
 | 
					            if (d->linestart) {
 | 
				
			||||||
 | 
					                char buf1[64];
 | 
				
			||||||
 | 
					                int64_t ti;
 | 
				
			||||||
 | 
					                int secs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
 | 
				
			||||||
 | 
					                if (d->timestamps_start == -1) {
 | 
				
			||||||
 | 
					                    d->timestamps_start = ti;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ti -= d->timestamps_start;
 | 
				
			||||||
 | 
					                secs = ti / 1000;
 | 
				
			||||||
 | 
					                snprintf(buf1, sizeof(buf1),
 | 
				
			||||||
 | 
					                         "[%02d:%02d:%02d.%03d] ",
 | 
				
			||||||
 | 
					                         secs / 3600,
 | 
				
			||||||
 | 
					                         (secs / 60) % 60,
 | 
				
			||||||
 | 
					                         secs % 60,
 | 
				
			||||||
 | 
					                         (int)(ti % 1000));
 | 
				
			||||||
 | 
					                /* XXX this blocks entire thread. Rewrite to use
 | 
				
			||||||
 | 
					                 * qemu_chr_fe_write and background I/O callbacks */
 | 
				
			||||||
 | 
					                qemu_chr_fe_write_all(&d->chr,
 | 
				
			||||||
 | 
					                                      (uint8_t *)buf1, strlen(buf1));
 | 
				
			||||||
 | 
					                d->linestart = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
 | 
				
			||||||
 | 
					            if (buf[i] == '\n') {
 | 
				
			||||||
 | 
					                d->linestart = 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char * const mux_help[] = {
 | 
				
			||||||
 | 
					    "% h    print this help\n\r",
 | 
				
			||||||
 | 
					    "% x    exit emulator\n\r",
 | 
				
			||||||
 | 
					    "% s    save disk data back to file (if -snapshot)\n\r",
 | 
				
			||||||
 | 
					    "% t    toggle console timestamps\n\r",
 | 
				
			||||||
 | 
					    "% b    send break (magic sysrq)\n\r",
 | 
				
			||||||
 | 
					    "% c    switch between console and monitor\n\r",
 | 
				
			||||||
 | 
					    "% %  sends %\n\r",
 | 
				
			||||||
 | 
					    NULL
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int term_escape_char = 0x01; /* ctrl-a is used for escape */
 | 
				
			||||||
 | 
					static void mux_print_help(Chardev *chr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int i, j;
 | 
				
			||||||
 | 
					    char ebuf[15] = "Escape-Char";
 | 
				
			||||||
 | 
					    char cbuf[50] = "\n\r";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (term_escape_char > 0 && term_escape_char < 26) {
 | 
				
			||||||
 | 
					        snprintf(cbuf, sizeof(cbuf), "\n\r");
 | 
				
			||||||
 | 
					        snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        snprintf(cbuf, sizeof(cbuf),
 | 
				
			||||||
 | 
					                 "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
 | 
				
			||||||
 | 
					                 term_escape_char);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /* XXX this blocks entire thread. Rewrite to use
 | 
				
			||||||
 | 
					     * qemu_chr_fe_write and background I/O callbacks */
 | 
				
			||||||
 | 
					    qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
 | 
				
			||||||
 | 
					    for (i = 0; mux_help[i] != NULL; i++) {
 | 
				
			||||||
 | 
					        for (j = 0; mux_help[i][j] != '\0'; j++) {
 | 
				
			||||||
 | 
					            if (mux_help[i][j] == '%') {
 | 
				
			||||||
 | 
					                qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    CharBackend *be = d->backends[mux_nr];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (be && be->chr_event) {
 | 
				
			||||||
 | 
					        be->chr_event(be->opaque, event);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (d->term_got_escape) {
 | 
				
			||||||
 | 
					        d->term_got_escape = 0;
 | 
				
			||||||
 | 
					        if (ch == term_escape_char) {
 | 
				
			||||||
 | 
					            goto send_char;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        switch (ch) {
 | 
				
			||||||
 | 
					        case '?':
 | 
				
			||||||
 | 
					        case 'h':
 | 
				
			||||||
 | 
					            mux_print_help(chr);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 'x':
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                 const char *term =  "QEMU: Terminated\n\r";
 | 
				
			||||||
 | 
					                 qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
 | 
				
			||||||
 | 
					                 exit(0);
 | 
				
			||||||
 | 
					                 break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        case 's':
 | 
				
			||||||
 | 
					            blk_commit_all();
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 'b':
 | 
				
			||||||
 | 
					            qemu_chr_be_event(chr, CHR_EVENT_BREAK);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 'c':
 | 
				
			||||||
 | 
					            assert(d->mux_cnt > 0); /* handler registered with first fe */
 | 
				
			||||||
 | 
					            /* Switch to the next registered device */
 | 
				
			||||||
 | 
					            mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 't':
 | 
				
			||||||
 | 
					            d->timestamps = !d->timestamps;
 | 
				
			||||||
 | 
					            d->timestamps_start = -1;
 | 
				
			||||||
 | 
					            d->linestart = 0;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if (ch == term_escape_char) {
 | 
				
			||||||
 | 
					        d->term_got_escape = 1;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    send_char:
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mux_chr_accept_input(Chardev *chr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(chr);
 | 
				
			||||||
 | 
					    int m = d->focus;
 | 
				
			||||||
 | 
					    CharBackend *be = d->backends[m];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (be && d->prod[m] != d->cons[m] &&
 | 
				
			||||||
 | 
					           be->chr_can_read && be->chr_can_read(be->opaque)) {
 | 
				
			||||||
 | 
					        be->chr_read(be->opaque,
 | 
				
			||||||
 | 
					                     &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mux_chr_can_read(void *opaque)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(opaque);
 | 
				
			||||||
 | 
					    int m = d->focus;
 | 
				
			||||||
 | 
					    CharBackend *be = d->backends[m];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (be && be->chr_can_read) {
 | 
				
			||||||
 | 
					        return be->chr_can_read(be->opaque);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Chardev *chr = CHARDEV(opaque);
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(opaque);
 | 
				
			||||||
 | 
					    int m = d->focus;
 | 
				
			||||||
 | 
					    CharBackend *be = d->backends[m];
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mux_chr_accept_input(opaque);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i = 0; i < size; i++)
 | 
				
			||||||
 | 
					        if (mux_proc_byte(chr, d, buf[i])) {
 | 
				
			||||||
 | 
					            if (d->prod[m] == d->cons[m] &&
 | 
				
			||||||
 | 
					                be && be->chr_can_read &&
 | 
				
			||||||
 | 
					                be->chr_can_read(be->opaque)) {
 | 
				
			||||||
 | 
					                be->chr_read(be->opaque, &buf[i], 1);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool muxes_realized;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mux_chr_event(void *opaque, int event)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(opaque);
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!muxes_realized) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Send the event to all registered listeners */
 | 
				
			||||||
 | 
					    for (i = 0; i < d->mux_cnt; i++) {
 | 
				
			||||||
 | 
					        mux_chr_send_event(d, i, event);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(s);
 | 
				
			||||||
 | 
					    Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
 | 
				
			||||||
 | 
					    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!cc->chr_add_watch) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return cc->chr_add_watch(chr, cond);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void char_mux_finalize(Object *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(obj);
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i = 0; i < d->mux_cnt; i++) {
 | 
				
			||||||
 | 
					        CharBackend *be = d->backends[i];
 | 
				
			||||||
 | 
					        if (be) {
 | 
				
			||||||
 | 
					            be->chr = NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    qemu_chr_fe_deinit(&d->chr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(chr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Fix up the real driver with mux routines */
 | 
				
			||||||
 | 
					    qemu_chr_fe_set_handlers(&d->chr,
 | 
				
			||||||
 | 
					                             mux_chr_can_read,
 | 
				
			||||||
 | 
					                             mux_chr_read,
 | 
				
			||||||
 | 
					                             mux_chr_event,
 | 
				
			||||||
 | 
					                             chr,
 | 
				
			||||||
 | 
					                             context, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mux_set_focus(Chardev *chr, int focus)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(chr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(focus >= 0);
 | 
				
			||||||
 | 
					    assert(focus < d->mux_cnt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (d->focus != -1) {
 | 
				
			||||||
 | 
					        mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    d->focus = focus;
 | 
				
			||||||
 | 
					    mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void qemu_chr_open_mux(Chardev *chr,
 | 
				
			||||||
 | 
					                              ChardevBackend *backend,
 | 
				
			||||||
 | 
					                              bool *be_opened,
 | 
				
			||||||
 | 
					                              Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ChardevMux *mux = backend->u.mux.data;
 | 
				
			||||||
 | 
					    Chardev *drv;
 | 
				
			||||||
 | 
					    MuxChardev *d = MUX_CHARDEV(chr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    drv = qemu_chr_find(mux->chardev);
 | 
				
			||||||
 | 
					    if (drv == NULL) {
 | 
				
			||||||
 | 
					        error_setg(errp, "mux: base chardev %s not found", mux->chardev);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    d->focus = -1;
 | 
				
			||||||
 | 
					    /* only default to opened state if we've realized the initial
 | 
				
			||||||
 | 
					     * set of muxes
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    *be_opened = muxes_realized;
 | 
				
			||||||
 | 
					    qemu_chr_fe_init(&d->chr, drv, errp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 | 
				
			||||||
 | 
					                               Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char *chardev = qemu_opt_get(opts, "chardev");
 | 
				
			||||||
 | 
					    ChardevMux *mux;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (chardev == NULL) {
 | 
				
			||||||
 | 
					        error_setg(errp, "chardev: mux: no chardev given");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    backend->type = CHARDEV_BACKEND_KIND_MUX;
 | 
				
			||||||
 | 
					    mux = backend->u.mux.data = g_new0(ChardevMux, 1);
 | 
				
			||||||
 | 
					    qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
 | 
				
			||||||
 | 
					    mux->chardev = g_strdup(chardev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void char_mux_class_init(ObjectClass *oc, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ChardevClass *cc = CHARDEV_CLASS(oc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cc->parse = qemu_chr_parse_mux;
 | 
				
			||||||
 | 
					    cc->open = qemu_chr_open_mux;
 | 
				
			||||||
 | 
					    cc->chr_write = mux_chr_write;
 | 
				
			||||||
 | 
					    cc->chr_accept_input = mux_chr_accept_input;
 | 
				
			||||||
 | 
					    cc->chr_add_watch = mux_chr_add_watch;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const TypeInfo char_mux_type_info = {
 | 
				
			||||||
 | 
					    .name = TYPE_CHARDEV_MUX,
 | 
				
			||||||
 | 
					    .parent = TYPE_CHARDEV,
 | 
				
			||||||
 | 
					    .class_init = char_mux_class_init,
 | 
				
			||||||
 | 
					    .instance_size = sizeof(MuxChardev),
 | 
				
			||||||
 | 
					    .instance_finalize = char_mux_finalize,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void register_types(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type_register_static(&char_mux_type_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type_init(register_types);
 | 
				
			||||||
							
								
								
									
										63
									
								
								chardev/char-mux.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								chardev/char-mux.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * QEMU System Emulator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (c) 2003-2008 Fabrice Bellard
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					 * all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					 * THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef CHAR_MUX_H
 | 
				
			||||||
 | 
					#define CHAR_MUX_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "sysemu/char.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern bool muxes_realized;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_MUX 4
 | 
				
			||||||
 | 
					#define MUX_BUFFER_SIZE 32 /* Must be a power of 2.  */
 | 
				
			||||||
 | 
					#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
 | 
				
			||||||
 | 
					typedef struct MuxChardev {
 | 
				
			||||||
 | 
					    Chardev parent;
 | 
				
			||||||
 | 
					    CharBackend *backends[MAX_MUX];
 | 
				
			||||||
 | 
					    CharBackend chr;
 | 
				
			||||||
 | 
					    int focus;
 | 
				
			||||||
 | 
					    int mux_cnt;
 | 
				
			||||||
 | 
					    int term_got_escape;
 | 
				
			||||||
 | 
					    int max_size;
 | 
				
			||||||
 | 
					    /* Intermediate input buffer catches escape sequences even if the
 | 
				
			||||||
 | 
					       currently active device is not accepting any input - but only until it
 | 
				
			||||||
 | 
					       is full as well. */
 | 
				
			||||||
 | 
					    unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
 | 
				
			||||||
 | 
					    int prod[MAX_MUX];
 | 
				
			||||||
 | 
					    int cons[MAX_MUX];
 | 
				
			||||||
 | 
					    int timestamps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Protected by the Chardev chr_write_lock.  */
 | 
				
			||||||
 | 
					    int linestart;
 | 
				
			||||||
 | 
					    int64_t timestamps_start;
 | 
				
			||||||
 | 
					} MuxChardev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
 | 
				
			||||||
 | 
					#define CHARDEV_IS_MUX(chr)                             \
 | 
				
			||||||
 | 
					    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
 | 
				
			||||||
 | 
					void mux_set_focus(Chardev *chr, int focus);
 | 
				
			||||||
 | 
					void mux_chr_send_event(MuxChardev *d, int mux_nr, int event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* CHAR_MUX_H */
 | 
				
			||||||
							
								
								
									
										352
									
								
								chardev/char.c
									
									
									
									
									
								
							
							
						
						
									
										352
									
								
								chardev/char.c
									
									
									
									
									
								
							@ -85,12 +85,12 @@
 | 
				
			|||||||
#include "qemu/sockets.h"
 | 
					#include "qemu/sockets.h"
 | 
				
			||||||
#include "ui/qemu-spice.h"
 | 
					#include "ui/qemu-spice.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "char-mux.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define READ_BUF_LEN 4096
 | 
					#define READ_BUF_LEN 4096
 | 
				
			||||||
#define READ_RETRIES 10
 | 
					#define READ_RETRIES 10
 | 
				
			||||||
#define TCP_MAX_FDS 16
 | 
					#define TCP_MAX_FDS 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct MuxChardev MuxChardev;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/***********************************************************/
 | 
					/***********************************************************/
 | 
				
			||||||
/* Socket address helpers */
 | 
					/* Socket address helpers */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -284,7 +284,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 | 
				
			|||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
 | 
					int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int offset;
 | 
					    int offset;
 | 
				
			||||||
    int res;
 | 
					    int res;
 | 
				
			||||||
@ -482,8 +482,6 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void remove_fd_in_watch(Chardev *chr);
 | 
					static void remove_fd_in_watch(Chardev *chr);
 | 
				
			||||||
static void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
 | 
					 | 
				
			||||||
static void mux_set_focus(Chardev *chr, int focus);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
 | 
					static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
 | 
				
			||||||
                           bool *be_opened, Error **errp)
 | 
					                           bool *be_opened, Error **errp)
 | 
				
			||||||
@ -560,235 +558,6 @@ static const TypeInfo char_type_info = {
 | 
				
			|||||||
    .class_init = char_class_init,
 | 
					    .class_init = char_class_init,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* MUX driver for serial I/O splitting */
 | 
					 | 
				
			||||||
#define MAX_MUX 4
 | 
					 | 
				
			||||||
#define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
 | 
					 | 
				
			||||||
#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
 | 
					 | 
				
			||||||
struct MuxChardev {
 | 
					 | 
				
			||||||
    Chardev parent;
 | 
					 | 
				
			||||||
    CharBackend *backends[MAX_MUX];
 | 
					 | 
				
			||||||
    CharBackend chr;
 | 
					 | 
				
			||||||
    int focus;
 | 
					 | 
				
			||||||
    int mux_cnt;
 | 
					 | 
				
			||||||
    int term_got_escape;
 | 
					 | 
				
			||||||
    int max_size;
 | 
					 | 
				
			||||||
    /* Intermediate input buffer allows to catch escape sequences even if the
 | 
					 | 
				
			||||||
       currently active device is not accepting any input - but only until it
 | 
					 | 
				
			||||||
       is full as well. */
 | 
					 | 
				
			||||||
    unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
 | 
					 | 
				
			||||||
    int prod[MAX_MUX];
 | 
					 | 
				
			||||||
    int cons[MAX_MUX];
 | 
					 | 
				
			||||||
    int timestamps;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Protected by the Chardev chr_write_lock.  */
 | 
					 | 
				
			||||||
    int linestart;
 | 
					 | 
				
			||||||
    int64_t timestamps_start;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Called with chr_write_lock held.  */
 | 
					 | 
				
			||||||
static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(chr);
 | 
					 | 
				
			||||||
    int ret;
 | 
					 | 
				
			||||||
    if (!d->timestamps) {
 | 
					 | 
				
			||||||
        ret = qemu_chr_fe_write(&d->chr, buf, len);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ret = 0;
 | 
					 | 
				
			||||||
        for (i = 0; i < len; i++) {
 | 
					 | 
				
			||||||
            if (d->linestart) {
 | 
					 | 
				
			||||||
                char buf1[64];
 | 
					 | 
				
			||||||
                int64_t ti;
 | 
					 | 
				
			||||||
                int secs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
 | 
					 | 
				
			||||||
                if (d->timestamps_start == -1)
 | 
					 | 
				
			||||||
                    d->timestamps_start = ti;
 | 
					 | 
				
			||||||
                ti -= d->timestamps_start;
 | 
					 | 
				
			||||||
                secs = ti / 1000;
 | 
					 | 
				
			||||||
                snprintf(buf1, sizeof(buf1),
 | 
					 | 
				
			||||||
                         "[%02d:%02d:%02d.%03d] ",
 | 
					 | 
				
			||||||
                         secs / 3600,
 | 
					 | 
				
			||||||
                         (secs / 60) % 60,
 | 
					 | 
				
			||||||
                         secs % 60,
 | 
					 | 
				
			||||||
                         (int)(ti % 1000));
 | 
					 | 
				
			||||||
                /* XXX this blocks entire thread. Rewrite to use
 | 
					 | 
				
			||||||
                 * qemu_chr_fe_write and background I/O callbacks */
 | 
					 | 
				
			||||||
                qemu_chr_fe_write_all(&d->chr,
 | 
					 | 
				
			||||||
                                      (uint8_t *)buf1, strlen(buf1));
 | 
					 | 
				
			||||||
                d->linestart = 0;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
 | 
					 | 
				
			||||||
            if (buf[i] == '\n') {
 | 
					 | 
				
			||||||
                d->linestart = 1;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const char * const mux_help[] = {
 | 
					 | 
				
			||||||
    "% h    print this help\n\r",
 | 
					 | 
				
			||||||
    "% x    exit emulator\n\r",
 | 
					 | 
				
			||||||
    "% s    save disk data back to file (if -snapshot)\n\r",
 | 
					 | 
				
			||||||
    "% t    toggle console timestamps\n\r",
 | 
					 | 
				
			||||||
    "% b    send break (magic sysrq)\n\r",
 | 
					 | 
				
			||||||
    "% c    switch between console and monitor\n\r",
 | 
					 | 
				
			||||||
    "% %  sends %\n\r",
 | 
					 | 
				
			||||||
    NULL
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int term_escape_char = 0x01; /* ctrl-a is used for escape */
 | 
					 | 
				
			||||||
static void mux_print_help(Chardev *chr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    int i, j;
 | 
					 | 
				
			||||||
    char ebuf[15] = "Escape-Char";
 | 
					 | 
				
			||||||
    char cbuf[50] = "\n\r";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (term_escape_char > 0 && term_escape_char < 26) {
 | 
					 | 
				
			||||||
        snprintf(cbuf, sizeof(cbuf), "\n\r");
 | 
					 | 
				
			||||||
        snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        snprintf(cbuf, sizeof(cbuf),
 | 
					 | 
				
			||||||
                 "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
 | 
					 | 
				
			||||||
                 term_escape_char);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    /* XXX this blocks entire thread. Rewrite to use
 | 
					 | 
				
			||||||
     * qemu_chr_fe_write and background I/O callbacks */
 | 
					 | 
				
			||||||
    qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
 | 
					 | 
				
			||||||
    for (i = 0; mux_help[i] != NULL; i++) {
 | 
					 | 
				
			||||||
        for (j=0; mux_help[i][j] != '\0'; j++) {
 | 
					 | 
				
			||||||
            if (mux_help[i][j] == '%')
 | 
					 | 
				
			||||||
                qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    CharBackend *be = d->backends[mux_nr];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (be && be->chr_event) {
 | 
					 | 
				
			||||||
        be->chr_event(be->opaque, event);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (d->term_got_escape) {
 | 
					 | 
				
			||||||
        d->term_got_escape = 0;
 | 
					 | 
				
			||||||
        if (ch == term_escape_char)
 | 
					 | 
				
			||||||
            goto send_char;
 | 
					 | 
				
			||||||
        switch(ch) {
 | 
					 | 
				
			||||||
        case '?':
 | 
					 | 
				
			||||||
        case 'h':
 | 
					 | 
				
			||||||
            mux_print_help(chr);
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case 'x':
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                 const char *term =  "QEMU: Terminated\n\r";
 | 
					 | 
				
			||||||
                 qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
 | 
					 | 
				
			||||||
                 exit(0);
 | 
					 | 
				
			||||||
                 break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        case 's':
 | 
					 | 
				
			||||||
            blk_commit_all();
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case 'b':
 | 
					 | 
				
			||||||
            qemu_chr_be_event(chr, CHR_EVENT_BREAK);
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case 'c':
 | 
					 | 
				
			||||||
            assert(d->mux_cnt > 0); /* handler registered with first fe */
 | 
					 | 
				
			||||||
            /* Switch to the next registered device */
 | 
					 | 
				
			||||||
            mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case 't':
 | 
					 | 
				
			||||||
            d->timestamps = !d->timestamps;
 | 
					 | 
				
			||||||
            d->timestamps_start = -1;
 | 
					 | 
				
			||||||
            d->linestart = 0;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else if (ch == term_escape_char) {
 | 
					 | 
				
			||||||
        d->term_got_escape = 1;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    send_char:
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void mux_chr_accept_input(Chardev *chr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(chr);
 | 
					 | 
				
			||||||
    int m = d->focus;
 | 
					 | 
				
			||||||
    CharBackend *be = d->backends[m];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while (be && d->prod[m] != d->cons[m] &&
 | 
					 | 
				
			||||||
           be->chr_can_read && be->chr_can_read(be->opaque)) {
 | 
					 | 
				
			||||||
        be->chr_read(be->opaque,
 | 
					 | 
				
			||||||
                     &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int mux_chr_can_read(void *opaque)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(opaque);
 | 
					 | 
				
			||||||
    int m = d->focus;
 | 
					 | 
				
			||||||
    CharBackend *be = d->backends[m];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (be && be->chr_can_read) {
 | 
					 | 
				
			||||||
        return be->chr_can_read(be->opaque);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    Chardev *chr = CHARDEV(opaque);
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(opaque);
 | 
					 | 
				
			||||||
    int m = d->focus;
 | 
					 | 
				
			||||||
    CharBackend *be = d->backends[m];
 | 
					 | 
				
			||||||
    int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    mux_chr_accept_input(opaque);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (i = 0; i < size; i++)
 | 
					 | 
				
			||||||
        if (mux_proc_byte(chr, d, buf[i])) {
 | 
					 | 
				
			||||||
            if (d->prod[m] == d->cons[m] &&
 | 
					 | 
				
			||||||
                be && be->chr_can_read &&
 | 
					 | 
				
			||||||
                be->chr_can_read(be->opaque))
 | 
					 | 
				
			||||||
                be->chr_read(be->opaque, &buf[i], 1);
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool muxes_realized;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void mux_chr_event(void *opaque, int event)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(opaque);
 | 
					 | 
				
			||||||
    int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!muxes_realized) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Send the event to all registered listeners */
 | 
					 | 
				
			||||||
    for (i = 0; i < d->mux_cnt; i++)
 | 
					 | 
				
			||||||
        mux_chr_send_event(d, i, event);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Called after processing of default and command-line-specified
 | 
					 * Called after processing of default and command-line-specified
 | 
				
			||||||
 * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
 | 
					 * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
 | 
				
			||||||
@ -827,85 +596,6 @@ static Notifier muxes_realize_notify = {
 | 
				
			|||||||
    .notify = muxes_realize_done,
 | 
					    .notify = muxes_realize_done,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(s);
 | 
					 | 
				
			||||||
    Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
 | 
					 | 
				
			||||||
    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!cc->chr_add_watch) {
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return cc->chr_add_watch(chr, cond);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void char_mux_finalize(Object *obj)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(obj);
 | 
					 | 
				
			||||||
    int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (i = 0; i < d->mux_cnt; i++) {
 | 
					 | 
				
			||||||
        CharBackend *be = d->backends[i];
 | 
					 | 
				
			||||||
        if (be) {
 | 
					 | 
				
			||||||
            be->chr = NULL;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    qemu_chr_fe_deinit(&d->chr);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(chr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Fix up the real driver with mux routines */
 | 
					 | 
				
			||||||
    qemu_chr_fe_set_handlers(&d->chr,
 | 
					 | 
				
			||||||
                             mux_chr_can_read,
 | 
					 | 
				
			||||||
                             mux_chr_read,
 | 
					 | 
				
			||||||
                             mux_chr_event,
 | 
					 | 
				
			||||||
                             chr,
 | 
					 | 
				
			||||||
                             context, true);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void mux_set_focus(Chardev *chr, int focus)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(chr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    assert(focus >= 0);
 | 
					 | 
				
			||||||
    assert(focus < d->mux_cnt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (d->focus != -1) {
 | 
					 | 
				
			||||||
        mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    d->focus = focus;
 | 
					 | 
				
			||||||
    chr->be = d->backends[focus];
 | 
					 | 
				
			||||||
    mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void qemu_chr_open_mux(Chardev *chr,
 | 
					 | 
				
			||||||
                              ChardevBackend *backend,
 | 
					 | 
				
			||||||
                              bool *be_opened,
 | 
					 | 
				
			||||||
                              Error **errp)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ChardevMux *mux = backend->u.mux.data;
 | 
					 | 
				
			||||||
    Chardev *drv;
 | 
					 | 
				
			||||||
    MuxChardev *d = MUX_CHARDEV(chr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    drv = qemu_chr_find(mux->chardev);
 | 
					 | 
				
			||||||
    if (drv == NULL) {
 | 
					 | 
				
			||||||
        error_setg(errp, "mux: base chardev %s not found", mux->chardev);
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    d->focus = -1;
 | 
					 | 
				
			||||||
    /* only default to opened state if we've realized the initial
 | 
					 | 
				
			||||||
     * set of muxes
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    *be_opened = muxes_realized;
 | 
					 | 
				
			||||||
    qemu_chr_fe_init(&d->chr, drv, errp);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Chardev *qemu_chr_fe_get_driver(CharBackend *be)
 | 
					Chardev *qemu_chr_fe_get_driver(CharBackend *be)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return be->chr;
 | 
					    return be->chr;
 | 
				
			||||||
@ -3950,41 +3640,6 @@ static const TypeInfo char_memory_type_info = {
 | 
				
			|||||||
    .parent = TYPE_CHARDEV_RINGBUF,
 | 
					    .parent = TYPE_CHARDEV_RINGBUF,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 | 
					 | 
				
			||||||
                               Error **errp)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    const char *chardev = qemu_opt_get(opts, "chardev");
 | 
					 | 
				
			||||||
    ChardevMux *mux;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    backend->type = CHARDEV_BACKEND_KIND_MUX;
 | 
					 | 
				
			||||||
    if (chardev == NULL) {
 | 
					 | 
				
			||||||
        error_setg(errp, "chardev: mux: no chardev given");
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    mux = backend->u.mux.data = g_new0(ChardevMux, 1);
 | 
					 | 
				
			||||||
    qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
 | 
					 | 
				
			||||||
    mux->chardev = g_strdup(chardev);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void char_mux_class_init(ObjectClass *oc, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ChardevClass *cc = CHARDEV_CLASS(oc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cc->parse = qemu_chr_parse_mux;
 | 
					 | 
				
			||||||
    cc->open = qemu_chr_open_mux;
 | 
					 | 
				
			||||||
    cc->chr_write = mux_chr_write;
 | 
					 | 
				
			||||||
    cc->chr_accept_input = mux_chr_accept_input;
 | 
					 | 
				
			||||||
    cc->chr_add_watch = mux_chr_add_watch;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const TypeInfo char_mux_type_info = {
 | 
					 | 
				
			||||||
    .name = TYPE_CHARDEV_MUX,
 | 
					 | 
				
			||||||
    .parent = TYPE_CHARDEV,
 | 
					 | 
				
			||||||
    .class_init = char_mux_class_init,
 | 
					 | 
				
			||||||
    .instance_size = sizeof(MuxChardev),
 | 
					 | 
				
			||||||
    .instance_finalize = char_mux_finalize,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
 | 
					static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
 | 
				
			||||||
                                  Error **errp)
 | 
					                                  Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -5101,7 +4756,6 @@ static void register_types(void)
 | 
				
			|||||||
    type_register_static(&char_console_type_info);
 | 
					    type_register_static(&char_console_type_info);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    type_register_static(&char_pipe_type_info);
 | 
					    type_register_static(&char_pipe_type_info);
 | 
				
			||||||
    type_register_static(&char_mux_type_info);
 | 
					 | 
				
			||||||
    type_register_static(&char_memory_type_info);
 | 
					    type_register_static(&char_memory_type_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* this must be done after machine init, since we register FEs with muxes
 | 
					    /* this must be done after machine init, since we register FEs with muxes
 | 
				
			||||||
 | 
				
			|||||||
@ -441,6 +441,7 @@ bool qemu_chr_has_feature(Chardev *chr,
 | 
				
			|||||||
void qemu_chr_set_feature(Chardev *chr,
 | 
					void qemu_chr_set_feature(Chardev *chr,
 | 
				
			||||||
                          ChardevFeature feature);
 | 
					                          ChardevFeature feature);
 | 
				
			||||||
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 | 
					QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 | 
				
			||||||
 | 
					int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define TYPE_CHARDEV "chardev"
 | 
					#define TYPE_CHARDEV "chardev"
 | 
				
			||||||
#define CHARDEV(obj) OBJECT_CHECK(Chardev, (obj), TYPE_CHARDEV)
 | 
					#define CHARDEV(obj) OBJECT_CHECK(Chardev, (obj), TYPE_CHARDEV)
 | 
				
			||||||
@ -463,8 +464,6 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 | 
				
			|||||||
#define TYPE_CHARDEV_SOCKET "chardev-socket"
 | 
					#define TYPE_CHARDEV_SOCKET "chardev-socket"
 | 
				
			||||||
#define TYPE_CHARDEV_UDP "chardev-udp"
 | 
					#define TYPE_CHARDEV_UDP "chardev-udp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CHARDEV_IS_MUX(chr) \
 | 
					 | 
				
			||||||
    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
 | 
					 | 
				
			||||||
#define CHARDEV_IS_RINGBUF(chr) \
 | 
					#define CHARDEV_IS_RINGBUF(chr) \
 | 
				
			||||||
    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_RINGBUF)
 | 
					    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_RINGBUF)
 | 
				
			||||||
#define CHARDEV_IS_PTY(chr) \
 | 
					#define CHARDEV_IS_PTY(chr) \
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user