]> Git Repo - qemu.git/blobdiff - chardev/char.c
iotests: skip 042 with qcow which dosn't support zero sized images
[qemu.git] / chardev / char.c
index b542c25cecf3bed3c0f0155c10a6cca9f3f7f01a..2b679a2295c6053086f50687925bf2d1845af7b8 100644 (file)
  * THE SOFTWARE.
  */
 #include "qemu/osdep.h"
-#include "qemu-common.h"
 #include "qemu/cutils.h"
 #include "monitor/monitor.h"
 #include "sysemu/sysemu.h"
-#include "sysemu/block-backend.h"
+#include "qemu/config-file.h"
 #include "qemu/error-report.h"
-#include "qemu/timer.h"
-#include "sysemu/char.h"
-#include "hw/usb.h"
+#include "chardev/char.h"
 #include "qmp-commands.h"
-#include "qapi/clone-visitor.h"
 #include "qapi-visit.h"
-#include "qemu/base64.h"
-#include "io/channel-socket.h"
-#include "io/channel-file.h"
-#include "io/channel-tls.h"
 #include "sysemu/replay.h"
 #include "qemu/help_option.h"
 
-#include <zlib.h>
-
-#ifndef _WIN32
-#include <sys/times.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <net/if.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/select.h>
-#ifdef CONFIG_BSD
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#include <dev/ppbus/ppi.h>
-#include <dev/ppbus/ppbconf.h>
-#elif defined(__DragonFly__)
-#include <dev/misc/ppi/ppi.h>
-#include <bus/ppbus/ppbconf.h>
-#endif
-#else
-#ifdef __linux__
-#include <linux/ppdev.h>
-#include <linux/parport.h>
-#endif
-#ifdef __sun__
-#include <sys/ethernet.h>
-#include <sys/sockio.h>
-#include <netinet/arp.h>
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h> // must come after ip.h
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#endif
-#endif
-#endif
-
-#include "qemu/sockets.h"
-#include "ui/qemu-spice.h"
-
-#include "char-mux.h"
-#include "char-fd.h"
-#include "char-io.h"
-#ifdef _WIN32
-#include "char-win.h"
-#endif
+#include "chardev/char-mux.h"
 
 /***********************************************************/
 /* character device */
 
-static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
-    QTAILQ_HEAD_INITIALIZER(chardevs);
+static Object *get_chardevs_root(void)
+{
+    return container_get(object_get_root(), "/chardevs");
+}
 
 void qemu_chr_be_event(Chardev *s, int event)
 {
@@ -119,16 +64,9 @@ void qemu_chr_be_event(Chardev *s, int event)
     be->chr_event(be->opaque, event);
 }
 
-void qemu_chr_be_generic_open(Chardev *s)
-{
-    qemu_chr_be_event(s, CHR_EVENT_OPENED);
-}
-
-
 /* Not reporting errors from writing to logfile, as logs are
  * defined to be "best effort" only */
-static void qemu_chr_fe_write_log(Chardev *s,
-                                  const uint8_t *buf, size_t len)
+static void qemu_chr_write_log(Chardev *s, const uint8_t *buf, size_t len)
 {
     size_t done = 0;
     ssize_t ret;
@@ -152,8 +90,9 @@ static void qemu_chr_fe_write_log(Chardev *s,
     }
 }
 
-static int qemu_chr_fe_write_buffer(Chardev *s,
-                                    const uint8_t *buf, int len, int *offset)
+static int qemu_chr_write_buffer(Chardev *s,
+                                 const uint8_t *buf, int len,
+                                 int *offset, bool write_all)
 {
     ChardevClass *cc = CHARDEV_GET_CLASS(s);
     int res = 0;
@@ -163,7 +102,7 @@ static int qemu_chr_fe_write_buffer(Chardev *s,
     while (*offset < len) {
     retry:
         res = cc->chr_write(s, buf + *offset, len - *offset);
-        if (res < 0 && errno == EAGAIN) {
+        if (res < 0 && errno == EAGAIN && write_all) {
             g_usleep(100);
             goto retry;
         }
@@ -173,68 +112,31 @@ static int qemu_chr_fe_write_buffer(Chardev *s,
         }
 
         *offset += res;
+        if (!write_all) {
+            break;
+        }
     }
     if (*offset > 0) {
-        qemu_chr_fe_write_log(s, buf, *offset);
+        qemu_chr_write_log(s, buf, *offset);
     }
     qemu_mutex_unlock(&s->chr_write_lock);
 
     return res;
 }
 
-static bool qemu_chr_replay(Chardev *chr)
-{
-    return qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
-}
-
-int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
-{
-    Chardev *s = be->chr;
-    ChardevClass *cc;
-    int ret;
-
-    if (!s) {
-        return 0;
-    }
-
-    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
-        int offset;
-        replay_char_write_event_load(&ret, &offset);
-        assert(offset <= len);
-        qemu_chr_fe_write_buffer(s, buf, offset, &offset);
-        return ret;
-    }
-
-    cc = CHARDEV_GET_CLASS(s);
-    qemu_mutex_lock(&s->chr_write_lock);
-    ret = cc->chr_write(s, buf, len);
-
-    if (ret > 0) {
-        qemu_chr_fe_write_log(s, buf, ret);
-    }
-
-    qemu_mutex_unlock(&s->chr_write_lock);
-    
-    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
-        replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
-    }
-    
-    return ret;
-}
-
-int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
+int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all)
 {
-    int offset;
+    int offset = 0;
     int res;
 
     if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
         replay_char_write_event_load(&res, &offset);
         assert(offset <= len);
-        qemu_chr_fe_write_buffer(s, buf, offset, &offset);
+        qemu_chr_write_buffer(s, buf, offset, &offset, true);
         return res;
     }
 
-    res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
+    res = qemu_chr_write_buffer(s, buf, len, &offset, write_all);
 
     if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
         replay_char_write_event_save(res, offset);
@@ -246,78 +148,6 @@ int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
     return offset;
 }
 
