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/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 (s) {
 202        if (CHARDEV_IS_MUX(s)) {
 203            MuxChardev *d = MUX_CHARDEV(s);
 204
 205            if (d->mux_cnt >= MAX_MUX) {
 206                goto unavailable;
 207            }
 208
 209            d->backends[d->mux_cnt] = b;
 210            tag = d->mux_cnt++;
 211        } else if (s->be) {
 212            goto unavailable;
 213        } else {
 214            s->be = b;
 215        }
 216    }
 217
 218    b->fe_open = false;
 219    b->tag = tag;
 220    b->chr = s;
 221    return true;
 222
 223unavailable:
 224    error_setg(errp, QERR_DEVICE_IN_USE, s->label);
 225    return false;
 226}
 227
 228void qemu_chr_fe_deinit(CharBackend *b, bool del)
 229{
 230    assert(b);
 231
 232    if (b->chr) {
 233        qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, NULL, true);
 234        if (b->chr->be == b) {
 235            b->chr->be = NULL;
 236        }
 237        if (CHARDEV_IS_MUX(b->chr)) {
 238            MuxChardev *d = MUX_CHARDEV(b->chr);
 239            d->backends[b->tag] = NULL;
 240        }
 241        if (del) {
 242            object_unparent(OBJECT(b->chr));
 243        }
 244        b->chr = NULL;
 245    }
 246}
 247
 248void qemu_chr_fe_set_handlers(CharBackend *b,
 249                              IOCanReadHandler *fd_can_read,
 250                              IOReadHandler *fd_read,
 251                              IOEventHandler *fd_event,
 252                              BackendChangeHandler *be_change,
 253                              void *opaque,
 254                              GMainContext *context,
 255                              bool set_open)
 256{
 257    Chardev *s;
 258    int fe_open;
 259
 260    s = b->chr;
 261    if (!s) {
 262        return;
 263    }
 264
 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
 277    qemu_chr_be_update_read_handlers(s, context);
 278
 279    if (set_open) {
 280        qemu_chr_fe_set_open(b, fe_open);
 281    }
 282
 283    if (fe_open) {
 284        qemu_chr_fe_take_focus(b);
 285        /* We're connecting to an already opened device, so let's make sure we
 286           also get the open event */
 287        if (s->be_open) {
 288            qemu_chr_be_event(s, CHR_EVENT_OPENED);
 289        }
 290    }
 291
 292    if (CHARDEV_IS_MUX(s)) {
 293        mux_chr_set_handlers(s, context);
 294    }
 295}
 296
 297void qemu_chr_fe_take_focus(CharBackend *b)
 298{
 299    if (!b->chr) {
 300        return;
 301    }
 302
 303    if (CHARDEV_IS_MUX(b->chr)) {
 304        mux_set_focus(b->chr, b->tag);
 305    }
 306}
 307
 308int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
 309{
 310    if (!be->chr) {
 311        error_setg(errp, "missing associated backend");
 312        return -1;
 313    }
 314
 315    return qemu_chr_wait_connected(be->chr, errp);
 316}
 317
 318void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 319{
 320    Chardev *chr = be->chr;
 321
 322    if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
 323        CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
 324    }
 325}
 326
 327void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 328{
 329    Chardev *chr = be->chr;
 330
 331    if (!chr) {
 332        return;
 333    }
 334
 335    if (be->fe_open == fe_open) {
 336        return;
 337    }
 338    be->fe_open = fe_open;
 339    if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
 340        CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
 341    }
 342}
 343
 344guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
 345                            GIOFunc func, void *user_data)
 346{
 347    Chardev *s = be->chr;
 348    GSource *src;
 349    guint tag;
 350
 351    if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
 352        return 0;
 353    }
 354
 355    src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
 356    if (!src) {
 357        return 0;
 358    }
 359
 360    g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
 361    tag = g_source_attach(src, s->gcontext);
 362    g_source_unref(src);
 363
 364    return tag;
 365}
 366
 367void qemu_chr_fe_disconnect(CharBackend *be)
 368{
 369    Chardev *chr = be->chr;
 370
 371    if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
 372        CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
 373    }
 374}
 375