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