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