qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 15/54] chardev: qom-ify


From: Marc-André Lureau
Subject: [Qemu-devel] [PATCH 15/54] chardev: qom-ify
Date: Tue, 13 Dec 2016 01:42:46 +0300

Turn Chardev into Object.

qemu_chr_alloc() is replaced by the qemu_chardev_new() constructor. It
will call qemu_char_open() to open/intialize the chardev with the
ChardevCommon *backend settings.

The CharDriver::create() callback is turned into a ChardevClass::open()
which is called from the newly introduced qemu_chardev_open().

"chardev-gdb" and "chardev-hci" are internal chardev and aren't
creatable directly with -chardev. Use a new internal flag to disable
them. We may want to use TYPE_USER_CREATABLE interface instead, or
perhaps allow -chardev usage.

Signed-off-by: Marc-André Lureau <address@hidden>
---
 backends/baum.c       |   70 +--
 backends/msmouse.c    |   57 ++-
 backends/testdev.c    |   34 +-
 gdbstub.c             |   33 +-
 hw/bt/hci-csr.c       |   54 +-
 monitor.c             |    2 +-
 qemu-char.c           | 1334 ++++++++++++++++++++++++++-----------------------
 spice-qemu-char.c     |  144 +++---
 ui/console.c          |   73 +--
 ui/gtk.c              |   51 +-
 vl.c                  |    2 +
 include/sysemu/char.h |  107 ++--
 include/ui/console.h  |    2 +
 13 files changed, 1084 insertions(+), 879 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 7fd1ebc557..80103b6098 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -102,6 +102,9 @@ typedef struct {
     QEMUTimer *cellCount_timer;
 } BaumChardev;
 
+#define TYPE_CHARDEV_BRAILLE "chardev-braille"
+#define BAUM_CHARDEV(obj) OBJECT_CHECK(BaumChardev, (obj), 
TYPE_CHARDEV_BRAILLE)
+
 /* Let's assume NABCC by default */
 enum way {
     DOTS2ASCII,
@@ -268,9 +271,9 @@ static int baum_deferred_init(BaumChardev *baum)
 }
 
 /* The serial port can receive more of our data */
-static void baum_accept_input(struct Chardev *chr)
+static void baum_chr_accept_input(struct Chardev *chr)
 {
-    BaumChardev *baum = (BaumChardev *)chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     int room, first;
 
     if (!baum->out_buf_used)
@@ -296,7 +299,7 @@ static void baum_accept_input(struct Chardev *chr)
 /* We want to send a packet */
 static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len)
 {
-    Chardev *chr = (Chardev *)baum;
+    Chardev *chr = CHARDEV(baum);
     uint8_t io_buf[1 + 2 * len], *cur = io_buf;
     int room;
     *cur++ = ESC;
@@ -337,7 +340,7 @@ static void baum_write_packet(BaumChardev *baum, const 
uint8_t *buf, int len)
 /* Called when the other end seems to have a wrong idea of our display size */
 static void baum_cellCount_timer_cb(void *opaque)
 {
-    BaumChardev *baum = opaque;
+    BaumChardev *baum = BAUM_CHARDEV(opaque);
     uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y };
     DPRINTF("Timeout waiting for DisplayData, sending cell count\n");
     baum_write_packet(baum, cell_count, sizeof(cell_count));
@@ -485,9 +488,9 @@ static int baum_eat_packet(BaumChardev *baum, const uint8_t 
*buf, int len)
 }
 
 /* The other end is writing some data.  Store it and try to interpret */
-static int baum_write(Chardev *chr, const uint8_t *buf, int len)
+static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    BaumChardev *baum = (BaumChardev *)chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     int tocopy, cur, eaten, orig_len = len;
 
     if (!len)
@@ -544,7 +547,7 @@ static void baum_send_key2(BaumChardev *baum, uint8_t type, 
uint8_t value,
 /* We got some data on the BrlAPI socket */
 static void baum_chr_read(void *opaque)
 {
-    BaumChardev *baum = opaque;
+    BaumChardev *baum = BAUM_CHARDEV(opaque);
     brlapi_keyCode_t code;
     int ret;
     if (!baum->brlapi)
@@ -628,9 +631,9 @@ static void baum_chr_read(void *opaque)
     }
 }
 
-static void baum_free(struct Chardev *chr)
+static void baum_chr_free(Chardev *chr)
 {
-    BaumChardev *baum = (BaumChardev *)chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
 
     timer_free(baum->cellCount_timer);
     if (baum->brlapi) {
@@ -639,24 +642,14 @@ static void baum_free(struct Chardev *chr)
     }
 }
 
-static Chardev *chr_baum_init(const CharDriver *driver,
-                              const char *id,
-                              ChardevBackend *backend,
-                              ChardevReturn *ret,
-                              bool *be_opened,
-                              Error **errp)
+static void baum_chr_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
 {
-    ChardevCommon *common = backend->u.braille.data;
-    BaumChardev *baum;
-    Chardev *chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     brlapi_handle_t *handle;
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    baum = (BaumChardev *)chr;
-
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
 
@@ -664,34 +657,41 @@ static Chardev *chr_baum_init(const CharDriver *driver,
     if (baum->brlapi_fd == -1) {
         error_setg(errp, "brlapi__openConnection: %s",
                    brlapi_strerror(brlapi_error_location()));
-        goto fail_handle;
+        g_free(handle);
+        return;
     }
     baum->deferred_init = 0;
 
     baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 
baum_cellCount_timer_cb, baum);
 
     qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
+}
 
-    return chr;
+static void char_braille_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-fail_handle:
-    g_free(handle);
-    g_free(chr);
-    return NULL;
+    cc->open = baum_chr_open;
+    cc->chr_write = baum_chr_write;
+    cc->chr_accept_input = baum_chr_accept_input;
+    cc->chr_free = baum_chr_free;
 }
 
+static const TypeInfo char_braille_type_info = {
+    .name = TYPE_CHARDEV_BRAILLE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(BaumChardev),
+    .class_init = char_braille_class_init,
+};
+
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(BaumChardev),
         .kind = CHARDEV_BACKEND_KIND_BRAILLE,
-        .parse = NULL, .create = chr_baum_init,
-        .chr_write = baum_write,
-        .chr_accept_input = baum_accept_input,
-        .chr_free = baum_free,
     };
 
     register_char_driver(&driver);
+    type_register_static(&char_braille_type_info);
 }
 
 type_init(register_types);
diff --git a/backends/msmouse.c b/backends/msmouse.c
index fabc2926d6..936a5476d5 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -41,9 +41,13 @@ typedef struct {
     int outlen;
 } MouseChardev;
 
+#define TYPE_CHARDEV_MSMOUSE "chardev-msmouse"
+#define MOUSE_CHARDEV(obj)                                      \
+    OBJECT_CHECK(MouseChardev, (obj), TYPE_CHARDEV_MSMOUSE)
+
 static void msmouse_chr_accept_input(Chardev *chr)
 {
-    MouseChardev *mouse = (MouseChardev *)chr;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
     int len;
 
     len = qemu_chr_be_can_write(chr);
@@ -98,7 +102,7 @@ static void msmouse_queue_event(MouseChardev *mouse)
 static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
                                 InputEvent *evt)
 {
-    MouseChardev *mouse = (MouseChardev *)dev;
+    MouseChardev *mouse = MOUSE_CHARDEV(dev);
     InputMoveEvent *move;
     InputBtnEvent *btn;
 
@@ -122,8 +126,8 @@ static void msmouse_input_event(DeviceState *dev, 
QemuConsole *src,
 
 static void msmouse_input_sync(DeviceState *dev)
 {
-    MouseChardev *mouse = (MouseChardev *)dev;
-    Chardev *chr = (Chardev *)dev;
+    MouseChardev *mouse = MOUSE_CHARDEV(dev);
+    Chardev *chr = CHARDEV(dev);
 
     msmouse_queue_event(mouse);
     msmouse_chr_accept_input(chr);
@@ -137,7 +141,7 @@ static int msmouse_chr_write(struct Chardev *s, const 
uint8_t *buf, int len)
 
 static void msmouse_chr_free(struct Chardev *chr)
 {
-    MouseChardev *mouse = (MouseChardev *)chr;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 
     qemu_input_handler_unregister(mouse->hs);
 }
@@ -149,42 +153,43 @@ static QemuInputHandler msmouse_handler = {
     .sync  = msmouse_input_sync,
 };
 
-static Chardev *qemu_chr_open_msmouse(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void msmouse_chr_open(Chardev *chr,
+                             ChardevBackend *backend,
+                             bool *be_opened,
+                             Error **errp)
 {
-    ChardevCommon *common = backend->u.msmouse.data;
-    MouseChardev *mouse;
-    Chardev *chr;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
     *be_opened = false;
-
-    mouse = (MouseChardev *)chr;
     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
                                             &msmouse_handler);
+}
 
+static void char_msmouse_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    return chr;
+    cc->open = msmouse_chr_open;
+    cc->chr_write = msmouse_chr_write;
+    cc->chr_accept_input = msmouse_chr_accept_input;
+    cc->chr_free = msmouse_chr_free;
 }
 
+static const TypeInfo char_msmouse_type_info = {
+    .name = TYPE_CHARDEV_MSMOUSE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(MouseChardev),
+    .class_init = char_msmouse_class_init,
+};
+
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(MouseChardev),
         .kind = CHARDEV_BACKEND_KIND_MSMOUSE,
-        .parse = NULL, .create = qemu_chr_open_msmouse,
-        .chr_write = msmouse_chr_write,
-        .chr_accept_input = msmouse_chr_accept_input,
-        .chr_free = msmouse_chr_free,
     };
+
     register_char_driver(&driver);
+    type_register_static(&char_msmouse_type_info);
 }
 
 type_init(register_types);
diff --git a/backends/testdev.c b/backends/testdev.c
index 1d06c8633e..ea15143713 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -36,6 +36,10 @@ typedef struct {
     int in_buf_used;
 } TestdevChardev;
 
+#define TYPE_CHARDEV_TESTDEV "chardev-testdev"
+#define TESTDEV_CHARDEV(obj)                                    \
+    OBJECT_CHECK(TestdevChardev, (obj), TYPE_CHARDEV_TESTDEV)
+
 /* Try to interpret a whole incoming packet */
 static int testdev_eat_packet(TestdevChardev *testdev)
 {
@@ -78,9 +82,9 @@ static int testdev_eat_packet(TestdevChardev *testdev)
 }
 
 /* The other end is writing some data.  Store it and try to interpret */
-static int testdev_write(Chardev *chr, const uint8_t *buf, int len)
+static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    TestdevChardev *testdev = (TestdevChardev *)chr;
+    TestdevChardev *testdev = TESTDEV_CHARDEV(chr);
     int tocopy, eaten, orig_len = len;
 
     while (len) {
@@ -103,30 +107,28 @@ static int testdev_write(Chardev *chr, const uint8_t 
*buf, int len)
     return orig_len;
 }
 
-static Chardev *chr_testdev_init(const CharDriver *driver,
-                                 const char *id,
-                                 ChardevBackend *backend,
-                                 ChardevReturn *ret,
-                                 bool *be_opened,
-                                 Error **errp)
+static void char_testdev_class_init(ObjectClass *oc, void *data)
 {
-    TestdevChardev *testdev = g_new0(TestdevChardev, 1);;
-    Chardev *chr = (Chardev *)testdev;
-
-    chr->driver = driver;
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    return chr;
+    cc->chr_write = testdev_chr_write;
 }
 
+static const TypeInfo char_testdev_type_info = {
+    .name = TYPE_CHARDEV_TESTDEV,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(TestdevChardev),
+    .class_init = char_testdev_class_init,
+};
+
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(TestdevChardev),
         .kind = CHARDEV_BACKEND_KIND_TESTDEV,
-        .parse = NULL, .create = chr_testdev_init,
-        .chr_write = testdev_write,
     };
+
     register_char_driver(&driver);
+    type_register_static(&char_testdev_type_info);
 }
 
 type_init(register_types);
diff --git a/gdbstub.c b/gdbstub.c
index 22fa291bc9..ee9286a01b 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1723,18 +1723,29 @@ static void gdb_sigterm_handler(int signal)
 }
 #endif
 