-int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
-{
-    Chardev *s = be->chr;
-
-    if (!s) {
-        return 0;
-    }
-
-    return qemu_chr_write_all(s, buf, len);
-}
-
-int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
-{
-    Chardev *s = be->chr;
-    int offset = 0, counter = 10;
-    int res;
-
-    if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
-        return 0;
-    }
-
-    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
-        return replay_char_read_all_load(buf);
-    }
-
-    while (offset < len) {
-    retry:
-        res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
-                                                  len - offset);
-        if (res == -1 && errno == EAGAIN) {
-            g_usleep(100);
-            goto retry;
-        }
-
-        if (res == 0) {
-            break;
-        }
-
-        if (res < 0) {
-            if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
-                replay_char_read_all_save_error(res);
-            }
-            return res;
-        }
-
-        offset += res;
-
-        if (!counter--) {
-            break;
-        }
-    }
-
-    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
-        replay_char_read_all_save_buf(buf, offset);
-    }
-    return offset;
-}
-
-int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
-{
-    Chardev *s = be->chr;
-    int res;
-
-    if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
-        res = -ENOTSUP;
-    } else {
-        res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
-    }
-
-    return res;
-}
-
 int qemu_chr_be_can_write(Chardev *s)
 {
     CharBackend *be = s->be;
@@ -350,75 +180,12 @@ void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
     }
 }
 
-int qemu_chr_fe_get_msgfd(CharBackend *be)
-{
-    Chardev *s = be->chr;
-    int fd;
-    int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
-    if (s && qemu_chr_replay(s)) {
-        error_report("Replay: get msgfd is not supported "
-                     "for serial devices yet");
-        exit(1);
-    }
-    return res;
-}
-
-int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
-{
-    Chardev *s = be->chr;
-
-    if (!s) {
-        return -1;
-    }
-
-    return CHARDEV_GET_CLASS(s)->get_msgfds ?
-        CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1;
-}
-
-int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
-{
-    Chardev *s = be->chr;
-
-    if (!s) {
-        return -1;
-    }
-
-    return CHARDEV_GET_CLASS(s)->set_msgfds ?
-        CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1;
-}
-
 int qemu_chr_add_client(Chardev *s, int fd)
 {
     return CHARDEV_GET_CLASS(s)->chr_add_client ?
         CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1;
 }
 
-void qemu_chr_fe_accept_input(CharBackend *be)
-{
-    Chardev *s = be->chr;
-
-    if (!s) {
-        return;
-    }
-
-    if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
-        CHARDEV_GET_CLASS(s)->chr_accept_input(s);
-    }
-    qemu_notify_event();
-}
-
-void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
-{
-    char buf[CHR_READ_BUF_LEN];
-    va_list ap;
-    va_start(ap, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-    /* XXX this blocks entire thread. Rewrite to use
-     * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
-    va_end(ap);
-}
-
 static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
                            bool *be_opened, Error **errp)
 {
@@ -506,66 +273,30 @@ static const TypeInfo char_type_info = {
  * mux will receive CHR_EVENT_OPENED notifications for the BE
  * immediately.
  */
-static void muxes_realize_done(Notifier *notifier, void *unused)
+static int open_muxes(Object *child, void *opaque)
 {
-    Chardev *chr;
+    if (CHARDEV_IS_MUX(child)) {
+        /* send OPENED to all already-attached FEs */
+        mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED);
+        /* mark mux as OPENED so any new FEs will immediately receive
+         * OPENED event
+         */
+        qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED);
+    }
 
-    QTAILQ_FOREACH(chr, &chardevs, next) {
-        if (CHARDEV_IS_MUX(chr)) {
-            MuxChardev *d = MUX_CHARDEV(chr);
-            int i;
+    return 0;
+}
 
-            /* send OPENED to all already-attached FEs */
-            for (i = 0; i < d->mux_cnt; i++) {
-                mux_chr_send_event(d, i, CHR_EVENT_OPENED);
-            }
-            /* mark mux as OPENED so any new FEs will immediately receive
-             * OPENED event
-             */
-            qemu_chr_be_generic_open(chr);
-        }
-    }
+static void muxes_realize_done(Notifier *notifier, void *unused)
+{
     muxes_realized = true;
+    object_child_foreach(get_chardevs_root(), open_muxes, NULL);
 }
 
 static Notifier muxes_realize_notify = {
     .notify = muxes_realize_done,
 };
 
-Chardev *qemu_chr_fe_get_driver(CharBackend *be)
-{
-    return be->chr;
-}
-
-bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
-{
-    int tag = 0;
-
-    if (CHARDEV_IS_MUX(s)) {
-        MuxChardev *d = MUX_CHARDEV(s);
-
-        if (d->mux_cnt >= MAX_MUX) {
-            goto unavailable;
-        }
-
-        d->backends[d->mux_cnt] = b;
-        tag = d->mux_cnt++;
-    } else if (s->be) {
-        goto unavailable;
-    } else {
-        s->be = b;
-    }
-
-    b->fe_open = false;
-    b->tag = tag;
-    b->chr = s;
-    return true;
-
-unavailable:
-    error_setg(errp, QERR_DEVICE_IN_USE, s->label);
-    return false;
-}
-
 static bool qemu_chr_is_busy(Chardev *s)
 {
     if (CHARDEV_IS_MUX(s)) {
@@ -576,727 +307,6 @@ static bool qemu_chr_is_busy(Chardev *s)
     }
 }
 
