qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 15/38] char: use qemu_chr_fe* functions with CharBac


From: Marc-André Lureau
Subject: [Qemu-devel] [PATCH 15/38] char: use qemu_chr_fe* functions with CharBackend argument
Date: Sat, 22 Oct 2016 12:52:55 +0300

This also switches from qemu_chr_add_handlers() to
qemu_chr_fe_set_handlers(). Note that qemu_chr_fe_set_handlers() now
takes the focus when fe_open (qemu_chr_add_handlers() did take the
focus)

Signed-off-by: Marc-André Lureau <address@hidden>
---
 backends/rng-egd.c          |  13 ++--
 gdbstub.c                   |  18 +++---
 hw/arm/omap2.c              |  11 ++--
 hw/arm/pxa2xx.c             |   6 +-
 hw/arm/strongarm.c          |  16 ++---
 hw/char/bcm2835_aux.c       |   8 +--
 hw/char/cadence_uart.c      |  20 +++---
 hw/char/debugcon.c          |   6 +-
 hw/char/digic-uart.c        |   5 +-
 hw/char/escc.c              |  17 +++---
 hw/char/etraxfs_ser.c       |   8 +--
 hw/char/exynos4210_uart.c   |  11 ++--
 hw/char/grlib_apbuart.c     |  15 ++---
 hw/char/imx_serial.c        |  14 ++---
 hw/char/ipoctal232.c        |  10 +--
 hw/char/lm32_juart.c        |   6 +-
 hw/char/lm32_uart.c         |   7 ++-
 hw/char/mcf_uart.c          |   8 +--
 hw/char/milkymist-uart.c    |   7 ++-
 hw/char/parallel.c          |  34 +++++------
 hw/char/pl011.c             |   8 +--
 hw/char/sclpconsole-lm.c    |  10 +--
 hw/char/sclpconsole.c       |   8 +--
 hw/char/serial.c            |  30 +++++----
 hw/char/sh_serial.c         |   9 +--
 hw/char/spapr_vty.c         |  10 +--
 hw/char/stm32f2xx_usart.c   |  10 +--
 hw/char/virtio-console.c    |  27 ++++----
 hw/char/xen_console.c       |  14 +++--
 hw/char/xilinx_uartlite.c   |   7 ++-
 hw/ipmi/ipmi_bmc_extern.c   |   7 ++-
 hw/mips/mips_malta.c        |  28 ++++-----
 hw/misc/ivshmem.c           |  21 ++++---
 hw/usb/ccid-card-passthru.c |  16 ++---
 hw/usb/dev-serial.c         |  21 ++++---
 hw/usb/redirect.c           |  18 +++---
 hw/virtio/vhost-user.c      |   4 +-
 monitor.c                   |  20 +++---
 net/colo-compare.c          |  34 ++++++-----
 net/filter-mirror.c         |  20 +++---
 net/slirp.c                 |   7 ++-
 net/vhost-user.c            |  21 ++++---
 qemu-char.c                 | 146 ++++++++++++++++++++++----------------------
 qtest.c                     |  23 +++----
 tests/vhost-user-test.c     |  35 ++++++-----
 include/hw/char/serial.h    |   1 +
 include/sysemu/char.h       |  51 ++++++----------
 47 files changed, 437 insertions(+), 409 deletions(-)

diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index e2f8189..d9e50bb 100644
--- a/backends/rng-egd.c
+++ b/backends/rng-egd.c
@@ -42,7 +42,7 @@ static void rng_egd_request_entropy(RngBackend *b, RngRequest 
*req)
 
         /* XXX this blocks entire thread. Rewrite to use
          * qemu_chr_fe_write and background I/O callbacks */
-        qemu_chr_fe_write_all(s->chr.chr, header, sizeof(header));
+        qemu_chr_fe_write_all(&s->chr, header, sizeof(header));
 
         size -= len;
     }
@@ -109,8 +109,8 @@ static void rng_egd_opened(RngBackend *b, Error **errp)
     }
 
     /* FIXME we should resubmit pending requests when the CDS reconnects. */
-    qemu_chr_add_handlers(s->chr.chr, rng_egd_chr_can_read,
-                          rng_egd_chr_read, NULL, s);
+    qemu_chr_fe_set_handlers(&s->chr, rng_egd_chr_can_read,
+                             rng_egd_chr_read, NULL, s, NULL);
 }
 
 static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
@@ -129,9 +129,10 @@ static void rng_egd_set_chardev(Object *obj, const char 
*value, Error **errp)
 static char *rng_egd_get_chardev(Object *obj, Error **errp)
 {
     RngEgd *s = RNG_EGD(obj);
+    CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr);
 
-    if (s->chr.chr && s->chr.chr->label) {
-        return g_strdup(s->chr.chr->label);
+    if (chr && chr->label) {
+        return g_strdup(chr->label);
     }
 
     return NULL;
@@ -149,7 +150,7 @@ static void rng_egd_finalize(Object *obj)
     RngEgd *s = RNG_EGD(obj);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL);
         qemu_chr_fe_release(s->chr.chr);
     }
 
diff --git a/gdbstub.c b/gdbstub.c
index 347bd4d..33b056e 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -404,7 +404,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int 
len)
 #else
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(s->chr.chr, buf, len);
+    qemu_chr_fe_write_all(&s->chr, buf, len);
 #endif
 }
 
@@ -1471,6 +1471,9 @@ void gdb_exit(CPUArchState *env, int code)
 {
   GDBState *s;
   char buf[4];
+#ifndef CONFIG_USER_ONLY
+  CharDriverState *chr;
+#endif
 
   s = gdbserver_state;
   if (!s) {
@@ -1481,7 +1484,8 @@ void gdb_exit(CPUArchState *env, int code)
       return;
   }
 #else
-  if (!s->chr.chr) {
+  chr = qemu_chr_fe_get_driver(&s->chr);
+  if (!chr) {
       return;
   }
 #endif
@@ -1490,7 +1494,7 @@ void gdb_exit(CPUArchState *env, int code)
   put_packet(s, buf);
 
 #ifndef CONFIG_USER_ONLY
-  qemu_chr_delete(s->chr.chr);
+  qemu_chr_delete(chr);
 #endif
 }
 
@@ -1764,8 +1768,8 @@ int gdbserver_start(const char *device)
         mon_chr->chr_write = gdb_monitor_write;
         monitor_init(mon_chr, 0);
     } else {
-        if (s->chr.chr) {
-            qemu_chr_delete(s->chr.chr);
+        if (qemu_chr_fe_get_driver(&s->chr)) {
+            qemu_chr_delete(qemu_chr_fe_get_driver(&s->chr));
         }
         mon_chr = s->mon_chr;
         memset(s, 0, sizeof(GDBState));
@@ -1775,8 +1779,8 @@ int gdbserver_start(const char *device)
     s->g_cpu = first_cpu;
     if (chr) {
         qemu_chr_fe_init(&s->chr, chr, &error_abort);
-        qemu_chr_add_handlers(s->chr.chr, gdb_chr_can_receive, gdb_chr_receive,
-                              gdb_chr_event, NULL);
+        qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive,
+                                 gdb_chr_event, NULL, NULL);
     }
     s->state = chr ? RS_IDLE : RS_INACTIVE;
     s->mon_chr = mon_chr;
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index 43d9c4b..6f05c98 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -771,14 +771,15 @@ static void omap_sti_fifo_write(void *opaque, hwaddr addr,
         /* Flush channel <i>value</i>.  */
         /* XXX this blocks entire thread. Rewrite to use
          * qemu_chr_fe_write and background I/O callbacks */
-        qemu_chr_fe_write_all(s->chr.chr, (const uint8_t *) "\r", 1);
+        qemu_chr_fe_write_all(&s->chr, (const uint8_t *) "\r", 1);
     } else if (ch == STI_TRACE_CONSOLE_CHANNEL || 1) {
         if (value == 0xc0 || value == 0xc3) {
             /* Open channel <i>ch</i>.  */
-        } else if (value == 0x00)
-            qemu_chr_fe_write_all(s->chr.chr, (const uint8_t *) "\n", 1);
-        else
-            qemu_chr_fe_write_all(s->chr.chr, &byte, 1);
+        } else if (value == 0x00) {
+            qemu_chr_fe_write_all(&s->chr, (const uint8_t *) "\n", 1);
+        } else {
+            qemu_chr_fe_write_all(&s->chr, &byte, 1);
+        }
     }
 }
 
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 27f112c..798c05b 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1906,7 +1906,7 @@ static void pxa2xx_fir_write(void *opaque, hwaddr addr,
         if (s->chr.chr && s->enable && (s->control[0] & (1 << 3))) { /* TXE */
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
         }
         break;
     case ICSR0:
@@ -1977,8 +1977,8 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error 
**errp)
 
     if (s->chr.chr) {
         qemu_chr_fe_claim_no_fail(s->chr.chr);
-        qemu_chr_add_handlers(s->chr.chr, pxa2xx_fir_is_empty,
-                        pxa2xx_fir_rx, pxa2xx_fir_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty,
+                                 pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL);
     }
 }
 
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index d3e8aff..fd13a39 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -1021,7 +1021,7 @@ static void 
strongarm_uart_update_parameters(StrongARMUARTState *s)
     ssp.stop_bits = stop_bits;
     s->char_transmit_time =  (NANOSECONDS_PER_SECOND / speed) * frame_size;
     if (s->chr.chr) {
-        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
     }
 
     DPRINTF(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
@@ -1107,10 +1107,10 @@ static void strongarm_uart_tx(void *opaque)
 
     if (s->utcr3 & UTCR3_LBM) /* loopback */ {
         strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
-    } else if (s->chr.chr) {
+    } else if (qemu_chr_fe_get_driver(&s->chr)) {
         /* XXX this blocks entire thread. Rewrite to use
          * qemu_chr_fe_write and background I/O callbacks */
-        qemu_chr_fe_write_all(s->chr.chr, &s->tx_fifo[s->tx_start], 1);
+        qemu_chr_fe_write_all(&s->chr, &s->tx_fifo[s->tx_start], 1);
     }
 
     s->tx_start = (s->tx_start + 1) % 8;
@@ -1240,11 +1240,11 @@ static void strongarm_uart_init(Object *obj)
     s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr,
-                        strongarm_uart_can_receive,
-                        strongarm_uart_receive,
-                        strongarm_uart_event,
-                        s);
+        qemu_chr_fe_set_handlers(&s->chr,
+                                 strongarm_uart_can_receive,
+                                 strongarm_uart_receive,
+                                 strongarm_uart_event,
+                                 s, NULL);
     }
 }
 
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index 30f4ea5..c49ec8c 100644
--- a/hw/char/bcm2835_aux.c
+++ b/hw/char/bcm2835_aux.c
@@ -80,7 +80,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, 
unsigned size)
             }
         }
         if (s->chr.chr) {
-            qemu_chr_fe_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(&s->chr);
         }
         bcm2835_aux_update(s);
         return c;
@@ -171,7 +171,7 @@ static void bcm2835_aux_write(void *opaque, hwaddr offset, 
uint64_t value,
         if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
         }
         break;
 
@@ -283,8 +283,8 @@ static void bcm2835_aux_realize(DeviceState *dev, Error 
**errp)
     BCM2835AuxState *s = BCM2835_AUX(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, bcm2835_aux_can_receive,
-                              bcm2835_aux_receive, NULL, s);
+        qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
+                                 bcm2835_aux_receive, NULL, s, NULL);
     }
 }
 
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index c027235..4459b2d 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -143,7 +143,7 @@ static void uart_rx_reset(CadenceUARTState *s)
     s->rx_wpos = 0;
     s->rx_count = 0;
     if (s->chr.chr) {
-        qemu_chr_fe_accept_input(s->chr.chr);
+        qemu_chr_fe_accept_input(&s->chr);
     }
 }
 
@@ -157,8 +157,8 @@ static void uart_send_breaks(CadenceUARTState *s)
     int break_enabled = 1;
 
     if (s->chr.chr) {
-        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_BREAK,
-                                   &break_enabled);
+        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+                          &break_enabled);
     }
 }
 
@@ -211,7 +211,7 @@ static void uart_parameters_setup(CadenceUARTState *s)
     packet_size += ssp.data_bits + ssp.stop_bits;
     s->char_tx_time = (NANOSECONDS_PER_SECOND / ssp.speed) * packet_size;
     if (s->chr.chr) {
-        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
     }
 }
 
@@ -278,7 +278,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, 
GIOCondition cond,
     int ret;
 
     /* instant drain the fifo when there's no back-end */
-    if (!s->chr.chr) {
+    if (!qemu_chr_fe_get_driver(&s->chr)) {
         s->tx_count = 0;
         return FALSE;
     }
@@ -287,7 +287,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, 
GIOCondition cond,
         return FALSE;
     }
 
-    ret = qemu_chr_fe_write(s->chr.chr, s->tx_fifo, s->tx_count);
+    ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_count);
 
     if (ret >= 0) {
         s->tx_count -= ret;
@@ -295,7 +295,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, 
GIOCondition cond,
     }
 
     if (s->tx_count) {
-        guint r = qemu_chr_fe_add_watch(s->chr.chr, G_IO_OUT | G_IO_HUP,
+        guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
                                         cadence_uart_xmit, s);
         if (!r) {
             s->tx_count = 0;
@@ -369,7 +369,7 @@ static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t 
*c)
         s->rx_count--;
 
         if (s->chr.chr) {
-            qemu_chr_fe_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(&s->chr);
         }
     } else {
         *c = 0;
@@ -475,8 +475,8 @@ static void cadence_uart_realize(DeviceState *dev, Error 
**errp)
                                           fifo_trigger_update, s);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, uart_can_receive, uart_receive,
