aboutsummaryrefslogtreecommitdiff
path: root/chardev
diff options
context:
space:
mode:
Diffstat (limited to 'chardev')
-rw-r--r--chardev/baum.c2
-rw-r--r--chardev/char-console.c2
-rw-r--r--chardev/char-fd.c4
-rw-r--r--chardev/char-fe.c15
-rw-r--r--chardev/char-file.c2
-rw-r--r--chardev/char-hmp-cmds.c2
-rw-r--r--chardev/char-hub.c301
-rw-r--r--chardev/char-mux.c93
-rw-r--r--chardev/char-null.c2
-rw-r--r--chardev/char-parallel.c2
-rw-r--r--chardev/char-pipe.c2
-rw-r--r--chardev/char-pty.c41
-rw-r--r--chardev/char-ringbuf.c2
-rw-r--r--chardev/char-serial.c2
-rw-r--r--chardev/char-socket.c84
-rw-r--r--chardev/char-stdio.c2
-rw-r--r--chardev/char-udp.c2
-rw-r--r--chardev/char-win-stdio.c7
-rw-r--r--chardev/char-win.c2
-rw-r--r--chardev/char.c112
-rw-r--r--chardev/chardev-internal.h67
-rw-r--r--chardev/meson.build1
-rw-r--r--chardev/msmouse.c4
-rw-r--r--chardev/spice.c6
-rw-r--r--chardev/testdev.c2
-rw-r--r--chardev/trace-events10
-rw-r--r--chardev/wctablet.c2
27 files changed, 653 insertions, 120 deletions
diff --git a/chardev/baum.c b/chardev/baum.c
index a1d9784..f3e8cd2 100644
--- a/chardev/baum.c
+++ b/chardev/baum.c
@@ -668,7 +668,7 @@ static void baum_chr_open(Chardev *chr,
qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
}
-static void char_braille_class_init(ObjectClass *oc, void *data)
+static void char_braille_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-console.c b/chardev/char-console.c
index 6c4ce5d..7e1bf64 100644
--- a/chardev/char-console.c
+++ b/chardev/char-console.c
@@ -34,7 +34,7 @@ static void qemu_chr_open_win_con(Chardev *chr,
win_chr_set_file(chr, GetStdHandle(STD_OUTPUT_HANDLE), true);
}
-static void char_console_class_init(ObjectClass *oc, void *data)
+static void char_console_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-fd.c b/chardev/char-fd.c
index d2c4923..6f03adf 100644
--- a/chardev/char-fd.c
+++ b/chardev/char-fd.c
@@ -50,7 +50,7 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
Chardev *chr = CHARDEV(opaque);
FDChardev *s = FD_CHARDEV(opaque);
int len;
- uint8_t buf[CHR_READ_BUF_LEN];
+ QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN];
ssize_t ret;
len = sizeof(buf);
@@ -238,7 +238,7 @@ void qemu_chr_open_fd(Chardev *chr,
}
}
-static void char_fd_class_init(ObjectClass *oc, void *data)
+static void char_fd_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index b214ba3..158a5f4 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -24,7 +24,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
-#include "sysemu/replay.h"
+#include "system/replay.h"
#include "chardev/char-fe.h"
#include "chardev/char-io.h"
@@ -191,22 +191,15 @@ bool qemu_chr_fe_backend_open(CharBackend *be)
bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
{
- int tag = 0;
+ unsigned int tag = 0;
if (s) {
if (CHARDEV_IS_MUX(s)) {
MuxChardev *d = MUX_CHARDEV(s);
- if (d->mux_cnt >= MAX_MUX) {
- error_setg(errp,
- "too many uses of multiplexed chardev '%s'"
- " (maximum is " stringify(MAX_MUX) ")",
- s->label);
+ if (!mux_chr_attach_frontend(d, b, &tag, errp)) {
return false;
}
-
- d->backends[d->mux_cnt] = b;
- tag = d->mux_cnt++;
} else if (s->be) {
error_setg(errp, "chardev '%s' is already in use", s->label);
return false;
@@ -232,7 +225,7 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del)
}
if (CHARDEV_IS_MUX(b->chr)) {
MuxChardev *d = MUX_CHARDEV(b->chr);
- d->backends[b->tag] = NULL;
+ mux_chr_detach_frontend(d, b->tag);
}
if (del) {
Object *obj = OBJECT(b->chr);
diff --git a/chardev/char-file.c b/chardev/char-file.c
index 263e6da..a9e8c5e 100644
--- a/chardev/char-file.c
+++ b/chardev/char-file.c
@@ -123,7 +123,7 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
file->append = qemu_opt_get_bool(opts, "append", false);
}
-static void char_file_class_init(ObjectClass *oc, void *data)
+static void char_file_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-hmp-cmds.c b/chardev/char-hmp-cmds.c
index 287c2b1..8e9e1c1 100644
--- a/chardev/char-hmp-cmds.c
+++ b/chardev/char-hmp-cmds.c
@@ -19,7 +19,7 @@
#include "monitor/monitor.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-char.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
diff --git a/chardev/char-hub.c b/chardev/char-hub.c
new file mode 100644
index 0000000..16ffee2
--- /dev/null
+++ b/chardev/char-hub.c
@@ -0,0 +1,301 @@
+/*
+ * QEMU Character Hub Device
+ *
+ * Author: Roman Penyaev <r.peniaev@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/option.h"
+#include "chardev/char.h"
+#include "chardev-internal.h"
+
+/*
+ * Character hub device aggregates input from multiple backend devices
+ * and forwards it to a single frontend device. Additionally, hub
+ * device takes the output from the frontend device and sends it back
+ * to all the connected backend devices.
+ */
+
+/*
+ * Write to all backends. Different backend devices accept data with
+ * various rate, so it is quite possible that one device returns less,
+ * then others. In this case we return minimum to the caller,
+ * expecting caller will repeat operation soon. When repeat happens
+ * send to the devices which consume data faster must be avoided
+ * for obvious reasons not to send data, which was already sent.
+ * Called with chr_write_lock held.
+ */
+static int hub_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+ HubChardev *d = HUB_CHARDEV(chr);
+ int r, i, ret = len;
+ unsigned int written;
+
+ /* Invalidate index on every write */
+ d->be_eagain_ind = -1;
+
+ for (i = 0; i < d->be_cnt; i++) {
+ if (!d->backends[i].be.chr->be_open) {
+ /* Skip closed backend */
+ continue;
+ }
+ written = d->be_written[i] - d->be_min_written;
+ if (written) {
+ /* Written in the previous call so take into account */
+ ret = MIN(written, ret);
+ continue;
+ }
+ r = qemu_chr_fe_write(&d->backends[i].be, buf, len);
+ if (r < 0) {
+ if (errno == EAGAIN) {
+ /* Set index and expect to be called soon on watch wake up */
+ d->be_eagain_ind = i;
+ }
+ return r;
+ }
+ d->be_written[i] += r;
+ ret = MIN(r, ret);
+ }
+ d->be_min_written += ret;
+
+
+ return ret;
+}
+
+static int hub_chr_can_read(void *opaque)
+{
+ HubCharBackend *backend = opaque;
+ CharBackend *fe = backend->hub->parent.be;
+
+ if (fe && fe->chr_can_read) {
+ return fe->chr_can_read(fe->opaque);
+ }
+
+ return 0;
+}
+
+static void hub_chr_read(void *opaque, const uint8_t *buf, int size)
+{
+ HubCharBackend *backend = opaque;
+ CharBackend *fe = backend->hub->parent.be;
+
+ if (fe && fe->chr_read) {
+ fe->chr_read(fe->opaque, buf, size);
+ }
+}
+
+static void hub_chr_event(void *opaque, QEMUChrEvent event)
+{
+ HubCharBackend *backend = opaque;
+ HubChardev *d = backend->hub;
+ CharBackend *fe = d->parent.be;
+
+ if (event == CHR_EVENT_OPENED) {
+ /*
+ * Catch up with what was already written while this backend
+ * was closed
+ */
+ d->be_written[backend->be_ind] = d->be_min_written;
+
+ if (d->be_event_opened_cnt++) {
+ /* Ignore subsequent open events from other backends */
+ return;
+ }
+ } else if (event == CHR_EVENT_CLOSED) {
+ if (!d->be_event_opened_cnt) {
+ /* Don't go below zero. Probably assert is better */
+ return;
+ }
+ if (--d->be_event_opened_cnt) {
+ /* Serve only the last one close event */
+ return;
+ }
+ }
+
+ if (fe && fe->chr_event) {
+ fe->chr_event(fe->opaque, event);
+ }
+}
+
+static GSource *hub_chr_add_watch(Chardev *s, GIOCondition cond)
+{
+ HubChardev *d = HUB_CHARDEV(s);
+ Chardev *chr;
+ ChardevClass *cc;
+
+ if (d->be_eagain_ind == -1) {
+ return NULL;
+ }
+
+ assert(d->be_eagain_ind < d->be_cnt);
+ chr = qemu_chr_fe_get_driver(&d->backends[d->be_eagain_ind].be);
+ cc = CHARDEV_GET_CLASS(chr);
+ if (!cc->chr_add_watch) {
+ return NULL;
+ }
+
+ return cc->chr_add_watch(chr, cond);
+}
+
+static bool hub_chr_attach_chardev(HubChardev *d, Chardev *chr,
+ Error **errp)
+{
+ bool ret;
+
+ if (d->be_cnt >= MAX_HUB) {
+ error_setg(errp, "hub: too many uses of chardevs '%s'"
+ " (maximum is " stringify(MAX_HUB) ")",
+ d->parent.label);
+ return false;
+ }
+ ret = qemu_chr_fe_init(&d->backends[d->be_cnt].be, chr, errp);
+ if (ret) {
+ d->backends[d->be_cnt].hub = d;
+ d->backends[d->be_cnt].be_ind = d->be_cnt;
+ d->be_cnt += 1;
+ }
+
+ return ret;
+}
+
+static void char_hub_finalize(Object *obj)
+{
+ HubChardev *d = HUB_CHARDEV(obj);
+ int i;
+
+ for (i = 0; i < d->be_cnt; i++) {
+ qemu_chr_fe_deinit(&d->backends[i].be, false);
+ }
+}
+
+static void hub_chr_update_read_handlers(Chardev *chr)
+{
+ HubChardev *d = HUB_CHARDEV(chr);
+ int i;
+
+ for (i = 0; i < d->be_cnt; i++) {
+ qemu_chr_fe_set_handlers_full(&d->backends[i].be,
+ hub_chr_can_read,
+ hub_chr_read,
+ hub_chr_event,
+ NULL,
+ &d->backends[i],
+ chr->gcontext, true, false);
+ }
+}
+
+static void qemu_chr_open_hub(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
+{
+ ChardevHub *hub = backend->u.hub.data;
+ HubChardev *d = HUB_CHARDEV(chr);
+ strList *list = hub->chardevs;
+
+ d->be_eagain_ind = -1;
+
+ if (list == NULL) {
+ error_setg(errp, "hub: 'chardevs' list is not defined");
+ return;
+ }
+
+ while (list) {
+ Chardev *s;
+
+ s = qemu_chr_find(list->value);
+ if (s == NULL) {
+ error_setg(errp, "hub: chardev can't be found by id '%s'",
+ list->value);
+ return;
+ }
+ if (CHARDEV_IS_HUB(s) || CHARDEV_IS_MUX(s)) {
+ error_setg(errp, "hub: multiplexers and hub devices can't be "
+ "stacked, check chardev '%s', chardev should not "
+ "be a hub device or have 'mux=on' enabled",
+ list->value);
+ return;
+ }
+ if (!hub_chr_attach_chardev(d, s, errp)) {
+ return;
+ }
+ list = list->next;
+ }
+
+ /* Closed until an explicit event from backend */
+ *be_opened = false;
+}
+
+static void qemu_chr_parse_hub(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ ChardevHub *hub;
+ strList **tail;
+ int i;
+
+ backend->type = CHARDEV_BACKEND_KIND_HUB;
+ hub = backend->u.hub.data = g_new0(ChardevHub, 1);
+ qemu_chr_parse_common(opts, qapi_ChardevHub_base(hub));
+
+ tail = &hub->chardevs;
+
+ for (i = 0; i < MAX_HUB; i++) {
+ char optbuf[16];
+ const char *dev;
+
+ snprintf(optbuf, sizeof(optbuf), "chardevs.%u", i);
+ dev = qemu_opt_get(opts, optbuf);
+ if (!dev) {
+ break;
+ }
+
+ QAPI_LIST_APPEND(tail, g_strdup(dev));
+ }
+}
+
+static void char_hub_class_init(ObjectClass *oc, const void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->parse = qemu_chr_parse_hub;
+ cc->open = qemu_chr_open_hub;
+ cc->chr_write = hub_chr_write;
+ cc->chr_add_watch = hub_chr_add_watch;
+ /* We handle events from backends only */
+ cc->chr_be_event = NULL;
+ cc->chr_update_read_handler = hub_chr_update_read_handlers;
+}
+
+static const TypeInfo char_hub_type_info = {
+ .name = TYPE_CHARDEV_HUB,
+ .parent = TYPE_CHARDEV,
+ .class_init = char_hub_class_init,
+ .instance_size = sizeof(HubChardev),
+ .instance_finalize = char_hub_finalize,
+};
+
+static void register_types(void)
+{
+ type_register_static(&char_hub_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index ee2d47b..6b36290 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -26,8 +26,9 @@
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/option.h"
+#include "qemu/bitops.h"
#include "chardev/char.h"
-#include "sysemu/block-backend.h"
+#include "system/block-backend.h"
#include "qapi/qapi-commands-control.h"
#include "chardev-internal.h"
@@ -73,11 +74,11 @@ static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
* qemu_chr_fe_write and background I/O callbacks */
qemu_chr_fe_write_all(&d->chr,
(uint8_t *)buf1, strlen(buf1));
- d->linestart = 0;
+ d->linestart = false;
}
ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
if (buf[i] == '\n') {
- d->linestart = 1;
+ d->linestart = true;
}
}
}
@@ -124,7 +125,8 @@ static void mux_print_help(Chardev *chr)
}
}
-static void mux_chr_send_event(MuxChardev *d, int mux_nr, QEMUChrEvent event)
+static void mux_chr_send_event(MuxChardev *d, unsigned int mux_nr,
+ QEMUChrEvent event)
{
CharBackend *be = d->backends[mux_nr];
@@ -145,7 +147,7 @@ static void mux_chr_be_event(Chardev *chr, QEMUChrEvent event)
static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
{
if (d->term_got_escape) {
- d->term_got_escape = 0;
+ d->term_got_escape = false;
if (ch == term_escape_char) {
goto send_char;
}
@@ -167,19 +169,26 @@ static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
case 'b':
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
break;
- case 'c':
- assert(d->mux_cnt > 0); /* handler registered with first fe */
+ case 'c': {
+ unsigned int bit;
+
+ /* Handler registered with first fe */
+ assert(d->mux_bitset != 0);
/* Switch to the next registered device */
- mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
+ bit = find_next_bit(&d->mux_bitset, MAX_MUX, d->focus + 1);
+ if (bit >= MAX_MUX) {
+ bit = find_next_bit(&d->mux_bitset, MAX_MUX, 0);
+ }
+ mux_set_focus(chr, bit);
break;
- case 't':
+ } case 't':
d->timestamps = !d->timestamps;
d->timestamps_start = -1;
- d->linestart = 0;
+ d->linestart = false;
break;
}
} else if (ch == term_escape_char) {
- d->term_got_escape = 1;
+ d->term_got_escape = true;
} else {
send_char:
return 1;
@@ -242,15 +251,16 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event)
{
MuxChardev *d = MUX_CHARDEV(chr);
- int i;
+ int bit;
if (!muxes_opened) {
return;
}
/* Send the event to all registered listeners */
- for (i = 0; i < d->mux_cnt; i++) {
- mux_chr_send_event(d, i, event);
+ bit = -1;
+ while ((bit = find_next_bit(&d->mux_bitset, MAX_MUX, bit + 1)) < MAX_MUX) {
+ mux_chr_send_event(d, bit, event);
}
}
@@ -275,14 +285,15 @@ static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
static void char_mux_finalize(Object *obj)
{
MuxChardev *d = MUX_CHARDEV(obj);
- int i;
+ int bit;
- for (i = 0; i < d->mux_cnt; i++) {
- CharBackend *be = d->backends[i];
- if (be) {
- be->chr = NULL;
- }
+ bit = -1;
+ while ((bit = find_next_bit(&d->mux_bitset, MAX_MUX, bit + 1)) < MAX_MUX) {
+ CharBackend *be = d->backends[bit];
+ be->chr = NULL;
+ d->backends[bit] = NULL;
}
+ d->mux_bitset = 0;
qemu_chr_fe_deinit(&d->chr, false);
}
@@ -300,12 +311,46 @@ static void mux_chr_update_read_handlers(Chardev *chr)
chr->gcontext, true, false);
}
-void mux_set_focus(Chardev *chr, int focus)
+bool mux_chr_attach_frontend(MuxChardev *d, CharBackend *b,
+ unsigned int *tag, Error **errp)
+{
+ unsigned int bit;
+
+ QEMU_BUILD_BUG_ON(MAX_MUX > (sizeof(d->mux_bitset) * BITS_PER_BYTE));
+
+ bit = find_next_zero_bit(&d->mux_bitset, MAX_MUX, 0);
+ if (bit >= MAX_MUX) {
+ error_setg(errp,
+ "too many uses of multiplexed chardev '%s'"
+ " (maximum is " stringify(MAX_MUX) ")",
+ d->parent.label);
+ return false;
+ }
+
+ d->mux_bitset |= (1ul << bit);
+ d->backends[bit] = b;
+ *tag = bit;
+
+ return true;
+}
+
+bool mux_chr_detach_frontend(MuxChardev *d, unsigned int tag)
+{
+ if (!(d->mux_bitset & (1ul << tag))) {
+ return false;
+ }
+
+ d->mux_bitset &= ~(1ul << tag);
+ d->backends[tag] = NULL;
+
+ return true;
+}
+
+void mux_set_focus(Chardev *chr, unsigned int focus)
{
MuxChardev *d = MUX_CHARDEV(chr);
- assert(focus >= 0);
- assert(focus < d->mux_cnt);
+ assert(d->mux_bitset & (1ul << focus));
if (d->focus != -1) {
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
@@ -402,7 +447,7 @@ void resume_mux_open(void)
chardev_options_parsed_cb, NULL);
}
-static void char_mux_class_init(ObjectClass *oc, void *data)
+static void char_mux_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-null.c b/chardev/char-null.c
index 1c6a290..89cb85d 100644
--- a/chardev/char-null.c
+++ b/chardev/char-null.c
@@ -34,7 +34,7 @@ static void null_chr_open(Chardev *chr,
*be_opened = false;
}
-static void char_null_class_init(ObjectClass *oc, void *data)
+static void char_null_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-parallel.c b/chardev/char-parallel.c
index 78697d7..62a44b2 100644
--- a/chardev/char-parallel.c
+++ b/chardev/char-parallel.c
@@ -270,7 +270,7 @@ static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
parallel->device = g_strdup(device);
}
-static void char_parallel_class_init(ObjectClass *oc, void *data)
+static void char_parallel_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c
index 5ad30bc..3d1b0ce 100644
--- a/chardev/char-pipe.c
+++ b/chardev/char-pipe.c
@@ -171,7 +171,7 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
dev->device = g_strdup(device);
}
-static void char_pipe_class_init(ObjectClass *oc, void *data)
+static void char_pipe_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-pty.c b/chardev/char-pty.c
index cc2f761..674e9b3 100644
--- a/chardev/char-pty.c
+++ b/chardev/char-pty.c
@@ -29,6 +29,7 @@
#include "qemu/sockets.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
+#include "qemu/option.h"
#include "qemu/qemu-print.h"
#include "chardev/char-io.h"
@@ -41,6 +42,7 @@ struct PtyChardev {
int connected;
GSource *timer_src;
+ char *path;
};
typedef struct PtyChardev PtyChardev;
@@ -152,7 +154,7 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
Chardev *chr = CHARDEV(opaque);
PtyChardev *s = PTY_CHARDEV(opaque);
gsize len;
- uint8_t buf[CHR_READ_BUF_LEN];
+ QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN];
ssize_t ret;
len = sizeof(buf);
@@ -179,6 +181,9 @@ static void pty_chr_state(Chardev *chr, int connected)
if (!connected) {
remove_fd_in_watch(chr);
+ if (s->connected) {
+ qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+ }
s->connected = 0;
/* (re-)connect poll interval for idle guests: once per second.
* We check more frequently in case the guests sends data to
@@ -204,10 +209,15 @@ static void char_pty_finalize(Object *obj)
Chardev *chr = CHARDEV(obj);
PtyChardev *s = PTY_CHARDEV(obj);
+ /* unlink symlink */
+ if (s->path) {
+ unlink(s->path);
+ g_free(s->path);
+ }
+
pty_chr_state(chr, 0);
object_unref(OBJECT(s->ioc));
pty_chr_timer_cancel(s);
- qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
#if defined HAVE_PTY_H
@@ -330,6 +340,7 @@ static void char_pty_open(Chardev *chr,
int master_fd, slave_fd;
char pty_name[PATH_MAX];
char *name;
+ char *path = backend->u.pty.data->path;
master_fd = qemu_openpty_raw(&slave_fd, pty_name);
if (master_fd < 0) {
@@ -354,12 +365,36 @@ static void char_pty_open(Chardev *chr,
g_free(name);
s->timer_src = NULL;
*be_opened = false;
+
+ /* create symbolic link */
+ if (path) {
+ int res = symlink(pty_name, path);
+
+ if (res != 0) {
+ error_setg_errno(errp, errno, "Failed to create PTY symlink");
+ } else {
+ s->path = g_strdup(path);
+ }
+ }
+}
+
+static void char_pty_parse(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ const char *path = qemu_opt_get(opts, "path");
+ ChardevPty *pty;
+
+ backend->type = CHARDEV_BACKEND_KIND_PTY;
+ pty = backend->u.pty.data = g_new0(ChardevPty, 1);
+ qemu_chr_parse_common(opts, qapi_ChardevPty_base(pty));
+ pty->path = g_strdup(path);
}
-static void char_pty_class_init(ObjectClass *oc, void *data)
+static void char_pty_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
+ cc->parse = char_pty_parse;
cc->open = char_pty_open;
cc->chr_write = char_pty_chr_write;
cc->chr_update_read_handler = pty_chr_update_read_handler;
diff --git a/chardev/char-ringbuf.c b/chardev/char-ringbuf.c
index d40d21d..98aadb6 100644
--- a/chardev/char-ringbuf.c
+++ b/chardev/char-ringbuf.c
@@ -223,7 +223,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
}
}
-static void char_ringbuf_class_init(ObjectClass *oc, void *data)
+static void char_ringbuf_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-serial.c b/chardev/char-serial.c
index 4b0b83d..0a68b4b 100644
--- a/chardev/char-serial.c
+++ b/chardev/char-serial.c
@@ -298,7 +298,7 @@ static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
serial->device = g_strdup(device);
}
-static void char_serial_class_init(ObjectClass *oc, void *data)
+static void char_serial_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 812d7aa..1e83139 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -33,6 +33,7 @@
#include "qapi/clone-visitor.h"
#include "qapi/qapi-visit-sockets.h"
#include "qemu/yank.h"
+#include "trace.h"
#include "chardev/char-io.h"
#include "chardev/char-socket.h"
@@ -73,7 +74,7 @@ static void qemu_chr_socket_restart_timer(Chardev *chr)
assert(!s->reconnect_timer);
name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label);
s->reconnect_timer = qemu_chr_timeout_add_ms(chr,
- s->reconnect_time * 1000,
+ s->reconnect_time_ms,
socket_reconnect_timeout,
chr);
g_source_set_name(s->reconnect_timer, name);
@@ -126,6 +127,7 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
if (ret < 0 && errno != EAGAIN) {
if (tcp_chr_read_poll(chr) <= 0) {
/* Perform disconnect and return error. */
+ trace_chr_socket_poll_err(chr, chr->label);
tcp_chr_disconnect_locked(chr);
} /* else let the read handler finish it properly */
}
@@ -279,15 +281,16 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
size_t i;
int *msgfds = NULL;
size_t msgfds_num = 0;
+ Error *err = NULL;
if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
ret = qio_channel_readv_full(s->ioc, &iov, 1,
&msgfds, &msgfds_num,
- 0, NULL);
+ 0, &err);
} else {
ret = qio_channel_readv_full(s->ioc, &iov, 1,
NULL, NULL,
- 0, NULL);
+ 0, &err);
}
if (msgfds_num) {
@@ -322,7 +325,11 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
errno = EAGAIN;
ret = -1;
} else if (ret == -1) {
+ trace_chr_socket_recv_err(chr, chr->label, error_get_pretty(err));
+ error_free(err);
errno = EIO;
+ } else if (ret == 0) {
+ trace_chr_socket_recv_eof(chr, chr->label);
}
return ret;
@@ -463,6 +470,7 @@ static void tcp_chr_disconnect_locked(Chardev *chr)
SocketChardev *s = SOCKET_CHARDEV(chr);
bool emit_close = s->state == TCP_CHARDEV_STATE_CONNECTED;
+ trace_chr_socket_disconnect(chr, chr->label);
tcp_chr_free_connection(chr);
if (s->listener) {
@@ -473,7 +481,7 @@ static void tcp_chr_disconnect_locked(Chardev *chr)
if (emit_close) {
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
- if (s->reconnect_time && !s->reconnect_timer) {
+ if (s->reconnect_time_ms && !s->reconnect_timer) {
qemu_chr_socket_restart_timer(chr);
}
}
@@ -489,7 +497,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
{
Chardev *chr = CHARDEV(opaque);
SocketChardev *s = SOCKET_CHARDEV(opaque);
- uint8_t buf[CHR_READ_BUF_LEN];
+ QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN];
int len, size;
if ((s->state != TCP_CHARDEV_STATE_CONNECTED) ||
@@ -521,6 +529,7 @@ static gboolean tcp_chr_hup(QIOChannel *channel,
void *opaque)
{
Chardev *chr = CHARDEV(opaque);
+ trace_chr_socket_hangup(chr, chr->label);
tcp_chr_disconnect(chr);
return G_SOURCE_REMOVE;
}
@@ -562,9 +571,13 @@ static char *qemu_chr_compute_filename(SocketChardev *s)
switch (ss->ss_family) {
case AF_UNIX:
- return g_strdup_printf("unix:%s%s",
- ((struct sockaddr_un *)(ss))->sun_path,
- s->is_listen ? ",server=on" : "");
+ if (s->is_listen) {
+ return g_strdup_printf("unix:%s,server=on",
+ ((struct sockaddr_un *)(ss))->sun_path);
+ } else {
+ return g_strdup_printf("unix:%s",
+ ((struct sockaddr_un *)(ps))->sun_path);
+ }
case AF_INET6:
left = "[";
right = "]";
@@ -672,15 +685,18 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
SocketChardev *s = user_data;
Chardev *chr = CHARDEV(s);
TCPChardevTelnetInit *init = s->telnet_init;
+ Error *err = NULL;
ssize_t ret;
assert(init);
- ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
+ ret = qio_channel_write(ioc, init->buf, init->buflen, &err);
if (ret < 0) {
if (ret == QIO_CHANNEL_ERR_BLOCK) {
ret = 0;
} else {
+ trace_chr_socket_write_err(chr, chr->label, error_get_pretty(err));
+ error_free(err);
tcp_chr_disconnect(chr);
goto end;
}
@@ -765,9 +781,9 @@ static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data)
Error *err = NULL;
if (qio_task_propagate_error(task, &err)) {
- error_reportf_err(err,
- "websock handshake of character device %s failed: ",
- chr->label);
+ trace_chr_socket_ws_handshake_err(chr, chr->label,
+ error_get_pretty(err));
+ error_free(err);
tcp_chr_disconnect(chr);
} else {
if (s->do_telnetopt) {
@@ -805,9 +821,9 @@ static void tcp_chr_tls_handshake(QIOTask *task,
Error *err = NULL;
if (qio_task_propagate_error(task, &err)) {
- error_reportf_err(err,
- "TLS handshake of character device %s failed: ",
- chr->label);
+ trace_chr_socket_tls_handshake_err(chr, chr->label,
+ error_get_pretty(err));
+ error_free(err);
tcp_chr_disconnect(chr);
} else {
if (s->is_websock) {
@@ -826,19 +842,22 @@ static void tcp_chr_tls_init(Chardev *chr)
SocketChardev *s = SOCKET_CHARDEV(chr);
QIOChannelTLS *tioc;
gchar *name;
+ Error *err = NULL;
if (s->is_listen) {
tioc = qio_channel_tls_new_server(
s->ioc, s->tls_creds,
s->tls_authz,
- NULL);
+ &err);
} else {
tioc = qio_channel_tls_new_client(
s->ioc, s->tls_creds,
s->addr->u.inet.host,
- NULL);
+ &err);
}
if (tioc == NULL) {
+ trace_chr_socket_tls_init_err(chr, chr->label, error_get_pretty(err));
+ error_free(err);
tcp_chr_disconnect(chr);
return;
}
@@ -1065,9 +1084,9 @@ static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
} else {
Error *err = NULL;
if (tcp_chr_connect_client_sync(chr, &err) < 0) {
- if (s->reconnect_time) {
+ if (s->reconnect_time_ms) {
error_free(err);
- g_usleep(s->reconnect_time * 1000ULL * 1000ULL);
+ g_usleep(s->reconnect_time_ms * 1000ULL);
} else {
error_propagate(errp, err);
return -1;
@@ -1252,13 +1271,13 @@ skip_listen:
static int qmp_chardev_open_socket_client(Chardev *chr,
- int64_t reconnect,
+ int64_t reconnect_ms,
Error **errp)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
- if (reconnect > 0) {
- s->reconnect_time = reconnect;
+ if (reconnect_ms > 0) {
+ s->reconnect_time_ms = reconnect_ms;
tcp_chr_connect_client_async(chr);
return 0;
} else {
@@ -1339,6 +1358,12 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock,
}
}
+ if (sock->has_reconnect_ms && sock->has_reconnect) {
+ error_setg(errp,
+ "'reconnect' and 'reconnect-ms' are mutually exclusive");
+ return false;
+ }
+
return true;
}
@@ -1356,7 +1381,7 @@ static void qmp_chardev_open_socket(Chardev *chr,
bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false;
bool is_waitconnect = sock->has_wait ? sock->wait : false;
bool is_websock = sock->has_websocket ? sock->websocket : false;
- int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
+ int64_t reconnect_ms = 0;
SocketAddress *addr;
s->is_listen = is_listen;
@@ -1428,7 +1453,13 @@ static void qmp_chardev_open_socket(Chardev *chr,
return;
}
} else {
- if (qmp_chardev_open_socket_client(chr, reconnect, errp) < 0) {
+ if (sock->has_reconnect) {
+ reconnect_ms = sock->reconnect * 1000ULL;
+ } else if (sock->has_reconnect_ms) {
+ reconnect_ms = sock->reconnect_ms;
+ }
+
+ if (qmp_chardev_open_socket_client(chr, reconnect_ms, errp) < 0) {
return;
}
}
@@ -1494,6 +1525,9 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
sock->wait = qemu_opt_get_bool(opts, "wait", true);
sock->has_reconnect = qemu_opt_find(opts, "reconnect");
sock->reconnect = qemu_opt_get_number(opts, "reconnect", 0);
+ sock->has_reconnect_ms = qemu_opt_find(opts, "reconnect-ms");
+ sock->reconnect_ms = qemu_opt_get_number(opts, "reconnect-ms", 0);
+
sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds"));
sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz"));
@@ -1547,7 +1581,7 @@ char_socket_get_connected(Object *obj, Error **errp)
return s->state == TCP_CHARDEV_STATE_CONNECTED;
}
-static void char_socket_class_init(ObjectClass *oc, void *data)
+static void char_socket_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c
index b960ddd..48db8d2 100644
--- a/chardev/char-stdio.c
+++ b/chardev/char-stdio.c
@@ -136,7 +136,7 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
stdio->signal = qemu_opt_get_bool(opts, "signal", true);
}
-static void char_stdio_class_init(ObjectClass *oc, void *data)
+static void char_stdio_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-udp.c b/chardev/char-udp.c
index 3d9a2d5..572fab0 100644
--- a/chardev/char-udp.c
+++ b/chardev/char-udp.c
@@ -219,7 +219,7 @@ static void qmp_chardev_open_udp(Chardev *chr,
*be_opened = false;
}
-static void char_udp_class_init(ObjectClass *oc, void *data)
+static void char_udp_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c
index 1a18999..fb802a0 100644
--- a/chardev/char-win-stdio.c
+++ b/chardev/char-win-stdio.c
@@ -33,6 +33,7 @@
struct WinStdioChardev {
Chardev parent;
HANDLE hStdIn;
+ DWORD dwOldMode;
HANDLE hInputReadyEvent;
HANDLE hInputDoneEvent;
HANDLE hInputThread;
@@ -159,6 +160,7 @@ static void qemu_chr_open_stdio(Chardev *chr,
}
is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
+ stdio->dwOldMode = dwMode;
if (is_console) {
if (qemu_add_wait_object(stdio->hStdIn,
@@ -221,6 +223,9 @@ static void char_win_stdio_finalize(Object *obj)
{
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj);
+ if (stdio->hStdIn != INVALID_HANDLE_VALUE) {
+ SetConsoleMode(stdio->hStdIn, stdio->dwOldMode);
+ }
if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
CloseHandle(stdio->hInputReadyEvent);
}
@@ -251,7 +256,7 @@ static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
return len - len1;
}
-static void char_win_stdio_class_init(ObjectClass *oc, void *data)
+static void char_win_stdio_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char-win.c b/chardev/char-win.c
index d4fb44c..fef45e8 100644
--- a/chardev/char-win.c
+++ b/chardev/char-win.c
@@ -220,7 +220,7 @@ void win_chr_set_file(Chardev *chr, HANDLE file, bool keep_open)
s->file = file;
}
-static void char_win_class_init(ObjectClass *oc, void *data)
+static void char_win_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/char.c b/chardev/char.c
index 3c43fb1..bbebd24 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -33,7 +33,7 @@
#include "qapi/error.h"
#include "qapi/qapi-commands-char.h"
#include "qapi/qmp/qerror.h"
-#include "sysemu/replay.h"
+#include "system/replay.h"
#include "qemu/help_option.h"
#include "qemu/module.h"
#include "qemu/option.h"
@@ -48,7 +48,7 @@
Object *get_chardevs_root(void)
{
- return container_get(object_get_root(), "/chardevs");
+ return object_get_container("chardevs");
}
static void chr_be_event(Chardev *s, QEMUChrEvent event)
@@ -295,7 +295,7 @@ static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
return len;
}
-static void char_class_init(ObjectClass *oc, void *data)
+static void char_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@@ -333,7 +333,7 @@ static bool qemu_chr_is_busy(Chardev *s)
{
if (CHARDEV_IS_MUX(s)) {
MuxChardev *d = MUX_CHARDEV(s);
- return d->mux_cnt >= 0;
+ return d->mux_bitset != 0;
} else {
return s->be != NULL;
}
@@ -428,6 +428,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
qemu_opt_set(opts, "path", p, &error_abort);
return opts;
}
+ if (strstart(filename, "pty:", &p)) {
+ qemu_opt_set(opts, "backend", "pty", &error_abort);
+ qemu_opt_set(opts, "path", p, &error_abort);
+ return opts;
+ }
if (strstart(filename, "tcp:", &p) ||
strstart(filename, "telnet:", &p) ||
strstart(filename, "tn3270:", &p) ||
@@ -615,11 +620,24 @@ ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp)
return backend;
}
-Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
- Error **errp)
+static void qemu_chardev_set_replay(Chardev *chr, Error **errp)
+{
+ if (replay_mode != REPLAY_MODE_NONE) {
+ if (CHARDEV_GET_CLASS(chr)->chr_ioctl) {
+ error_setg(errp, "Replay: ioctl is not supported "
+ "for serial devices yet");
+ return;
+ }
+ qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+ replay_register_char_driver(chr);
+ }
+}
+
+static Chardev *do_qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
+ bool replay, Error **errp)
{
const ChardevClass *cc;
- Chardev *chr = NULL;
+ Chardev *base = NULL, *chr = NULL;
ChardevBackend *backend = NULL;
const char *name = qemu_opt_get(opts, "backend");
const char *id = qemu_opts_id(opts);
@@ -657,11 +675,11 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
chr = qemu_chardev_new(bid ? bid : id,
object_class_get_name(OBJECT_CLASS(cc)),
backend, context, errp);
-
if (chr == NULL) {
goto out;
}
+ base = chr;
if (bid) {
Chardev *mux;
qapi_free_ChardevBackend(backend);
@@ -681,11 +699,25 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
out:
qapi_free_ChardevBackend(backend);
g_free(bid);
+
+ if (replay && base) {
+ /* RR should be set on the base device, not the mux */
+ qemu_chardev_set_replay(base, errp);
+ }
+
return chr;
}
-Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
- bool permit_mux_mon, GMainContext *context)
+Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
+ Error **errp)
+{
+ /* XXX: should this really not record/replay? */
+ return do_qemu_chr_new_from_opts(opts, context, false, errp);
+}
+
+static Chardev *qemu_chr_new_from_name(const char *label, const char *filename,
+ bool permit_mux_mon,
+ GMainContext *context, bool replay)
{
const char *p;
Chardev *chr;
@@ -693,14 +725,22 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
Error *err = NULL;
if (strstart(filename, "chardev:", &p)) {
- return qemu_chr_find(p);
+ chr = qemu_chr_find(p);
+ if (replay && chr) {
+ qemu_chardev_set_replay(chr, &err);
+ if (err) {
+ error_report_err(err);
+ return NULL;
+ }
+ }
+ return chr;
}
opts = qemu_chr_parse_compat(label, filename, permit_mux_mon);
if (!opts)
return NULL;
- chr = qemu_chr_new_from_opts(opts, context, &err);
+ chr = do_qemu_chr_new_from_opts(opts, context, replay, &err);
if (!chr) {
error_report_err(err);
goto out;
@@ -722,24 +762,20 @@ out:
return chr;
}
+Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
+ bool permit_mux_mon, GMainContext *context)
+{
+ return qemu_chr_new_from_name(label, filename, permit_mux_mon, context,
+ false);
+}
+
static Chardev *qemu_chr_new_permit_mux_mon(const char *label,
const char *filename,
bool permit_mux_mon,
GMainContext *context)
{
- Chardev *chr;
- chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon, context);
- if (chr) {
- if (replay_mode != REPLAY_MODE_NONE) {
- qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
- }
- if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) {
- error_report("Replay: ioctl is not supported "
- "for serial devices yet");
- }
- replay_register_char_driver(chr);
- }
- return chr;
+ return qemu_chr_new_from_name(label, filename, permit_mux_mon, context,
+ true);
}
Chardev *qemu_chr_new(const char *label, const char *filename,
@@ -860,6 +896,9 @@ QemuOptsList qemu_chardev_opts = {
.name = "reconnect",
.type = QEMU_OPT_NUMBER,
},{
+ .name = "reconnect-ms",
+ .type = QEMU_OPT_NUMBER,
+ },{
.name = "telnet",
.type = QEMU_OPT_BOOL,
},{
@@ -904,7 +943,26 @@ QemuOptsList qemu_chardev_opts = {
},{
.name = "chardev",
.type = QEMU_OPT_STRING,
+ },
+ /*
+ * Multiplexer options. Follows QAPI array syntax.
+ * See MAX_HUB macro to obtain array capacity.
+ */
+ {
+ .name = "chardevs.0",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "chardevs.1",
+ .type = QEMU_OPT_STRING,
},{
+ .name = "chardevs.2",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "chardevs.3",
+ .type = QEMU_OPT_STRING,
+ },
+
+ {
.name = "append",
.type = QEMU_OPT_BOOL,
},{
@@ -1067,8 +1125,8 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
return NULL;
}
- if (CHARDEV_IS_MUX(chr)) {
- error_setg(errp, "Mux device hotswap not supported yet");
+ if (CHARDEV_IS_MUX(chr) || CHARDEV_IS_HUB(chr)) {
+ error_setg(errp, "For mux or hub device hotswap is not supported yet");
return NULL;
}
diff --git a/chardev/chardev-internal.h b/chardev/chardev-internal.h
index 4e03af3..9752dd7 100644
--- a/chardev/chardev-internal.h
+++ b/chardev/chardev-internal.h
@@ -29,38 +29,89 @@
#include "chardev/char-fe.h"
#include "qom/object.h"
+#define MAX_HUB 4
#define MAX_MUX 4
#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
struct MuxChardev {
Chardev parent;
+ /* Linked frontends */
CharBackend *backends[MAX_MUX];
+ /* Linked backend */
CharBackend chr;
+ unsigned long mux_bitset;
int focus;
- int mux_cnt;
- int term_got_escape;
- int max_size;
+ bool term_got_escape;
/* Intermediate input buffer catches escape sequences even if the
currently active device is not accepting any input - but only until it
is full as well. */
unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
- int prod[MAX_MUX];
- int cons[MAX_MUX];
+ unsigned int prod[MAX_MUX];
+ unsigned int cons[MAX_MUX];
int timestamps;
/* Protected by the Chardev chr_write_lock. */
- int linestart;
+ bool linestart;
int64_t timestamps_start;
};
typedef struct MuxChardev MuxChardev;
+typedef struct HubChardev HubChardev;
+typedef struct HubCharBackend HubCharBackend;
+
+/*
+ * Back-pointer on a hub, actual backend and its index in
+ * `hub->backends` array
+ */
+struct HubCharBackend {
+ HubChardev *hub;
+ CharBackend be;
+ unsigned int be_ind;
+};
+
+struct HubChardev {
+ Chardev parent;
+ /* Linked backends */
+ HubCharBackend backends[MAX_HUB];
+ /*
+ * Number of backends attached to this hub. Once attached, a
+ * backend can't be detached, so the counter is only increasing.
+ * To safely remove a backend, hub has to be removed first.
+ */
+ unsigned int be_cnt;
+ /*
+ * Number of CHR_EVEN_OPENED events from all backends. Needed to
+ * send CHR_EVEN_CLOSED only when counter goes to zero.
+ */
+ unsigned int be_event_opened_cnt;
+ /*
+ * Counters of written bytes from a single frontend device
+ * to multiple backend devices.
+ */
+ unsigned int be_written[MAX_HUB];
+ unsigned int be_min_written;
+ /*
+ * Index of a backend device which got EAGAIN on last write,
+ * -1 is invalid index.
+ */
+ int be_eagain_ind;
+};
+typedef struct HubChardev HubChardev;
DECLARE_INSTANCE_CHECKER(MuxChardev, MUX_CHARDEV,
TYPE_CHARDEV_MUX)
-#define CHARDEV_IS_MUX(chr) \
+DECLARE_INSTANCE_CHECKER(HubChardev, HUB_CHARDEV,
+ TYPE_CHARDEV_HUB)
+
+#define CHARDEV_IS_MUX(chr) \
object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
+#define CHARDEV_IS_HUB(chr) \
+ object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_HUB)
-void mux_set_focus(Chardev *chr, int focus);
+bool mux_chr_attach_frontend(MuxChardev *d, CharBackend *b,
+ unsigned int *tag, Error **errp);
+bool mux_chr_detach_frontend(MuxChardev *d, unsigned int tag);
+void mux_set_focus(Chardev *chr, unsigned int focus);
void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event);
Object *get_chardevs_root(void);
diff --git a/chardev/meson.build b/chardev/meson.build
index 70070a8..56ee39a 100644
--- a/chardev/meson.build
+++ b/chardev/meson.build
@@ -3,6 +3,7 @@ chardev_ss.add(files(
'char-file.c',
'char-io.c',
'char-mux.c',
+ 'char-hub.c',
'char-null.c',
'char-pipe.c',
'char-ringbuf.c',
diff --git a/chardev/msmouse.c b/chardev/msmouse.c
index a774c39..1a55755 100644
--- a/chardev/msmouse.c
+++ b/chardev/msmouse.c
@@ -81,7 +81,7 @@ static void msmouse_chr_accept_input(Chardev *chr)
const uint8_t *buf;
uint32_t size;
- buf = fifo8_pop_buf(&mouse->outbuf, MIN(len, avail), &size);
+ buf = fifo8_pop_bufptr(&mouse->outbuf, MIN(len, avail), &size);
qemu_chr_be_write(chr, buf, size);
len = qemu_chr_be_can_write(chr);
avail -= size;
@@ -267,7 +267,7 @@ static void msmouse_chr_open(Chardev *chr,
fifo8_create(&mouse->outbuf, MSMOUSE_BUF_SZ);
}
-static void char_msmouse_class_init(ObjectClass *oc, void *data)
+static void char_msmouse_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/spice.c b/chardev/spice.c
index e843d96..db53b49 100644
--- a/chardev/spice.c
+++ b/chardev/spice.c
@@ -347,7 +347,7 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
spiceport->fqdn = g_strdup(name);
}
-static void char_spice_class_init(ObjectClass *oc, void *data)
+static void char_spice_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@@ -366,7 +366,7 @@ static const TypeInfo char_spice_type_info = {
};
module_obj(TYPE_CHARDEV_SPICE);
-static void char_spicevmc_class_init(ObjectClass *oc, void *data)
+static void char_spicevmc_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@@ -382,7 +382,7 @@ static const TypeInfo char_spicevmc_type_info = {
};
module_obj(TYPE_CHARDEV_SPICEVMC);
-static void char_spiceport_class_init(ObjectClass *oc, void *data)
+static void char_spiceport_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/testdev.c b/chardev/testdev.c
index a92caca..e91f4e8 100644
--- a/chardev/testdev.c
+++ b/chardev/testdev.c
@@ -110,7 +110,7 @@ static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len)
return orig_len;
}
-static void char_testdev_class_init(ObjectClass *oc, void *data)
+static void char_testdev_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
diff --git a/chardev/trace-events b/chardev/trace-events
index 027107b..7e97b8a 100644
--- a/chardev/trace-events
+++ b/chardev/trace-events
@@ -17,3 +17,13 @@ spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p"
spice_vmc_event(int event) "spice vmc event %d"
+# char-socket.c
+chr_socket_poll_err(void *chrdev, const char *label) "chardev socket poll error %p (%s)"
+chr_socket_recv_err(void *chrdev, const char *label, const char *err) "chardev socket recv error %p (%s): %s"
+chr_socket_recv_eof(void *chrdev, const char *label) "chardev socket recv end-of-file %p (%s)"
+chr_socket_write_err(void *chrdev, const char *label, const char *err) "chardev socket write error %p (%s): %s"
+chr_socket_disconnect(void *chrdev, const char *label) "chardev socket disconnect %p (%s)"
+chr_socket_hangup(void *chrdev, const char *label) "chardev socket hangup %p (%s)"
+chr_socket_ws_handshake_err(void *chrdev, const char *label, const char *err) "chardev socket websock handshake error %p (%s): %s"
+chr_socket_tls_handshake_err(void *chrdev, const char *label, const char *err) "chardev socket TLS handshake error %p (%s): %s"
+chr_socket_tls_init_err(void *chrdev, const char *label, const char *err) "chardev socket TLS init error %p (%s): %s"
diff --git a/chardev/wctablet.c b/chardev/wctablet.c
index f4008bf..0dc6ef0 100644
--- a/chardev/wctablet.c
+++ b/chardev/wctablet.c
@@ -342,7 +342,7 @@ static void wctablet_chr_open(Chardev *chr,
&wctablet_handler);
}
-static void wctablet_chr_class_init(ObjectClass *oc, void *data)
+static void wctablet_chr_class_init(ObjectClass *oc, const void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);