qemu/chardev/char-mux.c
<<
>>
Prefs
   1/*
   2 * QEMU System Emulator
   3 *
   4 * Copyright (c) 2003-2008 Fabrice Bellard
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "qapi/error.h"
  27#include "qemu/option.h"
  28#include "chardev/char.h"
  29#include "sysemu/block-backend.h"
  30#include "sysemu/sysemu.h"
  31#include "chardev/char-mux.h"
  32
  33/* MUX driver for serial I/O splitting */
  34
  35/* Called with chr_write_lock held.  */
  36static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
  37{
  38    MuxChardev *d = MUX_CHARDEV(chr);
  39    int ret;
  40    if (!d->timestamps) {
  41        ret = qemu_chr_fe_write(&d->chr, buf, len);
  42    } else {
  43        int i;
  44
  45        ret = 0;
  46        for (i = 0; i < len; i++) {
  47            if (d->linestart) {
  48                char buf1[64];
  49                int64_t ti;
  50                int secs;
  51
  52                ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
  53                if (d->timestamps_start == -1) {
  54                    d->timestamps_start = ti;
  55                }
  56                ti -= d->timestamps_start;
  57                secs = ti / 1000;
  58                snprintf(buf1, sizeof(buf1),
  59                         "[%02d:%02d:%02d.%03d] ",
  60                         secs / 3600,
  61                         (secs / 60) % 60,
  62                         secs % 60,
  63                         (int)(ti % 1000));
  64                /* XXX this blocks entire thread. Rewrite to use
  65                 * qemu_chr_fe_write and background I/O callbacks */
  66                qemu_chr_fe_write_all(&d->chr,
  67                                      (uint8_t *)buf1, strlen(buf1));
  68                d->linestart = 0;
  69            }
  70            ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
  71            if (buf[i] == '\n') {
  72                d->linestart = 1;
  73            }
  74        }
  75    }
  76    return ret;
  77}
  78
  79static const char * const mux_help[] = {
  80    "% h    print this help\n\r",
  81    "% x    exit emulator\n\r",
  82    "% s    save disk data back to file (if -snapshot)\n\r",
  83    "% t    toggle console timestamps\n\r",
  84    "% b    send break (magic sysrq)\n\r",
  85    "% c    switch between console and monitor\n\r",
  86    "% %  sends %\n\r",
  87    NULL
  88};
  89
  90int term_escape_char = 0x01; /* ctrl-a is used for escape */
  91static void mux_print_help(Chardev *chr)
  92{
  93    int i, j;
  94    char ebuf[15] = "Escape-Char";
  95    char cbuf[50] = "\n\r";
  96
  97    if (term_escape_char > 0 && term_escape_char < 26) {
  98        snprintf(cbuf, sizeof(cbuf), "\n\r");
  99        snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
 100    } else {
 101        snprintf(cbuf, sizeof(cbuf),
 102                 "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
 103                 term_escape_char);
 104    }
 105    /* XXX this blocks entire thread. Rewrite to use
 106     * qemu_chr_fe_write and background I/O callbacks */
 107    qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
 108    for (i = 0; mux_help[i] != NULL; i++) {
 109        for (j = 0; mux_help[i][j] != '\0'; j++) {
 110            if (mux_help[i][j] == '%') {
 111                qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
 112            } else {
 113                qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
 114            }
 115        }
 116    }
 117}
 118
 119static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
 120{
 121    CharBackend *be = d->backends[mux_nr];
 122
 123    if (be && be->chr_event) {
 124        be->chr_event(be->opaque, event);
 125    }
 126}
 127
 128static void mux_chr_be_event(Chardev *chr, int event)
 129{
 130    MuxChardev *d = MUX_CHARDEV(chr);
 131
 132    if (d->focus != -1) {
 133        mux_chr_send_event(d, d->focus, event);
 134    }
 135}
 136
 137static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
 138{
 139    if (d->term_got_escape) {
 140        d->term_got_escape = 0;
 141        if (ch == term_escape_char) {
 142            goto send_char;
 143        }
 144        switch (ch) {
 145        case '?':
 146        case 'h':
 147            mux_print_help(chr);
 148            break;
 149        case 'x':
 150            {
 151                 const char *term =  "QEMU: Terminated\n\r";
 152                 qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
 153                 exit(0);
 154                 break;
 155            }
 156        case 's':
 157            blk_commit_all();
 158            break;
 159        case 'b':
 160            qemu_chr_be_event(chr, CHR_EVENT_BREAK);
 161            break;
 162        case 'c':
 163            assert(d->mux_cnt > 0); /* handler registered with first fe */
 164            /* Switch to the next registered device */
 165            mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
 166            break;
 167        case 't':
 168            d->timestamps = !d->timestamps;
 169            d->timestamps_start = -1;
 170            d->linestart = 0;
 171            break;
 172        }
 173    } else if (ch == term_escape_char) {
 174        d->term_got_escape = 1;
 175    } else {
 176    send_char:
 177        return 1;
 178    }
 179    return 0;
 180}
 181
 182static void mux_chr_accept_input(Chardev *chr)
 183{
 184    MuxChardev *d = MUX_CHARDEV(chr);
 185    int m = d->focus;
 186    CharBackend *be = d->backends[m];
 187
 188    while (be && d->prod[m] != d->cons[m] &&
 189           be->chr_can_read && be->chr_can_read(be->opaque)) {
 190        be->chr_read(be->opaque,
 191                     &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
 192    }
 193}
 194
 195static int mux_chr_can_read(void *opaque)
 196{
 197    MuxChardev *d = MUX_CHARDEV(opaque);
 198    int m = d->focus;
 199    CharBackend *be = d->backends[m];
 200
 201    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
 202        return 1;
 203    }
 204
 205    if (be && be->chr_can_read) {
 206        return be->chr_can_read(be->opaque);
 207    }
 208
 209    return 0;
 210}
 211
 212static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 213{
 214    Chardev *chr = CHARDEV(opaque);
 215    MuxChardev *d = MUX_CHARDEV(opaque);
 216    int m = d->focus;
 217    CharBackend *be = d->backends[m];
 218    int i;
 219
 220    mux_chr_accept_input(opaque);
 221
 222    for (i = 0; i < size; i++)
 223        if (mux_proc_byte(chr, d, buf[i])) {
 224            if (d->prod[m] == d->cons[m] &&
 225                be && be->chr_can_read &&
 226                be->chr_can_read(be->opaque)) {
 227                be->chr_read(be->opaque, &buf[i], 1);
 228            } else {
 229                d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
 230            }
 231        }
 232}
 233
 234void mux_chr_send_all_event(Chardev *chr, int event)
 235{
 236    MuxChardev *d = MUX_CHARDEV(chr);
 237    int i;
 238
 239    if (!machine_init_done) {
 240        return;
 241    }
 242
 243    /* Send the event to all registered listeners */
 244    for (i = 0; i < d->mux_cnt; i++) {
 245        mux_chr_send_event(d, i, event);
 246    }
 247}
 248
 249static void mux_chr_event(void *opaque, int event)
 250{
 251    mux_chr_send_all_event(CHARDEV(opaque), event);
 252}
 253
 254static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
 255{
 256    MuxChardev *d = MUX_CHARDEV(s);
 257    Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
 258    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
 259
 260    if (!cc->chr_add_watch) {
 261        return NULL;
 262    }
 263
 264    return cc->chr_add_watch(chr, cond);
 265}
 266
 267static void char_mux_finalize(Object *obj)
 268{
 269    MuxChardev *d = MUX_CHARDEV(obj);
 270    int i;
 271
 272    for (i = 0; i < d->mux_cnt; i++) {
 273        CharBackend *be = d->backends[i];
 274        if (be) {
 275            be->chr = NULL;
 276        }
 277    }
 278    qemu_chr_fe_deinit(&d->chr, false);
 279}
 280
 281void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 282{
 283    MuxChardev *d = MUX_CHARDEV(chr);
 284
 285    /* Fix up the real driver with mux routines */
 286    qemu_chr_fe_set_handlers(&d->chr,
 287                             mux_chr_can_read,
 288                             mux_chr_read,
 289                             mux_chr_event,
 290                             NULL,
 291                             chr,
 292                             context, true);
 293}
 294
 295void mux_set_focus(Chardev *chr, int focus)
 296{
 297    MuxChardev *d = MUX_CHARDEV(chr);
 298
 299    assert(focus >= 0);
 300    assert(focus < d->mux_cnt);
 301
 302    if (d->focus != -1) {
 303        mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
 304    }
 305
 306    d->focus = focus;
 307    chr->be = d->backends[focus];
 308    mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 309}
 310
 311static void qemu_chr_open_mux(Chardev *chr,
 312                              ChardevBackend *backend,
 313                              bool *be_opened,
 314                              Error **errp)
 315{
 316    ChardevMux *mux = backend->u.mux.data;
 317    Chardev *drv;
 318    MuxChardev *d = MUX_CHARDEV(chr);
 319
 320    drv = qemu_chr_find(mux->chardev);
 321    if (drv == NULL) {
 322        error_setg(errp, "mux: base chardev %s not found", mux->chardev);
 323        return;
 324    }
 325
 326    d->focus = -1;
 327    /* only default to opened state if we've realized the initial
 328     * set of muxes
 329     */
 330    *be_opened = machine_init_done;
 331    qemu_chr_fe_init(&d->chr, drv, errp);
 332}
 333
 334static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 335                               Error **errp)
 336{
 337    const char *chardev = qemu_opt_get(opts, "chardev");
 338    ChardevMux *mux;
 339
 340    if (chardev == NULL) {
 341        error_setg(errp, "chardev: mux: no chardev given");
 342        return;
 343    }
 344    backend->type = CHARDEV_BACKEND_KIND_MUX;
 345    mux = backend->u.mux.data = g_new0(ChardevMux, 1);
 346    qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
 347    mux->chardev = g_strdup(chardev);
 348}
 349
 350/**
 351 * Called after processing of default and command-line-specified
 352 * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
 353 * to a mux chardev. This is done here to ensure that
 354 * output/prompts/banners are only displayed for the FE that has
 355 * focus when initial command-line processing/machine init is
 356 * completed.
 357 *
 358 * After this point, any new FE attached to any new or existing
 359 * mux will receive CHR_EVENT_OPENED notifications for the BE
 360 * immediately.
 361 */
 362static int open_muxes(Chardev *chr)
 363{
 364    /* send OPENED to all already-attached FEs */
 365    mux_chr_send_all_event(chr, CHR_EVENT_OPENED);
 366    /*
 367     * mark mux as OPENED so any new FEs will immediately receive
 368     * OPENED event
 369     */
 370    qemu_chr_be_event(chr, CHR_EVENT_OPENED);
 371
 372    return 0;
 373}
 374
 375static void char_mux_class_init(ObjectClass *oc, void *data)
 376{
 377    ChardevClass *cc = CHARDEV_CLASS(oc);
 378
 379    cc->parse = qemu_chr_parse_mux;
 380    cc->open = qemu_chr_open_mux;
 381    cc->chr_write = mux_chr_write;
 382    cc->chr_accept_input = mux_chr_accept_input;
 383    cc->chr_add_watch = mux_chr_add_watch;
 384    cc->chr_be_event = mux_chr_be_event;
 385    cc->chr_machine_done = open_muxes;
 386}
 387
 388static const TypeInfo char_mux_type_info = {
 389    .name = TYPE_CHARDEV_MUX,
 390    .parent = TYPE_CHARDEV,
 391    .class_init = char_mux_class_init,
 392    .instance_size = sizeof(MuxChardev),
 393    .instance_finalize = char_mux_finalize,
 394};
 395
 396static void register_types(void)
 397{
 398    type_register_static(&char_mux_type_info);
 399}
 400
 401type_init(register_types);
 402