]> git.proxmox.com Git - mirror_qemu.git/blobdiff - qemu-char.c
chardev: qom-ify
[mirror_qemu.git] / qemu-char.c
index 113c4a859e5a6eaf840c1dd6c38003603a4b6ffb..6b4a2997029338e22a6c1f4d737241824484832c 100644 (file)
@@ -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,34 +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(Chardev *chr, 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 = {
-    .instance_size = sizeof(Chardev),
     .kind = CHARDEV_BACKEND_KIND_NULL,
-    .create = qemu_chr_open_null,
-    .chr_write = null_chr_write,
+};
+
+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),
+    .class_init = char_null_class_init,
 };
 
 /* MUX driver for serial I/O splitting */
@@ -570,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);
@@ -707,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];
 
@@ -720,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];
 
@@ -737,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;
@@ -760,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) {
@@ -789,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 */
@@ -812,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++) {
@@ -838,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,
@@ -851,7 +888,7 @@ static void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 
 static void mux_set_focus(Chardev *chr, int focus)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
 
     assert(focus >= 0);
     assert(focus < d->mux_cnt);
@@ -865,40 +902,27 @@ static void mux_set_focus(Chardev *chr, 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)
@@ -910,8 +934,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;
@@ -937,8 +961,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;
@@ -954,8 +978,8 @@ void qemu_chr_fe_deinit(CharBackend *b)
         if (b->chr->be == b) {
             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;
@@ -971,6 +995,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
                               bool set_open)
 {
     Chardev *s;
+    ChardevClass *cc;
     int fe_open;
 
     s = b->chr;
@@ -978,6 +1003,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);
@@ -988,8 +1014,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) {
@@ -1005,7 +1031,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);
     }
 }
@@ -1016,7 +1042,7 @@ void qemu_chr_fe_take_focus(CharBackend *b)
         return;
     }
 
-    if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
+    if (CHARDEV_IS_MUX(b->chr)) {
         mux_set_focus(b->chr, b->tag);
     }
 }
@@ -1193,7 +1219,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;
@@ -1201,18 +1226,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;
@@ -1241,8 +1269,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;
@@ -1250,14 +1278,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) {
@@ -1270,7 +1298,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) {
@@ -1284,19 +1312,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);
@@ -1307,24 +1328,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);
@@ -1340,10 +1373,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 */
@@ -1395,26 +1428,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;
@@ -1427,16 +1456,12 @@ 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__) \
@@ -1457,13 +1482,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;
@@ -1479,7 +1506,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) {
@@ -1501,7 +1528,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);
@@ -1530,9 +1557,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 */
@@ -1546,7 +1573,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;
     }
@@ -1555,8 +1582,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;
@@ -1564,8 +1591,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;
@@ -1589,8 +1616,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);
@@ -1600,7 +1627,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) {
@@ -1634,7 +1661,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);
@@ -1647,61 +1674,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 = {
-    .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,
+};
+
+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),
+    .class_init = char_pty_class_init,
 };
 
 static void tty_serial_init(int fd, int speed,
@@ -1821,7 +1845,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) {
@@ -1905,6 +1929,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) {
@@ -1918,7 +1945,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;
 
@@ -1999,7 +2026,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);
@@ -2008,31 +2035,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__ */
 
@@ -2045,9 +2062,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) {
@@ -2087,23 +2107,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
 
@@ -2123,6 +2134,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;
@@ -2132,6 +2146,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
@@ -2142,7 +2160,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);
@@ -2166,7 +2184,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;
@@ -2234,7 +2252,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;
@@ -2268,7 +2286,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;
@@ -2276,7 +2294,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;
@@ -2298,7 +2317,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;
@@ -2310,8 +2329,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;
 
@@ -2327,8 +2346,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);
@@ -2344,7 +2363,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;
@@ -2406,65 +2425,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),
+    .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,
-    .create = qemu_chr_open_win_con,
-    .chr_write = win_chr_write,
+};
+
+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)
@@ -2488,8 +2508,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;
@@ -2522,7 +2542,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;
 
@@ -2560,8 +2580,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);
@@ -2572,7 +2592,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);
@@ -2586,7 +2606,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);
@@ -2599,29 +2619,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;
@@ -2668,7 +2685,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);
@@ -2677,7 +2694,6 @@ err2:
     CloseHandle(stdio->hInputDoneEvent);
 err1:
     qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
-    return NULL;
 }
 #endif /* !_WIN32 */
 
@@ -2693,10 +2709,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);
@@ -2704,8 +2722,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);
 