-                              uart_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
+                                 uart_event, s, NULL);
     }
 }
 
diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c
index b405109..2009c3e 100644
--- a/hw/char/debugcon.c
+++ b/hw/char/debugcon.c
@@ -62,7 +62,7 @@ static void debugcon_ioport_write(void *opaque, hwaddr addr, 
uint64_t val,
 
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+    qemu_chr_fe_write_all(&s->chr, &ch, 1);
 }
 
 
@@ -87,12 +87,12 @@ static const MemoryRegionOps debugcon_ops = {
 
 static void debugcon_realize_core(DebugconState *s, Error **errp)
 {
-    if (!s->chr.chr) {
+    if (!qemu_chr_fe_get_driver(&s->chr)) {
         error_setg(errp, "Can't create debugcon device, empty char device");
         return;
     }
 
-    qemu_chr_add_handlers(s->chr.chr, NULL, NULL, NULL, s);
+    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, s, NULL);
 }
 
 static void debugcon_isa_realizefn(DeviceState *dev, Error **errp)
diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c
index fb4969f..c7b3db6 100644
--- a/hw/char/digic-uart.c
+++ b/hw/char/digic-uart.c
@@ -79,7 +79,7 @@ static void digic_uart_write(void *opaque, hwaddr addr, 
uint64_t value,
         if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
         }
         break;
 
@@ -148,7 +148,8 @@ static void digic_uart_realize(DeviceState *dev, Error 
**errp)
     DigicUartState *s = DIGIC_UART(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, uart_can_rx, uart_rx, uart_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                                 uart_event, s, NULL);
     }
 }
 
diff --git a/hw/char/escc.c b/hw/char/escc.c
index f1e8fac..4578a46 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -416,7 +416,7 @@ static void escc_update_parameters(ChannelState *s)
     int speed, parity, data_bits, stop_bits;
     QEMUSerialSetParams ssp;
 
-    if (!s->chr.chr || s->type != ser)
+    if (!qemu_chr_fe_get_driver(&s->chr) || s->type != ser)
         return;
 
     if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) {
@@ -466,7 +466,7 @@ static void escc_update_parameters(ChannelState *s)
     ssp.data_bits = data_bits;
     ssp.stop_bits = stop_bits;
     trace_escc_update_parameters(CHN_C(s), speed, parity, data_bits, 
stop_bits);
-    qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 }
 
 static void escc_mem_write(void *opaque, hwaddr addr,
@@ -556,10 +556,10 @@ static void escc_mem_write(void *opaque, hwaddr addr,
         trace_escc_mem_writeb_data(CHN_C(s), val);
         s->tx = val;
         if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled
-            if (s->chr.chr) {
+            if (qemu_chr_fe_get_driver(&s->chr)) {
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(s->chr.chr, &s->tx, 1);
+                qemu_chr_fe_write_all(&s->chr, &s->tx, 1);
             } else if (s->type == kbd && !s->disabled) {
                 handle_kbd_command(s, val);
             }
@@ -600,7 +600,7 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr,
             ret = s->rx;
         trace_escc_mem_readb_data(CHN_C(s), ret);
         if (s->chr.chr) {
-            qemu_chr_fe_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(&s->chr);
         }
         return ret;
     default:
@@ -1014,10 +1014,11 @@ static void escc_realize(DeviceState *dev, Error **errp)
                           ESCC_SIZE << s->it_shift);
 
     for (i = 0; i < 2; i++) {
-        if (s->chn[i].chr.chr) {
+        if (qemu_chr_fe_get_driver(&s->chn[i].chr)) {
             s->chn[i].clock = s->frequency / 2;
-            qemu_chr_add_handlers(s->chn[i].chr.chr, serial_can_receive,
-                                  serial_receive1, serial_event, &s->chn[i]);
+            qemu_chr_fe_set_handlers(&s->chn[i].chr, serial_can_receive,
+                                     serial_receive1, serial_event,
+                                     &s->chn[i], NULL);
         }
     }
 
diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c
index 99c4801..d812954 100644
--- a/hw/char/etraxfs_ser.c
+++ b/hw/char/etraxfs_ser.c
@@ -128,7 +128,7 @@ ser_write(void *opaque, hwaddr addr,
         case RW_DOUT:
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->regs[R_INTR] |= 3;
             s->pending_tx = 1;
             s->regs[addr] = value;
@@ -232,9 +232,9 @@ static void etraxfs_ser_realize(DeviceState *dev, Error 
**errp)
     ETRAXSerial *s = ETRAX_SERIAL(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr,
-                              serial_can_receive, serial_receive,
-                              serial_event, s);
+        qemu_chr_fe_set_handlers(&s->chr,
+                                 serial_can_receive, serial_receive,
+                                 serial_event, s, NULL);
     }
 }
 
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index 3f71059..48216b1 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -346,7 +346,7 @@ static void 
exynos4210_uart_update_parameters(Exynos4210UartState *s)
     ssp.data_bits = data_bits;
     ssp.stop_bits = stop_bits;
 
-    qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 
     PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n",
                 s->channel, speed, parity, data_bits, stop_bits);
@@ -383,13 +383,13 @@ static void exynos4210_uart_write(void *opaque, hwaddr 
offset,
         break;
 
     case UTXH:
-        if (s->chr.chr) {
+        if (qemu_chr_fe_get_driver(&s->chr)) {
             s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY |
                     UTRSTAT_Tx_BUFFER_EMPTY);
             ch = (uint8_t)val;
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
 #if DEBUG_Tx_DATA
             fprintf(stderr, "%c", ch);
 #endif
@@ -640,8 +640,9 @@ static int exynos4210_uart_init(SysBusDevice *dev)
 
     sysbus_init_irq(dev, &s->irq);
 
-    qemu_chr_add_handlers(s->chr.chr, exynos4210_uart_can_receive,
-                          exynos4210_uart_receive, exynos4210_uart_event, s);
+    qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive,
+                             exynos4210_uart_receive, exynos4210_uart_event,
+                             s, NULL);
 
     return 0;
 }
diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c
index 13c9455..e50d65b 100644
--- a/hw/char/grlib_apbuart.c
+++ b/hw/char/grlib_apbuart.c
@@ -201,11 +201,12 @@ static void grlib_apbuart_write(void *opaque, hwaddr addr,
     case DATA_OFFSET:
     case DATA_OFFSET + 3:       /* When only one byte write */
         /* Transmit when character device available and transmitter enabled */
-        if (uart->chr.chr && (uart->control & UART_TRANSMIT_ENABLE)) {
+        if (qemu_chr_fe_get_driver(&uart->chr) &&
+            (uart->control & UART_TRANSMIT_ENABLE)) {
             c = value & 0xFF;
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(uart->chr.chr, &c, 1);
+            qemu_chr_fe_write_all(&uart->chr, &c, 1);
             /* Generate interrupt */
             if (uart->control & UART_TRANSMIT_INTERRUPT) {
                 qemu_irq_pulse(uart->irq);
@@ -242,11 +243,11 @@ static int grlib_apbuart_init(SysBusDevice *dev)
 {
     UART *uart = GRLIB_APB_UART(dev);
 
-    qemu_chr_add_handlers(uart->chr.chr,
-                          grlib_apbuart_can_receive,
-                          grlib_apbuart_receive,
-                          grlib_apbuart_event,
-                          uart);
+    qemu_chr_fe_set_handlers(&uart->chr,
+                             grlib_apbuart_can_receive,
+                             grlib_apbuart_receive,
+                             grlib_apbuart_event,
+                             uart, NULL);
 
     sysbus_init_irq(dev, &uart->irq);
 
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 2e39d31..d9a0a25 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -122,7 +122,7 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
             s->uts1 |= UTS1_RXEMPTY;
             imx_update(s);
             if (s->chr.chr) {
-                qemu_chr_fe_accept_input(s->chr.chr);
+                qemu_chr_fe_accept_input(&s->chr);
             }
         }
         return c;
@@ -172,11 +172,11 @@ static void imx_serial_write(void *opaque, hwaddr offset,
                              uint64_t value, unsigned size)
 {
     IMXSerialState *s = (IMXSerialState *)opaque;
+    CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr);
     unsigned char ch;
 
     DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n",
-            offset, (unsigned int)value,
-            s->chr.chr ? s->chr.chr->label : "NODEV");
+            offset, (unsigned int)value, chr ? chr->label : "NODEV");
 
     switch (offset >> 2) {
     case 0x10: /* UTXD */
@@ -185,7 +185,7 @@ static void imx_serial_write(void *opaque, hwaddr offset,
             if (s->chr.chr) {
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+                qemu_chr_fe_write_all(&s->chr, &ch, 1);
             }
             s->usr1 &= ~USR1_TRDY;
             imx_update(s);
@@ -216,7 +216,7 @@ static void imx_serial_write(void *opaque, hwaddr offset,
         if (value & UCR2_RXEN) {
             if (!(s->ucr2 & UCR2_RXEN)) {
                 if (s->chr.chr) {
-                    qemu_chr_fe_accept_input(s->chr.chr);
+                    qemu_chr_fe_accept_input(&s->chr);
                 }
             }
         }
@@ -320,8 +320,8 @@ static void imx_serial_realize(DeviceState *dev, Error 
**errp)
     IMXSerialState *s = IMX_SERIAL(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, imx_can_receive, imx_receive,
-                              imx_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive,
+                                 imx_event, s, NULL);
     } else {
         DPRINTF("No char dev for uart\n");
     }
diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c
index 875fe3b..d504721 100644
--- a/hw/char/ipoctal232.c
+++ b/hw/char/ipoctal232.c
@@ -289,7 +289,7 @@ static uint16_t io_read(IPackDevice *ip, uint8_t addr)
                 ch->sr &= ~SR_RXRDY;
                 blk->isr &= ~ISR_RXRDY(channel);
                 if (ch->dev.chr) {
-                    qemu_chr_fe_accept_input(ch->dev.chr);
+                    qemu_chr_fe_accept_input(&ch->dev);
                 }
             } else {
                 ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
@@ -362,7 +362,7 @@ static void io_write(IPackDevice *ip, uint8_t addr, 
uint16_t val)
                 uint8_t thr = reg;
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(ch->dev.chr, &thr, 1);
+                qemu_chr_fe_write_all(&ch->dev, &thr, 1);
             }
         } else {
             DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
@@ -546,9 +546,9 @@ static void ipoctal_realize(DeviceState *dev, Error **errp)
         ch->ipoctal = s;
 
         /* Redirect IP-Octal channels to host character devices */
-        if (ch->dev.chr) {
-            qemu_chr_add_handlers(ch->dev.chr, hostdev_can_receive,
-                                  hostdev_receive, hostdev_event, ch);
+        if (qemu_chr_fe_get_driver(&ch->dev)) {
+            qemu_chr_fe_set_handlers(&ch->dev, hostdev_can_receive,
+                                     hostdev_receive, hostdev_event, ch, NULL);
             DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label);
         } else {
             DPRINTF("Could not redirect channel %u, no chardev set\n", i);
diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c
index a0eb312..9629e9e 100644
--- a/hw/char/lm32_juart.c
+++ b/hw/char/lm32_juart.c
@@ -78,7 +78,7 @@ void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx)
     if (s->chr.chr) {
         /* XXX this blocks entire thread. Rewrite to use
          * qemu_chr_fe_write and background I/O callbacks */
-        qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
     }
 }
 
@@ -121,8 +121,8 @@ static void lm32_juart_realize(DeviceState *dev, Error 
**errp)
     LM32JuartState *s = LM32_JUART(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, juart_can_rx,
-                              juart_rx, juart_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx,
+                                 juart_event, s, NULL);
     }
 }
 
diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c
index a8d4a2d..e325b91 100644
--- a/hw/char/lm32_uart.c
+++ b/hw/char/lm32_uart.c
@@ -142,7 +142,7 @@ static uint64_t uart_read(void *opaque, hwaddr addr,
         r = s->regs[R_RXTX];
         s->regs[R_LSR] &= ~LSR_DR;
         uart_update_irq(s);
-        qemu_chr_fe_accept_input(s->chr.chr);
+        qemu_chr_fe_accept_input(&s->chr);
         break;
     case R_IIR:
     case R_LSR:
@@ -180,7 +180,7 @@ static void uart_write(void *opaque, hwaddr addr,
         if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
         }
         break;
     case R_IER:
@@ -268,7 +268,8 @@ static void lm32_uart_realize(DeviceState *dev, Error 
**errp)
     LM32UartState *s = LM32_UART(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, uart_can_rx, uart_rx, uart_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                                 uart_event, s, NULL);
     }
 }
 
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index 57b47c6..cc3db13 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -93,7 +93,7 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr,
             if (s->fifo_len == 0)
                 s->sr &= ~MCF_UART_RxRDY;
             mcf_uart_update(s);
-            qemu_chr_fe_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(&s->chr);
             return val;
         }
     case 0x10:
@@ -117,7 +117,7 @@ static void mcf_uart_do_tx(mcf_uart_state *s)
         if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, (unsigned char *)&s->tb, 1);
