qemu/chardev/char-fe.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#include "qemu/osdep.h"
  25#include "qemu/error-report.h"
  26#include "qapi/error.h"
  27#include "qapi-visit.h"
  28#include "sysemu/replay.h"
  29
  30#include "chardev/char-fe.h"
  31#include "chardev/char-io.h"
  32#include "chardev/char-mux.h"
  33
  34int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
  35{
  36    Chardev *s = be->chr;
  37
  38    if (!s) {
  39        return 0;
  40    }
  41
  42    return qemu_chr_write(s, buf, len, false);
  43}
  44
  45int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
  46{
  47    Chardev *s = be->chr;
  48
  49    if (!s) {
  50        return 0;
  51    }
  52
  53    return qemu_chr_write(s, buf, len, true);
  54}
  55
  56int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
  57{
  58    Chardev *s = be->chr;
  59    int offset = 0, counter = 10;
  60    int res;
  61
  62    if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
  63        return 0;
  64    }
  65
  66    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
  67        return replay_char_read_all_load(buf);
  68    }
  69
  70    while (offset < len) {
  71    retry:
  72        res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
  73                                                  len - offset);
  74        if (res == -1 && errno == EAGAIN) {
  75            g_usleep(100);
  76            goto retry;
  77        }
  78
  79        if (res == 0) {
  80            break;
  81        }
  82
  83        if (res < 0) {
  84            if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
  85                replay_char_read_all_save_error(res);
  86            }
  87            return res;
  88        }
  89
  90        offset += res;
  91
  92        if (!counter--) {
  93            break;
  94        }
  95    }
  96
  97    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
  98        replay_char_read_all_save_buf(buf, offset);
  99    }
 100    return offset;
 101}
 102
 103int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
 104{
 105    Chardev *s = be->chr;
 106    int res;
 107
 108    if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
 109        res = -ENOTSUP;
 110    } else {
 111        res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
 112    }
 113
 114    return res;
 115}
 116
 117int qemu_chr_fe_get_msgfd(CharBackend *be)
 118{
 119    Chardev *s = be->chr;
 120    int fd;
 121    int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
 122    if (s && qemu_chr_replay(s)) {
 123        error_report("Replay: get msgfd is not supported "
 124                     "for serial devices yet");
 125        exit(1);
 126    }
 127    return res;
 128}
 129
 130int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
 131{
 132    Chardev *s = be->chr;
 133
 134    if (!s) {
 135        return -1;
 136    }
 137
 138    return CHARDEV_GET_CLASS(s)->get_msgfds ?
 139        CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1;
 140}
 141
 142int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
 143{
 144    Chardev *s = be->chr;
 145
 146    if (!s) {
 147        return -1;
 148    }
 149
 150    return CHARDEV_GET_CLASS(s)->set_msgfds ?
 151        CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1;
 152}
 153
 154void qemu_chr_fe_accept_input(CharBackend *be)
 155{
 156    Chardev *s = be->chr;
 157
 158    if (!s) {
 159        return;
 160    }
 161
 162    if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
 163        CHARDEV_GET_CLASS(s)->chr_accept_input(s);
 164    }
 165    qemu_notify_event();
 166}
 167
 168void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
 169{
 170    char buf[CHR_READ_BUF_LEN];
 171    va_list ap;
 172    va_start(ap, fmt);
 173    vsnprintf(buf, sizeof(buf), fmt, ap);
 174    /* XXX this blocks entire thread. Rewrite to use
 175     * qemu_chr_fe_write and background I/O callbacks */
 176    qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
 177    va_end(ap);
 178}
 179
 180Chardev *qemu_chr_fe_get_driver(CharBackend *be)
 181{
 182    /* this is unsafe for the users that support chardev hotswap */
 183    assert(be->chr_be_change == NULL);
 184    return be->chr;
 185}
 186
 187bool qemu_chr_fe_backend_connected(CharBackend *be)
 188{
 189    return !!be->chr;
 190}
 191
 192bool qemu_chr_fe_backend_open(CharBackend *be)
 193{
 194    return be->chr && be->chr->be_open;
 195}
 196
 197bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
 198{
 199    int tag = 0;
 200
 201    if (CHARDEV_IS_MUX(s)) {
 202        MuxChardev *d = MUX_CHARDEV(s);
 203
 204        if (d->mux_cnt >= MAX_MUX) {
 205            goto unavailable;
 206        }
 207
 208        d->backends[d->mux_cnt] = b;
 209        tag = d->mux_cnt++;
 210    } else if (s->be) {
 211        goto unavailable;
 212    } else {
 213        s->be = b;
 214    }
 215
 216    b->fe_open = false;
 217    b->tag = tag;
 218    b->chr = s;
 219    return true;
 220
 221unavailable:
 222    error_setg(errp, QERR_DEVICE_IN_USE, s->label);
 223    return false;
 224}
 225
 226void qemu_chr_fe_deinit(CharBackend *b, bool del)
 227{
 228    assert(b);
 229
 230    if (b->chr) {
 231        qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, NULL, true);
 232        if (b->chr->be == b) {
 233            b->chr->be = NULL;
 234        }
 235        if (CHARDEV_IS_MUX(b->chr)) {
 236            MuxChardev *d = MUX_CHARDEV(b->chr);
 237            d->backends[b->tag] = NULL;
 238        }
 239        if (del) {
 240            object_unparent(OBJECT(b->chr));
 241        }
 242        b->chr = NULL;
 243    }
 244}
 245
 246void qemu_chr_fe_set_handlers(CharBackend *b,
 247                              IOCanReadHandler *fd_can_read,
 248                              IOReadHandler *fd_read,
 249                              IOEventHandler *fd_event,
 250                              BackendChangeHandler *be_change,
 251                              void *opaque,
 252                              GMainContext *context,
 253                              bool set_open)
 254{
 255    Chardev *s;
 256    ChardevClass *cc;
 257    int fe_open;
 258
 259    s = b->chr;
 260    if (!s) {
 261        return;
 262    }
 263
 264    cc = CHARDEV_GET_CLASS(s);
 265    if (!opaque && !fd_can_read && !fd_read && !fd_event) {
 266        fe_open = 0;
 267        remove_fd_in_watch(s);
 268    } else {
 269        fe_open = 1;
 270    }
 271    b->chr_can_read = fd_can_read;
 272    b->chr_read = fd_read;
 273    b->chr_event = fd_event;
 274    b->chr_be_change = be_change;
 275    b->opaque = opaque;
 276    if (cc->chr_update_read_handler) {
 277        cc->chr_update_read_handler(s, context);
 278    }
 279
 280    if (set_open) {
 281        qemu_chr_fe_set_open(b, fe_open);
 282    }
 283
 284    if (fe_open) {
 285        qemu_chr_fe_take_focus(b);
 286        /* We're connecting to an already opened device, so let's make sure we
 287           also get the open event */
 288        if (s->be_open) {
 289            qemu_chr_be_event(s, CHR_EVENT_OPENED);
 290        }
 291    }
 292
 293    if (CHARDEV_IS_MUX(s)) {
 294        mux_chr_set_handlers(s, context);
 295    }
 296}
 297
 298void qemu_chr_fe_take_focus(CharBackend *b)
 299{
 300    if (!b->chr) {
 301        return;
 302    }
 303
 304    if (CHARDEV_IS_MUX(b->chr)) {
 305        mux_set_focus(b->chr, b->tag);
 306    }
 307}
 308
 309int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
 310{
 311    if (!be->chr) {
 312        error_setg(errp, "missing associated backend");
 313        return -1;
 314    }
 315
 316    return qemu_chr_wait_connected(be->chr, errp);
 317}
 318
 319void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 320{
 321    Chardev *chr = be->chr;
 322
 323    if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
 324        CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
 325    }
 326}
 327
 328void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 329{
 330    Chardev *chr = be->chr;
 331
 332    if (!chr) {
 333        return;
 334    }
 335
 336    if (be->fe_open == fe_open) {
 337        return;
 338    }
 339    be->fe_open = fe_open;
 340    if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
 341        CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
 342    }
 343}
 344
 345guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
 346                            GIOFunc func, void *user_data)
 347{
 348    Chardev *s = be->chr;
 349    GSource *src;
 350    guint tag;
 351
 352    if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
 353        return 0;
 354    }
 355
 356    src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
 357    if (!src) {
 358        return 0;
 359    }
 360
 361    g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
 362    tag = g_source_attach(src, NULL);
 363    g_source_unref(src);
 364
 365    return tag;
 366}
 367
 368void qemu_chr_fe_disconnect(CharBackend *be)
 369{
 370    Chardev *chr = be->chr;
 371
 372    if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
 373        CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
 374    }
 375}
 376