-void qemu_chr_fe_deinit(CharBackend *b)
-{
-    assert(b);
-
-    if (b->chr) {
-        qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
-        if (b->chr->be == b) {
-            b->chr->be = NULL;
-        }
-        if (CHARDEV_IS_MUX(b->chr)) {
-            MuxChardev *d = MUX_CHARDEV(b->chr);
-            d->backends[b->tag] = NULL;
-        }
-        b->chr = NULL;
-    }
-}
-
-void qemu_chr_fe_set_handlers(CharBackend *b,
-                              IOCanReadHandler *fd_can_read,
-                              IOReadHandler *fd_read,
-                              IOEventHandler *fd_event,
-                              void *opaque,
-                              GMainContext *context,
-                              bool set_open)
-{
-    Chardev *s;
-    ChardevClass *cc;
-    int fe_open;
-
-    s = b->chr;
-    if (!s) {
-        return;
-    }
-
-    cc = CHARDEV_GET_CLASS(s);
-    if (!opaque && !fd_can_read && !fd_read && !fd_event) {
-        fe_open = 0;
-        remove_fd_in_watch(s);
-    } else {
-        fe_open = 1;
-    }
-    b->chr_can_read = fd_can_read;
-    b->chr_read = fd_read;
-    b->chr_event = fd_event;
-    b->opaque = opaque;
-    if (cc->chr_update_read_handler) {
-        cc->chr_update_read_handler(s, context);
-    }
-
-    if (set_open) {
-        qemu_chr_fe_set_open(b, fe_open);
-    }
-
-    if (fe_open) {
-        qemu_chr_fe_take_focus(b);
-        /* We're connecting to an already opened device, so let's make sure we
-           also get the open event */
-        if (s->be_open) {
-            qemu_chr_be_generic_open(s);
-        }
-    }
-
-    if (CHARDEV_IS_MUX(s)) {
-        mux_chr_set_handlers(s, context);
-    }
-}
-
-void qemu_chr_fe_take_focus(CharBackend *b)
-{
-    if (!b->chr) {
-        return;
-    }
-
-    if (CHARDEV_IS_MUX(b->chr)) {
-        mux_set_focus(b->chr, b->tag);
-    }
-}
-
-#ifndef _WIN32
-
-#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
-    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
-    || defined(__GLIBC__)
-
-#define HAVE_CHARDEV_SERIAL 1
-#define HAVE_CHARDEV_PTY 1
-
-typedef struct {
-    Chardev parent;
-    QIOChannel *ioc;
-    int read_bytes;
-
-    /* Protected by the Chardev chr_write_lock.  */
-    int connected;
-    guint timer_tag;
-    guint open_tag;
-} PtyChardev;
-
-#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
-
-static void pty_chr_update_read_handler_locked(Chardev *chr);
-static void pty_chr_state(Chardev *chr, int connected);
-
-static gboolean pty_chr_timer(gpointer opaque)
-{
-    struct Chardev *chr = CHARDEV(opaque);
-    PtyChardev *s = PTY_CHARDEV(opaque);
-
-    qemu_mutex_lock(&chr->chr_write_lock);
-    s->timer_tag = 0;
-    s->open_tag = 0;
-    if (!s->connected) {
-        /* Next poll ... */
-        pty_chr_update_read_handler_locked(chr);
-    }
-    qemu_mutex_unlock(&chr->chr_write_lock);
-    return FALSE;
-}
-
-/* Called with chr_write_lock held.  */
-static void pty_chr_rearm_timer(Chardev *chr, int ms)
-{
-    PtyChardev *s = PTY_CHARDEV(chr);
-    char *name;
-
-    if (s->timer_tag) {
-        g_source_remove(s->timer_tag);
-        s->timer_tag = 0;
-    }
-
-    if (ms == 1000) {
-        name = g_strdup_printf("pty-timer-secs-%s", chr->label);
-        s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
-    } else {
-        name = g_strdup_printf("pty-timer-ms-%s", chr->label);
-        s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
-    }
-    g_source_set_name_by_id(s->timer_tag, name);
-    g_free(name);
-}
-
-/* Called with chr_write_lock held.  */
-static void pty_chr_update_read_handler_locked(Chardev *chr)
-{
-    PtyChardev *s = PTY_CHARDEV(chr);
-    GPollFD pfd;
-    int rc;
-    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
-
-    pfd.fd = fioc->fd;
-    pfd.events = G_IO_OUT;
-    pfd.revents = 0;
-    do {
-        rc = g_poll(&pfd, 1, 0);
-    } while (rc == -1 && errno == EINTR);
-    assert(rc >= 0);
-
-    if (pfd.revents & G_IO_HUP) {
-        pty_chr_state(chr, 0);
-    } else {
-        pty_chr_state(chr, 1);
-    }
-}
-
-static void pty_chr_update_read_handler(Chardev *chr,
-                                        GMainContext *context)
-{
-    qemu_mutex_lock(&chr->chr_write_lock);
-    pty_chr_update_read_handler_locked(chr);
-    qemu_mutex_unlock(&chr->chr_write_lock);
-}
-
-/* Called with chr_write_lock held.  */
-static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
-{
-    PtyChardev *s = PTY_CHARDEV(chr);
-
-    if (!s->connected) {
-        /* guest sends data, check for (re-)connect */
-        pty_chr_update_read_handler_locked(chr);
-        if (!s->connected) {
-            return 0;
-        }
-    }
-    return io_channel_send(s->ioc, buf, len);
-}
-
-static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
-{
-    PtyChardev *s = PTY_CHARDEV(chr);
-    if (!s->connected) {
-        return NULL;
-    }
-    return qio_channel_create_watch(s->ioc, cond);
-}
-
-static int pty_chr_read_poll(void *opaque)
-{
-    Chardev *chr = CHARDEV(opaque);
-    PtyChardev *s = PTY_CHARDEV(opaque);
-
-    s->read_bytes = qemu_chr_be_can_write(chr);
-    return s->read_bytes;
-}
-
-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];
-    ssize_t ret;
-
-    len = sizeof(buf);
-    if (len > s->read_bytes)
-        len = s->read_bytes;
-    if (len == 0) {
-        return TRUE;
-    }
-    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
-    if (ret <= 0) {
-        pty_chr_state(chr, 0);
-        return FALSE;
-    } else {
-        pty_chr_state(chr, 1);
-        qemu_chr_be_write(chr, buf, ret);
-    }
-    return TRUE;
-}
-
-static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
-{
-    Chardev *chr = CHARDEV(opaque);
-    PtyChardev *s = PTY_CHARDEV(opaque);
-
-    s->open_tag = 0;
-    qemu_chr_be_generic_open(chr);
-    return FALSE;
-}
-
-/* Called with chr_write_lock held.  */
-static void pty_chr_state(Chardev *chr, int connected)
-{
-    PtyChardev *s = PTY_CHARDEV(chr);
-
-    if (!connected) {
-        if (s->open_tag) {
-            g_source_remove(s->open_tag);
-            s->open_tag = 0;
-        }
-        remove_fd_in_watch(chr);
-        s->connected = 0;
-        /* (re-)connect poll interval for idle guests: once per second.
-         * We check more frequently in case the guests sends data to
-         * the virtual device linked to our pty. */
-        pty_chr_rearm_timer(chr, 1000);
-    } else {
-        if (s->timer_tag) {
-            g_source_remove(s->timer_tag);
-            s->timer_tag = 0;
-        }
-        if (!s->connected) {
-            g_assert(s->open_tag == 0);
-            s->connected = 1;
-            s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
-        }
-        if (!chr->fd_in_tag) {
-            chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
-                                               pty_chr_read_poll,
-                                               pty_chr_read,
-                                               chr, NULL);
-        }
-    }
-}
-
-static void char_pty_finalize(Object *obj)
-{
-    Chardev *chr = CHARDEV(obj);
-    PtyChardev *s = PTY_CHARDEV(obj);
-
-    qemu_mutex_lock(&chr->chr_write_lock);
-    pty_chr_state(chr, 0);
-    object_unref(OBJECT(s->ioc));
-    if (s->timer_tag) {
-        g_source_remove(s->timer_tag);
-        s->timer_tag = 0;
-    }
-    qemu_mutex_unlock(&chr->chr_write_lock);
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-}
-
-static void char_pty_open(Chardev *chr,
-                          ChardevBackend *backend,
-                          bool *be_opened,
-                          Error **errp)
-{
-    PtyChardev *s;
-    int master_fd, slave_fd;
-    char pty_name[PATH_MAX];
-    char *name;
-
-    master_fd = qemu_openpty_raw(&slave_fd, pty_name);
-    if (master_fd < 0) {
-        error_setg_errno(errp, errno, "Failed to create PTY");
-        return;
-    }
-
-    close(slave_fd);
-    qemu_set_nonblock(master_fd);
-
-    chr->filename = g_strdup_printf("pty:%s", pty_name);
-    error_report("char device redirected to %s (label %s)",
-                 pty_name, chr->label);
-
-    s = PTY_CHARDEV(chr);
-    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
-    name = g_strdup_printf("chardev-pty-%s", chr->label);
-    qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
-    g_free(name);
-    s->timer_tag = 0;
-    *be_opened = false;
-}
-
-static void char_pty_class_init(ObjectClass *oc, void *data)
-{
-    ChardevClass *cc = CHARDEV_CLASS(oc);
-
-    cc->open = char_pty_open;
-    cc->chr_write = char_pty_chr_write;
-    cc->chr_update_read_handler = pty_chr_update_read_handler;
-    cc->chr_add_watch = pty_chr_add_watch;
-}
-
-static const TypeInfo char_pty_type_info = {
-    .name = TYPE_CHARDEV_PTY,
-    .parent = TYPE_CHARDEV,
-    .instance_size = sizeof(PtyChardev),
-    .instance_finalize = char_pty_finalize,
-    .class_init = char_pty_class_init,
-};
-
-static void tty_serial_init(int fd, int speed,
-                            int parity, int data_bits, int stop_bits)
-{
-    struct termios tty;
-    speed_t spd;
-
-#if 0
-    printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
-           speed, parity, data_bits, stop_bits);
-#endif
-    tcgetattr (fd, &tty);
-
-#define check_speed(val) if (speed <= val) { spd = B##val; break; }
-    speed = speed * 10 / 11;
-    do {
-        check_speed(50);
-        check_speed(75);
-        check_speed(110);
-        check_speed(134);
-        check_speed(150);
-        check_speed(200);
-        check_speed(300);
-        check_speed(600);
-        check_speed(1200);
-        check_speed(1800);
-        check_speed(2400);
-        check_speed(4800);
-        check_speed(9600);
-        check_speed(19200);
-        check_speed(38400);
-        /* Non-Posix values follow. They may be unsupported on some systems. */
-        check_speed(57600);
-        check_speed(115200);
-#ifdef B230400
-        check_speed(230400);
-#endif
-#ifdef B460800
-        check_speed(460800);
-#endif
-#ifdef B500000
-        check_speed(500000);
-#endif
-#ifdef B576000
-        check_speed(576000);
-#endif
-#ifdef B921600
-        check_speed(921600);
-#endif
-#ifdef B1000000
-        check_speed(1000000);
-#endif
-#ifdef B1152000
-        check_speed(1152000);
-#endif
-#ifdef B1500000
-        check_speed(1500000);
-#endif
-#ifdef B2000000
-        check_speed(2000000);
-#endif
-#ifdef B2500000
-        check_speed(2500000);
-#endif
-#ifdef B3000000
-        check_speed(3000000);
-#endif
-#ifdef B3500000
-        check_speed(3500000);
-#endif
-#ifdef B4000000
-        check_speed(4000000);
-#endif
-        spd = B115200;
-    } while (0);
-
-    cfsetispeed(&tty, spd);
-    cfsetospeed(&tty, spd);
-
-    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
-                          |INLCR|IGNCR|ICRNL|IXON);
-    tty.c_oflag |= OPOST;
-    tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
-    tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
-    switch(data_bits) {
-    default:
-    case 8:
-        tty.c_cflag |= CS8;
-        break;
-    case 7:
-        tty.c_cflag |= CS7;
-        break;
-    case 6:
-        tty.c_cflag |= CS6;
-        break;
-    case 5:
-        tty.c_cflag |= CS5;
-        break;
-    }
-    switch(parity) {
-    default:
-    case 'N':
-        break;
-    case 'E':
-        tty.c_cflag |= PARENB;
-        break;
-    case 'O':
-        tty.c_cflag |= PARENB | PARODD;
-        break;
-    }
-    if (stop_bits == 2)
-        tty.c_cflag |= CSTOPB;
-
-    tcsetattr (fd, TCSANOW, &tty);
-}
-
-static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
-{
-    FDChardev *s = FD_CHARDEV(chr);
-    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
-
-    switch(cmd) {
-    case CHR_IOCTL_SERIAL_SET_PARAMS:
-        {
-            QEMUSerialSetParams *ssp = arg;
-            tty_serial_init(fioc->fd,
-                            ssp->speed, ssp->parity,
-                            ssp->data_bits, ssp->stop_bits);
-        }
-        break;
-    case CHR_IOCTL_SERIAL_SET_BREAK:
-        {
-            int enable = *(int *)arg;
-            if (enable) {
-                tcsendbreak(fioc->fd, 1);
-            }
-        }
-        break;
-    case CHR_IOCTL_SERIAL_GET_TIOCM:
-        {
-            int sarg = 0;
-            int *targ = (int *)arg;
-            ioctl(fioc->fd, TIOCMGET, &sarg);
-            *targ = 0;
-            if (sarg & TIOCM_CTS)
-                *targ |= CHR_TIOCM_CTS;
-            if (sarg & TIOCM_CAR)
-                *targ |= CHR_TIOCM_CAR;
-            if (sarg & TIOCM_DSR)
-                *targ |= CHR_TIOCM_DSR;
-            if (sarg & TIOCM_RI)
-                *targ |= CHR_TIOCM_RI;
-            if (sarg & TIOCM_DTR)
-                *targ |= CHR_TIOCM_DTR;
-            if (sarg & TIOCM_RTS)
-                *targ |= CHR_TIOCM_RTS;
-        }
-        break;
-    case CHR_IOCTL_SERIAL_SET_TIOCM:
-        {
-            int sarg = *(int *)arg;
-            int targ = 0;
-            ioctl(fioc->fd, TIOCMGET, &targ);
-            targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
-                     | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
-            if (sarg & CHR_TIOCM_CTS)
-                targ |= TIOCM_CTS;
-            if (sarg & CHR_TIOCM_CAR)
-                targ |= TIOCM_CAR;
-            if (sarg & CHR_TIOCM_DSR)
-                targ |= TIOCM_DSR;
-            if (sarg & CHR_TIOCM_RI)
-                targ |= TIOCM_RI;
-            if (sarg & CHR_TIOCM_DTR)
-                targ |= TIOCM_DTR;
-            if (sarg & CHR_TIOCM_RTS)
-                targ |= TIOCM_RTS;
-            ioctl(fioc->fd, TIOCMSET, &targ);
-        }
-        break;
-    default:
-        return -ENOTSUP;
-    }
-    return 0;
-}
-#endif /* __linux__ || __sun__ */
-
-#if defined(__linux__)
-
-#define HAVE_CHARDEV_PARPORT 1
-
-typedef struct {
-    Chardev parent;
-    int fd;
-    int mode;
-} ParallelChardev;
-
-#define PARALLEL_CHARDEV(obj) \
-    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
-
-static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
-{
-    if (s->mode != mode) {
-       int m = mode;
-        if (ioctl(s->fd, PPSETMODE, &m) < 0)
-            return 0;
-       s->mode = mode;
-    }
-    return 1;
-}
-
-static int pp_ioctl(Chardev *chr, int cmd, void *arg)
-{
-    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
-    int fd = drv->fd;
-    uint8_t b;
-
-    switch(cmd) {
-    case CHR_IOCTL_PP_READ_DATA:
-        if (ioctl(fd, PPRDATA, &b) < 0)
-            return -ENOTSUP;
-        *(uint8_t *)arg = b;
-        break;
-    case CHR_IOCTL_PP_WRITE_DATA:
-        b = *(uint8_t *)arg;
-        if (ioctl(fd, PPWDATA, &b) < 0)
-            return -ENOTSUP;
-        break;
-    case CHR_IOCTL_PP_READ_CONTROL:
-        if (ioctl(fd, PPRCONTROL, &b) < 0)
-            return -ENOTSUP;
-       /* Linux gives only the lowest bits, and no way to know data
-          direction! For better compatibility set the fixed upper
-          bits. */
-        *(uint8_t *)arg = b | 0xc0;
-        break;
-    case CHR_IOCTL_PP_WRITE_CONTROL:
-        b = *(uint8_t *)arg;
-        if (ioctl(fd, PPWCONTROL, &b) < 0)
-            return -ENOTSUP;
-        break;
-    case CHR_IOCTL_PP_READ_STATUS:
-        if (ioctl(fd, PPRSTATUS, &b) < 0)
-            return -ENOTSUP;
-        *(uint8_t *)arg = b;
-        break;
-    case CHR_IOCTL_PP_DATA_DIR:
-        if (ioctl(fd, PPDATADIR, (int *)arg) < 0)
-            return -ENOTSUP;
-        break;
-    case CHR_IOCTL_PP_EPP_READ_ADDR:
-       if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
-           struct ParallelIOArg *parg = arg;
-           int n = read(fd, parg->buffer, parg->count);
-           if (n != parg->count) {
-               return -EIO;
-           }
-       }
-        break;
-    case CHR_IOCTL_PP_EPP_READ:
-       if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
-           struct ParallelIOArg *parg = arg;
-           int n = read(fd, parg->buffer, parg->count);
-           if (n != parg->count) {
-               return -EIO;
-           }
-       }
-        break;
-    case CHR_IOCTL_PP_EPP_WRITE_ADDR:
-       if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
-           struct ParallelIOArg *parg = arg;
-           int n = write(fd, parg->buffer, parg->count);
-           if (n != parg->count) {
-               return -EIO;
-           }
-       }
-        break;
-    case CHR_IOCTL_PP_EPP_WRITE:
-       if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
-           struct ParallelIOArg *parg = arg;
-           int n = write(fd, parg->buffer, parg->count);
-           if (n != parg->count) {
-               return -EIO;
-           }
-       }
-        break;
-    default:
-        return -ENOTSUP;
-    }
-    return 0;
-}
-
-static void qemu_chr_open_pp_fd(Chardev *chr,
-                                int fd,
-                                bool *be_opened,
-                                Error **errp)
-{
-    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
-
-    if (ioctl(fd, PPCLAIM) < 0) {
-        error_setg_errno(errp, errno, "not a parallel port");
-        close(fd);
-        return;
-    }
-
-    drv->fd = fd;
-    drv->mode = IEEE1284_MODE_COMPAT;
-}
-#endif /* __linux__ */
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-
-#define HAVE_CHARDEV_PARPORT 1
-
-typedef struct {
-    Chardev parent;
-    int fd;
-} ParallelChardev;
-
-#define PARALLEL_CHARDEV(obj)                                   \
-    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
-
-static int pp_ioctl(Chardev *chr, int cmd, void *arg)
-{
-    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
-    uint8_t b;
-
-    switch (cmd) {
-    case CHR_IOCTL_PP_READ_DATA:
-        if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
-            return -ENOTSUP;
-        }
-        *(uint8_t *)arg = b;
-        break;
-    case CHR_IOCTL_PP_WRITE_DATA:
-        b = *(uint8_t *)arg;
-        if (ioctl(drv->fd, PPISDATA, &b) < 0) {
-            return -ENOTSUP;
-        }
-        break;
-    case CHR_IOCTL_PP_READ_CONTROL:
-        if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
-            return -ENOTSUP;
-        }
-        *(uint8_t *)arg = b;
-        break;
-    case CHR_IOCTL_PP_WRITE_CONTROL:
-        b = *(uint8_t *)arg;
-        if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
-            return -ENOTSUP;
-        }
-        break;
-    case CHR_IOCTL_PP_READ_STATUS:
-        if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
-            return -ENOTSUP;
-        }
-        *(uint8_t *)arg = b;
-        break;
-    default:
-        return -ENOTSUP;
-    }
-    return 0;
-}
-
-static void qemu_chr_open_pp_fd(Chardev *chr,
-                                int fd,
-                                bool *be_opened,
-                                Error **errp)
-{
-    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
-    drv->fd = fd;
-    *be_opened = false;
-}
-#endif
-
-#else /* _WIN32 */
-
-#define HAVE_CHARDEV_SERIAL 1
-
-#endif /* !_WIN32 */
-
 int qemu_chr_wait_connected(Chardev *chr, Error **errp)
 {
     ChardevClass *cc = CHARDEV_GET_CLASS(chr);
@@ -1308,16 +318,6 @@ int qemu_chr_wait_connected(Chardev *chr, Error **errp)
     return 0;
 }
 