+            qemu_chr_fe_write_all(&s->chr, (unsigned char *)&s->tb, 1);
         }
         s->sr |= MCF_UART_TxEMP;
     }
@@ -286,8 +286,8 @@ void *mcf_uart_init(qemu_irq irq, CharDriverState *chr)
     if (chr) {
         qemu_chr_fe_init(&s->chr, chr, &error_abort);
         qemu_chr_fe_claim_no_fail(chr);
-        qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive,
-                              mcf_uart_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive,
+                                 mcf_uart_receive, mcf_uart_event, s, NULL);
     }
     mcf_uart_reset(s);
     return s;
diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c
index c75bcb5..0a4c617 100644
--- a/hw/char/milkymist-uart.c
+++ b/hw/char/milkymist-uart.c
@@ -125,7 +125,7 @@ static void uart_write(void *opaque, hwaddr addr, uint64_t 
value,
     switch (addr) {
     case R_RXTX:
         if (s->chr.chr) {
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
         }
         s->regs[R_STAT] |= STAT_TX_EVT;
         break;
@@ -138,7 +138,7 @@ static void uart_write(void *opaque, hwaddr addr, uint64_t 
value,
     case R_STAT:
         /* write one to clear bits */
         s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT));
-        qemu_chr_fe_accept_input(s->chr.chr);
+        qemu_chr_fe_accept_input(&s->chr);
         break;
 
     default:
@@ -201,7 +201,8 @@ static void milkymist_uart_realize(DeviceState *dev, Error 
**errp)
     MilkymistUartState *s = MILKYMIST_UART(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, uart_can_rx, uart_rx, uart_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                                 uart_event, s, NULL);
     }
 }
 
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index 80576af..f2d5666 100644
--- a/hw/char/parallel.c
+++ b/hw/char/parallel.c
@@ -131,7 +131,7 @@ parallel_ioport_write_sw(void *opaque, uint32_t addr, 
uint32_t val)
                 if ((s->control & PARA_CTR_STROBE) == 0)
                     /* XXX this blocks entire thread. Rewrite to use
                      * qemu_chr_fe_write and background I/O callbacks */
-                    qemu_chr_fe_write_all(s->chr.chr, &s->dataw, 1);
+                    qemu_chr_fe_write_all(&s->chr, &s->dataw, 1);
             } else {
                 if (s->control & PARA_CTR_INTEN) {
                     s->irq_pending = 1;
@@ -161,7 +161,7 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t 
addr, uint32_t val)
         if (s->dataw == val)
             return;
         pdebug("wd%02x\n", val);
-        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_WRITE_DATA, &parm);
+        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm);
         s->dataw = val;
         break;
     case PARA_REG_STS:
@@ -181,11 +181,11 @@ static void parallel_ioport_write_hw(void *opaque, 
uint32_t addr, uint32_t val)
             } else {
                 dir = 0;
             }
-            qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_DATA_DIR, &dir);
+            qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_DATA_DIR, &dir);
             parm &= ~PARA_CTR_DIR;
         }
 
-        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm);
+        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm);
         s->control = val;
         break;
     case PARA_REG_EPP_ADDR:
@@ -194,7 +194,7 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t 
addr, uint32_t val)
             pdebug("wa%02x s\n", val);
         else {
             struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr.chr,
+            if (qemu_chr_fe_ioctl(&s->chr,
                                   CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
                 s->epp_timeout = 1;
                 pdebug("wa%02x t\n", val);
@@ -209,7 +209,7 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t 
addr, uint32_t val)
             pdebug("we%02x s\n", val);
         else {
             struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) 
{
+            if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) {
                 s->epp_timeout = 1;
                 pdebug("we%02x t\n", val);
             }
@@ -234,7 +234,7 @@ parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t 
addr, uint32_t val)
         pdebug("we%04x s\n", val);
         return;
     }
-    err = qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+    err = qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
     if (err) {
         s->epp_timeout = 1;
         pdebug("we%04x t\n", val);
@@ -257,7 +257,7 @@ parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t 
addr, uint32_t val)
         pdebug("we%08x s\n", val);
         return;
     }
-    err = qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+    err = qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
     if (err) {
         s->epp_timeout = 1;
         pdebug("we%08x t\n", val);
@@ -309,13 +309,13 @@ static uint32_t parallel_ioport_read_hw(void *opaque, 
uint32_t addr)
     addr &= 7;
     switch(addr) {
     case PARA_REG_DATA:
-        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_READ_DATA, &ret);
+        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_DATA, &ret);
         if (s->last_read_offset != addr || s->datar != ret)
             pdebug("rd%02x\n", ret);
         s->datar = ret;
         break;
     case PARA_REG_STS:
-        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_READ_STATUS, &ret);
+        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_STATUS, &ret);
         ret &= ~PARA_STS_TMOUT;
         if (s->epp_timeout)
             ret |= PARA_STS_TMOUT;
@@ -327,7 +327,7 @@ static uint32_t parallel_ioport_read_hw(void *opaque, 
uint32_t addr)
         /* s->control has some bits fixed to 1. It is zero only when
            it has not been yet written to.  */
         if (s->control == 0) {
-            qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_READ_CONTROL, &ret);
+            qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret);
             if (s->last_read_offset != addr)
                 pdebug("rc%02x\n", ret);
             s->control = ret;
@@ -345,7 +345,7 @@ static uint32_t parallel_ioport_read_hw(void *opaque, 
uint32_t addr)
             pdebug("ra%02x s\n", ret);
         else {
             struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr.chr,
+            if (qemu_chr_fe_ioctl(&s->chr,
                                   CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
                 s->epp_timeout = 1;
                 pdebug("ra%02x t\n", ret);
@@ -361,7 +361,7 @@ static uint32_t parallel_ioport_read_hw(void *opaque, 
uint32_t addr)
             pdebug("re%02x s\n", ret);
         else {
             struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) {
+            if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) {
                 s->epp_timeout = 1;
                 pdebug("re%02x t\n", ret);
             }
@@ -389,7 +389,7 @@ parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t 
addr)
         pdebug("re%04x s\n", eppdata);
         return eppdata;
     }
-    err = qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+    err = qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
     ret = le16_to_cpu(eppdata);
 
     if (err) {
@@ -416,7 +416,7 @@ parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t 
addr)
         pdebug("re%08x s\n", eppdata);
         return eppdata;
     }
-    err = qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+    err = qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
     ret = le32_to_cpu(eppdata);
 
     if (err) {
@@ -512,7 +512,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error 
**errp)
     int base;
     uint8_t dummy;
 
-    if (!s->chr.chr) {
+    if (!qemu_chr_fe_get_driver(&s->chr)) {
         error_setg(errp, "Can't create parallel device, empty char device");
         return;
     }
@@ -534,7 +534,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error 
**errp)
     isa_init_irq(isadev, &s->irq, isa->isairq);
     qemu_register_reset(parallel_reset, s);
 
-    if (qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) {
+    if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) {
         s->hw_driver = 1;
         s->status = dummy;
     }
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 29fb725..52ec866 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -88,7 +88,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
         s->rsr = c >> 8;
         pl011_update(s);
         if (s->chr.chr) {
-            qemu_chr_fe_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(&s->chr);
         }
         r = c;
         break;
@@ -171,7 +171,7 @@ static void pl011_write(void *opaque, hwaddr offset,
         if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
         }
         s->int_level |= PL011_INT_TX;
         pl011_update(s);
@@ -333,8 +333,8 @@ static void pl011_realize(DeviceState *dev, Error **errp)
     PL011State *s = PL011(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, pl011_can_receive, pl011_receive,
-                              pl011_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
+                                 pl011_event, s, NULL);
     }
 }
 
diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c
index 3ef1517..0660cbc 100644
--- a/hw/char/sclpconsole-lm.c
+++ b/hw/char/sclpconsole-lm.c
@@ -91,7 +91,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int 
size)
     if (scon->echo) {
         /* XXX this blocks entire thread. Rewrite to use
          * qemu_chr_fe_write and background I/O callbacks */
-        qemu_chr_fe_write_all(scon->chr.chr, buf, size);
+        qemu_chr_fe_write_all(&scon->chr, buf, size);
     }
 }
 
@@ -195,14 +195,14 @@ static int write_console_data(SCLPEvent *event, const 
uint8_t *buf, int len)
 {
     SCLPConsoleLM *scon = SCLPLM_CONSOLE(event);
 
-    if (!scon->chr.chr) {
+    if (!qemu_chr_fe_get_driver(&scon->chr)) {
         /* If there's no backend, we can just say we consumed all data. */
         return len;
     }
 
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    return qemu_chr_fe_write_all(scon->chr.chr, buf, len);
+    return qemu_chr_fe_write_all(&scon->chr, buf, len);
 }
 
 static int process_mdb(SCLPEvent *event, MDBO *mdbo)
@@ -313,8 +313,8 @@ static int console_init(SCLPEvent *event)
     console_available = true;
 
     if (scon->chr.chr) {
-        qemu_chr_add_handlers(scon->chr.chr, chr_can_read,
-                              chr_read, NULL, scon);
+        qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
+                                 chr_read, NULL, scon, NULL);
     }
 
     return 0;
diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c
index bb51a2c..0559208 100644
--- a/hw/char/sclpconsole.c
+++ b/hw/char/sclpconsole.c
@@ -163,14 +163,14 @@ static ssize_t write_console_data(SCLPEvent *event, const 
uint8_t *buf,
 {
     SCLPConsole *scon = SCLP_CONSOLE(event);
 
-    if (!scon->chr.chr) {
+    if (!qemu_chr_fe_get_driver(&scon->chr)) {
         /* If there's no backend, we can just say we consumed all data. */
         return len;
     }
 
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    return qemu_chr_fe_write_all(scon->chr.chr, buf, len);
+    return qemu_chr_fe_write_all(&scon->chr, buf, len);
 }
 
 static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
@@ -228,8 +228,8 @@ static int console_init(SCLPEvent *event)
     }
     console_available = true;
     if (scon->chr.chr) {
-        qemu_chr_add_handlers(scon->chr.chr, chr_can_read,
-                              chr_read, NULL, scon);
+        qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
+                                 chr_read, NULL, scon, NULL);
     }
 
     return 0;
diff --git a/hw/char/serial.c b/hw/char/serial.c
index c9a9eeb..257be46 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -181,7 +181,7 @@ static void serial_update_parameters(SerialState *s)
     ssp.data_bits = data_bits;
     ssp.stop_bits = stop_bits;
     s->char_transmit_time =  (NANOSECONDS_PER_SECOND / speed) * frame_size;
-    qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 
     DPRINTF("speed=%d parity=%c data=%d stop=%d\n",
            speed, parity, data_bits, stop_bits);
@@ -194,7 +194,7 @@ static void serial_update_msl(SerialState *s)
 
     timer_del(s->modem_status_poll);
 
-    if (qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_GET_TIOCM,
+    if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM,
                           &flags) == -ENOTSUP) {
         s->poll_msl = -1;
         return;
@@ -260,11 +260,11 @@ static void serial_xmit(SerialState *s)
         if (s->mcr & UART_MCR_LOOP) {
             /* in loopback mode, say that we just received a char */
             serial_receive1(s, &s->tsr, 1);
-        } else if (qemu_chr_fe_write(s->chr.chr, &s->tsr, 1) != 1 &&
+        } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) != 1 &&
                    s->tsr_retry < MAX_XMIT_RETRY) {
             assert(s->watch_tag == 0);
             s->watch_tag =
-                qemu_chr_fe_add_watch(s->chr.chr, G_IO_OUT | G_IO_HUP,
+                qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
                                       serial_watch_cb, s);
             if (s->watch_tag > 0) {
                 s->tsr_retry++;
@@ -418,8 +418,8 @@ static void serial_ioport_write(void *opaque, hwaddr addr, 
uint64_t val,
             break_enable = (val >> 6) & 1;
             if (break_enable != s->last_break_enable) {
                 s->last_break_enable = break_enable;
-                qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_BREAK,
-                               &break_enable);
+                qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+                                  &break_enable);
             }
         }
         break;
@@ -433,8 +433,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, 
uint64_t val,
 
             if (s->poll_msl >= 0 && old_mcr != s->mcr) {
 
-                qemu_chr_fe_ioctl(s->chr.chr,
-                                  CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
+                qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
 
                 flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR);
 
@@ -443,8 +442,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, 
uint64_t val,
                 if (val & UART_MCR_DTR)
                     flags |= CHR_TIOCM_DTR;
 
-                qemu_chr_fe_ioctl(s->chr.chr,
-                                  CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
+                qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
                 /* Update the modem status after a one-character-send 
wait-time, since there may be a response
                    from the device/computer at the other end of the serial 
line */
                 timer_mod(s->modem_status_poll, 
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time);
@@ -489,7 +487,7 @@ static uint64_t serial_ioport_read(void *opaque, hwaddr 
addr, unsigned size)
             serial_update_irq(s);
             if (!(s->mcr & UART_MCR_LOOP)) {
                 /* in loopback mode, don't receive any data */
-                qemu_chr_fe_accept_input(s->chr.chr);
+                qemu_chr_fe_accept_input(&s->chr);
             }
         }
         break;
@@ -662,7 +660,7 @@ static int serial_post_load(void *opaque, int version_id)
         }
 
         assert(s->watch_tag == 0);