+
+static void char_gdb_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->internal = true;
+    cc->chr_write = gdb_monitor_write;
+}
+
+#define TYPE_CHARDEV_GDB "chardev-gdb"
+
+static const TypeInfo char_gdb_type_info = {
+    .name = TYPE_CHARDEV_GDB,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_gdb_class_init,
+};
+
 int gdbserver_start(const char *device)
 {
     GDBState *s;
     char gdbstub_device_name[128];
     Chardev *chr = NULL;
     Chardev *mon_chr;
-    ChardevCommon common = { 0 };
-    static const CharDriver driver = {
-        .instance_size = sizeof(Chardev),
-        .kind = -1,
-        .chr_write = gdb_monitor_write
-    };
 
     if (!device)
         return -1;
@@ -1767,7 +1778,8 @@ int gdbserver_start(const char *device)
         qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
 
         /* Initialize a monitor terminal for gdb */
-        mon_chr = qemu_chr_alloc(&driver, &common, &error_abort);
+        mon_chr = qemu_chardev_new("gdb", TYPE_CHARDEV_GDB,
+                                   NULL, &error_abort);
         monitor_init(mon_chr, 0);
     } else {
         if (qemu_chr_fe_get_driver(&s->chr)) {
@@ -1790,4 +1802,11 @@ int gdbserver_start(const char *device)
 
     return 0;
 }
+
+static void register_types(void)
+{
+    type_register_static(&char_gdb_type_info);
+}
+
+type_init(register_types);
 #endif
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index 153ed2f1ba..c9dbbcf3a5 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -55,6 +55,9 @@ struct csrhci_s {
     struct HCIInfo *hci;
 };
 
+#define TYPE_CHARDEV_HCI "chardev-hci"
+#define HCI_CHARDEV(obj) OBJECT_CHECK(struct csrhci_s, (obj), TYPE_CHARDEV_HCI)
+
 /* H4+ packet types */
 enum {
     H4_CMD_PKT   = 1,
@@ -462,23 +465,12 @@ qemu_irq *csrhci_pins_get(Chardev *chr)
     return s->pins;
 }
 
-Chardev *uart_hci_init(void)
+static void csrhci_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
 {
-    static const CharDriver hci_driver = {
-        .instance_size = sizeof(struct csrhci_s),
-        .kind = -1,
-        .chr_write = csrhci_write,
-        .chr_ioctl = csrhci_ioctl,
-    };
-    Error *err = NULL;
-    ChardevCommon common = { 0, };
-    Chardev *chr = qemu_chr_alloc(&hci_driver, &common, &err);
-    struct csrhci_s *s = (struct csrhci_s *)chr;
-
-    if (err) {
-        error_report_err(err);
-        return NULL;
-    }
+    struct csrhci_s *s = HCI_CHARDEV(chr);
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
@@ -488,6 +480,34 @@ Chardev *uart_hci_init(void)
     s->out_tm = timer_new_ns(QEMU_CLOCK_VIRTUAL, csrhci_out_tick, s);
     s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
     csrhci_reset(s);
+}
+
+static void char_hci_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->internal = true;
+    cc->open = csrhci_open;
+    cc->chr_write = csrhci_write;
+    cc->chr_ioctl = csrhci_ioctl;
+}
+
+static const TypeInfo char_hci_type_info = {
+    .name = TYPE_CHARDEV_HCI,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(struct csrhci_s),
+    .class_init = char_hci_class_init,
+};
 
-    return chr;
+Chardev *uart_hci_init(void)
+{
+    return qemu_chardev_new("hci-csr", TYPE_CHARDEV_HCI,
+                            NULL, &error_abort);
 }
+
+static void register_types(void)
+{
+    type_register_static(&char_hci_type_info);
+}
+
+type_init(register_types);
diff --git a/monitor.c b/monitor.c
index a82f547488..90ca4d737a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3194,7 +3194,7 @@ static void ringbuf_completion(ReadLineState *rs, const 
char *str)
 
         if (!strncmp(chr_info->label, str, len)) {
             Chardev *chr = qemu_chr_find(chr_info->label);
-            if (chr && qemu_chr_is_ringbuf(chr)) {
+            if (chr && CHARDEV_IS_RINGBUF(chr)) {
                 readline_add_completion(rs, chr_info->label);
             }
         }
diff --git a/qemu-char.c b/qemu-char.c
index 315037049b..d5656007da 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -160,43 +160,6 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, 
socklen_t ss_len,
 static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
     QTAILQ_HEAD_INITIALIZER(chardevs);
 
-static void qemu_chr_free_common(Chardev *chr);
-
-Chardev *qemu_chr_alloc(const CharDriver *driver,
-                                ChardevCommon *backend, Error **errp)
-{
-    Chardev *chr;
-
-    assert(driver);
-    assert(driver->chr_write);
-    assert(driver->instance_size >= sizeof(Chardev));
-
-    chr = g_malloc0(driver->instance_size);
-    qemu_mutex_init(&chr->chr_write_lock);
-    if (backend->has_logfile) {
-        int flags = O_WRONLY | O_CREAT;
-        if (backend->has_logappend &&
-            backend->logappend) {
-            flags |= O_APPEND;
-        } else {
-            flags |= O_TRUNC;
-        }
-        chr->logfd = qemu_open(backend->logfile, flags, 0666);
-        if (chr->logfd < 0) {
-            error_setg_errno(errp, errno,
-                             "Unable to open logfile %s",
-                             backend->logfile);
-            g_free(chr);
-            return NULL;
-        }
-    } else {
-        chr->logfd = -1;
-    }
-    chr->driver = driver;
-
-    return chr;
-}
-
 void qemu_chr_be_event(Chardev *s, int event)
 {
     CharBackend *be = s->be;
@@ -254,13 +217,14 @@ 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)
 {
+    ChardevClass *cc = CHARDEV_GET_CLASS(s);
     int res = 0;
     *offset = 0;
 
     qemu_mutex_lock(&s->chr_write_lock);
     while (*offset < len) {
     retry:
-        res = s->driver->chr_write(s, buf + *offset, len - *offset);
+        res = cc->chr_write(s, buf + *offset, len - *offset);
         if (res < 0 && errno == EAGAIN) {
             g_usleep(100);
             goto retry;
@@ -288,6 +252,7 @@ static bool qemu_chr_replay(Chardev *chr)
 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 {
     Chardev *s = be->chr;
+    ChardevClass *cc;
     int ret;
 
     if (!s) {
@@ -302,8 +267,9 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, 
int len)
         return ret;
     }
 
+    cc = CHARDEV_GET_CLASS(s);
     qemu_mutex_lock(&s->chr_write_lock);
-    ret = s->driver->chr_write(s, buf, len);
+    ret = cc->chr_write(s, buf, len);
 
     if (ret > 0) {
         qemu_chr_fe_write_log(s, buf, ret);
@@ -359,7 +325,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int 
len)
     int offset = 0, counter = 10;
     int res;
 
-    if (!s || !s->driver->chr_sync_read) {
+    if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
         return 0;
     }
 
@@ -369,7 +335,8 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int 
len)
 
     while (offset < len) {
     retry:
-        res = s->driver->chr_sync_read(s, buf + offset, len - offset);
+        res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
+                                                  len - offset);
         if (res == -1 && errno == EAGAIN) {
             g_usleep(100);
             goto retry;
@@ -404,10 +371,10 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
     Chardev *s = be->chr;
     int res;
 
-    if (!s || !s->driver->chr_ioctl || qemu_chr_replay(s)) {
+    if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
         res = -ENOTSUP;
     } else {
-        res = s->driver->chr_ioctl(s, cmd, arg);
+        res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
     }
 
     return res;
@@ -466,7 +433,8 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int 
len)
         return -1;
     }
 
-    return s->driver->get_msgfds ? s->driver->get_msgfds(s, fds, len) : -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)
@@ -477,12 +445,14 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int 
num)
         return -1;
     }
 
-    return s->driver->set_msgfds ? s->driver->set_msgfds(s, fds, num) : -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 s->driver->chr_add_client ? s->driver->chr_add_client(s, fd) : -1;
+    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)
@@ -493,8 +463,8 @@ void qemu_chr_fe_accept_input(CharBackend *be)
         return;
     }
 
-    if (s->driver->chr_accept_input) {
-        s->driver->chr_accept_input(s);
+    if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
+        CHARDEV_GET_CLASS(s)->chr_accept_input(s);
     }
     qemu_notify_event();
 }
@@ -515,33 +485,98 @@ static void remove_fd_in_watch(Chardev *chr);
 static void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
 static void mux_set_focus(MuxChardev *d, int focus);
 
-static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
+static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
+                           bool *be_opened, Error **errp)
 {
-    return len;
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+    /* Any ChardevCommon member would work */
+    ChardevCommon *common = backend ? backend->u.null.data : NULL;
+
+    if (common && common->has_logfile) {
+        int flags = O_WRONLY | O_CREAT;
+        if (common->has_logappend &&
+            common->logappend) {
+            flags |= O_APPEND;
+        } else {
+            flags |= O_TRUNC;
+        }
+        chr->logfd = qemu_open(common->logfile, flags, 0666);
+        if (chr->logfd < 0) {
+            error_setg_errno(errp, errno,
+                             "Unable to open logfile %s",
+                             common->logfile);
+            return;
+        }
+    }
+
+    if (cc->open) {
+        cc->open(chr, backend, be_opened, errp);
+    }
 }
 
-static Chardev *qemu_chr_open_null(const CharDriver *driver,
-                                   const char *id,
-                                   ChardevBackend *backend,
-                                   ChardevReturn *ret,
-                                   bool *be_opened,
-                                   Error **errp)
+static void char_init(Object *obj)
 {
-    Chardev *chr;
-    ChardevCommon *common = backend->u.null.data;
+    Chardev *chr = CHARDEV(obj);
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
+    chr->logfd = -1;
+    qemu_mutex_init(&chr->chr_write_lock);
+}
+
+static void char_finalize(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+
+    if (chr->be) {
+        chr->be->chr = NULL;
+    }
+    g_free(chr->filename);
+    g_free(chr->label);
+    if (chr->logfd != -1) {
+        close(chr->logfd);
     }
+    qemu_mutex_destroy(&chr->chr_write_lock);
+}
+
+static const TypeInfo char_type_info = {
+    .name = TYPE_CHARDEV,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(Chardev),
+    .instance_init = char_init,
+    .instance_finalize = char_finalize,
+    .abstract = true,
+    .class_size = sizeof(ChardevClass),
+};
+
+static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    return len;
+}
+
+static void null_chr_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
+{
     *be_opened = false;
-    return chr;
 }
 
 static const CharDriver null_driver = {
+    .kind = CHARDEV_BACKEND_KIND_NULL,
+};
+
+static void char_null_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = null_chr_open;
+    cc->chr_write = null_chr_write;
+}
+
+static const TypeInfo char_null_type_info = {
+    .name = TYPE_CHARDEV_NULL,
+    .parent = TYPE_CHARDEV,
     .instance_size = sizeof(Chardev),
-    .kind = CHARDEV_BACKEND_KIND_NULL, .create = qemu_chr_open_null,
-    .chr_write = null_chr_write
+    .class_init = char_null_class_init,
 };
 
 /* MUX driver for serial I/O splitting */
@@ -569,10 +604,12 @@ struct MuxChardev {
     int64_t timestamps_start;
 };
 