-int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
-{
-    if (!be->chr) {
-        error_setg(errp, "missing associated backend");
-        return -1;
-    }
-
-    return qemu_chr_wait_connected(be->chr, errp);
-}
-
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
 {
     char host[65], port[33], width[8], height[8];
@@ -1348,6 +348,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
     if (strcmp(filename, "null")    == 0 ||
         strcmp(filename, "pty")     == 0 ||
         strcmp(filename, "msmouse") == 0 ||
+        strcmp(filename, "wctablet") == 0 ||
         strcmp(filename, "braille") == 0 ||
         strcmp(filename, "testdev") == 0 ||
         strcmp(filename, "stdio")   == 0) {
@@ -1391,7 +392,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
         return opts;
     }
     if (strstart(filename, "tcp:", &p) ||
-        strstart(filename, "telnet:", &p)) {
+        strstart(filename, "telnet:", &p) ||
+        strstart(filename, "tn3270:", &p)) {
         if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
             host[0] = 0;
             if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
@@ -1407,8 +409,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
                 goto fail;
             }
         }
-        if (strstart(filename, "telnet:", &p))
+        if (strstart(filename, "telnet:", &p)) {
             qemu_opt_set(opts, "telnet", "on", &error_abort);
+        } else if (strstart(filename, "tn3270:", &p)) {
+            qemu_opt_set(opts, "tn3270", "on", &error_abort);
+        }
         return opts;
     }
     if (strstart(filename, "udp:", &p)) {
@@ -1445,12 +450,12 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
     }
     if (strstart(filename, "/dev/parport", NULL) ||
         strstart(filename, "/dev/ppi", NULL)) {
-        qemu_opt_set(opts, "backend", "parport", &error_abort);
+        qemu_opt_set(opts, "backend", "parallel", &error_abort);
         qemu_opt_set(opts, "path", filename, &error_abort);
         return opts;
     }
     if (strstart(filename, "/dev/", NULL)) {
-        qemu_opt_set(opts, "backend", "tty", &error_abort);
+        qemu_opt_set(opts, "backend", "serial", &error_abort);
         qemu_opt_set(opts, "path", filename, &error_abort);
         return opts;
     }