-        s->watch_tag = qemu_chr_fe_add_watch(s->chr.chr, G_IO_OUT | G_IO_HUP,
+        s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
                                              serial_watch_cb, s);
     } else {
         /* tsr_retry == 0 implies LSR.TEMT = 1 (transmitter empty).  */
@@ -887,7 +885,7 @@ static void serial_reset(void *opaque)
 
 void serial_realize_core(SerialState *s, Error **errp)
 {
-    if (!s->chr.chr) {
+    if (!qemu_chr_fe_get_driver(&s->chr)) {
         error_setg(errp, "Can't create serial device, empty char device");
         return;
     }
@@ -897,8 +895,8 @@ void serial_realize_core(SerialState *s, Error **errp)
     s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) 
fifo_timeout_int, s);
     qemu_register_reset(serial_reset, s);
 
-    qemu_chr_add_handlers(s->chr.chr, serial_can_receive1, serial_receive1,
-                          serial_event, s);
+    qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1,
+                             serial_event, s, NULL);
     fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH);
     fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH);
     serial_reset(s);
@@ -906,7 +904,7 @@ void serial_realize_core(SerialState *s, Error **errp)
 
 void serial_exit_core(SerialState *s)
 {
-    qemu_chr_add_handlers(s->chr.chr, NULL, NULL, NULL, NULL);
+    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL);
     qemu_unregister_reset(serial_reset, s);
 }
 
diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
index c8b91bb..8d82986 100644
--- a/hw/char/sh_serial.c
+++ b/hw/char/sh_serial.c
@@ -110,11 +110,11 @@ static void sh_serial_write(void *opaque, hwaddr offs,
         }
         return;
     case 0x0c: /* FTDR / TDR */
-        if (s->chr.chr) {
+        if (qemu_chr_fe_get_driver(&s->chr)) {
             ch = val;
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
        }
        s->dr = val;
        s->flags &= ~SH_SERIAL_FLAG_TDE;
@@ -399,8 +399,9 @@ void sh_serial_init(MemoryRegion *sysmem,
     if (chr) {
         qemu_chr_fe_claim_no_fail(chr);
         qemu_chr_fe_init(&s->chr, chr, &error_abort);
-        qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
-                             sh_serial_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1,
+                                 sh_serial_receive1,
+                                 sh_serial_event, s, NULL);
     }
 
     s->eri = eri_source;
diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c
index 7afc6a5..8d39d40 100644
--- a/hw/char/spapr_vty.c
+++ b/hw/char/spapr_vty.c
@@ -51,7 +51,7 @@ static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, 
int max)
         buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE];
     }
 
-    qemu_chr_fe_accept_input(dev->chardev.chr);
+    qemu_chr_fe_accept_input(&dev->chardev);
 
     return n;
 }
@@ -62,20 +62,20 @@ void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int 
len)
 
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(dev->chardev.chr, buf, len);
+    qemu_chr_fe_write_all(&dev->chardev, buf, len);
 }
 
 static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp)
 {
     VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev);
 
-    if (!dev->chardev.chr) {
+    if (!qemu_chr_fe_get_driver(&dev->chardev)) {
         error_setg(errp, "chardev property not set");
         return;
     }
 
-    qemu_chr_add_handlers(dev->chardev.chr, vty_can_receive,
-                          vty_receive, NULL, dev);
+    qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive,
+                             vty_receive, NULL, dev, NULL);
 }
 
 /* Forward declaration */
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 8619d10..03ccc52 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -98,7 +98,7 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr 
addr,
         retvalue = s->usart_sr;
         s->usart_sr &= ~USART_SR_TC;
         if (s->chr.chr) {
-            qemu_chr_fe_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(&s->chr);
         }
         return retvalue;
     case USART_DR:
@@ -106,7 +106,7 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr 
addr,
         s->usart_sr |= USART_SR_TXE;
         s->usart_sr &= ~USART_SR_RXNE;
         if (s->chr.chr) {
-            qemu_chr_fe_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(&s->chr);
         }
         qemu_set_irq(s->irq, 0);
         return s->usart_dr & 0x3FF;
@@ -155,7 +155,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
             if (s->chr.chr) {
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+                qemu_chr_fe_write_all(&s->chr, &ch, 1);
             }
             s->usart_sr |= USART_SR_TC;
             s->usart_sr &= ~USART_SR_TXE;
@@ -213,8 +213,8 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error 
**errp)
     STM32F2XXUsartState *s = STM32F2XX_USART(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, stm32f2xx_usart_can_receive,
-                              stm32f2xx_usart_receive, NULL, s);
+        qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive,
+                                 stm32f2xx_usart_receive, NULL, s, NULL);
     }
 }
 
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 93acbec..135f589 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -49,12 +49,12 @@ static ssize_t flush_buf(VirtIOSerialPort *port,
     VirtConsole *vcon = VIRTIO_CONSOLE(port);
     ssize_t ret;
 
-    if (!vcon->chr.chr) {
+    if (!qemu_chr_fe_get_driver(&vcon->chr)) {
         /* If there's no backend, we can just say we consumed all data. */
         return len;
     }
 
-    ret = qemu_chr_fe_write(vcon->chr.chr, buf, len);
+    ret = qemu_chr_fe_write(&vcon->chr, buf, len);
     trace_virtio_console_flush_buf(port->id, len, ret);
 
     if (ret < len) {
@@ -92,8 +92,8 @@ static ssize_t flush_buf(VirtIOSerialPort *port,
         if (!k->is_console) {
             virtio_serial_throttle_port(port, true);
             if (!vcon->watch) {
-                vcon->watch = qemu_chr_fe_add_watch(vcon->chr.chr,
-                                                    G_IO_OUT | G_IO_HUP,
+                vcon->watch = qemu_chr_fe_add_watch(&vcon->chr,
+                                                    G_IO_OUT|G_IO_HUP,
                                                     chr_write_unblocked, vcon);
             }
         }
@@ -109,7 +109,7 @@ static void set_guest_connected(VirtIOSerialPort *port, int 
guest_connected)
     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
     if (vcon->chr.chr && !k->is_console) {
-        qemu_chr_fe_set_open(vcon->chr.chr, guest_connected);
+        qemu_chr_fe_set_open(&vcon->chr, guest_connected);
     }
 
     if (dev->id) {
@@ -123,7 +123,7 @@ static void guest_writable(VirtIOSerialPort *port)
     VirtConsole *vcon = VIRTIO_CONSOLE(port);
 
     if (vcon->chr.chr) {
-        qemu_chr_fe_accept_input(vcon->chr.chr);
+        qemu_chr_fe_accept_input(&vcon->chr);
     }
 }
 
@@ -170,6 +170,7 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
     VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
     VirtConsole *vcon = VIRTIO_CONSOLE(dev);
     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev);
+    CharDriverState *chr = qemu_chr_fe_get_driver(&vcon->chr);
 
     if (port->id == 0 && !k->is_console) {
         error_setg(errp, "Port number 0 on virtio-serial devices reserved "
@@ -177,7 +178,7 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
         return;
     }
 
-    if (vcon->chr.chr) {
+    if (chr) {
         /*
          * For consoles we don't block guest data transfer just
          * because nothing is connected - we'll just let it go
@@ -188,14 +189,14 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
          * trigger open/close of the device
          */
         if (k->is_console) {
-            vcon->chr.chr->explicit_fe_open = 0;
-            qemu_chr_add_handlers(vcon->chr.chr, chr_can_read, chr_read,
-                                  NULL, vcon);
+            chr->explicit_fe_open = 0;
+            qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
+                                     NULL, vcon, NULL);
             virtio_serial_open(port);
         } else {
-            vcon->chr.chr->explicit_fe_open = 1;
-            qemu_chr_add_handlers(vcon->chr.chr, chr_can_read, chr_read,
-                                  chr_event, vcon);
+            chr->explicit_fe_open = 1;
+            qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
+                                     chr_event, vcon, NULL);
         }
     }
 }
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index c1d36dc..5e5aca1 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -23,6 +23,7 @@
 #include <sys/select.h>
 #include <termios.h>
 
+#include "qapi/error.h"
 #include "hw/hw.h"
 #include "sysemu/char.h"
 #include "hw/xen/xen_backend.h"
@@ -149,8 +150,8 @@ static void xencons_send(struct XenConsole *con)
     ssize_t len, size;
 
     size = con->buffer.size - con->buffer.consumed;
-    if (con->chr.chr) {
-        len = qemu_chr_fe_write(con->chr.chr,
+    if (qemu_chr_fe_get_driver(&con->chr)) {
+        len = qemu_chr_fe_write(&con->chr,
                                 con->buffer.data + con->buffer.consumed,
                                 size);
     } else {
@@ -209,7 +210,8 @@ static int con_init(struct XenDevice *xendev)
                          qemu_chr_new(label, output), &error_abort);
     }
 
-    xenstore_store_pv_console_info(con->xendev.dev, con->chr.chr);
+    xenstore_store_pv_console_info(con->xendev.dev,
+                                   qemu_chr_fe_get_driver(&con->chr));
 
 out:
     g_free(type);
@@ -244,8 +246,8 @@ static int con_initialise(struct XenDevice *xendev)
     xen_be_bind_evtchn(&con->xendev);
     if (con->chr.chr) {
         if (qemu_chr_fe_claim(con->chr.chr) == 0) {
-            qemu_chr_add_handlers(con->chr.chr, xencons_can_receive,
-                                  xencons_receive, NULL, con);
+            qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
+                                     xencons_receive, NULL, con, NULL);
         } else {
             xen_be_printf(xendev, 0,
                           "xen_console_init error chardev %s already used\n",
@@ -267,7 +269,7 @@ static void con_disconnect(struct XenDevice *xendev)
     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
 
     if (con->chr.chr) {
-        qemu_chr_add_handlers(con->chr.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&con->chr, NULL, NULL, NULL, NULL, NULL);
         qemu_chr_fe_release(con->chr.chr);
     }
     xen_be_unbind_evtchn(&con->xendev);
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
index 185c63b..d6df643 100644
--- a/hw/char/xilinx_uartlite.c
+++ b/hw/char/xilinx_uartlite.c
@@ -107,7 +107,7 @@ uart_read(void *opaque, hwaddr addr, unsigned int size)
                 s->rx_fifo_len--;
             uart_update_status(s);
             uart_update_irq(s);
-            qemu_chr_fe_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(&s->chr);
             break;
 
         default:
@@ -146,7 +146,7 @@ uart_write(void *opaque, hwaddr addr,
             if (s->chr.chr) {
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+                qemu_chr_fe_write_all(&s->chr, &ch, 1);
             }
             s->regs[addr] = value;
 
@@ -214,7 +214,8 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error 
**errp)
     XilinxUARTLite *s = XILINX_UARTLITE(dev);
 
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, uart_can_rx, uart_rx, uart_event, s);
+        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                                 uart_event, s, NULL);
     }
 }
 
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index af9b6f3..5530870 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -105,7 +105,7 @@ static void continue_send(IPMIBmcExtern *ibe)
         goto check_reset;
     }
  send:
-    ret = qemu_chr_fe_write(ibe->chr.chr, ibe->outbuf + ibe->outpos,
+    ret = qemu_chr_fe_write(&ibe->chr, ibe->outbuf + ibe->outpos,
                             ibe->outlen - ibe->outpos);
     if (ret > 0) {
         ibe->outpos += ret;
@@ -442,12 +442,13 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, 
Error **errp)
 {
     IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
 
-    if (!ibe->chr.chr) {
+    if (!qemu_chr_fe_get_driver(&ibe->chr)) {
         error_setg(errp, "IPMI external bmc requires chardev attribute");
         return;
     }
 
-    qemu_chr_add_handlers(ibe->chr.chr, can_receive, receive, chr_event, ibe);
+    qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
+                             chr_event, ibe, NULL);
 }
 
 static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index d5601b1..273ec6d 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -125,9 +125,9 @@ static void malta_fpga_update_display(void *opaque)
     }
     leds_text[8] = '\0';
 
-    qemu_chr_fe_printf(s->display.chr, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n",
+    qemu_chr_fe_printf(&s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n",
                        leds_text);
-    qemu_chr_fe_printf(s->display.chr, "\n\n\n\n|\e[31m%-8.8s\e[00m|",
+    qemu_chr_fe_printf(&s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|",
                        s->display_text);
 }
 
@@ -538,15 +538,15 @@ static void malta_fgpa_display_event(void *opaque, int 
event)
     MaltaFPGAState *s = opaque;
 
     if (event == CHR_EVENT_OPENED && !s->display_inited) {
-        qemu_chr_fe_printf(s->display.chr, "\e[HMalta LEDBAR\r\n");
-        qemu_chr_fe_printf(s->display.chr, "+--------+\r\n");
-        qemu_chr_fe_printf(s->display.chr, "+        +\r\n");
-        qemu_chr_fe_printf(s->display.chr, "+--------+\r\n");
-        qemu_chr_fe_printf(s->display.chr, "\n");
-        qemu_chr_fe_printf(s->display.chr, "Malta ASCII\r\n");
-        qemu_chr_fe_printf(s->display.chr, "+--------+\r\n");
-        qemu_chr_fe_printf(s->display.chr, "+        +\r\n");
-        qemu_chr_fe_printf(s->display.chr, "+--------+\r\n");
+        qemu_chr_fe_printf(&s->display, "\e[HMalta LEDBAR\r\n");
+        qemu_chr_fe_printf(&s->display, "+--------+\r\n");
+        qemu_chr_fe_printf(&s->display, "+        +\r\n");
+        qemu_chr_fe_printf(&s->display, "+--------+\r\n");
+        qemu_chr_fe_printf(&s->display, "\n");
+        qemu_chr_fe_printf(&s->display, "Malta ASCII\r\n");
+        qemu_chr_fe_printf(&s->display, "+--------+\r\n");
+        qemu_chr_fe_printf(&s->display, "+        +\r\n");
+        qemu_chr_fe_printf(&s->display, "+--------+\r\n");
         s->display_inited = true;
     }
 }
@@ -570,9 +570,9 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion 
*address_space,
     memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi);
 
     chr = qemu_chr_new("fpga", "vc:320x200");