@@ -2722,8 +2740,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) {
@@ -2750,7 +2768,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) {
@@ -2763,7 +2781,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) {
@@ -2801,11 +2819,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);
@@ -2819,7 +2840,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",
@@ -2836,7 +2857,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,
@@ -2859,8 +2880,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);
@@ -2919,7 +2940,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;
 
@@ -2945,7 +2966,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);
@@ -2970,7 +2991,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;
@@ -3027,13 +3048,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) {
@@ -3062,7 +3083,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;
@@ -3084,8 +3105,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;
 
@@ -3111,7 +3132,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) {
@@ -3129,8 +3150,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(
@@ -3151,7 +3172,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;
@@ -3202,7 +3223,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;
@@ -3253,7 +3274,7 @@ static void tcp_chr_tls_handshake(QIOTask *task,
 
 static void tcp_chr_tls_init(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     QIOChannelTLS *tioc;
     Error *err = NULL;
     gchar *name;
@@ -3292,7 +3313,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",
@@ -3304,7 +3325,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;
@@ -3358,7 +3379,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),
@@ -3376,7 +3397,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
@@ -3405,8 +3426,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;
@@ -3424,7 +3447,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);
 
@@ -3451,8 +3474,8 @@ static void tcp_chr_free(Chardev *chr)
 static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
 {
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
-    Chardev *chr = opaque;
-    SocketChardev *s = (SocketChardev *)chr;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     Error *err = NULL;
 
     if (qio_task_propagate_error(task, &err)) {
@@ -3480,9 +3503,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;
 }
@@ -3490,7 +3516,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)) {
@@ -3509,7 +3535,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);
@@ -3523,51 +3549,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,
@@ -3585,7 +3590,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;
     }
@@ -3629,7 +3634,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;
     }
@@ -3849,20 +3854,31 @@ 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,
+};
+
+static void char_stdio_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_stdio;
 #ifdef _WIN32
-    sizeof(WinStdioChardev),
-    .chr_write = win_stdio_write,
-    .chr_set_echo = qemu_chr_set_echo_win_stdio,
-    .chr_free = win_stdio_free,
+    cc->chr_write = win_stdio_write;
+    cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
+    cc->chr_free = win_stdio_free;
 #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,
+    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
+    .parent = TYPE_CHARDEV_WIN_STDIO,
+#else
+    .parent = TYPE_CHARDEV_FD,
+#endif
+    .class_init = char_stdio_class_init,
 };
 
 #ifdef HAVE_CHARDEV_SERIAL
@@ -3917,18 +3933,23 @@ 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,
+};
+
+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,
@@ -3948,22 +3969,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,
+};
+
+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,
+};
+
+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,
@@ -3982,14 +4016,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,
+};
+
+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,
@@ -4269,7 +4315,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");
         }
@@ -4282,8 +4328,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);
     }
 }
 
@@ -4299,8 +4345,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);
     }
 }
 
@@ -4311,11 +4357,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;
     }
@@ -4331,31 +4377,17 @@ void qemu_chr_fe_disconnect(CharBackend *be)
 {
     Chardev *chr = be->chr;
 
-    if (chr && chr->driver->chr_disconnect) {
-        chr->driver->chr_disconnect(chr);
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
+        CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
     }
 }
 
-static void qemu_chr_free_common(Chardev *chr)
-{
-    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);
-    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)
@@ -4523,22 +4555,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) {
@@ -4555,33 +4584,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 */
@@ -4598,15 +4614,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;
@@ -4618,7 +4631,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) {
@@ -4626,70 +4639,76 @@ 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),
     .kind = CHARDEV_BACKEND_KIND_PARALLEL,
     .alias = "parport",
     .parse = qemu_chr_parse_parallel,
-    .create = qmp_chardev_open_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
 
@@ -4698,45 +4717,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,
+};
+
+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 = {
     .kind = CHARDEV_BACKEND_KIND_SERIAL,
     .alias = "tty",
     .parse = qemu_chr_parse_serial,
-    .create = qmp_chardev_open_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;
@@ -4754,15 +4791,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;
@@ -4770,15 +4804,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;
@@ -4870,82 +4897,92 @@ 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,
 };
 
-static Chardev *qmp_chardev_open_udp(const CharDriver *driver,
-                                     const char *id,
-                                     ChardevBackend *backend,
-                                     ChardevReturn *ret,
-                                     bool *be_opened,
-                                     Error **errp)
+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 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);
     /* 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,
+};
+
+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,
@@ -4960,47 +4997,96 @@ 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);
+    if (cc->internal) {
+        error_setg(errp, "'%s' is not a valid char driver name", driver);
+        return NULL;
+    }
+
+    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)
@@ -5035,33 +5121,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