@@ -1465,48 +470,12 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
     const char *logfile = qemu_opt_get(opts, "logfile");
 
     backend->has_logfile = logfile != NULL;
-    backend->logfile = logfile ? g_strdup(logfile) : NULL;
+    backend->logfile = g_strdup(logfile);
 
     backend->has_logappend = true;
     backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
 }
 
-#ifdef HAVE_CHARDEV_SERIAL
-static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
-                                  Error **errp)
-{
-    const char *device = qemu_opt_get(opts, "path");
-    ChardevHostdev *serial;
-
-    backend->type = CHARDEV_BACKEND_KIND_SERIAL;
-    if (device == NULL) {
-        error_setg(errp, "chardev: serial/tty: no device path given");
-        return;
-    }
-    serial = backend->u.serial.data = g_new0(ChardevHostdev, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial));
-    serial->device = g_strdup(device);
-}
-#endif
-
-#ifdef HAVE_CHARDEV_PARPORT
-static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
-                                    Error **errp)
-{
-    const char *device = qemu_opt_get(opts, "path");
-    ChardevHostdev *parallel;
-
-    backend->type = CHARDEV_BACKEND_KIND_PARALLEL;
-    if (device == NULL) {
-        error_setg(errp, "chardev: parallel: no device path given");
-        return;
-    }
-    parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
-    parallel->device = g_strdup(device);
-}
-#endif
-
 static const ChardevClass *char_get_class(const char *driver, Error **errp)
 {
     ObjectClass *oc;
@@ -1536,26 +505,6 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp)
     return cc;
 }
 