-    qemu_chr_fe_init(&s->display, chr, &error_abort);
-    qemu_chr_add_handlers(s->display.chr, NULL, NULL,
-                          malta_fgpa_display_event, s);
+    qemu_chr_fe_init(&s->display, chr, NULL);
+    qemu_chr_fe_set_handlers(&s->display, NULL, NULL,
+                             malta_fgpa_display_event, s, NULL);
 
     s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq,
                              230400, uart_chr, DEVICE_NATIVE_ENDIAN);
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index efca8b0..bb70704 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -627,7 +627,7 @@ static void ivshmem_read(void *opaque, const uint8_t *buf, 
int size)
     msg = le64_to_cpu(s->msg_buf);
     s->msg_buffered_bytes = 0;
 
-    fd = qemu_chr_fe_get_msgfd(s->server_chr.chr);
+    fd = qemu_chr_fe_get_msgfd(&s->server_chr);
 
     process_msg(s, msg, fd, &err);
     if (err) {
@@ -642,8 +642,8 @@ static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, 
Error **errp)
 
     n = 0;
     do {
-        ret = qemu_chr_fe_read_all(s->server_chr.chr, (uint8_t *)&msg + n,
-                                 sizeof(msg) - n);
+        ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n,
+                                   sizeof(msg) - n);
         if (ret < 0 && ret != -EINTR) {
             error_setg_errno(errp, -ret, "read from server failed");
             return INT64_MIN;
@@ -651,7 +651,7 @@ static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, 
Error **errp)
         n += ret;
     } while (n < sizeof(msg));
 
-    *pfd = qemu_chr_fe_get_msgfd(s->server_chr.chr);
+    *pfd = qemu_chr_fe_get_msgfd(&s->server_chr);
     return msg;
 }
 
@@ -868,10 +868,11 @@ static void ivshmem_common_realize(PCIDevice *dev, Error 
**errp)
         s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem,
                                                          &error_abort);
     } else {
-        assert(s->server_chr.chr);
+        CharDriverState *chr = qemu_chr_fe_get_driver(&s->server_chr);
+        assert(chr);
 
         IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
-                        s->server_chr.chr->filename);
+                        chr->filename);
 
         /* we allocate enough space for 16 peers and grow as needed */
         resize_peers(s, 16);
@@ -893,8 +894,8 @@ static void ivshmem_common_realize(PCIDevice *dev, Error 
**errp)
             return;
         }
 
-        qemu_chr_add_handlers(s->server_chr.chr, ivshmem_can_receive,
-                              ivshmem_read, NULL, s);
+        qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive,
+                                 ivshmem_read, NULL, s, NULL);
 
         if (ivshmem_setup_interrupts(s) < 0) {
             error_setg(errp, "failed to initialize interrupts");
@@ -1121,7 +1122,7 @@ static void ivshmem_doorbell_realize(PCIDevice *dev, 
Error **errp)
 {
     IVShmemState *s = IVSHMEM_COMMON(dev);
 
-    if (!s->server_chr.chr) {
+    if (!qemu_chr_fe_get_driver(&s->server_chr)) {
         error_setg(errp, "You must specify a 'chardev'");
         return;
     }
@@ -1250,7 +1251,7 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp)
                      " or ivshmem-doorbell instead");
     }
 