+#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
+
 /* Called with chr_write_lock held.  */
 static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int ret;
     if (!d->timestamps) {
         ret = qemu_chr_fe_write(&d->chr, buf, len);
@@ -706,7 +743,7 @@ static int mux_proc_byte(Chardev *chr, MuxChardev *d, int 
ch)
 
 static void mux_chr_accept_input(Chardev *chr)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -719,7 +756,7 @@ static void mux_chr_accept_input(Chardev *chr)
 
 static int mux_chr_can_read(void *opaque)
 {
-    MuxChardev *d = opaque;
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -736,8 +773,8 @@ static int mux_chr_can_read(void *opaque)
 
 static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 {
-    Chardev *chr = opaque;
-    MuxChardev *d = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int m = d->focus;
     CharBackend *be = d->backends[m];
     int i;
@@ -759,7 +796,7 @@ static bool muxes_realized;
 
 static void mux_chr_event(void *opaque, int event)
 {
-    MuxChardev *d = opaque;
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int i;
 
     if (!muxes_realized) {
@@ -788,8 +825,8 @@ static void muxes_realize_done(Notifier *notifier, void 
*unused)
     Chardev *chr;
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
-        if (qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxChardev *d = (MuxChardev *)chr;
+        if (CHARDEV_IS_MUX(chr)) {
+            MuxChardev *d = MUX_CHARDEV(chr);
             int i;
 
             /* send OPENED to all already-attached FEs */
@@ -811,19 +848,20 @@ static Notifier muxes_realize_notify = {
 
 static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
 {
-    MuxChardev *d = (MuxChardev *)s;
+    MuxChardev *d = MUX_CHARDEV(s);
     Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
 
-    if (!chr->driver->chr_add_watch) {
+    if (!cc->chr_add_watch) {
         return NULL;
     }
 
-    return chr->driver->chr_add_watch(chr, cond);
+    return cc->chr_add_watch(chr, cond);
 }
 
 static void mux_chr_free(struct Chardev *chr)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int i;
 
     for (i = 0; i < d->mux_cnt; i++) {
@@ -837,7 +875,7 @@ static void mux_chr_free(struct Chardev *chr)
 
 static void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
 
     /* Fix up the real driver with mux routines */
     qemu_chr_fe_set_handlers(&d->chr,
@@ -861,40 +899,27 @@ static void mux_set_focus(MuxChardev *d, int focus)
     mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 }
 
-static Chardev *qemu_chr_open_mux(const CharDriver *driver,
-                                  const char *id,
-                                  ChardevBackend *backend,
-                                  ChardevReturn *ret,
-                                  bool *be_opened,
-                                  Error **errp)
+static void qemu_chr_open_mux(Chardev *chr,
+                              ChardevBackend *backend,
+                              bool *be_opened,
+                              Error **errp)
 {
     ChardevMux *mux = backend->u.mux.data;
-    Chardev *chr, *drv;
-    MuxChardev *d;
-    ChardevCommon *common = qapi_ChardevMux_base(mux);
+    Chardev *drv;
+    MuxChardev *d = MUX_CHARDEV(chr);
 
     drv = qemu_chr_find(mux->chardev);
     if (drv == NULL) {
         error_setg(errp, "mux: base chardev %s not found", mux->chardev);
-        return NULL;
+        return;
     }
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    d = (MuxChardev *)chr;
     d->focus = -1;
     /* only default to opened state if we've realized the initial
      * set of muxes
      */
     *be_opened = muxes_realized;
-    if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
-        qemu_chr_free(chr);
-        return NULL;
-    }
-
-    return chr;
+    qemu_chr_fe_init(&d->chr, drv, errp);
 }
 
 Chardev *qemu_chr_fe_get_driver(CharBackend *be)
@@ -906,8 +931,8 @@ bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error 
**errp)
 {
     int tag = 0;
 
-    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxChardev *d = (MuxChardev *)s;
+    if (CHARDEV_IS_MUX(s)) {
+        MuxChardev *d = MUX_CHARDEV(s);
 
         if (d->mux_cnt >= MAX_MUX) {
             goto unavailable;
@@ -933,8 +958,8 @@ unavailable:
 
 static bool qemu_chr_is_busy(Chardev *s)
 {
-    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxChardev *d = (MuxChardev *)s;
+    if (CHARDEV_IS_MUX(s)) {
+        MuxChardev *d = MUX_CHARDEV(s);
         return d->mux_cnt >= 0;
     } else {
         return s->be != NULL;
@@ -948,8 +973,8 @@ void qemu_chr_fe_deinit(CharBackend *b)
     if (b->chr) {
         qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
         b->chr->be = NULL;
-        if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxChardev *d = (MuxChardev *)b->chr;
+        if (CHARDEV_IS_MUX(b->chr)) {
+            MuxChardev *d = MUX_CHARDEV(b->chr);
             d->backends[b->tag] = NULL;
         }
         b->chr = NULL;
@@ -965,6 +990,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
                               bool set_open)
 {
     Chardev *s;
+    ChardevClass *cc;
     int fe_open;
 
     s = b->chr;
@@ -972,6 +998,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         return;
     }
 
+    cc = CHARDEV_GET_CLASS(s);
     if (!opaque && !fd_can_read && !fd_read && !fd_event) {
         fe_open = 0;
         remove_fd_in_watch(s);
@@ -982,8 +1009,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
     b->chr_read = fd_read;
     b->chr_event = fd_event;
     b->opaque = opaque;
-    if (s->driver->chr_update_read_handler) {
-        s->driver->chr_update_read_handler(s, context);
+    if (cc->chr_update_read_handler) {
+        cc->chr_update_read_handler(s, context);
     }
 
     if (set_open) {
@@ -999,7 +1026,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         }
     }
 
-    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
+    if (CHARDEV_IS_MUX(s)) {
         mux_chr_set_handlers(s, context);
     }
 }
@@ -1010,8 +1037,8 @@ void qemu_chr_fe_take_focus(CharBackend *b)
         return;
     }
 
-    if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
-        mux_set_focus((MuxChardev *)b->chr, b->tag);
+    if (CHARDEV_IS_MUX(b->chr)) {
+        mux_set_focus(MUX_CHARDEV(b->chr), b->tag);
     }
 }
 
@@ -1187,7 +1214,6 @@ static int io_channel_send(QIOChannel *ioc, const void 
*buf, size_t len)
     return io_channel_send_full(ioc, buf, len, NULL, 0);
 }
 
-
 typedef struct FDChardev {
     Chardev parent;
     Chardev *chr;
@@ -1195,18 +1221,21 @@ typedef struct FDChardev {
     int max_size;
 } FDChardev;
 
+#define TYPE_CHARDEV_FD "chardev-fd"
+#define FD_CHARDEV(obj) OBJECT_CHECK(FDChardev, (obj), TYPE_CHARDEV_FD)
+
 /* Called with chr_write_lock held.  */
 static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
 
     return io_channel_send(s->ioc_out, buf, len);
 }
 
 static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    Chardev *chr = opaque;
-    FDChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    FDChardev *s = FD_CHARDEV(opaque);
     int len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1235,8 +1264,8 @@ static gboolean fd_chr_read(QIOChannel *chan, 
GIOCondition cond, void *opaque)
 
 static int fd_chr_read_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    FDChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    FDChardev *s = FD_CHARDEV(opaque);
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -1244,14 +1273,14 @@ static int fd_chr_read_poll(void *opaque)
 
 static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
     return qio_channel_create_watch(s->ioc_out, cond);
 }
 
 static void fd_chr_update_read_handler(Chardev *chr,
                                        GMainContext *context)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1264,7 +1293,7 @@ static void fd_chr_update_read_handler(Chardev *chr,
 
 static void fd_chr_free(struct Chardev *chr)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1278,19 +1307,12 @@ static void fd_chr_free(struct Chardev *chr)
 }
 
 /* open a character device to a unix fd */
-static Chardev *qemu_chr_open_fd(const CharDriver *driver,
-                                 int fd_in, int fd_out,
-                                 ChardevCommon *backend, Error **errp)
+static void qemu_chr_open_fd(Chardev *chr,
+                             int fd_in, int fd_out)
 {
-    Chardev *chr;
-    FDChardev *s;
+    FDChardev *s = FD_CHARDEV(chr);
     char *name;
 
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = (FDChardev *)chr;
     s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
     name = g_strdup_printf("chardev-file-in-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
@@ -1301,24 +1323,36 @@ static Chardev *qemu_chr_open_fd(const CharDriver 
*driver,
     g_free(name);
     qemu_set_nonblock(fd_out);
     s->chr = chr;
+}
 
-    return chr;
+static void char_fd_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_add_watch = fd_chr_add_watch;
+    cc->chr_write = fd_chr_write;
+    cc->chr_update_read_handler = fd_chr_update_read_handler;
+    cc->chr_free = fd_chr_free;
 }
 
-static Chardev *qemu_chr_open_pipe(const CharDriver *driver,
-                                   const char *id,
-                                   ChardevBackend *backend,
-                                   ChardevReturn *ret,
-                                   bool *be_opened,
-                                   Error **errp)
+static const TypeInfo char_fd_type_info = {
+    .name = TYPE_CHARDEV_FD,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(FDChardev),
+    .class_init = char_fd_class_init,
+    .abstract = true,
+};
+
+static void qemu_chr_open_pipe(Chardev *chr,
+                               ChardevBackend *backend,
+                               bool *be_opened,
+                               Error **errp)
 {
     ChardevHostdev *opts = backend->u.pipe.data;
     int fd_in, fd_out;
     char *filename_in;
     char *filename_out;
     const char *filename = opts->device;
-    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
 
     filename_in = g_strdup_printf("%s.in", filename);
     filename_out = g_strdup_printf("%s.out", filename);
@@ -1334,10 +1368,10 @@ static Chardev *qemu_chr_open_pipe(const CharDriver 
*driver,
         TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
         if (fd_in < 0) {
             error_setg_file_open(errp, errno, filename);
-            return NULL;
+            return;
         }
     }
-    return qemu_chr_open_fd(driver, fd_in, fd_out, common, errp);
+    qemu_chr_open_fd(chr, fd_in, fd_out);
 }
 
 /* init terminal so that we can grab keys */
@@ -1389,26 +1423,22 @@ static void qemu_chr_free_stdio(struct Chardev *chr)
     fd_chr_free(chr);
 }
 
-static Chardev *qemu_chr_open_stdio(const CharDriver *driver,
-                                    const char *id,
-                                    ChardevBackend *backend,
-                                    ChardevReturn *ret,
-                                    bool *be_opened,
-                                    Error **errp)
+static void qemu_chr_open_stdio(Chardev *chr,
+                                ChardevBackend *backend,
+                                bool *be_opened,
+                                Error **errp)
 {
     ChardevStdio *opts = backend->u.stdio.data;
-    Chardev *chr;
     struct sigaction act;
-    ChardevCommon *common = qapi_ChardevStdio_base(opts);
 
     if (is_daemonized()) {
         error_setg(errp, "cannot use stdio with -daemonize");
-        return NULL;
+        return;
     }
 
     if (stdio_in_use) {
         error_setg(errp, "cannot use stdio by multiple character devices");
-        return NULL;
+        return;
     }
 
     stdio_in_use = true;
@@ -1421,19 +1451,15 @@ static Chardev *qemu_chr_open_stdio(const CharDriver 
*driver,
     act.sa_handler = term_stdio_handler;
     sigaction(SIGCONT, &act, NULL);
 
-    chr = qemu_chr_open_fd(driver, 0, 1, common, errp);
-    if (!chr) {
-        return NULL;
-    }
+    qemu_chr_open_fd(chr, 0, 1);
+
     if (opts->has_signal) {
         stdio_allow_signal = opts->signal;
     }
     qemu_chr_set_echo_stdio(chr, false);
-
-    return chr;
 }
 
-#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)     \
     || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
     || defined(__GLIBC__)
 
@@ -1451,13 +1477,15 @@ typedef struct {
     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 = opaque;
-    PtyChardev *s = opaque;
+    struct Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     qemu_mutex_lock(&chr->chr_write_lock);
     s->timer_tag = 0;
@@ -1473,7 +1501,7 @@ static gboolean pty_chr_timer(gpointer opaque)
 /* Called with chr_write_lock held.  */
 static void pty_chr_rearm_timer(Chardev *chr, int ms)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
     char *name;
 
     if (s->timer_tag) {
@@ -1495,7 +1523,7 @@ static void pty_chr_rearm_timer(Chardev *chr, int ms)
 /* Called with chr_write_lock held.  */
 static void pty_chr_update_read_handler_locked(Chardev *chr)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
     GPollFD pfd;
     int rc;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
@@ -1524,9 +1552,9 @@ static void pty_chr_update_read_handler(Chardev *chr,
 }
 
 /* Called with chr_write_lock held.  */
-static int pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
+static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     if (!s->connected) {
         /* guest sends data, check for (re-)connect */
@@ -1540,7 +1568,7 @@ static int pty_chr_write(Chardev *chr, const uint8_t 
*buf, int len)
 
 static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
     if (!s->connected) {
         return NULL;
     }
@@ -1549,8 +1577,8 @@ static GSource *pty_chr_add_watch(Chardev *chr, 
GIOCondition cond)
 
 static int pty_chr_read_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    PtyChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     s->read_bytes = qemu_chr_be_can_write(chr);
     return s->read_bytes;
@@ -1558,8 +1586,8 @@ static int pty_chr_read_poll(void *opaque)
 
 static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    Chardev *chr = opaque;
-    PtyChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
     gsize len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1583,8 +1611,8 @@ static gboolean pty_chr_read(QIOChannel *chan, 
GIOCondition cond, void *opaque)
 
 static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 {
-    Chardev *chr = opaque;
-    PtyChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     s->open_tag = 0;
     qemu_chr_be_generic_open(chr);
@@ -1594,7 +1622,7 @@ static gboolean qemu_chr_be_generic_open_func(gpointer 
opaque)
 /* Called with chr_write_lock held.  */
 static void pty_chr_state(Chardev *chr, int connected)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     if (!connected) {
         if (s->open_tag) {
@@ -1628,7 +1656,7 @@ static void pty_chr_state(Chardev *chr, int connected)
 
 static void pty_chr_free(struct Chardev *chr)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_state(chr, 0);
@@ -1641,60 +1669,58 @@ static void pty_chr_free(struct Chardev *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static Chardev *qemu_chr_open_pty(const CharDriver *driver,
-                                  const char *id,
-                                  ChardevBackend *backend,
-                                  ChardevReturn *ret,
-                                  bool *be_opened,
-                                  Error **errp)
+static void char_pty_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
 {
-    Chardev *chr;
     PtyChardev *s;
     int master_fd, slave_fd;
     char pty_name[PATH_MAX];
-    ChardevCommon *common = backend->u.pty.data;
     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 NULL;
+        return;
     }
 
     close(slave_fd);
     qemu_set_nonblock(master_fd);
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        close(master_fd);
-        return NULL;
-    }
-
     chr->filename = g_strdup_printf("pty:%s", pty_name);
-    ret->pty = g_strdup(pty_name);
-    ret->has_pty = true;
-
     error_report("char device redirected to %s (label %s)",
-                 pty_name, id);
+                 pty_name, chr->label);
 
-    s = (PtyChardev *)chr;
+    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;
-
-    return chr;
 }
 
 static const CharDriver pty_driver = {
+    .kind = CHARDEV_BACKEND_KIND_PTY
+};
+
+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;
+    cc->chr_free = pty_chr_free;
+}
+
+static const TypeInfo char_pty_type_info = {
+    .name = TYPE_CHARDEV_PTY,
+    .parent = TYPE_CHARDEV,
     .instance_size = sizeof(PtyChardev),
-    .kind = CHARDEV_BACKEND_KIND_PTY, .create = qemu_chr_open_pty,
-    .chr_write = pty_chr_write,
-    .chr_update_read_handler = pty_chr_update_read_handler,
-    .chr_add_watch = pty_chr_add_watch,
-    .chr_free = pty_chr_free,
+    .class_init = char_pty_class_init,
 };
 
 static void tty_serial_init(int fd, int speed,
@@ -1814,7 +1840,7 @@ static void tty_serial_init(int fd, int speed,
 
 static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
 
     switch(cmd) {
@@ -1898,6 +1924,9 @@ typedef struct {
     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) {
@@ -1911,7 +1940,7 @@ static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
 
 static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    ParallelChardev *drv = (ParallelChardev *)chr;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     int fd = drv->fd;
     uint8_t b;
 
@@ -1992,7 +2021,7 @@ static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 
 static void pp_free(Chardev *chr)
 {
-    ParallelChardev *drv = (ParallelChardev *)chr;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     int fd = drv->fd;
 
     pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
@@ -2001,31 +2030,21 @@ static void pp_free(Chardev *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static Chardev *qemu_chr_open_pp_fd(const CharDriver *driver,
-                                    int fd,
-                                    ChardevCommon *backend,
-                                    bool *be_opened,
-                                    Error **errp)
+static void qemu_chr_open_pp_fd(Chardev *chr,
+                                int fd,
+                                bool *be_opened,
+                                Error **errp)
 {
-    Chardev *chr;
-    ParallelChardev *drv;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 
     if (ioctl(fd, PPCLAIM) < 0) {
         error_setg_errno(errp, errno, "not a parallel port");
         close(fd);
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
-    drv = (ParallelChardev *)chr;
     drv->fd = fd;
     drv->mode = IEEE1284_MODE_COMPAT;
-
-    return chr;
 }
 #endif /* __linux__ */
 
@@ -2038,9 +2057,12 @@ typedef struct {
     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 = (ParallelChardev *)chr;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     uint8_t b;
 
     switch (cmd) {
@@ -2080,23 +2102,14 @@ static int pp_ioctl(Chardev *chr, int cmd, void *arg)
     return 0;
 }
 
-static Chardev *qemu_chr_open_pp_fd(const CharDriver *driver,
-                                    int fd,
-                                    ChardevCommon *backend,
-                                    bool *be_opened,
-                                    Error **errp)
+static void qemu_chr_open_pp_fd(Chardev *chr,
+                                int fd,
+                                bool *be_opened,
+                                Error **errp)
 {
-    Chardev *chr;
-    ParallelChardev *drv;
-
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    drv = (ParallelChardev *)chr;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     drv->fd = fd;
     *be_opened = false;
-    return chr;
 }
 #endif
 
@@ -2116,6 +2129,9 @@ typedef struct {
     OVERLAPPED osend;
 } WinChardev;
 
+#define TYPE_CHARDEV_WIN "chardev-win"
+#define WIN_CHARDEV(obj) OBJECT_CHECK(WinChardev, (obj), TYPE_CHARDEV_WIN)
+
 typedef struct {
     Chardev parent;
     HANDLE  hStdIn;
@@ -2125,6 +2141,10 @@ typedef struct {
     uint8_t win_stdio_buf;
 } WinStdioChardev;
 
+#define TYPE_CHARDEV_WIN_STDIO "chardev-win-stdio"
+#define WIN_STDIO_CHARDEV(obj)                                  \
+    OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO)
+
 #define NSENDBUF 2048
 #define NRECVBUF 2048
 #define MAXCONNECT 1
@@ -2135,7 +2155,7 @@ static int win_chr_pipe_poll(void *opaque);
 
 static void win_chr_free(Chardev *chr)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
 
     if (s->hsend) {
         CloseHandle(s->hsend);
@@ -2159,7 +2179,7 @@ static void win_chr_free(Chardev *chr)
 
 static int win_chr_init(Chardev *chr, const char *filename, Error **errp)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
     COMMCONFIG comcfg;
     COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
     COMSTAT comstat;
@@ -2227,7 +2247,7 @@ static int win_chr_init(Chardev *chr, const char 
*filename, Error **errp)
 /* Called with chr_write_lock held.  */
 static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
     DWORD len, ret, size, err;
 
     len = len1;
@@ -2261,7 +2281,7 @@ static int win_chr_write(Chardev *chr, const uint8_t 
*buf, int len1)
 
 static int win_chr_read_poll(Chardev *chr)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -2269,7 +2289,8 @@ static int win_chr_read_poll(Chardev *chr)
 
 static void win_chr_readfile(Chardev *chr)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
+
     int ret, err;
     uint8_t buf[READ_BUF_LEN];
     DWORD size;
@@ -2291,7 +2312,7 @@ static void win_chr_readfile(Chardev *chr)
 
 static void win_chr_read(Chardev *chr)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
 
     if (s->len > s->max_size)
         s->len = s->max_size;
@@ -2303,8 +2324,8 @@ static void win_chr_read(Chardev *chr)
 
 static int win_chr_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    WinChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinChardev *s = WIN_CHARDEV(opaque);
     COMSTAT status;
     DWORD comerr;
 
@@ -2320,8 +2341,8 @@ static int win_chr_poll(void *opaque)
 
 static int win_chr_pipe_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    WinChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinChardev *s = WIN_CHARDEV(opaque);
     DWORD size;
 
     PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
@@ -2337,7 +2358,7 @@ static int win_chr_pipe_poll(void *opaque)
 static int win_chr_pipe_init(Chardev *chr, const char *filename,
                              Error **errp)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
     OVERLAPPED ov;
     int ret;
     DWORD size;
@@ -2399,64 +2420,66 @@ static int win_chr_pipe_init(Chardev *chr, const char 
*filename,
 }
 
 
-static Chardev *qemu_chr_open_pipe(const CharDriver *driver,
-                                   const char *id,
-                                   ChardevBackend *backend,
-                                   ChardevReturn *ret,
-                                   bool *be_opened,
-                                   Error **errp)
+static void qemu_chr_open_pipe(Chardev *chr,
+                               ChardevBackend *backend,
+                               bool *be_opened,
+                               Error **errp)
 {
     ChardevHostdev *opts = backend->u.pipe.data;
     const char *filename = opts->device;
-    Chardev *chr;
-    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
 
     if (win_chr_pipe_init(chr, filename, errp) < 0) {
-        qemu_chr_free_common(chr);
-        return NULL;
+        return;
     }
-    return chr;
 }
 
-static Chardev *qemu_chr_open_win_file(const CharDriver *driver,
-                                       HANDLE fd_out,
-                                       ChardevCommon *backend,
-                                       Error **errp)
+static void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out)
 {
-    Chardev *chr;
-    WinChardev *s;
+    WinChardev *s = WIN_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = (WinChardev *)chr;
     s->hcom = fd_out;
-    return chr;
 }
 
-static Chardev *qemu_chr_open_win_con(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void char_win_class_init(ObjectClass *oc, void *data)
 {
-    ChardevCommon *common = backend->u.console.data;
-    return qemu_chr_open_win_file(driver,
-                                  GetStdHandle(STD_OUTPUT_HANDLE),
-                                  common, errp);
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_write = win_chr_write;
+    cc->chr_free = win_chr_free;
 }
 
-static const CharDriver console_driver = {
+static const TypeInfo char_win_type_info = {
+    .name = TYPE_CHARDEV_WIN,
+    .parent = TYPE_CHARDEV,
     .instance_size = sizeof(WinChardev),
-    .kind = CHARDEV_BACKEND_KIND_CONSOLE, .create = qemu_chr_open_win_con,
-    .chr_write = win_chr_write,
+    .class_init = char_win_class_init,
+    .abstract = true,
+};
+
+static void qemu_chr_open_win_con(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
+{
+    qemu_chr_open_win_file(chr, GetStdHandle(STD_OUTPUT_HANDLE));
+}
+
+static const CharDriver console_driver = {
+    .kind = CHARDEV_BACKEND_KIND_CONSOLE
+};
+
+static void char_console_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_win_con;
+    cc->chr_free = NULL;
+}
+
+static const TypeInfo char_console_type_info = {
+    .name = TYPE_CHARDEV_CONSOLE,
+    .parent = TYPE_CHARDEV_WIN,
+    .class_init = char_console_class_init,
 };
 
 static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
@@ -2480,8 +2503,8 @@ static int win_stdio_write(Chardev *chr, const uint8_t 
*buf, int len)
 
 static void win_stdio_wait_func(void *opaque)
 {
-    Chardev   *chr   = opaque;
-    WinStdioChardev *stdio = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
     INPUT_RECORD       buf[4];
     int                ret;
     DWORD              dwSize;
@@ -2514,7 +2537,7 @@ static void win_stdio_wait_func(void *opaque)
 
 static DWORD WINAPI win_stdio_thread(LPVOID param)
 {
-    WinStdioChardev *stdio = param;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
     int                ret;
     DWORD              dwSize;
 
@@ -2552,8 +2575,8 @@ static DWORD WINAPI win_stdio_thread(LPVOID param)
 
 static void win_stdio_thread_wait_func(void *opaque)
 {
-    Chardev   *chr   = opaque;
-    WinStdioChardev *stdio = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
 
     if (qemu_chr_be_can_write(chr)) {
         qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
@@ -2564,7 +2587,7 @@ static void win_stdio_thread_wait_func(void *opaque)
 
 static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
 {
-    WinStdioChardev *stdio  = (WinStdioChardev *)chr;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
     DWORD              dwMode = 0;
 
     GetConsoleMode(stdio->hStdIn, &dwMode);
@@ -2578,7 +2601,7 @@ static void qemu_chr_set_echo_win_stdio(Chardev *chr, 
bool echo)
 
 static void win_stdio_free(Chardev *chr)
 {
-    WinStdioChardev *stdio = (WinStdioChardev *)chr;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
 
     if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
         CloseHandle(stdio->hInputReadyEvent);
@@ -2591,29 +2614,26 @@ static void win_stdio_free(Chardev *chr)
     }
 }
 
-static Chardev *qemu_chr_open_stdio(const CharDriver *driver,
-                                    const char *id,
-                                    ChardevBackend *backend,
-                                    ChardevReturn *ret,
-                                    bool *be_opened,
-                                    Error **errp)
+static const TypeInfo char_win_stdio_type_info = {
+    .name = TYPE_CHARDEV_WIN_STDIO,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(WinStdioChardev),
+    .abstract = true,
+};
+
+static void qemu_chr_open_stdio(Chardev *chr,
+                                ChardevBackend *backend,
+                                bool *be_opened,
+                                Error **errp)
 {
-    Chardev   *chr;
-    WinStdioChardev *stdio;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
     DWORD              dwMode;
     int                is_console = 0;
-    ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data);
-
-    chr   = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    stdio = (WinStdioChardev *)chr;
 
     stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
     if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
         error_setg(errp, "cannot open stdio: invalid handle");
-        return NULL;
+        return;
     }
 
     is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
@@ -2660,7 +2680,7 @@ static Chardev *qemu_chr_open_stdio(const CharDriver 
*driver,
 
     qemu_chr_set_echo_win_stdio(chr, false);
 
-    return chr;
+    return;
 
 err3:
     qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
@@ -2669,7 +2689,6 @@ err2:
     CloseHandle(stdio->hInputDoneEvent);
 err1:
     qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
-    return NULL;
 }
 #endif /* !_WIN32 */
 
@@ -2685,10 +2704,12 @@ typedef struct {
     int max_size;
 } UdpChardev;
 
+#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP)
+
 /* Called with chr_write_lock held.  */
 static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    UdpChardev *s = (UdpChardev *)chr;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     return qio_channel_write(
         s->ioc, (const char *)buf, len, NULL);
@@ -2696,8 +2717,8 @@ static int udp_chr_write(Chardev *chr, const uint8_t 
*buf, int len)
 
 static int udp_chr_read_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    UdpChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    UdpChardev *s = UDP_CHARDEV(opaque);
 
     s->max_size = qemu_chr_be_can_write(chr);
 
@@ -2714,8 +2735,8 @@ static int udp_chr_read_poll(void *opaque)
 
 static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    Chardev *chr = opaque;
-    UdpChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    UdpChardev *s = UDP_CHARDEV(opaque);
     ssize_t ret;
 
     if (s->max_size == 0) {
@@ -2742,7 +2763,7 @@ static gboolean udp_chr_read(QIOChannel *chan, 
GIOCondition cond, void *opaque)
 static void udp_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
-    UdpChardev *s = (UdpChardev *)chr;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2755,7 +2776,7 @@ static void udp_chr_update_read_handler(Chardev *chr,
 
 static void udp_chr_free(Chardev *chr)
 {
-    UdpChardev *s = (UdpChardev *)chr;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2793,11 +2814,14 @@ typedef struct {
     bool connect_err_reported;
 } SocketChardev;
 
+#define SOCKET_CHARDEV(obj)                                     \
+    OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
+
 static gboolean socket_reconnect_timeout(gpointer opaque);
 
 static void qemu_chr_socket_restart_timer(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     char *name;
 
     assert(s->connected == 0);
@@ -2811,7 +2835,7 @@ static void qemu_chr_socket_restart_timer(Chardev *chr)
 static void check_report_connect_error(Chardev *chr,
                                        Error *err)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connect_err_reported) {
         error_report("Unable to connect character device %s: %s",
@@ -2828,7 +2852,7 @@ static gboolean tcp_chr_accept(QIOChannel *chan,
 /* Called with chr_write_lock held.  */
 static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (s->connected) {
         int ret =  io_channel_send_full(s->ioc, buf, len,
@@ -2851,8 +2875,8 @@ static int tcp_chr_write(Chardev *chr, const uint8_t 
*buf, int len)
 
 static int tcp_chr_read_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    SocketChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     if (!s->connected)
         return 0;
     s->max_size = qemu_chr_be_can_write(chr);
@@ -2911,7 +2935,7 @@ static void tcp_chr_process_IAC_bytes(Chardev *chr,
 
 static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
 
@@ -2937,7 +2961,7 @@ static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
 
 static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     /* clear old pending fd array */
     g_free(s->write_msgfds);
@@ -2962,7 +2986,7 @@ static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
 
 static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     struct iovec iov = { .iov_base = buf, .iov_len = len };
     int ret;
     size_t i;
@@ -3019,13 +3043,13 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, 
size_t len)
 
 static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     return qio_channel_create_watch(s->ioc, cond);
 }
 
 static void tcp_chr_free_connection(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     int i;
 
     if (!s->connected) {
@@ -3054,7 +3078,7 @@ static void tcp_chr_free_connection(Chardev *chr)
 
 static void tcp_chr_disconnect(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connected) {
         return;
@@ -3076,8 +3100,8 @@ static void tcp_chr_disconnect(Chardev *chr)
 
 static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    Chardev *chr = opaque;
-    SocketChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
@@ -3103,7 +3127,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, 
GIOCondition cond, void *opaque)
 
 static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     int size;
 
     if (!s->connected) {
@@ -3121,8 +3145,8 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t 
*buf, int len)
 
 static void tcp_chr_connect(void *opaque)
 {
-    Chardev *chr = opaque;
-    SocketChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
 
     g_free(chr->filename);
     chr->filename = sockaddr_to_str(
@@ -3143,7 +3167,7 @@ static void tcp_chr_connect(void *opaque)
 static void tcp_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connected) {
         return;
@@ -3194,7 +3218,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
 
 static void tcp_chr_telnet_init(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     TCPCharDriverTelnetInit *init =
         g_new0(TCPCharDriverTelnetInit, 1);
     size_t n = 0;
@@ -3246,7 +3270,7 @@ static void tcp_chr_tls_handshake(Object *source,
 
 static void tcp_chr_tls_init(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     QIOChannelTLS *tioc;
     Error *err = NULL;
     gchar *name;
@@ -3285,7 +3309,7 @@ static void tcp_chr_tls_init(Chardev *chr)
 static void tcp_chr_set_client_ioc_name(Chardev *chr,
                                         QIOChannelSocket *sioc)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     char *name;
     name = g_strdup_printf("chardev-tcp-%s-%s",
                            s->is_listen ? "server" : "client",
@@ -3297,7 +3321,7 @@ static void tcp_chr_set_client_ioc_name(Chardev *chr,
 
 static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (s->ioc != NULL) {
        return -1;
@@ -3351,7 +3375,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
                                GIOCondition cond,
                                void *opaque)
 {
-    Chardev *chr = opaque;
+    Chardev *chr = CHARDEV(opaque);
     QIOChannelSocket *sioc;
 
     sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
@@ -3369,7 +3393,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
 
 static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     QIOChannelSocket *sioc;
 
     /* It can't wait on s->connected, since it is set asynchronously
@@ -3398,8 +3422,10 @@ static int tcp_chr_wait_connected(Chardev *chr, Error 
**errp)
 
 static int qemu_chr_wait_connected(Chardev *chr, Error **errp)
 {
-    if (chr->driver->chr_wait_connected) {
-        return chr->driver->chr_wait_connected(chr, errp);
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+
+    if (cc->chr_wait_connected) {
+        return cc->chr_wait_connected(chr, errp);
     }
 
     return 0;
@@ -3417,7 +3443,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error 
**errp)
 
 static void tcp_chr_free(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     tcp_chr_free_connection(chr);
 
@@ -3444,8 +3470,8 @@ static void tcp_chr_free(Chardev *chr)
 static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque)
 {
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(src);
-    Chardev *chr = opaque;
-    SocketChardev *s = (SocketChardev *)chr;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (err) {
         check_report_connect_error(chr, err);
@@ -3470,9 +3496,12 @@ typedef struct {
     uint8_t *cbuf;
 } RingBufChardev;
 
+#define RINGBUF_CHARDEV(obj)                                    \
+    OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF)
+
 static size_t ringbuf_count(const Chardev *chr)
 {
-    const RingBufChardev *d = (RingBufChardev *)chr;
+    const RingBufChardev *d = RINGBUF_CHARDEV(chr);
 
     return d->prod - d->cons;
 }
@@ -3480,7 +3509,7 @@ static size_t ringbuf_count(const Chardev *chr)
 /* Called with chr_write_lock held.  */
 static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    RingBufChardev *d = (RingBufChardev *)chr;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
     int i;
 
     if (!buf || (len < 0)) {
@@ -3499,7 +3528,7 @@ static int ringbuf_chr_write(Chardev *chr, const uint8_t 
*buf, int len)
 
 static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
 {
-    RingBufChardev *d = (RingBufChardev *)chr;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
     int i;
 
     qemu_mutex_lock(&chr->chr_write_lock);
@@ -3513,51 +3542,30 @@ static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, 
int len)
 
 static void ringbuf_chr_free(struct Chardev *chr)
 {
-    RingBufChardev *d = (RingBufChardev *)chr;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
 
     g_free(d->cbuf);
 }
 
-static Chardev *qemu_chr_open_ringbuf(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void qemu_chr_open_ringbuf(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
 {
     ChardevRingbuf *opts = backend->u.ringbuf.data;
-    ChardevCommon *common = qapi_ChardevRingbuf_base(opts);
-    Chardev *chr;
-    RingBufChardev *d;
-
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    d = (RingBufChardev *)chr;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
 
     d->size = opts->has_size ? opts->size : 65536;
 
     /* The size must be power of 2 */
     if (d->size & (d->size - 1)) {
         error_setg(errp, "size of ringbuf chardev must be power of two");
-        goto fail;
+        return;
     }
 
     d->prod = 0;
     d->cons = 0;
     d->cbuf = g_malloc0(d->size);
-
-    return chr;
-
-fail:
-    qemu_chr_free_common(chr);
-    return NULL;
-}
-
-ChardevBackendKind qemu_chr_get_kind(const Chardev *chr)
-{
-    return chr->driver->kind;
 }
 
 void qmp_ringbuf_write(const char *device, const char *data,
@@ -3575,7 +3583,7 @@ void qmp_ringbuf_write(const char *device, const char 
*data,
         return;
     }
 
-    if (!qemu_chr_is_ringbuf(chr)) {
+    if (!CHARDEV_IS_RINGBUF(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return;
     }
@@ -3619,7 +3627,7 @@ char *qmp_ringbuf_read(const char *device, int64_t size,
         return NULL;
     }
 
-    if (!qemu_chr_is_ringbuf(chr)) {
+    if (!CHARDEV_IS_RINGBUF(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return NULL;
     }
@@ -3838,20 +3846,32 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, 
ChardevBackend *backend,
 
 static const CharDriver stdio_driver = {
     .kind = CHARDEV_BACKEND_KIND_STDIO,
-    .parse = qemu_chr_parse_stdio, .create = qemu_chr_open_stdio,
+    .parse = qemu_chr_parse_stdio
+};
+
+static void char_stdio_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_stdio;
+#ifdef _WIN32
+    cc->chr_write = win_stdio_write;
+    cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
+    cc->chr_free = win_stdio_free;
+#else
+    cc->chr_set_echo = qemu_chr_set_echo_stdio;
+    cc->chr_free = qemu_chr_free_stdio;
+#endif
+}
+
+static const TypeInfo char_stdio_type_info = {
+    .name = TYPE_CHARDEV_STDIO,
 #ifdef _WIN32
-    sizeof(WinStdioChardev),
-    .chr_write = win_stdio_write,
-    .chr_set_echo = qemu_chr_set_echo_win_stdio,
-    .chr_free = win_stdio_free,
+    .parent = TYPE_CHARDEV_WIN_STDIO,
 #else
-    sizeof(FDChardev),
-    .chr_add_watch = fd_chr_add_watch,
-    .chr_write = fd_chr_write,
-    .chr_update_read_handler = fd_chr_update_read_handler,
-    .chr_set_echo = qemu_chr_set_echo_stdio,
-    .chr_free = qemu_chr_free_stdio,
+    .parent = TYPE_CHARDEV_FD,
 #endif
+    .class_init = char_stdio_class_init,
 };
 
 #ifdef HAVE_CHARDEV_SERIAL
@@ -3905,18 +3925,24 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, 
ChardevBackend *backend,
 
 static const CharDriver pipe_driver = {
     .kind = CHARDEV_BACKEND_KIND_PIPE,
-    .parse = qemu_chr_parse_pipe, .create = qemu_chr_open_pipe,
+    .parse = qemu_chr_parse_pipe
+};
+
+static void char_pipe_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_pipe;
+}
+
+static const TypeInfo char_pipe_type_info = {
+    .name = TYPE_CHARDEV_PIPE,
 #ifdef _WIN32
-    sizeof(WinChardev),
-    .chr_write = win_chr_write,
-    .chr_free = win_chr_free,
+    .parent = TYPE_CHARDEV_WIN,
 #else
-    sizeof(FDChardev),
-    .chr_add_watch = fd_chr_add_watch,
-    .chr_write = fd_chr_write,
-    .chr_update_read_handler = fd_chr_update_read_handler,
-    .chr_free = fd_chr_free,
+    .parent = TYPE_CHARDEV_FD,
 #endif
+    .class_init = char_pipe_class_init,
 };
 
 static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
@@ -3936,20 +3962,35 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, 
ChardevBackend *backend,
 }
 
 static const CharDriver ringbuf_driver = {
-    .instance_size = sizeof(RingBufChardev),
     .kind = CHARDEV_BACKEND_KIND_RINGBUF,
-    .parse = qemu_chr_parse_ringbuf, .create = qemu_chr_open_ringbuf,
-    .chr_write = ringbuf_chr_write,
-    .chr_free = ringbuf_chr_free,
+    .parse = qemu_chr_parse_ringbuf
+};
+
+static void char_ringbuf_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_ringbuf;
+    cc->chr_write = ringbuf_chr_write;
+    cc->chr_free = ringbuf_chr_free;
+}
+
+static const TypeInfo char_ringbuf_type_info = {
+    .name = TYPE_CHARDEV_RINGBUF,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_ringbuf_class_init,
+    .instance_size = sizeof(RingBufChardev),
 };
 
 /* Bug-compatibility: */
 static const CharDriver memory_driver = {
-    .instance_size = sizeof(RingBufChardev),
     .kind = CHARDEV_BACKEND_KIND_MEMORY,
-    .parse = qemu_chr_parse_ringbuf, .create = qemu_chr_open_ringbuf,
-    .chr_write = ringbuf_chr_write,
-    .chr_free = ringbuf_chr_free,
+    .parse = qemu_chr_parse_ringbuf
+};
+
+static const TypeInfo char_memory_type_info = {
+    .name = TYPE_CHARDEV_MEMORY,
+    .parent = TYPE_CHARDEV_RINGBUF,
 };
 
 static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
@@ -3968,13 +4009,26 @@ static void qemu_chr_parse_mux(QemuOpts *opts, 
ChardevBackend *backend,
 }
 
 static const CharDriver mux_driver = {
-    .instance_size = sizeof(MuxChardev),
     .kind = CHARDEV_BACKEND_KIND_MUX,
-    .parse = qemu_chr_parse_mux, .create = qemu_chr_open_mux,
-    .chr_free = mux_chr_free,
-    .chr_write = mux_chr_write,
-    .chr_accept_input = mux_chr_accept_input,
-    .chr_add_watch = mux_chr_add_watch,
+    .parse = qemu_chr_parse_mux
+};
+
+static void char_mux_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_mux;
+    cc->chr_free = mux_chr_free;
+    cc->chr_write = mux_chr_write;
+    cc->chr_accept_input = mux_chr_accept_input;
+    cc->chr_add_watch = mux_chr_add_watch;
+}
+
+static const TypeInfo char_mux_type_info = {
+    .name = TYPE_CHARDEV_MUX,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_mux_class_init,
+    .instance_size = sizeof(MuxChardev),
 };
 
 static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
@@ -4257,7 +4311,7 @@ Chardev *qemu_chr_new(const char *label, const char 
*filename)
         if (replay_mode != REPLAY_MODE_NONE) {
             qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
         }
-        if (qemu_chr_replay(chr) && chr->driver->chr_ioctl) {
+        if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) {
             error_report("Replay: ioctl is not supported "
                          "for serial devices yet");
         }
@@ -4270,8 +4324,8 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 {
     Chardev *chr = be->chr;
 
-    if (chr && chr->driver->chr_set_echo) {
-        chr->driver->chr_set_echo(chr, echo);
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
+        CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
     }
 }
 
@@ -4287,8 +4341,8 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
         return;
     }
     be->fe_open = fe_open;
-    if (chr->driver->chr_set_fe_open) {
-        chr->driver->chr_set_fe_open(chr, fe_open);
+    if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
+        CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
     }
 }
 
@@ -4299,11 +4353,11 @@ guint qemu_chr_fe_add_watch(CharBackend *be, 
GIOCondition cond,
     GSource *src;
     guint tag;
 
-    if (!s || s->driver->chr_add_watch == NULL) {
+    if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
         return 0;
     }
 
-    src = s->driver->chr_add_watch(s, cond);
+    src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
     if (!src) {
         return 0;
     }
@@ -4319,31 +4373,17 @@ void qemu_chr_fe_disconnect(CharBackend *be)
 {
     Chardev *chr = be->chr;
 
-    if (chr && chr->driver->chr_disconnect) {
-        chr->driver->chr_disconnect(chr);
-    }
-}
-
-static void qemu_chr_free_common(Chardev *chr)
-{
-    if (chr->be) {
-        chr->be->chr = NULL;
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
+        CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
     }
-    g_free(chr->filename);
-    g_free(chr->label);
-    if (chr->logfd != -1) {
-        close(chr->logfd);
-    }
-    qemu_mutex_destroy(&chr->chr_write_lock);
-    g_free(chr);
 }
 
 void qemu_chr_free(Chardev *chr)
 {
-    if (chr->driver->chr_free) {
-        chr->driver->chr_free(chr);
+    if (CHARDEV_GET_CLASS(chr)->chr_free) {
+        CHARDEV_GET_CLASS(chr)->chr_free(chr);
     }
-    qemu_chr_free_common(chr);
+    object_unref(OBJECT(chr));
 }
 
 void qemu_chr_delete(Chardev *chr)
@@ -4513,22 +4553,19 @@ QemuOptsList qemu_chardev_opts = {
 
 #ifdef _WIN32
 
-static Chardev *qmp_chardev_open_file(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void qmp_chardev_open_file(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
 {
     ChardevFile *file = backend->u.file.data;
-    ChardevCommon *common = qapi_ChardevFile_base(file);
     HANDLE out;
     DWORD accessmode;
     DWORD flags;
 
     if (file->has_in) {
         error_setg(errp, "input file not supported");
-        return NULL;
+        return;
     }
 
     if (file->has_append && file->append) {
@@ -4545,33 +4582,20 @@ static Chardev *qmp_chardev_open_file(const CharDriver 
*driver,
                      FILE_ATTRIBUTE_NORMAL, NULL);
     if (out == INVALID_HANDLE_VALUE) {
         error_setg(errp, "open %s failed", file->out);
-        return NULL;
+        return;
     }
-    return qemu_chr_open_win_file(driver, out, common, errp);
+
+    qemu_chr_open_win_file(chr, out);
 }
 
-static Chardev *qmp_chardev_open_serial(const CharDriver *driver,
-                                        const char *id,
-                                        ChardevBackend *backend,
-                                        ChardevReturn *ret,
-                                        bool *be_opened,
-                                        Error **errp)
+static void qmp_chardev_open_serial(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(serial);
-    Chardev *chr;
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-
-    if (win_chr_init(chr, serial->device, errp) < 0) {
-        qemu_chr_free_common(chr);
-        return NULL;
-    }
-
-    return chr;
+    win_chr_init(chr, serial->device, errp);
 }
 
 #else /* WIN32 */
@@ -4588,15 +4612,12 @@ static int qmp_chardev_open_file_source(char *src, int 
flags,
     return fd;
 }
 
-static Chardev *qmp_chardev_open_file(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void qmp_chardev_open_file(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
 {
     ChardevFile *file = backend->u.file.data;
-    ChardevCommon *common = qapi_ChardevFile_base(file);
     int flags, in = -1, out;
 
     flags = O_WRONLY | O_CREAT | O_BINARY;
@@ -4608,7 +4629,7 @@ static Chardev *qmp_chardev_open_file(const CharDriver 
*driver,
 
     out = qmp_chardev_open_file_source(file->out, flags, errp);
     if (out < 0) {
-        return NULL;
+        return;
     }
 
     if (file->has_in) {
@@ -4616,68 +4637,75 @@ static Chardev *qmp_chardev_open_file(const CharDriver 
*driver,
         in = qmp_chardev_open_file_source(file->in, flags, errp);
         if (in < 0) {
             qemu_close(out);
-            return NULL;
+            return;
         }
     }
 
-    return qemu_chr_open_fd(driver, in, out, common, errp);
+    qemu_chr_open_fd(chr, in, out);
 }
 
 #ifdef HAVE_CHARDEV_SERIAL
-static Chardev *qmp_chardev_open_serial(const CharDriver *driver,
-                                        const char *id,
-                                        ChardevBackend *backend,
-                                        ChardevReturn *ret,
-                                        bool *be_opened,
-                                        Error **errp)
+static void qmp_chardev_open_serial(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(serial);
     int fd;
 
     fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
     if (fd < 0) {
-        return NULL;
+        return;
     }
     qemu_set_nonblock(fd);
     tty_serial_init(fd, 115200, 'N', 8, 1);
 
-    return qemu_chr_open_fd(driver, fd, fd, common, errp);
+    qemu_chr_open_fd(chr, fd, fd);
 }
 #endif
 
 #ifdef HAVE_CHARDEV_PARPORT
-static Chardev *qmp_chardev_open_parallel(const CharDriver *driver,
-                                          const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret,
-                                          bool *be_opened,
-                                          Error **errp)
+static void qmp_chardev_open_parallel(Chardev *chr,
+                                      ChardevBackend *backend,
+                                      bool *be_opened,
+                                      Error **errp)
 {
     ChardevHostdev *parallel = backend->u.parallel.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(parallel);
     int fd;
 
     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
     if (fd < 0) {
-        return NULL;
+        return;
     }
-    return qemu_chr_open_pp_fd(driver, fd, common, be_opened, errp);
+    qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
 }
 
 static const CharDriver parallel_driver = {
-    .instance_size = sizeof(ParallelChardev),
     .alias = "parport", .kind = CHARDEV_BACKEND_KIND_PARALLEL,
-    .parse = qemu_chr_parse_parallel, .create = qmp_chardev_open_parallel,
+    .parse = qemu_chr_parse_parallel
+};
+
+static void char_parallel_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_parallel;
 #if defined(__linux__)
-    .chr_write = null_chr_write,
-    .chr_ioctl = pp_ioctl,
-    .chr_free = pp_free,
+    cc->chr_write = null_chr_write;
+    cc->chr_ioctl = pp_ioctl;
+    cc->chr_free = pp_free;
 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || 
defined(__DragonFly__)
-    .chr_write = null_chr_write;
-    .chr_ioctl = pp_ioctl;
     /* FIXME: no chr_free */
+    cc->chr_write = null_chr_write;
+    cc->chr_ioctl = pp_ioctl;
 #endif
+}
+
+static const TypeInfo char_parallel_type_info = {
+    .name = TYPE_CHARDEV_PARALLEL,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(ParallelChardev),
+    .class_init = char_parallel_class_init,
 };
 #endif
 
@@ -4685,43 +4713,63 @@ static const CharDriver parallel_driver = {
 
 static const CharDriver file_driver = {
     .kind = CHARDEV_BACKEND_KIND_FILE,
-    .parse = qemu_chr_parse_file_out, .create = qmp_chardev_open_file,
+    .parse = qemu_chr_parse_file_out
+};
+
+static void char_file_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_file;
 #ifdef _WIN32
-    sizeof(WinChardev),
-    .chr_write = win_chr_write,
     /* FIXME: no chr_free */
+    cc->chr_free = NULL;
+#endif
+}
+
+static const TypeInfo char_file_type_info = {
+    .name = TYPE_CHARDEV_FILE,
+#ifdef _WIN32
+    .parent = TYPE_CHARDEV_WIN,
 #else
-    sizeof(FDChardev),
-    .chr_add_watch = fd_chr_add_watch,
-    .chr_write = fd_chr_write,
-    .chr_update_read_handler = fd_chr_update_read_handler,
-    .chr_free = fd_chr_free,
+    .parent = TYPE_CHARDEV_FD,
 #endif
+    .class_init = char_file_class_init,
 };
 
 #ifdef HAVE_CHARDEV_SERIAL
+
 static const CharDriver serial_driver = {
     .alias = "tty", .kind = CHARDEV_BACKEND_KIND_SERIAL,
-    .parse = qemu_chr_parse_serial, .create = qmp_chardev_open_serial,
+    .parse = qemu_chr_parse_serial
+};
+
+static void char_serial_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_serial;
+#ifndef _WIN32
+    cc->chr_ioctl = tty_serial_ioctl;
+    cc->chr_free = qemu_chr_free_tty;
+#endif
+}
+
+static const TypeInfo char_serial_type_info = {
+    .name = TYPE_CHARDEV_SERIAL,
 #ifdef _WIN32
-    sizeof(WinChardev),
-    .chr_write = win_chr_write,
-    .chr_free = win_chr_free,
+    .parent = TYPE_CHARDEV_WIN,
 #else
-    sizeof(FDChardev),
-    .chr_add_watch = fd_chr_add_watch,
-    .chr_write = fd_chr_write,
-    .chr_update_read_handler = fd_chr_update_read_handler,
-    .chr_ioctl = tty_serial_ioctl,
-    .chr_free = qemu_chr_free_tty,
+    .parent = TYPE_CHARDEV_FD,
 #endif
+    .class_init = char_serial_class_init,
 };
 #endif
 
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
-    Chardev *chr = opaque;
-    SocketChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     QIOChannelSocket *sioc;
 
     s->reconnect_timer = 0;
@@ -4739,15 +4787,12 @@ static gboolean socket_reconnect_timeout(gpointer 
opaque)
     return false;
 }
 
-static Chardev *qmp_chardev_open_socket(const CharDriver *driver,
-                                        const char *id,
-                                        ChardevBackend *backend,
-                                        ChardevReturn *ret,
-                                        bool *be_opened,
-                                        Error **errp)
+static void qmp_chardev_open_socket(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
-    Chardev *chr;
-    SocketChardev *s;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     ChardevSocket *sock = backend->u.socket.data;
     SocketAddress *addr = sock->addr;
     bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
@@ -4755,15 +4800,8 @@ static Chardev *qmp_chardev_open_socket(const CharDriver 
*driver,
     bool is_telnet      = sock->has_telnet  ? sock->telnet  : false;
     bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
     int64_t reconnect   = sock->has_reconnect ? sock->reconnect : 0;
-    ChardevCommon *common = qapi_ChardevSocket_base(sock);
     QIOChannelSocket *sioc = NULL;
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = (SocketChardev *)chr;
-
     s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
@@ -4855,82 +4893,94 @@ static Chardev *qmp_chardev_open_socket(const 
CharDriver *driver,
         }
     }
 
-    return chr;
+    return;
 
- error:
+error:
     if (sioc) {
         object_unref(OBJECT(sioc));
     }
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
-    qemu_chr_free_common(chr);
-    return NULL;
 }
 
 static const CharDriver socket_driver = {
-    .instance_size = sizeof(SocketChardev),
     .kind = CHARDEV_BACKEND_KIND_SOCKET,
-    .parse = qemu_chr_parse_socket, .create = qmp_chardev_open_socket,
-    .chr_wait_connected = tcp_chr_wait_connected,
-    .chr_write = tcp_chr_write,
-    .chr_sync_read = tcp_chr_sync_read,
-    .chr_disconnect = tcp_chr_disconnect,
-    .get_msgfds = tcp_get_msgfds,
-    .set_msgfds = tcp_set_msgfds,
-    .chr_add_client = tcp_chr_add_client,
-    .chr_add_watch = tcp_chr_add_watch,
-    .chr_update_read_handler = tcp_chr_update_read_handler,
-    .chr_free = tcp_chr_free,
+    .parse = qemu_chr_parse_socket
+};
+
+static void char_socket_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_socket;
+    cc->chr_wait_connected = tcp_chr_wait_connected;
+    cc->chr_write = tcp_chr_write;
+    cc->chr_sync_read = tcp_chr_sync_read;
+    cc->chr_disconnect = tcp_chr_disconnect;
+    cc->get_msgfds = tcp_get_msgfds;
+    cc->set_msgfds = tcp_set_msgfds;
+    cc->chr_add_client = tcp_chr_add_client;
+    cc->chr_add_watch = tcp_chr_add_watch;
+    cc->chr_update_read_handler = tcp_chr_update_read_handler;
+    cc->chr_free = tcp_chr_free;
+}
+
+static const TypeInfo char_socket_type_info = {
+    .name = TYPE_CHARDEV_SOCKET,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(SocketChardev),
+    .class_init = char_socket_class_init,
 };
 
-static Chardev *qmp_chardev_open_udp(const CharDriver *driver,
-                                     const char *id,
-                                     ChardevBackend *backend,
-                                     ChardevReturn *ret,
-                                     bool *be_opened,
-                                     Error **errp)
+static void qmp_chardev_open_udp(Chardev *chr,
+                                 ChardevBackend *backend,
+                                 bool *be_opened,
+                                 Error **errp)
 {
     ChardevUdp *udp = backend->u.udp.data;
-    ChardevCommon *common = qapi_ChardevUdp_base(udp);
     QIOChannelSocket *sioc = qio_channel_socket_new();
     char *name;
-    Chardev *chr;
-    UdpChardev *s;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     if (qio_channel_socket_dgram_sync(sioc,
                                       udp->local, udp->remote,
                                       errp) < 0) {
         object_unref(OBJECT(sioc));
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
     name = g_strdup_printf("chardev-udp-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(sioc), name);
     g_free(name);
 
-    s = (UdpChardev *)chr;
     s->ioc = QIO_CHANNEL(sioc);
     s->bufcnt = 0;
     s->bufptr = 0;
     /* be isn't opened until we get a connection */
     *be_opened = false;
-
-    return chr;
 }
 
 static const CharDriver udp_driver = {
-    .instance_size = sizeof(UdpChardev),
     .kind = CHARDEV_BACKEND_KIND_UDP,
-    .parse = qemu_chr_parse_udp, .create = qmp_chardev_open_udp,
-    .chr_write = udp_chr_write,
-    .chr_update_read_handler = udp_chr_update_read_handler,
-    .chr_free = udp_chr_free,
+    .parse = qemu_chr_parse_udp
+};
+
+static void char_udp_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_udp;
+    cc->chr_write = udp_chr_write;
+    cc->chr_update_read_handler = udp_chr_update_read_handler;
+    cc->chr_free = udp_chr_free;
+}
+
+static const TypeInfo char_udp_type_info = {
+    .name = TYPE_CHARDEV_UDP,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(UdpChardev),
+    .class_init = char_udp_class_init,
 };
 
 bool qemu_chr_has_feature(Chardev *chr,
@@ -4945,47 +4995,92 @@ void qemu_chr_set_feature(Chardev *chr,
     return set_bit(feature, chr->features);
 }
 
-ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
-                               Error **errp)
+static const ChardevClass *char_get_class(const char *driver, Error **errp)
+{
+    ObjectClass *oc;
+    const ChardevClass *cc;
+    char *typename = g_strdup_printf("chardev-%s", driver);
+
+    oc = object_class_by_name(typename);
+    g_free(typename);
+
+    if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) {
+        error_setg(errp, "'%s' is not a valid char driver name", driver);
+        return NULL;
+    }
+
+    if (object_class_is_abstract(oc)) {
+        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+                   "abstract device type");
+        return NULL;
+    }
+
+    cc = CHARDEV_CLASS(oc);
+
+    return cc;
+}
+
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+                          ChardevBackend *backend, Error **errp)
 {
-    ChardevReturn *ret = g_new0(ChardevReturn, 1);
     Chardev *chr = NULL;
-    const CharDriver *cd;
     Error *local_err = NULL;
     bool be_opened = true;
 
-    chr = qemu_chr_find(id);
-    if (chr) {
-        error_setg(errp, "Chardev '%s' already exists", id);
-        goto out_error;
-    }
+    assert(g_str_has_prefix(typename, "chardev-"));
 
-    cd = (int)backend->type >= 0 && backend->type < ARRAY_SIZE(backends) ?
-        backends[backend->type] : NULL;
-    if (cd == NULL) {
-        error_setg(errp, "chardev backend not available");
-        goto out_error;
-    }
+    chr = CHARDEV(object_new(typename));
+    chr->label = g_strdup(id);
 
-    chr = cd->create(cd, id, backend, ret, &be_opened, &local_err);
+    qemu_char_open(chr, backend, &be_opened, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
-        goto out_error;
+        object_unref(OBJECT(chr));
+        return NULL;
     }
 
-    chr->label = g_strdup(id);
     if (!chr->filename) {
-        chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
+        chr->filename = g_strdup(typename + 8);
     }
     if (be_opened) {
         qemu_chr_be_event(chr, CHR_EVENT_OPENED);
     }
+
+    return chr;
+}
+
+ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
+                               Error **errp)
+{
+    const ChardevClass *cc;
+    ChardevReturn *ret;
+    Chardev *chr;
+
+    chr = qemu_chr_find(id);
+    if (chr) {
+        error_setg(errp, "Chardev '%s' already exists", id);
+        return NULL;
+    }
+
+    cc = char_get_class(ChardevBackendKind_lookup[backend->type], errp);
+    if (!cc) {
+        return NULL;
+    }
+
+    chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
+                           backend, errp);
+    if (!chr) {
+        return NULL;
+    }
+
+    ret = g_new0(ChardevReturn, 1);
+    if (CHARDEV_IS_PTY(chr)) {
+        ret->pty = g_strdup(chr->filename + 4);
+        ret->has_pty = true;
+    }
+
     QTAILQ_INSERT_TAIL(&chardevs, chr, next);
     return ret;
-
-out_error:
-    g_free(ret);
-    return NULL;
 }
 
 void qmp_chardev_remove(const char *id, Error **errp)
@@ -5020,33 +5115,44 @@ void qemu_chr_cleanup(void)
 
 static void register_types(void)
 {
-    static const CharDriver *drivers[] = {
-        &null_driver,
-        &socket_driver,
-        &udp_driver,
-        &ringbuf_driver,
-        &file_driver,
-        &stdio_driver,
+    static const struct {
+        const CharDriver *driver;
+        const TypeInfo *type;
+    } chardevs[] = {
+        { &null_driver, &char_null_type_info },
+        { &socket_driver, &char_socket_type_info },
+        { &udp_driver, &char_udp_type_info },
+        { &ringbuf_driver, &char_ringbuf_type_info },
+        { &file_driver, &char_file_type_info },
+        { &stdio_driver, &char_stdio_type_info },
 #ifdef HAVE_CHARDEV_SERIAL
-        &serial_driver,
+        { &serial_driver, &char_serial_type_info },
 #endif
 #ifdef HAVE_CHARDEV_PARPORT
-        &parallel_driver,
+        { &parallel_driver, &char_parallel_type_info },
 #endif
 #ifdef HAVE_CHARDEV_PTY
-        &pty_driver,
+        { &pty_driver, &char_pty_type_info },
 #endif
 #ifdef _WIN32
-        &console_driver,
+        { &console_driver, &char_console_type_info },
 #endif
-        &pipe_driver,
-        &mux_driver,
-        &memory_driver
+        { &pipe_driver, &char_pipe_type_info },
+        { &mux_driver, &char_mux_type_info },
+        { &memory_driver, &char_memory_type_info }
     };
     int i;
 
-    for (i = 0; i < ARRAY_SIZE(drivers); i++) {
-        register_char_driver(drivers[i]);
+    type_register_static(&char_type_info);
+#ifndef _WIN32
+    type_register_static(&char_fd_type_info);
+#else
+    type_register_static(&char_win_type_info);
+    type_register_static(&char_win_stdio_type_info);
+#endif
+    for (i = 0; i < ARRAY_SIZE(chardevs); i++) {
+        type_register_static(chardevs[i].type);
+        register_char_driver(chardevs[i].driver);
     }
 
     /* this must be done after machine init, since we register FEs with muxes
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 4e934cf224..e27b86622a 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -18,6 +18,12 @@ typedef struct SpiceChardev {
     QLIST_ENTRY(SpiceChardev) next;
 } SpiceChardev;
 
+#define TYPE_CHARDEV_SPICE "chardev-spice"
+#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc"
+#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport"
+
+#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), 
TYPE_CHARDEV_SPICE)
+
 typedef struct SpiceCharSource {
     GSource               source;
     SpiceChardev       *scd;
@@ -29,7 +35,7 @@ static QLIST_HEAD(, SpiceChardev) spice_chars =
 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 {
     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
-    Chardev *chr = (Chardev *)scd;
+    Chardev *chr = CHARDEV(scd);
     ssize_t out = 0;
     ssize_t last_out;
     uint8_t* p = (uint8_t*)buf;
@@ -73,7 +79,7 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t 
*buf, int len)
 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
 {
     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
-    Chardev *chr = (Chardev *)scd;
+    Chardev *chr = CHARDEV(scd);
     int chr_event;
 
     switch (event) {
@@ -92,7 +98,7 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t 
event)
 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
 {
     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
-    Chardev *chr = (Chardev *)scd;
+    Chardev *chr = CHARDEV(scd);
 
     if ((chr->be_open && connected) ||
         (!chr->be_open && !connected)) {
@@ -173,7 +179,7 @@ static GSourceFuncs SpiceCharSourceFuncs = {
 
 static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    SpiceChardev *scd = (SpiceChardev *)chr;
+    SpiceChardev *scd = SPICE_CHARDEV(chr);
     SpiceCharSource *src;
 
     assert(cond & G_IO_OUT);
@@ -187,7 +193,7 @@ static GSource *spice_chr_add_watch(Chardev *chr, 
GIOCondition cond)
 
 static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
     int read_bytes;
 
     assert(s->datalen == 0);
@@ -206,7 +212,7 @@ static int spice_chr_write(Chardev *chr, const uint8_t 
*buf, int len)
 
 static void spice_chr_free(struct Chardev *chr)
 {
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     vmc_unregister_interface(s);
     QLIST_REMOVE(s, next);
@@ -219,7 +225,7 @@ static void spice_chr_free(struct Chardev *chr)
 
 static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
 {
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
     if (fe_open) {
         vmc_register_interface(s);
     } else {
@@ -230,7 +236,7 @@ static void spice_vmc_set_fe_open(struct Chardev *chr, int 
fe_open)
 static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
 {
 #if SPICE_SERVER_VERSION >= 0x000c02
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     if (fe_open) {
         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
@@ -242,43 +248,29 @@ static void spice_port_set_fe_open(struct Chardev *chr, 
int fe_open)
 
 static void spice_chr_accept_input(struct Chardev *chr)
 {
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     spice_server_char_device_wakeup(&s->sin);
 }
 
-static Chardev *chr_open(const CharDriver *driver,
-                                 const char *subtype,
-                                 ChardevCommon *backend,
-                                 Error **errp)
+static void chr_open(Chardev *chr, const char *subtype)
 {
-    Chardev *chr;
-    SpiceChardev *s;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = (SpiceChardev *)chr;
     s->active = false;
     s->sin.subtype = g_strdup(subtype);
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
-
-    return chr;
 }
 
-static Chardev *qemu_chr_open_spice_vmc(const CharDriver *driver,
-                                                const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
+static void qemu_chr_open_spice_vmc(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
     ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
     const char *type = spicevmc->type;
     const char **psubtype = spice_server_char_device_recognized_subtypes();
-    ChardevCommon *common = qapi_ChardevSpiceChannel_base(spicevmc);
 
     for (; *psubtype != NULL; ++psubtype) {
         if (strcmp(type, *psubtype) == 0) {
@@ -293,41 +285,33 @@ static Chardev *qemu_chr_open_spice_vmc(const CharDriver 
*driver,
         error_report("allowed spice char type names: %s", subtypes);
 
         g_free(subtypes);
-        return NULL;
+        return;
     }
 
     *be_opened = false;
-    return chr_open(driver, type, common, errp);
+    chr_open(chr, type);
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
-static Chardev *qemu_chr_open_spice_port(const CharDriver *driver,
-                                                 const char *id,
-                                                 ChardevBackend *backend,
-                                                 ChardevReturn *ret,
-                                                 bool *be_opened,
-                                                 Error **errp)
+static void qemu_chr_open_spice_port(Chardev *chr,
+                                     ChardevBackend *backend,
+                                     bool *be_opened,
+                                     Error **errp)
 {
     ChardevSpicePort *spiceport = backend->u.spiceport.data;
     const char *name = spiceport->fqdn;
-    ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport);
-    Chardev *chr;
     SpiceChardev *s;
 
     if (name == NULL) {
         error_setg(errp, "missing name parameter");
-        return NULL;
+        return;
     }
 
-    chr = chr_open(driver, "port", common, errp);
-    if (!chr) {
-        return NULL;
-    }
+    chr_open(chr, "port");
+
     *be_opened = false;
-    s = (SpiceChardev *)chr;
+    s = SPICE_CHARDEV(chr);
     s->sin.portname = g_strdup(name);
-
-    return chr;
 }
 
 void qemu_spice_register_ports(void)
@@ -373,30 +357,68 @@ 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)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_write = spice_chr_write;
+    cc->chr_add_watch = spice_chr_add_watch;
+    cc->chr_accept_input = spice_chr_accept_input;
+    cc->chr_free = spice_chr_free;
+}
+
+static const TypeInfo char_spice_type_info = {
+    .name = TYPE_CHARDEV_SPICE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(SpiceChardev),
+    .class_init = char_spice_class_init,
+    .abstract = true,
+};
+
+static void char_spicevmc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_spice_vmc;
+    cc->chr_set_fe_open = spice_vmc_set_fe_open;
+}
+
+static const TypeInfo char_spicevmc_type_info = {
+    .name = TYPE_CHARDEV_SPICEVMC,
+    .parent = TYPE_CHARDEV_SPICE,
+    .class_init = char_spicevmc_class_init,
+};
+
+static void char_spiceport_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_spice_port;
+    cc->chr_set_fe_open = spice_port_set_fe_open;
+}
+
+static const TypeInfo char_spiceport_type_info = {
+    .name = TYPE_CHARDEV_SPICEPORT,
+    .parent = TYPE_CHARDEV_SPICE,
+    .class_init = char_spiceport_class_init,
+};
+
 static void register_types(void)
 {
     static const CharDriver vmc_driver = {
-        .instance_size = sizeof(SpiceChardev),
         .kind = CHARDEV_BACKEND_KIND_SPICEVMC,
-        .parse = qemu_chr_parse_spice_vmc, .create = qemu_chr_open_spice_vmc,
-        .chr_write = spice_chr_write,
-        .chr_add_watch = spice_chr_add_watch,
-        .chr_set_fe_open = spice_vmc_set_fe_open,
-        .chr_accept_input = spice_chr_accept_input,
-        .chr_free = spice_chr_free,
+        .parse = qemu_chr_parse_spice_vmc
     };
     static const CharDriver port_driver = {
-        .instance_size = sizeof(SpiceChardev),
         .kind = CHARDEV_BACKEND_KIND_SPICEPORT,
-        .parse = qemu_chr_parse_spice_port, .create = qemu_chr_open_spice_port,
-        .chr_write = spice_chr_write,
-        .chr_add_watch = spice_chr_add_watch,
-        .chr_set_fe_open = spice_port_set_fe_open,
-        .chr_accept_input = spice_chr_accept_input,
-        .chr_free = spice_chr_free,
+        .parse = qemu_chr_parse_spice_port
     };
     register_char_driver(&vmc_driver);
     register_char_driver(&port_driver);
+
+    type_register_static(&char_spice_type_info);
+    type_register_static(&char_spicevmc_type_info);
+    type_register_static(&char_spiceport_type_info);
 }
 
 type_init(register_types);
diff --git a/ui/console.c b/ui/console.c
index 9a16e1b743..5fc29e36f6 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1040,9 +1040,12 @@ typedef struct VCChardev {
     QemuConsole *console;
 } VCChardev;
 
-static int console_puts(Chardev *chr, const uint8_t *buf, int len)
+#define TYPE_CHARDEV_VC "chardev-vc"
+#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+
+static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    VCChardev *drv = (VCChardev *)chr;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s = drv->console;
     int i;
 
@@ -1128,13 +1131,13 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym)
             *q++ = '[';
             *q++ = keysym & 0xff;
         } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
-            console_puts(s->chr, (const uint8_t *) "\r", 1);
+            vc_chr_write(s->chr, (const uint8_t *) "\r", 1);
             *q++ = '\n';
         } else {
             *q++ = keysym;
         }
         if (s->echo) {
-            console_puts(s->chr, buf, q - buf);
+            vc_chr_write(s->chr, buf, q - buf);
         }
         be = s->chr->be;
         if (be && be->chr_read) {
@@ -1951,9 +1954,9 @@ int qemu_console_get_height(QemuConsole *con, int 
fallback)
     return con ? surface_height(con->surface) : fallback;
 }
 
-static void text_console_set_echo(Chardev *chr, bool echo)
+static void vc_chr_set_echo(Chardev *chr, bool echo)
 {
-    VCChardev *drv = (VCChardev *)chr;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s = drv->console;
 
     s->echo = echo;
@@ -1994,7 +1997,7 @@ static const GraphicHwOps text_console_ops = {
 
 static void text_console_do_init(Chardev *chr, DisplayState *ds)
 {
-    VCChardev *drv = (VCChardev *)chr;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s = drv->console;
     int g_width = 80 * FONT_WIDTH;
     int g_height = 24 * FONT_HEIGHT;
@@ -2038,7 +2041,7 @@ static void text_console_do_init(Chardev *chr, 
DisplayState *ds)
 
         s->t_attrib.bgcol = QEMU_COLOR_BLUE;
         len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
-        console_puts(chr, (uint8_t*)msg, len);
+        vc_chr_write(chr, (uint8_t *)msg, len);
         s->t_attrib = s->t_attrib_default;
     }
 
@@ -2047,24 +2050,17 @@ static void text_console_do_init(Chardev *chr, 
DisplayState *ds)
 
 static const CharDriver vc_driver;
 
-static Chardev *vc_init(const CharDriver *driver,
-                        const char *id, ChardevBackend *backend,
-                        ChardevReturn *ret, bool *be_opened,
-                        Error **errp)
+static void vc_open(Chardev *chr,
+                    ChardevBackend *backend,
+                    bool *be_opened,
+                    Error **errp)
 {
     ChardevVC *vc = backend->u.vc.data;
-    ChardevCommon *common = qapi_ChardevVC_base(vc);
-    Chardev *chr;
-    VCChardev *drv;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s;
     unsigned width = 0;
     unsigned height = 0;
 
-    chr = qemu_chr_alloc(&vc_driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-
     if (vc->has_width) {
         width = vc->width;
     } else if (vc->has_cols) {
@@ -2086,13 +2082,11 @@ static Chardev *vc_init(const CharDriver *driver,
     }
 
     if (!s) {
-        g_free(chr);
         error_setg(errp, "cannot create text console");
-        return NULL;
+        return;
     }
 
     s->chr = chr;
-    drv = (VCChardev *)chr;
     drv->console = s;
 
     if (display_state) {
@@ -2103,8 +2097,6 @@ static Chardev *vc_init(const CharDriver *driver,
      * stage, so defer OPENED events until they are fully initialized
      */
     *be_opened = false;
-
-    return chr;
 }
 
 void qemu_console_resize(QemuConsole *s, int width, int height)
@@ -2182,18 +2174,39 @@ static const TypeInfo qemu_console_info = {
     .class_size = sizeof(QemuConsoleClass),
 };
 
-static const CharDriver vc_driver = {
+static void char_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = vc_open;
+    cc->chr_write = vc_chr_write;
+    cc->chr_set_echo = vc_chr_set_echo;
+}
+
+static const TypeInfo char_vc_type_info = {
+    .name = TYPE_CHARDEV_VC,
+    .parent = TYPE_CHARDEV,
     .instance_size = sizeof(VCChardev),
+    .class_init = char_vc_class_init,
+};
+
+void qemu_console_early_init(void)
+{
+    /* set the default vc driver */
+    if (!object_class_by_name(TYPE_CHARDEV_VC)) {
+        type_register(&char_vc_type_info);
+        register_char_driver(&vc_driver);
+    }
+}
+
+static const CharDriver vc_driver = {
     .kind = CHARDEV_BACKEND_KIND_VC,
-    .parse = qemu_chr_parse_vc, .create = vc_init,
-    .chr_write = console_puts,
-    .chr_set_echo = text_console_set_echo,
+    .parse = qemu_chr_parse_vc
 };
 
 static void register_types(void)
 {
     type_register_static(&qemu_console_info);
-    register_char_driver(&vc_driver);
 }
 
 type_init(register_types);
diff --git a/ui/gtk.c b/ui/gtk.c
index efa50524bf..03573a3ff3 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -184,6 +184,9 @@ typedef struct VCChardev {
     bool echo;
 } VCChardev;
 
+#define TYPE_CHARDEV_VC "chardev-vc"
+#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+
 static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
 static void gd_ungrab_pointer(GtkDisplayState *s);
 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
@@ -1681,7 +1684,7 @@ static void gd_vc_adjustment_changed(GtkAdjustment 
*adjustment, void *opaque)
 
 static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    VCChardev *vcd = (VCChardev *)chr;
+    VCChardev *vcd = VC_CHARDEV(chr);
     VirtualConsole *vc = vcd->console;
 
     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
@@ -1690,7 +1693,7 @@ static int gd_vc_chr_write(Chardev *chr, const uint8_t 
*buf, int len)
 
 static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
 {
-    VCChardev *vcd = (VCChardev *)chr;
+    VCChardev *vcd = VC_CHARDEV(chr);
     VirtualConsole *vc = vcd->console;
 
     if (vc) {
@@ -1704,23 +1707,14 @@ static int nb_vcs;
 static Chardev *vcs[MAX_VCS];
 static const CharDriver gd_vc_driver;
 
-static Chardev *vc_init(const CharDriver *driver,
-                        const char *id, ChardevBackend *backend,
-                        ChardevReturn *ret, bool *be_opened,
-                        Error **errp)
+static void gd_vc_open(Chardev *chr,
+                       ChardevBackend *backend,
+                       bool *be_opened,
+                       Error **errp)
 {
-    ChardevVC *vc = backend->u.vc.data;
-    ChardevCommon *common = qapi_ChardevVC_base(vc);
-    Chardev *chr;
-
     if (nb_vcs == MAX_VCS) {
         error_setg(errp, "Maximum number of consoles reached");
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(&gd_vc_driver, common, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
     vcs[nb_vcs++] = chr;
@@ -1729,16 +1723,27 @@ static Chardev *vc_init(const CharDriver *driver,
      * stage, so defer OPENED events until they are fully initialized
      */
     *be_opened = false;
+}
 
-    return chr;
+static void char_gd_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = gd_vc_open;
+    cc->chr_write = gd_vc_chr_write;
+    cc->chr_set_echo = gd_vc_chr_set_echo;
 }
 
-static const CharDriver gd_vc_driver = {
+static const TypeInfo char_gd_vc_type_info = {
+    .name = TYPE_CHARDEV_VC,
+    .parent = TYPE_CHARDEV,
     .instance_size = sizeof(VCChardev),
+    .class_init = char_gd_vc_class_init,
+};
+
+static const CharDriver gd_vc_driver = {
     .kind = CHARDEV_BACKEND_KIND_VC,
-    .parse = qemu_chr_parse_vc, .create = vc_init,
-    .chr_write = gd_vc_chr_write,
-    .chr_set_echo = gd_vc_chr_set_echo,
+    .parse = qemu_chr_parse_vc
 };
 
 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
@@ -1776,7 +1781,7 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, 
VirtualConsole *vc,
     GtkWidget *box;
     GtkWidget *scrollbar;
     GtkAdjustment *vadjustment;
-    VCChardev *vcd = (VCChardev *)chr;
+    VCChardev *vcd = VC_CHARDEV(chr);
 
     vc->s = s;
     vc->vte.echo = vcd->echo;
@@ -2328,7 +2333,7 @@ void early_gtk_display_init(int opengl)
     }
 
 #if defined(CONFIG_VTE)
-    /* overwrite the console.c vc driver */
+    type_register(&char_gd_vc_type_info);
     register_char_driver(&gd_vc_driver);
 #endif
 }
diff --git a/vl.c b/vl.c
index 8296f8da49..144a472990 100644
--- a/vl.c
+++ b/vl.c
@@ -4315,6 +4315,8 @@ int main(int argc, char **argv, char **envp)
         sdl_display_early_init(request_opengl);
     }
 
+    qemu_console_early_init();
+
     if (request_opengl == 1 && display_opengl == 0) {
 #if defined(CONFIG_OPENGL)
         error_report("OpenGL is not supported by the display");
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 384f3ce9b7..1ee8aa4325 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -10,6 +10,7 @@
 #include "qapi/qmp/qstring.h"
 #include "qemu/main-loop.h"
 #include "qemu/bitmap.h"
+#include "qom/object.h"
 
 /* character device */
 
@@ -90,7 +91,8 @@ typedef struct CharBackend {
 typedef struct CharDriver CharDriver;
 
 struct Chardev {
-    const CharDriver *driver;
+    Object parent_obj;
+
     QemuMutex chr_write_lock;
     CharBackend *be;
     char *label;
@@ -102,18 +104,6 @@ struct Chardev {
     QTAILQ_ENTRY(Chardev) next;
 };
 
-/**
- * qemu_chr_alloc:
- * @backend: the common backend config
- * @errp: pointer to a NULL-initialized error object
- *
- * Allocate and initialize a new Chardev.
- *
- * Returns: a newly allocated Chardev, or NULL on error.
- */
-Chardev *qemu_chr_alloc(const CharDriver *driver,
-                        ChardevCommon *backend, Error **errp);
-
 /**
  * @qemu_chr_new_from_opts:
  *
@@ -453,54 +443,73 @@ void qemu_chr_fe_accept_input(CharBackend *be);
 int qemu_chr_add_client(Chardev *s, int fd);
 Chardev *qemu_chr_find(const char *name);
 
-/**
- * @qemu_chr_get_kind:
- *
- * Returns the kind of char backend, or -1 if unspecified.
- */
-ChardevBackendKind qemu_chr_get_kind(const Chardev *chr);
-
-static inline bool qemu_chr_is_ringbuf(const Chardev *chr)
-{
-    return qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_RINGBUF ||
-        qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MEMORY;
-}
-
 bool qemu_chr_has_feature(Chardev *chr,
                           CharDriverFeature feature);
 void qemu_chr_set_feature(Chardev *chr,
                           CharDriverFeature feature);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
+#define TYPE_CHARDEV "chardev"
+#define CHARDEV(obj) OBJECT_CHECK(Chardev, (obj), TYPE_CHARDEV)
+#define CHARDEV_CLASS(klass) \
+    OBJECT_CLASS_CHECK(ChardevClass, (klass), TYPE_CHARDEV)
+#define CHARDEV_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ChardevClass, (obj), TYPE_CHARDEV)
+
+#define TYPE_CHARDEV_NULL "chardev-null"
+#define TYPE_CHARDEV_MUX "chardev-mux"
+#define TYPE_CHARDEV_RINGBUF "chardev-ringbuf"
+#define TYPE_CHARDEV_PTY "chardev-pty"
+#define TYPE_CHARDEV_CONSOLE "chardev-console"
+#define TYPE_CHARDEV_STDIO "chardev-stdio"
+#define TYPE_CHARDEV_PIPE "chardev-pipe"
+#define TYPE_CHARDEV_MEMORY "chardev-memory"
+#define TYPE_CHARDEV_PARALLEL "chardev-parallel"
+#define TYPE_CHARDEV_FILE "chardev-file"
+#define TYPE_CHARDEV_SERIAL "chardev-serial"
+#define TYPE_CHARDEV_SOCKET "chardev-socket"
+#define TYPE_CHARDEV_UDP "chardev-udp"
+
+#define CHARDEV_IS_MUX(chr) \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
+#define CHARDEV_IS_RINGBUF(chr) \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_RINGBUF)
+#define CHARDEV_IS_PTY(chr) \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_PTY)
+
+typedef struct ChardevClass {
+    ObjectClass parent_class;
+
+    bool internal; /* TODO: eventually use TYPE_USER_CREATABLE */
+
+    void (*open)(Chardev *chr, ChardevBackend *backend,
+                 bool *be_opened, Error **errp);
+
+    int (*chr_write)(Chardev *s, const uint8_t *buf, int len);
+    int (*chr_sync_read)(Chardev *s, const uint8_t *buf, int len);
+    GSource *(*chr_add_watch)(Chardev *s, GIOCondition cond);
+    void (*chr_update_read_handler)(Chardev *s, GMainContext *context);
+    int (*chr_ioctl)(Chardev *s, int cmd, void *arg);
+    int (*get_msgfds)(Chardev *s, int* fds, int num);
+    int (*set_msgfds)(Chardev *s, int *fds, int num);
+    int (*chr_add_client)(Chardev *chr, int fd);
+    int (*chr_wait_connected)(Chardev *chr, Error **errp);
+    void (*chr_free)(Chardev *chr);
+    void (*chr_disconnect)(Chardev *chr);
+    void (*chr_accept_input)(Chardev *chr);
+    void (*chr_set_echo)(Chardev *chr, bool echo);
+    void (*chr_set_fe_open)(Chardev *chr, int fe_open);
+} ChardevClass;
+
 struct CharDriver {
     const char *alias;
     ChardevBackendKind kind;
     void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
-    Chardev *(*create)(const CharDriver *driver,
-                       const char *id,
-                       ChardevBackend *backend,
-                       ChardevReturn *ret, bool *be_opened,
-                       Error **errp);
-    size_t instance_size;
-
-    int (*chr_write)(struct Chardev *s, const uint8_t *buf, int len);
-    int (*chr_sync_read)(struct Chardev *s,
-                         const uint8_t *buf, int len);
-    GSource *(*chr_add_watch)(struct Chardev *s, GIOCondition cond);
-    void (*chr_update_read_handler)(struct Chardev *s,
-                                    GMainContext *context);
-    int (*chr_ioctl)(struct Chardev *s, int cmd, void *arg);
-    int (*get_msgfds)(struct Chardev *s, int* fds, int num);
-    int (*set_msgfds)(struct Chardev *s, int *fds, int num);
-    int (*chr_add_client)(struct Chardev *chr, int fd);
-    int (*chr_wait_connected)(struct Chardev *chr, Error **errp);
-    void (*chr_free)(struct Chardev *chr);
-    void (*chr_disconnect)(struct Chardev *chr);
-    void (*chr_accept_input)(struct Chardev *chr);
-    void (*chr_set_echo)(struct Chardev *chr, bool echo);
-    void (*chr_set_fe_open)(struct Chardev *chr, int fe_open);
 };
 
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+                          ChardevBackend *backend, Error **errp);
+
 void register_char_driver(const CharDriver *driver);
 
 extern int term_escape_char;
diff --git a/include/ui/console.h b/include/ui/console.h
index e2589e2134..32dafdb038 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -380,6 +380,8 @@ void graphic_hw_invalidate(QemuConsole *con);
 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
 void graphic_hw_gl_block(QemuConsole *con, bool block);
 
+void qemu_console_early_init(void);
+
 QemuConsole *qemu_console_lookup_by_index(unsigned int index);
 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
-- 
2.11.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]