-static Chardev *qemu_chardev_add(const char *id, const char *typename,
-                                 ChardevBackend *backend, Error **errp)
-{
-    Chardev *chr;
-
-    chr = qemu_chr_find(id);
-    if (chr) {
-        error_setg(errp, "Chardev '%s' already exists", id);
-        return NULL;
-    }
-
-    chr = qemu_chardev_new(id, typename, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-
-    QTAILQ_INSERT_TAIL(&chardevs, chr, next);
-    return chr;
-}
-
 static const struct ChardevAlias {
     const char *typename;
     const char *alias;
@@ -1594,7 +543,7 @@ chardev_name_foreach(void (*fn)(const char *name, void *opaque), void *opaque)
 
     object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe);
 
-    for (i = 0; i < ARRAY_SIZE(chardev_alias_table); i++) {
+    for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
         fn(chardev_alias_table[i].alias, opaque);
     }
 }
@@ -1640,7 +589,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
         return NULL;
     }
 
-    for (i = 0; i < ARRAY_SIZE(chardev_alias_table); i++) {
+    for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
         if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) {
             name = chardev_alias_table[i].typename;
             break;
@@ -1672,9 +621,10 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
         backend->u.null.data = ccom; /* Any ChardevCommon member would work */
     }
 
-    chr = qemu_chardev_add(bid ? bid : id,
+    chr = qemu_chardev_new(bid ? bid : id,
                            object_class_get_name(OBJECT_CLASS(cc)),
                            backend, errp);
+
     if (chr == NULL) {
         goto out;
     }
@@ -1686,9 +636,9 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
         backend->type = CHARDEV_BACKEND_KIND_MUX;
         backend->u.mux.data = g_new0(ChardevMux, 1);
         backend->u.mux.data->chardev = g_strdup(bid);
-        mux = qemu_chardev_add(id, TYPE_CHARDEV_MUX, backend, errp);
+        mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, errp);
         if (mux == NULL) {
-            qemu_chr_delete(chr);
+            object_unparent(OBJECT(chr));
             chr = NULL;
             goto out;
         }