-    if (!!s->server_chr.chr + !!s->shmobj != 1) {
+    if (!!qemu_chr_fe_get_driver(&s->server_chr) + !!s->shmobj != 1) {
         error_setg(errp, "You must specify either 'shm' or 'chardev'");
         return;
     }
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index ebe3942..a8c8684 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -77,9 +77,9 @@ static void ccid_card_vscard_send_msg(PassthruState *s,
     scr_msg_header.length = htonl(length);
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(s->cs.chr, (uint8_t *)&scr_msg_header,
+    qemu_chr_fe_write_all(&s->cs, (uint8_t *)&scr_msg_header,
                           sizeof(VSCMsgHeader));
-    qemu_chr_fe_write_all(s->cs.chr, payload, length);
+    qemu_chr_fe_write_all(&s->cs, payload, length);
 }
 
 static void ccid_card_vscard_send_apdu(PassthruState *s,
@@ -264,7 +264,9 @@ static void ccid_card_vscard_handle_message(PassthruState 
*card,
 
 static void ccid_card_vscard_drop_connection(PassthruState *card)
 {
-    qemu_chr_delete(card->cs.chr);
+    CharDriverState *chr = qemu_chr_fe_get_driver(&card->cs);
+
+    qemu_chr_delete(chr);
     card->vscard_in_pos = card->vscard_in_hdr = 0;
 }
 
@@ -324,7 +326,7 @@ static void passthru_apdu_from_guest(
 {
     PassthruState *card = PASSTHRU_CCID_CARD(base);
 
-    if (!card->cs.chr) {
+    if (!qemu_chr_fe_get_driver(&card->cs)) {
         printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
         return;
     }
@@ -345,12 +347,12 @@ static int passthru_initfn(CCIDCardState *base)
 
     card->vscard_in_pos = 0;
     card->vscard_in_hdr = 0;
-    if (card->cs.chr) {
+    if (qemu_chr_fe_get_driver(&card->cs)) {
         DPRINTF(card, D_INFO, "initing chardev\n");
-        qemu_chr_add_handlers(card->cs.chr,
+        qemu_chr_fe_set_handlers(&card->cs,
             ccid_card_vscard_can_read,
             ccid_card_vscard_read,
-            ccid_card_vscard_event, card);
+            ccid_card_vscard_event, card, NULL);
         ccid_card_vscard_send_init(card);
     } else {
         error_report("missing chardev");
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index b8774b4..a69b9a3 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -209,7 +209,7 @@ static uint8_t usb_get_modem_lines(USBSerialState *s)
     int flags;
     uint8_t ret;
 
-    if (qemu_chr_fe_ioctl(s->cs.chr,
+    if (qemu_chr_fe_ioctl(&s->cs,
                           CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
         return FTDI_CTS|FTDI_DSR|FTDI_RLSD;
     }
@@ -262,7 +262,7 @@ static void usb_serial_handle_control(USBDevice *dev, 
USBPacket *p,
     case DeviceOutVendor | FTDI_SET_MDM_CTRL:
     {
         static int flags;
-        qemu_chr_fe_ioctl(s->cs.chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
+        qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
         if (value & FTDI_SET_RTS) {
             if (value & FTDI_RTS)
                 flags |= CHR_TIOCM_RTS;
@@ -275,7 +275,7 @@ static void usb_serial_handle_control(USBDevice *dev, 
USBPacket *p,
             else
                 flags &= ~CHR_TIOCM_DTR;
         }
-        qemu_chr_fe_ioctl(s->cs.chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
+        qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
         break;
     }
     case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
@@ -294,7 +294,7 @@ static void usb_serial_handle_control(USBDevice *dev, 
USBPacket *p,
             divisor = 1;
 
         s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
-        qemu_chr_fe_ioctl(s->cs.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+        qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
         break;
     }
     case DeviceOutVendor | FTDI_SET_DATA:
@@ -323,7 +323,7 @@ static void usb_serial_handle_control(USBDevice *dev, 
USBPacket *p,
                 DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
                 goto fail;
         }
-        qemu_chr_fe_ioctl(s->cs.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+        qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
         /* TODO: TX ON/OFF */
         break;
     case DeviceInVendor | FTDI_GET_MDM_ST:
@@ -370,7 +370,7 @@ static void usb_serial_handle_data(USBDevice *dev, 
USBPacket *p)
             iov = p->iov.iov + i;
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->cs.chr, iov->iov_base, iov->iov_len);
+            qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len);
         }
         p->actual_length = p->iov.size;
         break;
@@ -485,12 +485,13 @@ static void usb_serial_realize(USBDevice *dev, Error 
**errp)
 {
     USBSerialState *s = USB_SERIAL_DEV(dev);
     Error *local_err = NULL;
+    CharDriverState *chr = qemu_chr_fe_get_driver(&s->cs);
 
     usb_desc_create_serial(dev);
     usb_desc_init(dev);
     dev->auto_attach = 0;
 
-    if (!s->cs.chr) {
+    if (!chr) {
         error_setg(errp, "Property chardev is required");
         return;
     }
@@ -501,11 +502,11 @@ static void usb_serial_realize(USBDevice *dev, Error 
**errp)
         return;
     }
 
-    qemu_chr_add_handlers(s->cs.chr, usb_serial_can_read, usb_serial_read,
-                          usb_serial_event, s);
+    qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read,
+                             usb_serial_event, s, NULL);
     usb_serial_handle_reset(dev);
 
-    if (s->cs.chr->be_open && !dev->attached) {
+    if (chr->be_open && !dev->attached) {
         usb_device_attach(dev, &error_abort);
     }
 }
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 0b21804..0fb2e9a 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -283,9 +283,10 @@ static gboolean usbredir_write_unblocked(GIOChannel *chan, 
GIOCondition cond,
 static int usbredir_write(void *priv, uint8_t *data, int count)
 {
     USBRedirDevice *dev = priv;
+    CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs);
     int r;
 
-    if (!dev->cs.chr->be_open) {
+    if (!chr->be_open) {
         return 0;
     }
 
@@ -294,10 +295,10 @@ static int usbredir_write(void *priv, uint8_t *data, int 
count)
         return 0;
     }
 
-    r = qemu_chr_fe_write(dev->cs.chr, data, count);
+    r = qemu_chr_fe_write(&dev->cs, data, count);
     if (r < count) {
         if (!dev->watch) {
-            dev->watch = qemu_chr_fe_add_watch(dev->cs.chr, G_IO_OUT | 
G_IO_HUP,
+            dev->watch = qemu_chr_fe_add_watch(&dev->cs, G_IO_OUT | G_IO_HUP,
                                                usbredir_write_unblocked, dev);
         }
         if (r < 0) {
@@ -1375,7 +1376,7 @@ static void usbredir_realize(USBDevice *udev, Error 
**errp)
     USBRedirDevice *dev = USB_REDIRECT(udev);
     int i;
 
-    if (dev->cs.chr == NULL) {
+    if (!qemu_chr_fe_get_driver(&dev->cs)) {
         error_setg(errp, QERR_MISSING_PARAMETER, "chardev");
         return;
     }
@@ -1406,8 +1407,9 @@ static void usbredir_realize(USBDevice *udev, Error 
**errp)
     dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH;
 
     /* Let the backend know we are ready */
-    qemu_chr_add_handlers(dev->cs.chr, usbredir_chardev_can_read,
-                          usbredir_chardev_read, usbredir_chardev_event, dev);
+    qemu_chr_fe_set_handlers(&dev->cs, usbredir_chardev_can_read,
+                             usbredir_chardev_read, usbredir_chardev_event,
+                             dev, NULL);
 
     qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
 }
@@ -1426,8 +1428,10 @@ static void 
usbredir_cleanup_device_queues(USBRedirDevice *dev)
 static void usbredir_handle_destroy(USBDevice *udev)
 {
     USBRedirDevice *dev = USB_REDIRECT(udev);
+    CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs);
+
+    qemu_chr_delete(chr);
 
-    qemu_chr_delete(dev->cs.chr);
     dev->cs.chr = NULL;
     /* Note must be done after qemu_chr_close, as that causes a close event */
     qemu_bh_delete(dev->chardev_close_bh);
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index b57454a..7ee92b3 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -116,7 +116,7 @@ static bool ioeventfd_enabled(void)
 
 static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
 {
-    CharDriverState *chr = dev->opaque;
+    CharBackend *chr = dev->opaque;
     uint8_t *p = (uint8_t *) msg;
     int r, size = VHOST_USER_HDR_SIZE;
 
@@ -196,7 +196,7 @@ static bool vhost_user_one_time_request(VhostUserRequest 
request)
 static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
                             int *fds, int fd_num)
 {
-    CharDriverState *chr = dev->opaque;
+    CharBackend *chr = dev->opaque;
     int ret, size = VHOST_USER_HDR_SIZE + msg->size;
 
     /*
diff --git a/monitor.c b/monitor.c
index 40712f7..1c6f28f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -297,7 +297,7 @@ static void monitor_flush_locked(Monitor *mon)
     len = qstring_get_length(mon->outbuf);
 
     if (len && !mon->mux_out) {
-        rc = qemu_chr_fe_write(mon->chr.chr, (const uint8_t *) buf, len);
+        rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len);
         if ((rc < 0 && errno != EAGAIN) || (rc == len)) {
             /* all flushed or error */
             QDECREF(mon->outbuf);
@@ -312,7 +312,7 @@ static void monitor_flush_locked(Monitor *mon)
         }
         if (mon->out_watch == 0) {
             mon->out_watch =
-                qemu_chr_fe_add_watch(mon->chr.chr, G_IO_OUT | G_IO_HUP,
+                qemu_chr_fe_add_watch(&mon->chr, G_IO_OUT | G_IO_HUP,
                                       monitor_unblocked, mon);
         }
     }
@@ -583,7 +583,7 @@ static void monitor_data_init(Monitor *mon)
 static void monitor_data_destroy(Monitor *mon)
 {
     if (mon->chr.chr) {
-        qemu_chr_add_handlers(mon->chr.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&mon->chr, NULL, NULL, NULL, NULL, NULL);
     }
     if (monitor_is_qmp(mon)) {
         json_message_parser_destroy(&mon->qmp.parser);
@@ -1746,7 +1746,7 @@ void qmp_getfd(const char *fdname, Error **errp)
     mon_fd_t *monfd;
     int fd;
 
-    fd = qemu_chr_fe_get_msgfd(cur_mon->chr.chr);
+    fd = qemu_chr_fe_get_msgfd(&cur_mon->chr);
     if (fd == -1) {
         error_setg(errp, QERR_FD_NOT_SUPPLIED);
         return;
@@ -1871,7 +1871,7 @@ AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t 
fdset_id, bool has_opaque,
     Monitor *mon = cur_mon;
     AddfdInfo *fdinfo;
 
-    fd = qemu_chr_fe_get_msgfd(mon->chr.chr);
+    fd = qemu_chr_fe_get_msgfd(&mon->chr);
     if (fd == -1) {
         error_setg(errp, QERR_FD_NOT_SUPPLIED);
         goto error;
@@ -3989,13 +3989,13 @@ void monitor_init(CharDriverState *chr, int flags)
     }
 
     if (monitor_is_qmp(mon)) {
-        qemu_chr_add_handlers(chr, monitor_can_read, monitor_qmp_read,
-                              monitor_qmp_event, mon);
-        qemu_chr_fe_set_echo(chr, true);
+        qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read,
+                                 monitor_qmp_event, mon, NULL);
+        qemu_chr_fe_set_echo(&mon->chr, true);
         json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
     } else {
-        qemu_chr_add_handlers(chr, monitor_can_read, monitor_read,
-                              monitor_event, mon);
+        qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
+                                 monitor_event, mon, NULL);
     }
 
     qemu_mutex_lock(&monitor_lock);
diff --git a/net/colo-compare.c b/net/colo-compare.c
index b115465..63d92cb 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -101,7 +101,7 @@ enum {
     SECONDARY_IN,
 };
 
-static int compare_chr_send(CharDriverState *out,
+static int compare_chr_send(CharBackend *out,
                             const uint8_t *buf,
                             uint32_t size);
 
@@ -385,7 +385,7 @@ static void colo_compare_connection(void *opaque, void 
*user_data)
         }
 
         if (result) {
-            ret = compare_chr_send(s->chr_out.chr, pkt->data, pkt->size);
+            ret = compare_chr_send(&s->chr_out, pkt->data, pkt->size);
             if (ret < 0) {
                 error_report("colo_send_primary_packet failed");
             }
@@ -408,7 +408,7 @@ static void colo_compare_connection(void *opaque, void 
*user_data)
     }
 }
 
-static int compare_chr_send(CharDriverState *out,
+static int compare_chr_send(CharBackend *out,
                             const uint8_t *buf,
                             uint32_t size)
 {
@@ -451,7 +451,7 @@ static void compare_pri_chr_in(void *opaque, const uint8_t 
*buf, int size)
 
     ret = net_fill_rstate(&s->pri_rs, buf, size);
     if (ret == -1) {
-        qemu_chr_add_handlers(s->chr_pri_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, NULL, NULL);
         error_report("colo-compare primary_in error");
     }
 }
@@ -467,7 +467,7 @@ static void compare_sec_chr_in(void *opaque, const uint8_t 
*buf, int size)
 
     ret = net_fill_rstate(&s->sec_rs, buf, size);
     if (ret == -1) {
-        qemu_chr_add_handlers(s->chr_sec_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, NULL, NULL);
         error_report("colo-compare secondary_in error");
     }
 }
@@ -480,10 +480,10 @@ static void *colo_compare_thread(void *opaque)
 
     worker_context = g_main_context_new();
 
-    qemu_chr_add_handlers_full(s->chr_pri_in.chr, compare_chr_can_read,
-                          compare_pri_chr_in, NULL, s, worker_context);
-    qemu_chr_add_handlers_full(s->chr_sec_in.chr, compare_chr_can_read,
-                          compare_sec_chr_in, NULL, s, worker_context);
+    qemu_chr_fe_set_handlers(&s->chr_pri_in, compare_chr_can_read,
+                             compare_pri_chr_in, NULL, s, worker_context);
+    qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read,
+                             compare_sec_chr_in, NULL, s, worker_context);
 
     compare_loop = g_main_loop_new(worker_context, FALSE);
 
@@ -545,7 +545,7 @@ static void compare_pri_rs_finalize(SocketReadState *pri_rs)
 
     if (packet_enqueue(s, PRIMARY_IN)) {
         trace_colo_compare_main("primary: unsupported packet in");
-        compare_chr_send(s->chr_out.chr, pri_rs->buf, pri_rs->packet_len);
+        compare_chr_send(&s->chr_out, pri_rs->buf, pri_rs->packet_len);
     } else {
         /* compare connection */
         g_queue_foreach(&s->conn_list, colo_compare_connection, s);
@@ -626,6 +626,7 @@ static void check_old_packet_regular(void *opaque)
 static void colo_compare_complete(UserCreatable *uc, Error **errp)
 {
     CompareState *s = COLO_COMPARE(uc);
+    CharDriverState *chr;
     char thread_name[64];
     static int compare_id;
 
@@ -641,15 +642,18 @@ static void colo_compare_complete(UserCreatable *uc, 
Error **errp)
         return;
     }
 
-    if (find_and_check_chardev(&s->chr_pri_in.chr, s->pri_indev, errp)) {
+    if (find_and_check_chardev(&chr, s->pri_indev, errp) ||
+        !qemu_chr_fe_init(&s->chr_pri_in, chr, errp)) {
         return;
     }
 
-    if (find_and_check_chardev(&s->chr_sec_in.chr, s->sec_indev, errp)) {
+    if (find_and_check_chardev(&chr, s->sec_indev, errp) ||
+        !qemu_chr_fe_init(&s->chr_sec_in, chr, errp)) {
         return;
     }
 
-    if (find_and_check_chardev(&s->chr_out.chr, s->outdev, errp)) {
+    if (find_and_check_chardev(&chr, s->outdev, errp) ||
+        !qemu_chr_fe_init(&s->chr_out, chr, errp)) {
         return;
     }
 
@@ -704,11 +708,11 @@ static void colo_compare_finalize(Object *obj)
     CompareState *s = COLO_COMPARE(obj);
 
     if (s->chr_pri_in.chr) {
-        qemu_chr_add_handlers(s->chr_pri_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, NULL, NULL);
         qemu_chr_fe_release(s->chr_pri_in.chr);
     }
     if (s->chr_sec_in.chr) {
-        qemu_chr_add_handlers(s->chr_sec_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, NULL, NULL);
         qemu_chr_fe_release(s->chr_sec_in.chr);
     }
     if (s->chr_out.chr) {
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index 425e146..12d79cd 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -43,7 +43,7 @@ typedef struct MirrorState {
     SocketReadState rs;
 } MirrorState;
 
-static int filter_mirror_send(CharDriverState *chr_out,
+static int filter_mirror_send(CharBackend *chr_out,
                               const struct iovec *iov,
                               int iovcnt)
 {
@@ -110,7 +110,7 @@ static void redirector_chr_read(void *opaque, const uint8_t 
*buf, int size)
     ret = net_fill_rstate(&s->rs, buf, size);
 
     if (ret == -1) {
-        qemu_chr_add_handlers(s->chr_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, NULL, NULL);
     }
 }
 
@@ -121,7 +121,7 @@ static void redirector_chr_event(void *opaque, int event)
 
     switch (event) {
     case CHR_EVENT_CLOSED:
-        qemu_chr_add_handlers(s->chr_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, NULL, NULL);
         break;
     default:
         break;
@@ -138,7 +138,7 @@ static ssize_t filter_mirror_receive_iov(NetFilterState *nf,
     MirrorState *s = FILTER_MIRROR(nf);
     int ret;
 
-    ret = filter_mirror_send(s->chr_out.chr, iov, iovcnt);
+    ret = filter_mirror_send(&s->chr_out, iov, iovcnt);
     if (ret) {
         error_report("filter_mirror_send failed(%s)", strerror(-ret));
     }
@@ -160,8 +160,8 @@ static ssize_t filter_redirector_receive_iov(NetFilterState 
*nf,
     MirrorState *s = FILTER_REDIRECTOR(nf);
     int ret;
 
-    if (s->chr_out.chr) {
-        ret = filter_mirror_send(s->chr_out.chr, iov, iovcnt);
+    if (qemu_chr_fe_get_driver(&s->chr_out)) {
+        ret = filter_mirror_send(&s->chr_out, iov, iovcnt);
         if (ret) {
             error_report("filter_mirror_send failed(%s)", strerror(-ret));
         }
@@ -185,7 +185,7 @@ static void filter_redirector_cleanup(NetFilterState *nf)
     MirrorState *s = FILTER_REDIRECTOR(nf);
 
     if (s->chr_in.chr) {
-        qemu_chr_add_handlers(s->chr_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, NULL, NULL);
         qemu_chr_fe_release(s->chr_in.chr);
     }
     if (s->chr_out.chr) {
@@ -258,8 +258,10 @@ static void filter_redirector_setup(NetFilterState *nf, 
Error **errp)
         if (!qemu_chr_fe_init(&s->chr_in, chr, errp)) {
             return;
         }
-        qemu_chr_add_handlers(s->chr_in.chr, redirector_chr_can_read,
-                              redirector_chr_read, redirector_chr_event, nf);
+
+        qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read,
+                                 redirector_chr_read, redirector_chr_event,
+                                 nf, NULL);
     }
 
     if (s->outdev) {
diff --git a/net/slirp.c b/net/slirp.c
index 407e8aa..f9f6fc6 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -763,7 +763,8 @@ static int slirp_guestfwd(SlirpState *s, const char 
*config_str,
             return -1;
         }
 
-        if (slirp_add_exec(s->slirp, 3, fwd->hd.chr, &server, port) < 0) {
+        if (slirp_add_exec(s->slirp, 3, qemu_chr_fe_get_driver(&fwd->hd),
+                           &server, port) < 0) {
             error_report("conflicting/invalid host:port in guest forwarding "
                          "rule '%s'", config_str);
             g_free(fwd);
@@ -774,8 +775,8 @@ static int slirp_guestfwd(SlirpState *s, const char 
*config_str,
         fwd->slirp = s->slirp;
 
         qemu_chr_fe_claim_no_fail(fwd->hd.chr);
-        qemu_chr_add_handlers(fwd->hd.chr, guestfwd_can_read, guestfwd_read,
-                              NULL, fwd);
+        qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read,
+                                 NULL, fwd, NULL);
     }
     return 0;
 
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 4578247..8b7e98d 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -78,7 +78,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[])
         s = DO_UPCAST(VhostUserState, nc, ncs[i]);
 
         options.net_backend = ncs[i];
-        options.opaque      = s->chr.chr;
+        options.opaque      = &s->chr;
         options.busyloop_timeout = 0;
         net = vhost_net_init(&options);
         if (!net) {
@@ -151,7 +151,7 @@ static void vhost_user_cleanup(NetClientState *nc)
         s->vhost_net = NULL;
     }
     if (s->chr.chr) {
-        qemu_chr_add_handlers(s->chr.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL);
         qemu_chr_fe_release(s->chr.chr);
         s->chr.chr = NULL;
     }
@@ -187,7 +187,7 @@ static gboolean net_vhost_user_watch(GIOChannel *chan, 
GIOCondition cond,
 {
     VhostUserState *s = opaque;
 
-    qemu_chr_fe_disconnect(s->chr.chr);
+    qemu_chr_fe_disconnect(&s->chr);
 
     return FALSE;
 }
@@ -197,6 +197,7 @@ static void net_vhost_user_event(void *opaque, int event)
     const char *name = opaque;
     NetClientState *ncs[MAX_QUEUE_NUM];
     VhostUserState *s;
+    CharDriverState *chr;
     Error *err = NULL;
     int queues;
 
@@ -206,13 +207,14 @@ static void net_vhost_user_event(void *opaque, int event)
     assert(queues < MAX_QUEUE_NUM);
 
     s = DO_UPCAST(VhostUserState, nc, ncs[0]);
-    trace_vhost_user_event(s->chr.chr->label, event);
+    chr = qemu_chr_fe_get_driver(&s->chr);
+    trace_vhost_user_event(chr->label, event);
     switch (event) {
     case CHR_EVENT_OPENED:
-        s->watch = qemu_chr_fe_add_watch(s->chr.chr, G_IO_HUP,
+        s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
                                          net_vhost_user_watch, s);
         if (vhost_user_start(queues, ncs) < 0) {
-            qemu_chr_fe_disconnect(s->chr.chr);
+            qemu_chr_fe_disconnect(&s->chr);
             return;
         }
         qmp_set_link(name, true, &err);
@@ -255,6 +257,7 @@ static int net_vhost_user_init(NetClientState *peer, const 
char *device,
         nc->queue_index = i;
 
         s = DO_UPCAST(VhostUserState, nc, nc);
+
         if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
             error_report_err(err);
             return -1;
@@ -263,12 +266,12 @@ static int net_vhost_user_init(NetClientState *peer, 
const char *device,
 
     s = DO_UPCAST(VhostUserState, nc, nc0);
     do {
-        if (qemu_chr_wait_connected(chr, &err) < 0) {
+        if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
             error_report_err(err);
             return -1;
         }
-        qemu_chr_add_handlers(chr, NULL, NULL,
-                              net_vhost_user_event, nc0->name);
+        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
+                                 net_vhost_user_event, nc0->name, NULL);
     } while (!s->started);
 
     assert(s->vhost_net);
diff --git a/qemu-char.c b/qemu-char.c
index a7e8547..b10db09 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -268,8 +268,9 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, 
const uint8_t *buf, int
     return res;
 }
 
-int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
+int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 {
+    CharDriverState *s = be->chr;
     int ret;
 
     if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
@@ -296,7 +297,7 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t 
*buf, int len)
     return ret;
 }
 
-int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
+static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len)
 {
     int offset;
     int res;
@@ -320,8 +321,16 @@ int qemu_chr_fe_write_all(CharDriverState *s, const 
uint8_t *buf, int len)
     return offset;
 }
 
-int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
+int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
 {
+    CharDriverState *s = be->chr;
+
+    return qemu_chr_write_all(s, buf, len);
+}
+
+int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
+{
+    CharDriverState *s = be->chr;
     int offset = 0, counter = 10;
     int res;
 
@@ -365,8 +374,9 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, 
int len)
     return offset;
 }
 
-int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
+int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
 {
+    CharDriverState *s = be->chr;
     int res;
     if (!s->chr_ioctl || s->replay) {
         res = -ENOTSUP;
@@ -403,10 +413,11 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, 
int len)
     }
 }
 
-int qemu_chr_fe_get_msgfd(CharDriverState *s)
+int qemu_chr_fe_get_msgfd(CharBackend *be)
 {
+    CharDriverState *s = be->chr;
     int fd;
-    int res = (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
+    int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
     if (s->replay) {
         fprintf(stderr,
                 "Replay: get msgfd is not supported for serial devices yet\n");
@@ -415,13 +426,17 @@ int qemu_chr_fe_get_msgfd(CharDriverState *s)
     return res;
 }
 
-int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len)
+int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
 {
+    CharDriverState *s = be->chr;
+
     return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
 }
 
-int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num)
+int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
 {
+    CharDriverState *s = be->chr;
+
     return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1;
 }
 
@@ -430,14 +445,16 @@ int qemu_chr_add_client(CharDriverState *s, int fd)
     return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
 }
 
-void qemu_chr_fe_accept_input(CharDriverState *s)
+void qemu_chr_fe_accept_input(CharBackend *be)
 {
+    CharDriverState *s = be->chr;
+
     if (s->chr_accept_input)
         s->chr_accept_input(s);
     qemu_notify_event();
 }
 
-void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...)
+void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
 {
     char buf[READ_BUF_LEN];
     va_list ap;
@@ -445,21 +462,21 @@ void qemu_chr_fe_printf(CharDriverState *s, const char 
*fmt, ...)
     vsnprintf(buf, sizeof(buf), fmt, ap);
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(s, (uint8_t *)buf, strlen(buf));
+    qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
     va_end(ap);
 }
 
 static void remove_fd_in_watch(CharDriverState *chr);
 
 static void
-qemu_chr_set_handlers(CharDriverState *s,
+qemu_chr_set_handlers(CharBackend *be,
                       IOCanReadHandler *fd_can_read,
                       IOReadHandler *fd_read,
                       IOEventHandler *fd_event,
                       void *opaque,
-                      GMainContext *context,
-                      int tag)
+                      GMainContext *context)
 {
+    CharDriverState *s = be->chr;
     int fe_open;
 
     if (!opaque && !fd_can_read && !fd_read && !fd_event) {
@@ -473,17 +490,20 @@ qemu_chr_set_handlers(CharDriverState *s,
     s->chr_event = fd_event;
     s->handler_opaque = opaque;
     if (s->chr_update_read_handler) {
-        s->chr_update_read_handler(s, context, tag);
+        s->chr_update_read_handler(s, context, be->tag);
     }
 
     if (!s->explicit_fe_open) {
-        qemu_chr_fe_set_open(s, fe_open);
+        qemu_chr_fe_set_open(be, fe_open);
     }
 
     /* We're connecting to an already opened device, so let's make sure we
        also get the open event */
-    if (fe_open && s->be_open) {
-        qemu_chr_be_generic_open(s);
+    if (fe_open) {
+        qemu_chr_fe_take_focus(be);
+        if (s->be_open) {
+            qemu_chr_be_generic_open(s);
+        }
     }
 }
 
@@ -491,38 +511,6 @@ static int mux_chr_new_handler_tag(CharDriverState *chr, 
Error **errp);
 static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context);
 static void mux_set_focus(MuxDriver *d, int focus);
 
-void qemu_chr_add_handlers_full(CharDriverState *s,
-                                IOCanReadHandler *fd_can_read,
-                                IOReadHandler *fd_read,
-                                IOEventHandler *fd_event,
-                                void *opaque,
-                                GMainContext *context)
-{
-    int tag = 0;
-
-    if (s->is_mux) {
-        tag = mux_chr_new_handler_tag(s, &error_abort);
-        mux_chr_set_handlers(s, context);
-    }
-
-    qemu_chr_set_handlers(s, fd_can_read, fd_read,
-                          fd_event, opaque, context, tag);
-
-    if (s->is_mux) {
-        mux_set_focus(s->opaque, tag);
-    }
-}
-
-void qemu_chr_add_handlers(CharDriverState *s,
-                           IOCanReadHandler *fd_can_read,
-                           IOReadHandler *fd_read,
-                           IOEventHandler *fd_event,
-                           void *opaque)
-{
-    qemu_chr_add_handlers_full(s, fd_can_read, fd_read,
-                               fd_event, opaque, NULL);
-}
-
 static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     return len;
@@ -554,7 +542,6 @@ struct MuxDriver {
     IOReadHandler *chr_read[MAX_MUX];
     IOEventHandler *chr_event[MAX_MUX];
     void *ext_opaque[MAX_MUX];
-    CharDriverState *drv;
     CharBackend chr;
     int focus;
     int mux_cnt;
@@ -579,7 +566,7 @@ static int mux_chr_write(CharDriverState *chr, const 
uint8_t *buf, int len)
     MuxDriver *d = chr->opaque;
     int ret;
     if (!d->timestamps) {
-        ret = qemu_chr_fe_write(d->drv, buf, len);
+        ret = qemu_chr_fe_write(&d->chr, buf, len);
     } else {
         int i;
 
@@ -603,10 +590,11 @@ static int mux_chr_write(CharDriverState *chr, const 
uint8_t *buf, int len)
                          (int)(ti % 1000));
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(d->drv, (uint8_t *)buf1, strlen(buf1));
+                qemu_chr_fe_write_all(&d->chr,
+                                      (uint8_t *)buf1, strlen(buf1));
                 d->linestart = 0;
             }
-            ret += qemu_chr_fe_write(d->drv, buf+i, 1);
+            ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
             if (buf[i] == '\n') {
                 d->linestart = 1;
             }
@@ -643,13 +631,13 @@ static void mux_print_help(CharDriverState *chr)
     }
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
+    qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
     for (i = 0; mux_help[i] != NULL; i++) {
         for (j=0; mux_help[i][j] != '\0'; j++) {
             if (mux_help[i][j] == '%')
-                qemu_chr_fe_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
+                qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
             else
-                qemu_chr_fe_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
+                qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
         }
     }
 }
@@ -674,7 +662,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver 
*d, int ch)
         case 'x':
             {
                  const char *term =  "QEMU: Terminated\n\r";
-                 qemu_chr_fe_write_all(chr, (uint8_t *)term, strlen(term));
+                 qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
                  exit(0);
                  break;
             }
@@ -819,7 +807,9 @@ static Notifier muxes_realize_notify = {
 static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
 {
     MuxDriver *d = s->opaque;
-    return d->drv->chr_add_watch(d->drv, cond);
+    CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
+
+    return chr->chr_add_watch(chr, cond);
 }
 
 static void mux_chr_close(struct CharDriverState *chr)
@@ -889,7 +879,6 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
     d = g_new0(MuxDriver, 1);
 
     chr->opaque = d;
-    d->drv = drv;
     d->focus = -1;
     chr->chr_close = mux_chr_close;
     chr->chr_write = mux_chr_write;
@@ -905,7 +894,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
      */
     chr->explicit_be_open = muxes_realized ? 0 : 1;
     chr->is_mux = 1;
-    if (!qemu_chr_fe_init(&d->chr, d->drv, errp)) {
+    if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
         qemu_chr_free(chr);
         return NULL;
     }
@@ -953,8 +942,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         return;
     }
 
-    qemu_chr_set_handlers(b->chr, fd_can_read, fd_read,
-                          fd_event, opaque, context, b->tag);
+    qemu_chr_set_handlers(b, fd_can_read, fd_read,
+                          fd_event, opaque, context);
 
     if (b->chr->is_mux) {
         mux_chr_set_handlers(b->chr, context);
@@ -1374,7 +1363,7 @@ static CharDriverState *qemu_chr_open_stdio(const char 
*id,
     if (opts->has_signal) {
         stdio_allow_signal = opts->signal;
     }
-    qemu_chr_fe_set_echo(chr, false);
+    qemu_chr_set_echo_stdio(chr, false);
 
     return chr;
 }
@@ -2622,7 +2611,7 @@ static CharDriverState *qemu_chr_open_stdio(const char 
*id,
     SetConsoleMode(stdio->hStdIn, dwMode);
 
     chr->chr_set_echo = qemu_chr_set_echo_win_stdio;
-    qemu_chr_fe_set_echo(chr, false);
+    qemu_chr_set_echo_win_stdio(chr, false);
 
     return chr;
 
@@ -2637,7 +2626,6 @@ err1:
 }
 #endif /* !_WIN32 */
 
-
 /***********************************************************/
 /* UDP Net console */
 
@@ -3358,7 +3346,7 @@ static int tcp_chr_wait_connected(CharDriverState *chr, 
Error **errp)
     return 0;
 }
 
-int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
+static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
 {
     if (chr->chr_wait_connected) {
         return chr->chr_wait_connected(chr, errp);
@@ -3367,6 +3355,11 @@ int qemu_chr_wait_connected(CharDriverState *chr, Error 
**errp)
     return 0;
 }
 
+int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
+{
+    return qemu_chr_wait_connected(be->chr, errp);
+}
+
 static void tcp_chr_close(CharDriverState *chr)
 {
     TCPCharDriver *s = chr->opaque;
@@ -4165,15 +4158,19 @@ CharDriverState *qemu_chr_new(const char *label, const 
char *filename)
     return chr;
 }
 
-void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
+void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 {
+    CharDriverState *chr = be->chr;
+
     if (chr->chr_set_echo) {
         chr->chr_set_echo(chr, echo);
     }
 }
 
-void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open)
+void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 {
+    CharDriverState *chr = be->chr;
+
     if (chr->fe_open == fe_open) {
         return;
     }
@@ -4183,16 +4180,19 @@ void qemu_chr_fe_set_open(struct CharDriverState *chr, 
int fe_open)
     }
 }
 
-void qemu_chr_fe_event(struct CharDriverState *chr, int event)
+void qemu_chr_fe_event(CharBackend *be, int event)
 {
+    CharDriverState *chr = be->chr;
+
     if (chr->chr_fe_event) {
         chr->chr_fe_event(chr, event);
     }
 }
 
-guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
+guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
                             GIOFunc func, void *user_data)
 {
+    CharDriverState *s = be->chr;
     GSource *src;
     guint tag;
 
@@ -4235,8 +4235,10 @@ void qemu_chr_fe_release(CharDriverState *s)
     s->avail_connections++;
 }
 
-void qemu_chr_fe_disconnect(CharDriverState *chr)
+void qemu_chr_fe_disconnect(CharBackend *be)
 {
+    CharDriverState *chr = be->chr;
+
     if (chr->chr_disconnect) {
         chr->chr_disconnect(chr);
     }
diff --git a/qtest.c b/qtest.c
index 3fb3c11..1a6c8b1 100644
--- a/qtest.c
+++ b/qtest.c
@@ -190,7 +190,7 @@ static void qtest_get_time(qemu_timeval *tv)
     }
 }
 
-static void qtest_send_prefix(CharDriverState *chr)
+static void qtest_send_prefix(CharBackend *chr)
 {
     qemu_timeval tv;
 
@@ -218,7 +218,7 @@ static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char 
*fmt, ...)
     va_end(ap);
 }
 
-static void do_qtest_send(CharDriverState *chr, const char *str, size_t len)
+static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
 {
     qemu_chr_fe_write_all(chr, (uint8_t *)str, len);
     if (qtest_log_fp && qtest_opened) {
@@ -226,12 +226,12 @@ static void do_qtest_send(CharDriverState *chr, const 
char *str, size_t len)
     }
 }
 
-static void qtest_send(CharDriverState *chr, const char *str)
+static void qtest_send(CharBackend *chr, const char *str)
 {
     do_qtest_send(chr, str, strlen(str));
 }
 
-static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharDriverState *chr,
+static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr,
                                            const char *fmt, ...)
 {
     va_list ap;
@@ -249,7 +249,7 @@ static void qtest_irq_handler(void *opaque, int n, int 
level)
     qemu_set_irq(old_irq, level);
 
     if (irq_levels[n] != level) {
-        CharDriverState *chr = qtest_chr.chr;
+        CharBackend *chr = &qtest_chr;
         irq_levels[n] = level;
         qtest_send_prefix(chr);
         qtest_sendf(chr, "IRQ %s %d\n",
@@ -257,7 +257,7 @@ static void qtest_irq_handler(void *opaque, int n, int 
level)
     }
 }
 
-static void qtest_process_command(CharDriverState *chr, gchar **words)
+static void qtest_process_command(CharBackend *chr, gchar **words)
 {
     const gchar *command;
 
@@ -585,7 +585,7 @@ static void qtest_process_command(CharDriverState *chr, 
gchar **words)
     }
 }
 
-static void qtest_process_inbuf(CharDriverState *chr, GString *inbuf)
+static void qtest_process_inbuf(CharBackend *chr, GString *inbuf)
 {
     char *end;
 
@@ -609,7 +609,7 @@ static void qtest_process_inbuf(CharDriverState *chr, 
GString *inbuf)
 
 static void qtest_read(void *opaque, const uint8_t *buf, int size)
 {
-    CharDriverState *chr = opaque;
+    CharBackend *chr = opaque;
 
     g_string_append_len(inbuf, (const gchar *)buf, size);
     qtest_process_inbuf(chr, inbuf);
@@ -686,11 +686,12 @@ void qtest_init(const char *qtest_chrdev, const char 
*qtest_log, Error **errp)
         qtest_log_fp = stderr;
     }
 
-    qemu_chr_add_handlers(chr, qtest_can_read, qtest_read, qtest_event, chr);
-    qemu_chr_fe_set_echo(chr, true);
+    qemu_chr_fe_init(&qtest_chr, chr, errp);
+    qemu_chr_fe_set_handlers(&qtest_chr, qtest_can_read, qtest_read,
+                             qtest_event, &qtest_chr, NULL);
+    qemu_chr_fe_set_echo(&qtest_chr, true);
 
     inbuf = g_string_new("");
-    qemu_chr_fe_init(&qtest_chr, chr, errp);
 }
 
 bool qtest_driver(void)
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 6e5383d..2d86de1 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -11,6 +11,7 @@
 #include "qemu/osdep.h"
 
 #include "libqtest.h"
+#include "qapi/error.h"
 #include "qemu/option.h"
 #include "qemu/range.h"
 #include "qemu/sockets.h"
@@ -268,7 +269,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int 
size)
     int fd;
 
     if (s->test_fail) {
-        qemu_chr_fe_disconnect(chr->chr);
+        qemu_chr_fe_disconnect(chr);
         /* now switch to non-failure */
         s->test_fail = false;
     }
@@ -283,7 +284,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int 
size)
 
     if (msg.size) {
         p += VHOST_USER_HDR_SIZE;
-        size = qemu_chr_fe_read_all(chr->chr, p, msg.size);
+        size = qemu_chr_fe_read_all(chr, p, msg.size);
         if (size != msg.size) {
             g_test_message("Wrong message size received %d != %d\n",
                            size, msg.size);
@@ -306,14 +307,14 @@ static void chr_read(void *opaque, const uint8_t *buf, 
int size)
             s->test_flags = TEST_FLAGS_END;
         }
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE + msg.size);
+        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
         break;
 
     case VHOST_USER_SET_FEATURES:
        g_assert_cmpint(msg.payload.u64 & (0x1ULL << 
VHOST_USER_F_PROTOCOL_FEATURES),
                        !=, 0ULL);
         if (s->test_flags == TEST_FLAGS_DISCONNECT) {
-            qemu_chr_fe_disconnect(chr->chr);
+            qemu_chr_fe_disconnect(chr);
             s->test_flags = TEST_FLAGS_BAD;
         }
         break;
@@ -327,7 +328,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int 
size)
             msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ;
         }
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE + msg.size);
+        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
         break;
 
     case VHOST_USER_GET_VRING_BASE:
@@ -336,7 +337,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int 
size)
         msg.size = sizeof(m.payload.state);
         msg.payload.state.num = 0;
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE + msg.size);
+        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
 
         assert(msg.payload.state.index < s->queues * 2);
         s->rings &= ~(0x1ULL << msg.payload.state.index);
@@ -345,7 +346,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int 
size)
     case VHOST_USER_SET_MEM_TABLE:
         /* received the mem table */
         memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
-        s->fds_num = qemu_chr_fe_get_msgfds(chr->chr, s->fds,
+        s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds,
                                             G_N_ELEMENTS(s->fds));
 
         /* signal the test that it can continue */
@@ -355,7 +356,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int 
size)
     case VHOST_USER_SET_VRING_KICK:
     case VHOST_USER_SET_VRING_CALL:
         /* consume the fd */
-        qemu_chr_fe_get_msgfds(chr->chr, &fd, 1);
+        qemu_chr_fe_get_msgfds(chr, &fd, 1);
         /*
          * This is a non-blocking eventfd.
          * The receive function forces it to be blocking,
@@ -369,11 +370,11 @@ static void chr_read(void *opaque, const uint8_t *buf, 
int size)
             close(s->log_fd);
             s->log_fd = -1;
         }
-        qemu_chr_fe_get_msgfds(chr->chr, &s->log_fd, 1);
+        qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
         msg.flags |= VHOST_USER_REPLY_MASK;
         msg.size = 0;
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE);
+        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
 
         g_cond_signal(&s->data_cond);
         break;
@@ -388,7 +389,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int 
size)
         msg.size = sizeof(m.payload.u64);
         msg.payload.u64 = s->queues;
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE + msg.size);
+        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
         break;
 
     default:
@@ -456,13 +457,14 @@ static void test_server_create_chr(TestServer *server, 
const gchar *opt)
 {
     gchar *chr_path;
     CharDriverState *chr;
+
     chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
     chr = qemu_chr_new(server->chr_name, chr_path);
-    qemu_chr_fe_init(&server->chr, chr, &error_abort);
     g_free(chr_path);
 
-    qemu_chr_add_handlers(server->chr.chr, chr_can_read, chr_read,
-                          chr_event, server);
+    qemu_chr_fe_init(&server->chr, chr, &error_abort);
+    qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
+                             chr_event, server, NULL);
 }
 
 static void test_server_listen(TestServer *server)
@@ -486,8 +488,9 @@ static inline void test_server_connect(TestServer *server)
 static gboolean _test_server_free(TestServer *server)
 {
     int i;
+    CharDriverState *chr = qemu_chr_fe_get_driver(&server->chr);
 
-    qemu_chr_delete(server->chr.chr);
+    qemu_chr_delete(chr);
 
     for (i = 0; i < server->fds_num; i++) {
         close(server->fds[i]);
@@ -724,7 +727,7 @@ reconnect_cb(gpointer user_data)
 {
     TestServer *s = user_data;
 
-    qemu_chr_fe_disconnect(s->chr.chr);
+    qemu_chr_fe_disconnect(&s->chr);
 
     return FALSE;
 }
diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
index c3312fb..c928d7d 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -28,6 +28,7 @@
 
 #include "hw/hw.h"
 #include "sysemu/sysemu.h"
+#include "sysemu/char.h"
 #include "exec/memory.h"
 #include "qemu/fifo8.h"
 #include "sysemu/char.h"
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index d72d4f5..0bd7506 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -169,7 +169,7 @@ CharDriverState *qemu_chr_new(const char *label, const char 
*filename);
  *
  * Close a fd accpeted by character backend.
  */
-void qemu_chr_fe_disconnect(CharDriverState *chr);
+void qemu_chr_fe_disconnect(CharBackend *be);
 
 /**
  * @qemu_chr_cleanup:
@@ -179,11 +179,11 @@ void qemu_chr_fe_disconnect(CharDriverState *chr);
 void qemu_chr_cleanup(void);
 
 /**
- * @qemu_chr_wait_connected:
+ * @qemu_chr_fe_wait_connected:
  *
  * Wait for characted backend to be connected.
  */
-int qemu_chr_wait_connected(CharDriverState *chr, Error **errp);
+int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp);
 
 /**
  * @qemu_chr_new_noreplay:
@@ -223,7 +223,7 @@ void qemu_chr_free(CharDriverState *chr);
  *
  * @echo true to enable echo, false to disable echo
  */
-void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo);
+void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
 
 /**
  * @qemu_chr_fe_set_open:
@@ -231,7 +231,7 @@ void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool 
echo);
  * Set character frontend open status.  This is an indication that the
  * front end is ready (or not) to begin doing I/O.
  */
-void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open);
+void qemu_chr_fe_set_open(CharBackend *be, int fe_open);
 
 /**
  * @qemu_chr_fe_event:
@@ -240,7 +240,7 @@ void qemu_chr_fe_set_open(struct CharDriverState *chr, int 
fe_open);
  *
  * @event the event to send
  */
-void qemu_chr_fe_event(CharDriverState *s, int event);
+void qemu_chr_fe_event(CharBackend *be, int event);
 
 /**
  * @qemu_chr_fe_printf:
@@ -250,7 +250,7 @@ void qemu_chr_fe_event(CharDriverState *s, int event);
  *
  * @fmt see #printf
  */
-void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...)
+void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
     GCC_FMT_ATTR(2, 3);
 
 /**
@@ -265,7 +265,7 @@ void qemu_chr_fe_printf(CharDriverState *s, const char 
*fmt, ...)
  * @func the function to call when the condition happens
  * @user_data the opaque pointer to pass to @func
  */
-guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
+guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
                             GIOFunc func, void *user_data);
 
 /**
@@ -280,7 +280,7 @@ guint qemu_chr_fe_add_watch(CharDriverState *s, 
GIOCondition cond,
  *
  * Returns: the number of bytes consumed
  */
-int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len);
+int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
 
 /**
  * @qemu_chr_fe_write_all:
@@ -295,7 +295,7 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t 
*buf, int len);
  *
  * Returns: the number of bytes consumed
  */
-int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len);
+int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
 
 /**
  * @qemu_chr_fe_read_all:
@@ -307,7 +307,7 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t 
*buf, int len);
  *
  * Returns: the number of bytes read
  */
-int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len);
+int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
 
 /**
  * @qemu_chr_fe_ioctl:
@@ -320,7 +320,7 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, 
int len);
  * Returns: if @cmd is not supported by the backend, -ENOTSUP, otherwise the
  *          return value depends on the semantics of @cmd
  */
-int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg);
+int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg);
 
 /**
  * @qemu_chr_fe_get_msgfd:
@@ -333,7 +333,7 @@ int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void 
*arg);
  *          this function will return -1 until a client sends a new file
  *          descriptor.
  */