@@ -1744,85 +694,29 @@ Chardev *qemu_chr_new(const char *label, const char *filename)
     return chr;
 }
 
-void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
-{
-    Chardev *chr = be->chr;
-
-    if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
-        CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
-    }
-}
-
-void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
+static int qmp_query_chardev_foreach(Object *obj, void *data)
 {
-    Chardev *chr = be->chr;
-
-    if (!chr) {
-        return;
-    }
-
-    if (be->fe_open == fe_open) {
-        return;
-    }
-    be->fe_open = fe_open;
-    if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
-        CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
-    }
-}
-
-guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
-                            GIOFunc func, void *user_data)
-{
-    Chardev *s = be->chr;
-    GSource *src;
-    guint tag;
-
-    if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
-        return 0;
-    }
-
-    src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
-    if (!src) {
-        return 0;
-    }
-
-    g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
-    tag = g_source_attach(src, NULL);
-    g_source_unref(src);
-
-    return tag;
-}
+    Chardev *chr = CHARDEV(obj);
+    ChardevInfoList **list = data;
+    ChardevInfoList *info = g_malloc0(sizeof(*info));
 
-void qemu_chr_fe_disconnect(CharBackend *be)
-{
-    Chardev *chr = be->chr;
+    info->value = g_malloc0(sizeof(*info->value));
+    info->value->label = g_strdup(chr->label);
+    info->value->filename = g_strdup(chr->filename);
+    info->value->frontend_open = chr->be && chr->be->fe_open;
 
-    if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
-        CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
-    }
-}
+    info->next = *list;
+    *list = info;
 
-void qemu_chr_delete(Chardev *chr)
-{
-    QTAILQ_REMOVE(&chardevs, chr, next);
-    object_unref(OBJECT(chr));
+    return 0;
 }
 
 ChardevInfoList *qmp_query_chardev(Error **errp)
 {
     ChardevInfoList *chr_list = NULL;
-    Chardev *chr;
 
-    QTAILQ_FOREACH(chr, &chardevs, next) {
-        ChardevInfoList *info = g_malloc0(sizeof(*info));
-        info->value = g_malloc0(sizeof(*info->value));
-        info->value->label = g_strdup(chr->label);
-        info->value->filename = g_strdup(chr->filename);
-        info->value->frontend_open = chr->be && chr->be->fe_open;
-
-        info->next = chr_list;
-        chr_list = info;
-    }
+    object_child_foreach(get_chardevs_root(),
+                         qmp_query_chardev_foreach, &chr_list);
 
     return chr_list;
 }