-int qemu_chr_fe_get_msgfd(CharDriverState *s);
+int qemu_chr_fe_get_msgfd(CharBackend *be);
 
 /**
  * @qemu_chr_fe_get_msgfds:
@@ -346,7 +346,7 @@ int qemu_chr_fe_get_msgfd(CharDriverState *s);
  *          this function will return -1 until a client sends a new set of file
  *          descriptors.
  */
-int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int num);
+int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int num);
 
 /**
  * @qemu_chr_fe_set_msgfds:
@@ -359,13 +359,13 @@ int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, 
int num);
  *
  * Returns: -1 if fd passing isn't supported.
  */
-int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num);
+int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num);
 
 /**
  * @qemu_chr_fe_claim:
  *
  * Claim a backend before using it, should be called before calling
- * qemu_chr_add_handlers(). 
+ * qemu_chr_fe_set_handlers().
  *
  * Returns: -1 if the backend is already in use by another frontend, 0 on
  *          success.
@@ -436,7 +436,8 @@ void qemu_chr_be_event(CharDriverState *s, int event);
 /**
  * @qemu_chr_fe_init:
  *
- * Initializes a front end for the given CharBackend and CharDriver.
+ * Initializes a front end for the given CharBackend and
+ * CharDriver.
  *
  * Returns: false on error.
  */
@@ -475,22 +476,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
  */
 void qemu_chr_fe_take_focus(CharBackend *b);
 
-void qemu_chr_add_handlers(CharDriverState *s,
-                           IOCanReadHandler *fd_can_read,
-                           IOReadHandler *fd_read,
-                           IOEventHandler *fd_event,
-                           void *opaque);
-
-/* This API can make handler run in the context what you pass to. */
-void qemu_chr_add_handlers_full(CharDriverState *s,
-                                IOCanReadHandler *fd_can_read,
-                                IOReadHandler *fd_read,
-                                IOEventHandler *fd_event,
-                                void *opaque,
-                                GMainContext *context);
-
 void qemu_chr_be_generic_open(CharDriverState *s);
-void qemu_chr_fe_accept_input(CharDriverState *s);
+void qemu_chr_fe_accept_input(CharBackend *be);
 int qemu_chr_add_client(CharDriverState *s, int fd);
 CharDriverState *qemu_chr_find(const char *name);
 bool chr_is_ringbuf(const CharDriverState *chr);
-- 
2.10.0




reply via email to

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