@@ -1850,14 +744,9 @@ ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
 
 Chardev *qemu_chr_find(const char *name)
 {
-    Chardev *chr;
+    Object *obj = object_resolve_path_component(get_chardevs_root(), name);
 
-    QTAILQ_FOREACH(chr, &chardevs, next) {
-        if (strcmp(chr->label, name) != 0)
-            continue;
-        return chr;
-    }
-    return NULL;
+    return obj ? CHARDEV(obj) : NULL;
 }
 
 QemuOptsList qemu_chardev_opts = {
@@ -1907,6 +796,9 @@ QemuOptsList qemu_chardev_opts = {
         },{
             .name = "telnet",
             .type = QEMU_OPT_BOOL,
+        },{
+            .name = "tn3270",
+            .type = QEMU_OPT_BOOL,
         },{
             .name = "tls-creds",
             .type = QEMU_OPT_STRING,
@@ -1954,120 +846,6 @@ QemuOptsList qemu_chardev_opts = {
     },
 };
 
-#ifdef _WIN32
-
-static void qmp_chardev_open_serial(Chardev *chr,
-                                    ChardevBackend *backend,
-                                    bool *be_opened,
-                                    Error **errp)
-{
-    ChardevHostdev *serial = backend->u.serial.data;
-
-    win_chr_init(chr, serial->device, errp);
-}
-
-#else /* WIN32 */
-
-#ifdef HAVE_CHARDEV_SERIAL
-static void qmp_chardev_open_serial(Chardev *chr,
-                                    ChardevBackend *backend,
-                                    bool *be_opened,
-                                    Error **errp)
-{
-    ChardevHostdev *serial = backend->u.serial.data;
-    int fd;
-
-    fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
-    if (fd < 0) {
-        return;
-    }
-    qemu_set_nonblock(fd);
-    tty_serial_init(fd, 115200, 'N', 8, 1);
-
-    qemu_chr_open_fd(chr, fd, fd);
-}
-#endif
-
-#ifdef HAVE_CHARDEV_PARPORT
-static void qmp_chardev_open_parallel(Chardev *chr,
-                                      ChardevBackend *backend,
-                                      bool *be_opened,
-                                      Error **errp)
-{
-    ChardevHostdev *parallel = backend->u.parallel.data;
-    int fd;
-
-    fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
-    if (fd < 0) {
-        return;
-    }
-    qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
-}
-
-static void char_parallel_class_init(ObjectClass *oc, void *data)
-{
-    ChardevClass *cc = CHARDEV_CLASS(oc);
-
-    cc->parse = qemu_chr_parse_parallel;
-    cc->open = qmp_chardev_open_parallel;
-#if defined(__linux__)
-    cc->chr_ioctl = pp_ioctl;
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-    cc->chr_ioctl = pp_ioctl;
-#endif
-}
-
-static void char_parallel_finalize(Object *obj)
-{
-#if defined(__linux__)
-    Chardev *chr = CHARDEV(obj);
-    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
-    int fd = drv->fd;
-
-    pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
-    ioctl(fd, PPRELEASE);
-    close(fd);
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-    /* FIXME: close fd? */
-#endif
-}
-
-static const TypeInfo char_parallel_type_info = {
-    .name = TYPE_CHARDEV_PARALLEL,
-    .parent = TYPE_CHARDEV,
-    .instance_size = sizeof(ParallelChardev),
-    .instance_finalize = char_parallel_finalize,
-    .class_init = char_parallel_class_init,
-};
-#endif
-
-#endif /* WIN32 */
-
-#ifdef HAVE_CHARDEV_SERIAL
-
-static void char_serial_class_init(ObjectClass *oc, void *data)
-{
-    ChardevClass *cc = CHARDEV_CLASS(oc);
-
-    cc->parse = qemu_chr_parse_serial;
-    cc->open = qmp_chardev_open_serial;
-#ifndef _WIN32
-    cc->chr_ioctl = tty_serial_ioctl;
-#endif
-}
-
-static const TypeInfo char_serial_type_info = {
-    .name = TYPE_CHARDEV_SERIAL,
-#ifdef _WIN32
-    .parent = TYPE_CHARDEV_WIN,
-#else
-    .parent = TYPE_CHARDEV_FD,
-#endif
-    .class_init = char_serial_class_init,
-};
-#endif
-
 bool qemu_chr_has_feature(Chardev *chr,
                           ChardevFeature feature)
 {
@@ -2081,22 +859,23 @@ void qemu_chr_set_feature(Chardev *chr,
 }
 
 Chardev *qemu_chardev_new(const char *id, const char *typename,
-                          ChardevBackend *backend, Error **errp)
+                          ChardevBackend *backend,
+                          Error **errp)
 {
+    Object *obj;
     Chardev *chr = NULL;
     Error *local_err = NULL;
     bool be_opened = true;
 
     assert(g_str_has_prefix(typename, "chardev-"));
 
-    chr = CHARDEV(object_new(typename));
+    obj = object_new(typename);
+    chr = CHARDEV(obj);
     chr->label = g_strdup(id);
 
     qemu_char_open(chr, backend, &be_opened, &local_err);
     if (local_err) {
-        error_propagate(errp, local_err);
-        object_unref(OBJECT(chr));
-        return NULL;
+        goto end;
     }
 
     if (!chr->filename) {
@@ -2106,6 +885,21 @@ Chardev *qemu_chardev_new(const char *id, const char *typename,
         qemu_chr_be_event(chr, CHR_EVENT_OPENED);
     }
 
+    if (id) {
+        object_property_add_child(get_chardevs_root(), id, obj, &local_err);
+        if (local_err) {
+            goto end;
+        }
+        object_unref(obj);
+    }
+
+end:
+    if (local_err) {
+        error_propagate(errp, local_err);
+        object_unref(obj);
+        return NULL;
+    }
+
     return chr;
 }
 
@@ -2121,7 +915,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
         return NULL;
     }
 
-    chr = qemu_chardev_add(id, object_class_get_name(OBJECT_CLASS(cc)),
+    chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
                            backend, errp);
     if (!chr) {
         return NULL;
@@ -2154,30 +948,29 @@ void qmp_chardev_remove(const char *id, Error **errp)
             "Chardev '%s' cannot be unplugged in record/replay mode", id);
         return;
     }
-    qemu_chr_delete(chr);
+    object_unparent(OBJECT(chr));
 }
 
-void qemu_chr_cleanup(void)
+void qmp_chardev_send_break(const char *id, Error **errp)
 {
-    Chardev *chr, *tmp;
+    Chardev *chr;
 
-    QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
-        qemu_chr_delete(chr);
+    chr = qemu_chr_find(id);
+    if (chr == NULL) {
+        error_setg(errp, "Chardev '%s' not found", id);
+        return;
     }
+    qemu_chr_be_event(chr, CHR_EVENT_BREAK);
+}
+
+void qemu_chr_cleanup(void)
+{
+    object_unparent(get_chardevs_root());
 }
 
 static void register_types(void)
 {
     type_register_static(&char_type_info);
-#ifdef HAVE_CHARDEV_SERIAL
-    type_register_static(&char_serial_type_info);
-#endif
-#ifdef HAVE_CHARDEV_PARPORT
-    type_register_static(&char_parallel_type_info);
-#endif
-#ifdef HAVE_CHARDEV_PTY
-    type_register_static(&char_pty_type_info);
-#endif
 
     /* this must be done after machine init, since we register FEs with muxes
      * as part of realize functions like serial_isa_realizefn when -nographic
This page took 0.064933 seconds and 4 git commands to generate.