summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/alpha/dp264.c2
-rw-r--r--hw/arm/fsl-imx25.c2
-rw-r--r--hw/arm/fsl-imx31.c2
-rw-r--r--hw/arm/fsl-imx6.c2
-rw-r--r--hw/arm/omap2.c16
-rw-r--r--hw/arm/pxa2xx.c19
-rw-r--r--hw/arm/strongarm.c22
-rw-r--r--hw/bt/hci-csr.c13
-rw-r--r--hw/char/bcm2835_aux.c18
-rw-r--r--hw/char/cadence_uart.c30
-rw-r--r--hw/char/debugcon.c8
-rw-r--r--hw/char/digic-uart.c13
-rw-r--r--hw/char/escc.c22
-rw-r--r--hw/char/etraxfs_ser.c12
-rw-r--r--hw/char/exynos4210_uart.c15
-rw-r--r--hw/char/grlib_apbuart.c17
-rw-r--r--hw/char/imx_serial.c29
-rw-r--r--hw/char/ipoctal232.c23
-rw-r--r--hw/char/lm32_juart.c15
-rw-r--r--hw/char/lm32_uart.c17
-rw-r--r--hw/char/mcf_uart.c20
-rw-r--r--hw/char/milkymist-uart.c13
-rw-r--r--hw/char/omap_uart.c4
-rw-r--r--hw/char/parallel.c46
-rw-r--r--hw/char/pl011.c19
-rw-r--r--hw/char/sclpconsole-lm.c13
-rw-r--r--hw/char/sclpconsole.c12
-rw-r--r--hw/char/serial-isa.c7
-rw-r--r--hw/char/serial.c39
-rw-r--r--hw/char/sh_serial.c16
-rw-r--r--hw/char/spapr_vty.c12
-rw-r--r--hw/char/stm32f2xx_usart.c22
-rw-r--r--hw/char/virtio-console.c29
-rw-r--r--hw/char/xen_console.c43
-rw-r--r--hw/char/xilinx_uartlite.c16
-rw-r--r--hw/core/qdev-properties-system.c80
-rw-r--r--hw/i386/pc.c2
-rw-r--r--hw/ipmi/ipmi_bmc_extern.c9
-rw-r--r--hw/isa/pc87312.c4
-rw-r--r--hw/mips/mips_fulong2e.c2
-rw-r--r--hw/mips/mips_malta.c44
-rw-r--r--hw/mips/mips_r4k.c2
-rw-r--r--hw/misc/ivshmem.c23
-rw-r--r--hw/sparc64/sun4u.c2
-rw-r--r--hw/ssi/xilinx_spips.c7
-rw-r--r--hw/usb/ccid-card-passthru.c21
-rw-r--r--hw/usb/dev-serial.c31
-rw-r--r--hw/usb/redirect.c22
-rw-r--r--hw/virtio/vhost-user.c4
-rw-r--r--hw/xtensa/xtfpga.c2
50 files changed, 421 insertions, 442 deletions
diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c
index f1267b5441..d6431fd586 100644
--- a/hw/alpha/dp264.c
+++ b/hw/alpha/dp264.c
@@ -88,7 +88,7 @@ static void clipper_init(MachineState *machine)
     pci_vga_init(pci_bus);
 
     /* Serial code setup.  */
-    serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
+    serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
 
     /* Network setup.  e1000 is good enough, failing Tulip support.  */
     for (i = 0; i < nb_nics; i++) {
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index b4e358db65..7bb7be76b6 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -125,7 +125,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
             if (!chr) {
                 char label[20];
                 snprintf(label, sizeof(label), "imx31.uart%d", i);
-                chr = qemu_chr_new(label, "null", NULL);
+                chr = qemu_chr_new(label, "null");
             }
 
             qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr);
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index fe204ace62..f23672b222 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -114,7 +114,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
             if (!chr) {
                 char label[20];
                 snprintf(label, sizeof(label), "imx31.uart%d", i);
-                chr = qemu_chr_new(label, "null", NULL);
+                chr = qemu_chr_new(label, "null");
             }
 
             qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr);
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index 6a1bf263a5..e93532fb57 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -193,7 +193,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
 
             if (!chr) {
                 char *label = g_strdup_printf("imx6.uart%d", i + 1);
-                chr = qemu_chr_new(label, "null", NULL);
+                chr = qemu_chr_new(label, "null");
                 g_free(label);
                 serial_hds[i] = chr;
             }
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index 7e11c65cba..6f05c98d3e 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -621,7 +621,7 @@ struct omap_sti_s {
     qemu_irq irq;
     MemoryRegion iomem;
     MemoryRegion iomem_fifo;
-    CharDriverState *chr;
+    CharBackend chr;
 
     uint32_t sysconfig;
     uint32_t systest;
@@ -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, (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, (const uint8_t *) "\n", 1);
-        else
-            qemu_chr_fe_write_all(s->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);
+        }
     }
 }
 
@@ -798,7 +799,8 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta,
     s->irq = irq;
     omap_sti_reset(s);
 
-    s->chr = chr ?: qemu_chr_new("null", "null", NULL);
+    qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null"),
+                     &error_abort);
 
     memory_region_init_io(&s->iomem, NULL, &omap_sti_ops, s, "omap.sti",
                           omap_l4_region_size(ta, 0));
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 98982872d7..42cdde0478 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1764,7 +1764,7 @@ struct PXA2xxFIrState {
     qemu_irq rx_dma;
     qemu_irq tx_dma;
     uint32_t enable;
-    CharDriverState *chr;
+    CharBackend chr;
 
     uint8_t control[3];
     uint8_t status[2];
@@ -1898,14 +1898,16 @@ static void pxa2xx_fir_write(void *opaque, hwaddr addr,
         pxa2xx_fir_update(s);
         break;
     case ICDR:
-        if (s->control[2] & (1 << 2))			/* TXP */
+        if (s->control[2] & (1 << 2)) { /* TXP */
             ch = value;
-        else
+        } else {
             ch = ~value;
-        if (s->chr && s->enable && (s->control[0] & (1 << 3)))	/* TXE */
+        }
+        if (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, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
+        }
         break;
     case ICSR0:
         s->status[0] &= ~(value & 0x66);
@@ -1973,11 +1975,8 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
 {
     PXA2xxFIrState *s = PXA2XX_FIR(dev);
 
-    if (s->chr) {
-        qemu_chr_fe_claim_no_fail(s->chr);
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id)
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index 021cbf9a0f..85db1e2813 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -912,7 +912,7 @@ typedef struct StrongARMUARTState {
     SysBusDevice parent_obj;
 
     MemoryRegion iomem;
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
 
     uint8_t utcr0;
@@ -1020,9 +1020,7 @@ static void strongarm_uart_update_parameters(StrongARMUARTState *s)
     ssp.data_bits = data_bits;
     ssp.stop_bits = stop_bits;
     s->char_transmit_time =  (NANOSECONDS_PER_SECOND / speed) * frame_size;
-    if (s->chr) {
-        qemu_chr_fe_ioctl(s->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,
             speed, parity, data_bits, stop_bits);
@@ -1107,10 +1105,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) {
+    } 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, &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;
@@ -1239,13 +1237,11 @@ static void strongarm_uart_init(Object *obj)
     s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s);
     s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static void strongarm_uart_reset(DeviceState *dev)
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index b77c0366e4..fbb3109cc1 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -78,15 +78,17 @@ enum {
 
 static inline void csrhci_fifo_wake(struct csrhci_s *s)
 {
+    CharBackend *be = s->chr.be;
+
     if (!s->enable || !s->out_len)
         return;
 
     /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */
-    if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) &&
-                    s->chr.chr_read) {
-        s->chr.chr_read(s->chr.handler_opaque,
-                        s->outfifo + s->out_start ++, 1);
-        s->out_len --;
+    if (be && be->chr_can_read && be->chr_can_read(be->opaque) &&
+        be->chr_read) {
+        be->chr_read(be->opaque,
+                     s->outfifo + s->out_start++, 1);
+        s->out_len--;
         if (s->out_start >= s->out_size) {
             s->out_start = 0;
             s->out_size = FIFO_LEN;
@@ -466,7 +468,6 @@ CharDriverState *uart_hci_init(void)
     s->chr.opaque = s;
     s->chr.chr_write = csrhci_write;
     s->chr.chr_ioctl = csrhci_ioctl;
-    s->chr.avail_connections = 1;
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index f7a845d3e2..4d46ad60ae 100644
--- a/hw/char/bcm2835_aux.c
+++ b/hw/char/bcm2835_aux.c
@@ -79,9 +79,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
                 s->read_pos = 0;
             }
         }
-        if (s->chr) {
-            qemu_chr_accept_input(s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         bcm2835_aux_update(s);
         return c;
 
@@ -168,11 +166,9 @@ static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
     case AUX_MU_IO_REG:
         /* "DLAB bit set means access baudrate register" is NYI */
         ch = value;
-        if (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, &ch, 1);
-        }
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         break;
 
     case AUX_MU_IER_REG:
@@ -282,10 +278,8 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
 {
     BCM2835AuxState *s = BCM2835_AUX(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static Property bcm2835_aux_props[] = {
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index e3bc52f7df..c2b9154305 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -142,9 +142,7 @@ static void uart_rx_reset(CadenceUARTState *s)
 {
     s->rx_wpos = 0;
     s->rx_count = 0;
-    if (s->chr) {
-        qemu_chr_accept_input(s->chr);
-    }
+    qemu_chr_fe_accept_input(&s->chr);
 }
 
 static void uart_tx_reset(CadenceUARTState *s)
@@ -156,10 +154,8 @@ static void uart_send_breaks(CadenceUARTState *s)
 {
     int break_enabled = 1;
 
-    if (s->chr) {
-        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
-                                   &break_enabled);
-    }
+    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+                      &break_enabled);
 }
 
 static void uart_parameters_setup(CadenceUARTState *s)
@@ -210,9 +206,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) {
-        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
-    }
+    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 }
 
 static int uart_can_receive(void *opaque)
@@ -278,7 +272,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) {
+    if (!qemu_chr_fe_get_driver(&s->chr)) {
         s->tx_count = 0;
         return FALSE;
     }
@@ -287,7 +281,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
         return FALSE;
     }
 
-    ret = qemu_chr_fe_write(s->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 +289,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
     }
 
     if (s->tx_count) {
-        guint r = qemu_chr_fe_add_watch(s->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;
@@ -368,9 +362,7 @@ static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t *c)
         *c = s->rx_fifo[rx_rpos];
         s->rx_count--;
 
-        if (s->chr) {
-            qemu_chr_accept_input(s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
     } else {
         *c = 0;
     }
@@ -474,10 +466,8 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
     s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                           fifo_trigger_update, s);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static void cadence_uart_init(Object *obj)
diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c
index 4402033861..80dce07e7f 100644
--- a/hw/char/debugcon.c
+++ b/hw/char/debugcon.c
@@ -39,7 +39,7 @@
 
 typedef struct DebugconState {
     MemoryRegion io;
-    CharDriverState *chr;
+    CharBackend chr;
     uint32_t readback;
 } DebugconState;
 
@@ -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, &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) {
+    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, NULL, NULL, NULL, s);
+    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, s, NULL, true);
 }
 
 static void debugcon_isa_realizefn(DeviceState *dev, Error **errp)
diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c
index e96a9b2d8d..029f5bbf5e 100644
--- a/hw/char/digic-uart.c
+++ b/hw/char/digic-uart.c
@@ -76,11 +76,9 @@ static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value,
 
     switch (addr) {
     case R_TX:
-        if (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, &ch, 1);
-        }
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         break;
 
     case R_ST:
@@ -147,9 +145,8 @@ static void digic_uart_realize(DeviceState *dev, Error **errp)
 {
     DigicUartState *s = DIGIC_UART(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static void digic_uart_init(Object *obj)
diff --git a/hw/char/escc.c b/hw/char/escc.c
index aa1739762b..d6662dc77d 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -88,7 +88,7 @@ typedef struct ChannelState {
     uint32_t reg;
     uint8_t wregs[SERIAL_REGS], rregs[SERIAL_REGS];
     SERIOQueue queue;
-    CharDriverState *chr;
+    CharBackend chr;
     int e0_mode, led_mode, caps_lock_mode, num_lock_mode;
     int disabled;
     int clock;
@@ -416,7 +416,7 @@ static void escc_update_parameters(ChannelState *s)
     int speed, parity, data_bits, stop_bits;
     QEMUSerialSetParams ssp;
 
-    if (!s->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_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,11 +556,11 @@ 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)
+            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, &s->tx, 1);
-            else if (s->type == kbd && !s->disabled) {
+                qemu_chr_fe_write_all(&s->chr, &s->tx, 1);
+            } else if (s->type == kbd && !s->disabled) {
                 handle_kbd_command(s, val);
             }
         }
@@ -599,8 +599,7 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr,
         else
             ret = s->rx;
         trace_escc_mem_readb_data(CHN_C(s), ret);
-        if (s->chr)
-            qemu_chr_accept_input(s->chr);
+        qemu_chr_fe_accept_input(&s->chr);
         return ret;
     default:
         break;
@@ -1013,10 +1012,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) {
+        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, 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, true);
         }
     }
 
diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c
index c99cc5d130..54383878e0 100644
--- a/hw/char/etraxfs_ser.c
+++ b/hw/char/etraxfs_ser.c
@@ -53,7 +53,7 @@ typedef struct ETRAXSerial {
     SysBusDevice parent_obj;
 
     MemoryRegion mmio;
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
 
     int pending_tx;
@@ -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, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->regs[R_INTR] |= 3;
             s->pending_tx = 1;
             s->regs[addr] = value;
@@ -231,11 +231,9 @@ static void etraxfs_ser_realize(DeviceState *dev, Error **errp)
 {
     ETRAXSerial *s = ETRAX_SERIAL(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static void etraxfs_ser_class_init(ObjectClass *klass, void *data)
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index 1107578138..571c324004 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -181,7 +181,7 @@ typedef struct Exynos4210UartState {
     Exynos4210UartFIFO   rx;
     Exynos4210UartFIFO   tx;
 
-    CharDriverState  *chr;
+    CharBackend       chr;
     qemu_irq          irq;
 
     uint32_t channel;
@@ -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_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) {
+        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, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
 #if DEBUG_Tx_DATA
             fprintf(stderr, "%c", ch);
 #endif
@@ -606,7 +606,7 @@ DeviceState *exynos4210_uart_create(hwaddr addr,
         chr = serial_hds[channel];
         if (!chr) {
             snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel);
-            chr = qemu_chr_new(label, "null", NULL);
+            chr = qemu_chr_new(label, "null");
             if (!(chr)) {
                 error_report("Can't assign serial port to UART%d", channel);
                 exit(1);
@@ -640,8 +640,9 @@ static int exynos4210_uart_init(SysBusDevice *dev)
 
     sysbus_init_irq(dev, &s->irq);
 
-    qemu_chr_add_handlers(s->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, true);
 
     return 0;
 }
diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c
index 778148a15e..db686e6a6f 100644
--- a/hw/char/grlib_apbuart.c
+++ b/hw/char/grlib_apbuart.c
@@ -78,7 +78,7 @@ typedef struct UART {
     MemoryRegion iomem;
     qemu_irq irq;
 
-    CharDriverState *chr;
+    CharBackend chr;
 
     /* registers */
     uint32_t status;
@@ -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) && (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, &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,
-                          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, true);
 
     sysbus_init_irq(dev, &uart->irq);
 
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 5c3fa61e4c..99545fc359 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -121,9 +121,7 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
             s->usr2 &= ~USR2_RDR;
             s->uts1 |= UTS1_RXEMPTY;
             imx_update(s);
-            if (s->chr) {
-                qemu_chr_accept_input(s->chr);
-            }
+            qemu_chr_fe_accept_input(&s->chr);
         }
         return c;
 
@@ -172,20 +170,19 @@ 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 ? s->chr->label : "NODEV");
+            offset, (unsigned int)value, chr ? chr->label : "NODEV");
 
     switch (offset >> 2) {
     case 0x10: /* UTXD */
         ch = value;
         if (s->ucr2 & UCR2_TXEN) {
-            if (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, &ch, 1);
-            }
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->usr1 &= ~USR1_TRDY;
             imx_update(s);
             s->usr1 |= USR1_TRDY;
@@ -214,9 +211,7 @@ static void imx_serial_write(void *opaque, hwaddr offset,
         }
         if (value & UCR2_RXEN) {
             if (!(s->ucr2 & UCR2_RXEN)) {
-                if (s->chr) {
-                    qemu_chr_accept_input(s->chr);
-                }
+                qemu_chr_fe_accept_input(&s->chr);
             }
         }
         s->ucr2 = value & 0xffff;
@@ -318,12 +313,10 @@ static void imx_serial_realize(DeviceState *dev, Error **errp)
 {
     IMXSerialState *s = IMX_SERIAL(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive,
-                              imx_event, s);
-    } else {
-        DPRINTF("No char dev for uart\n");
-    }
+    DPRINTF("char dev for uart: %p\n", qemu_chr_fe_get_driver(&s->chr));
+
+    qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive,
+                             imx_event, s, NULL, true);
 }
 
 static void imx_serial_init(Object *obj)
diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c
index 2859fdd7fb..93929c2880 100644
--- a/hw/char/ipoctal232.c
+++ b/hw/char/ipoctal232.c
@@ -93,7 +93,7 @@ typedef struct SCC2698Block SCC2698Block;
 
 struct SCC2698Channel {
     IPOctalState *ipoctal;
-    CharDriverState *dev;
+    CharBackend dev;
     bool rx_enabled;
     uint8_t mr[2];
     uint8_t mr_idx;
@@ -288,9 +288,7 @@ static uint16_t io_read(IPackDevice *ip, uint8_t addr)
             if (ch->rx_pending == 0) {
                 ch->sr &= ~SR_RXRDY;
                 blk->isr &= ~ISR_RXRDY(channel);
-                if (ch->dev) {
-                    qemu_chr_accept_input(ch->dev);
-                }
+                qemu_chr_fe_accept_input(&ch->dev);
             } else {
                 ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
             }
@@ -357,13 +355,11 @@ static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
     case REG_THRa:
     case REG_THRb:
         if (ch->sr & SR_TXRDY) {
+            uint8_t thr = reg;
             DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
-            if (ch->dev) {
-                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, &thr, 1);
-            }
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&ch->dev, &thr, 1);
         } else {
             DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
         }
@@ -546,9 +542,10 @@ static void ipoctal_realize(DeviceState *dev, Error **errp)
         ch->ipoctal = s;
 
         /* Redirect IP-Octal channels to host character devices */
-        if (ch->dev) {
-            qemu_chr_add_handlers(ch->dev, 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, true);
             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 cb1ac76731..f8c1e0d076 100644
--- a/hw/char/lm32_juart.c
+++ b/hw/char/lm32_juart.c
@@ -44,7 +44,7 @@ enum {
 struct LM32JuartState {
     SysBusDevice parent_obj;
 
-    CharDriverState *chr;
+    CharBackend chr;
 
     uint32_t jtx;
     uint32_t jrx;
@@ -75,11 +75,9 @@ void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx)
     trace_lm32_juart_set_jtx(s->jtx);
 
     s->jtx = jtx;
-    if (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, &ch, 1);
-    }
+    /* XXX this blocks entire thread. Rewrite to use
+     * qemu_chr_fe_write and background I/O callbacks */
+    qemu_chr_fe_write_all(&s->chr, &ch, 1);
 }
 
 void lm32_juart_set_jrx(DeviceState *d, uint32_t jtx)
@@ -120,9 +118,8 @@ static void lm32_juart_realize(DeviceState *dev, Error **errp)
 {
     LM32JuartState *s = LM32_JUART(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static const VMStateDescription vmstate_lm32_juart = {
diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c
index be93697a39..7f3597c4b0 100644
--- a/hw/char/lm32_uart.c
+++ b/hw/char/lm32_uart.c
@@ -97,7 +97,7 @@ struct LM32UartState {
     SysBusDevice parent_obj;
 
     MemoryRegion iomem;
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
 
     uint32_t regs[R_MAX];
@@ -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_accept_input(s->chr);
+        qemu_chr_fe_accept_input(&s->chr);
         break;
     case R_IIR:
     case R_LSR:
@@ -177,11 +177,9 @@ static void uart_write(void *opaque, hwaddr addr,
     addr >>= 2;
     switch (addr) {
     case R_RXTX:
-        if (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, &ch, 1);
-        }
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         break;
     case R_IER:
     case R_LCR:
@@ -267,9 +265,8 @@ static void lm32_uart_realize(DeviceState *dev, Error **errp)
 {
     LM32UartState *s = LM32_UART(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static const VMStateDescription vmstate_lm32_uart = {
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index c184859c83..ecaa091190 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -10,6 +10,7 @@
 #include "hw/m68k/mcf.h"
 #include "sysemu/char.h"
 #include "exec/address-spaces.h"
+#include "qapi/error.h"
 
 typedef struct {
     MemoryRegion iomem;
@@ -26,7 +27,7 @@ typedef struct {
     int tx_enabled;
     int rx_enabled;
     qemu_irq irq;
-    CharDriverState *chr;
+    CharBackend chr;
 } mcf_uart_state;
 
 /* UART Status Register bits.  */
@@ -92,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_accept_input(s->chr);
+            qemu_chr_fe_accept_input(&s->chr);
             return val;
         }
     case 0x10:
@@ -113,10 +114,9 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr,
 static void mcf_uart_do_tx(mcf_uart_state *s)
 {
     if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) {
-        if (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, (unsigned char *)&s->tb, 1);
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, (unsigned char *)&s->tb, 1);
         s->sr |= MCF_UART_TxEMP;
     }
     if (s->tx_enabled) {
@@ -280,12 +280,12 @@ void *mcf_uart_init(qemu_irq irq, CharDriverState *chr)
     mcf_uart_state *s;
 
     s = g_malloc0(sizeof(mcf_uart_state));
-    s->chr = chr;
     s->irq = irq;
     if (chr) {
-        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_init(&s->chr, chr, &error_abort);
+        qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive,
+                                 mcf_uart_receive, mcf_uart_event,
+                                 s, NULL, true);
     }
     mcf_uart_reset(s);
     return s;
diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c
index baddb37648..ae8e2f3554 100644
--- a/hw/char/milkymist-uart.c
+++ b/hw/char/milkymist-uart.c
@@ -61,7 +61,7 @@ struct MilkymistUartState {
     SysBusDevice parent_obj;
 
     MemoryRegion regs_region;
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
 
     uint32_t regs[R_MAX];
@@ -124,9 +124,7 @@ static void uart_write(void *opaque, hwaddr addr, uint64_t value,
     addr >>= 2;
     switch (addr) {
     case R_RXTX:
-        if (s->chr) {
-            qemu_chr_fe_write_all(s->chr, &ch, 1);
-        }
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         s->regs[R_STAT] |= STAT_TX_EVT;
         break;
     case R_DIV:
@@ -138,7 +136,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_accept_input(s->chr);
+        qemu_chr_fe_accept_input(&s->chr);
         break;
 
     default:
@@ -200,9 +198,8 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp)
 {
     MilkymistUartState *s = MILKYMIST_UART(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static void milkymist_uart_init(Object *obj)
diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c
index 415bec5fac..893ab108bc 100644
--- a/hw/char/omap_uart.c
+++ b/hw/char/omap_uart.c
@@ -63,7 +63,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base,
     s->irq = irq;
     s->serial = serial_mm_init(get_system_memory(), base, 2, irq,
                                omap_clk_getrate(fclk)/16,
-                               chr ?: qemu_chr_new(label, "null", NULL),
+                               chr ?: qemu_chr_new(label, "null"),
                                DEVICE_NATIVE_ENDIAN);
     return s;
 }
@@ -183,6 +183,6 @@ void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr)
     /* TODO: Should reuse or destroy current s->serial */
     s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
                                omap_clk_getrate(s->fclk) / 16,
-                               chr ?: qemu_chr_new("null", "null", NULL),
+                               chr ?: qemu_chr_new("null", "null"),
                                DEVICE_NATIVE_ENDIAN);
 }
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index da22e36356..f2d56666b7 100644
--- a/hw/char/parallel.c
+++ b/hw/char/parallel.c
@@ -74,7 +74,7 @@ typedef struct ParallelState {
     uint8_t control;
     qemu_irq irq;
     int irq_pending;
-    CharDriverState *chr;
+    CharBackend chr;
     int hw_driver;
     int epp_timeout;
     uint32_t last_read_offset; /* For debugging */
@@ -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, &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_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_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_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,8 @@ 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_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
+            if (qemu_chr_fe_ioctl(&s->chr,
+                                  CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
                 s->epp_timeout = 1;
                 pdebug("wa%02x t\n", val);
             }
@@ -208,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_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);
             }
@@ -233,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_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);
@@ -256,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_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);
@@ -308,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_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_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;
@@ -326,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_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;
@@ -338,12 +339,14 @@ static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr)
         }
         break;
     case PARA_REG_EPP_ADDR:
-        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+        if ((s->control & (PARA_CTR_DIR | PARA_CTR_SIGNAL)) !=
+            (PARA_CTR_DIR | PARA_CTR_INIT))
             /* Controls not correct for EPP addr cycle, so do nothing */
             pdebug("ra%02x s\n", ret);
         else {
             struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
+            if (qemu_chr_fe_ioctl(&s->chr,
+                                  CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
                 s->epp_timeout = 1;
                 pdebug("ra%02x t\n", ret);
             }
@@ -352,12 +355,13 @@ static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr)
         }
         break;
     case PARA_REG_EPP_DATA:
-        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+        if ((s->control & (PARA_CTR_DIR | PARA_CTR_SIGNAL)) !=
+            (PARA_CTR_DIR | PARA_CTR_INIT))
             /* Controls not correct for EPP data cycle, so do nothing */
             pdebug("re%02x s\n", ret);
         else {
             struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->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);
             }
@@ -385,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_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) {
@@ -412,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_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) {
@@ -508,7 +512,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp)
     int base;
     uint8_t dummy;
 
-    if (!s->chr) {
+    if (!qemu_chr_fe_get_driver(&s->chr)) {
         error_setg(errp, "Can't create parallel device, empty char device");
         return;
     }
@@ -530,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_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;
     }
@@ -605,7 +609,7 @@ bool parallel_mm_init(MemoryRegion *address_space,
 
     s = g_malloc0(sizeof(ParallelState));
     s->irq = irq;
-    s->chr = chr;
+    qemu_chr_fe_init(&s->chr, chr, &error_abort);
     s->it_shift = it_shift;
     qemu_register_reset(parallel_reset, s);
 
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 1a7911f81f..24ea9738b6 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -36,7 +36,7 @@ typedef struct PL011State {
     int read_pos;
     int read_count;
     int read_trigger;
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
     const unsigned char *id;
 } PL011State;
@@ -87,9 +87,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
         trace_pl011_read_fifo(s->read_count);
         s->rsr = c >> 8;
         pl011_update(s);
-        if (s->chr) {
-            qemu_chr_accept_input(s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         r = c;
         break;
     case 1: /* UARTRSR */
@@ -168,10 +166,9 @@ static void pl011_write(void *opaque, hwaddr offset,
     case 0: /* UARTDR */
         /* ??? Check if transmitter is enabled.  */
         ch = value;
-        if (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, &ch, 1);
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         s->int_level |= PL011_INT_TX;
         pl011_update(s);
         break;
@@ -331,10 +328,8 @@ static void pl011_realize(DeviceState *dev, Error **errp)
 {
     PL011State *s = PL011(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static void pl011_class_init(ObjectClass *oc, void *data)
diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c
index 9a563269e6..07d6ebd112 100644
--- a/hw/char/sclpconsole-lm.c
+++ b/hw/char/sclpconsole-lm.c
@@ -37,7 +37,7 @@ typedef struct OprtnsCommand {
 
 typedef struct SCLPConsoleLM {
     SCLPEvent event;
-    CharDriverState *chr;
+    CharBackend chr;
     bool echo;                  /* immediate echo of input if true        */
     uint32_t write_errors;      /* errors writing to char layer           */
     uint32_t length;            /* length of byte stream in buffer        */
@@ -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, 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) {
+    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, buf, len);
+    return qemu_chr_fe_write_all(&scon->chr, buf, len);
 }
 
 static int process_mdb(SCLPEvent *event, MDBO *mdbo)
@@ -312,9 +312,8 @@ static int console_init(SCLPEvent *event)
     }
     console_available = true;
 
-    if (scon->chr) {
-        qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon);
-    }
+    qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
+                             chr_read, NULL, scon, NULL, true);
 
     return 0;
 }
diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c
index a75ad4f60a..b78f240a73 100644
--- a/hw/char/sclpconsole.c
+++ b/hw/char/sclpconsole.c
@@ -31,7 +31,7 @@ typedef struct ASCIIConsoleData {
 
 typedef struct SCLPConsole {
     SCLPEvent event;
-    CharDriverState *chr;
+    CharBackend chr;
     uint8_t iov[SIZE_BUFFER_VT220];
     uint32_t iov_sclp;      /* offset in buf for SCLP read operation       */
     uint32_t iov_bs;        /* offset in buf for char layer read operation */
@@ -163,14 +163,14 @@ static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf,
 {
     SCLPConsole *scon = SCLP_CONSOLE(event);
 
-    if (!scon->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, buf, len);
+    return qemu_chr_fe_write_all(&scon->chr, buf, len);
 }
 
 static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
@@ -227,10 +227,8 @@ static int console_init(SCLPEvent *event)
         return -1;
     }
     console_available = true;
-    if (scon->chr) {
-        qemu_chr_add_handlers(scon->chr, chr_can_read,
-                              chr_read, NULL, scon);
-    }
+    qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
+                             chr_read, NULL, scon, NULL, true);
 
     return 0;
 }
diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c
index 1594ec4db3..54d3a12f51 100644
--- a/hw/char/serial-isa.c
+++ b/hw/char/serial-isa.c
@@ -133,13 +133,14 @@ static void serial_isa_init(ISABus *bus, int index, CharDriverState *chr)
     qdev_init_nofail(dev);
 }
 
-void serial_hds_isa_init(ISABus *bus, int n)
+void serial_hds_isa_init(ISABus *bus, int from, int to)
 {
     int i;
 
-    assert(n <= MAX_SERIAL_PORTS);
+    assert(from >= 0);
+    assert(to <= MAX_SERIAL_PORTS);
 
-    for (i = 0; i < n; ++i) {
+    for (i = from; i < to; ++i) {
         if (serial_hds[i]) {
             serial_isa_init(bus, i, serial_hds[i]);
         }
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 3442f47d36..ffbacd8227 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -153,8 +153,9 @@ static void serial_update_parameters(SerialState *s)
     int speed, parity, data_bits, stop_bits, frame_size;
     QEMUSerialSetParams ssp;
 
-    if (s->divider == 0)
+    if (s->divider == 0 || s->divider > s->baudbase) {
         return;
+    }
 
     /* Start bit. */
     frame_size = 1;
@@ -181,7 +182,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_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 +195,8 @@ static void serial_update_msl(SerialState *s)
 
     timer_del(s->modem_status_poll);
 
-    if (qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
+    if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM,
+                          &flags) == -ENOTSUP) {
         s->poll_msl = -1;
         return;
     }
@@ -259,11 +261,12 @@ 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, &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, G_IO_OUT|G_IO_HUP,
-                                                 serial_watch_cb, s);
+            s->watch_tag =
+                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++;
                 return;
@@ -416,8 +419,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_IOCTL_SERIAL_SET_BREAK,
-                               &break_enable);
+                qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+                                  &break_enable);
             }
         }
         break;
@@ -431,7 +434,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_IOCTL_SERIAL_GET_TIOCM, &flags);
+                qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
 
                 flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR);
 
@@ -440,7 +443,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_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);
@@ -485,7 +488,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_accept_input(s->chr);
+                qemu_chr_fe_accept_input(&s->chr);
             }
         }
         break;
@@ -658,7 +661,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, 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).  */
@@ -883,7 +886,7 @@ static void serial_reset(void *opaque)
 
 void serial_realize_core(SerialState *s, Error **errp)
 {
-    if (!s->chr) {
+    if (!qemu_chr_fe_get_driver(&s->chr)) {
         error_setg(errp, "Can't create serial device, empty char device");
         return;
     }
@@ -893,8 +896,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, serial_can_receive1, serial_receive1,
-                          serial_event, s);
+    qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1,
+                             serial_event, s, NULL, true);
     fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH);
     fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH);
     serial_reset(s);
@@ -902,7 +905,7 @@ void serial_realize_core(SerialState *s, Error **errp)
 
 void serial_exit_core(SerialState *s)
 {
-    qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
+    qemu_chr_fe_deinit(&s->chr);
     qemu_unregister_reset(serial_reset, s);
 }
 
@@ -932,7 +935,7 @@ SerialState *serial_init(int base, qemu_irq irq, int baudbase,
 
     s->irq = irq;
     s->baudbase = baudbase;
-    s->chr = chr;
+    qemu_chr_fe_init(&s->chr, chr, &error_abort);
     serial_realize_core(s, &error_fatal);
 
     vmstate_register(NULL, base, &vmstate_serial, s);
@@ -989,7 +992,7 @@ SerialState *serial_mm_init(MemoryRegion *address_space,
     s->it_shift = it_shift;
     s->irq = irq;
     s->baudbase = baudbase;
-    s->chr = chr;
+    qemu_chr_fe_init(&s->chr, chr, &error_abort);
 
     serial_realize_core(s, &error_fatal);
     vmstate_register(NULL, base, &vmstate_serial, s);
diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
index 97ce5629a4..9d35564bcf 100644
--- a/hw/char/sh_serial.c
+++ b/hw/char/sh_serial.c
@@ -29,6 +29,7 @@
 #include "hw/sh4/sh.h"
 #include "sysemu/char.h"
 #include "exec/address-spaces.h"
+#include "qapi/error.h"
 
 //#define DEBUG_SERIAL
 
@@ -62,7 +63,7 @@ typedef struct {
     int flags;
     int rtrg;
 
-    CharDriverState *chr;
+    CharBackend chr;
 
     qemu_irq eri;
     qemu_irq rxi;
@@ -109,11 +110,11 @@ static void sh_serial_write(void *opaque, hwaddr offs,
         }
         return;
     case 0x0c: /* FTDR / TDR */
-        if (s->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, &ch, 1);
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
 	}
 	s->dr = val;
 	s->flags &= ~SH_SERIAL_FLAG_TDE;
@@ -395,12 +396,11 @@ void sh_serial_init(MemoryRegion *sysmem,
                              0, 0x28);
     memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
 
-    s->chr = chr;
-
     if (chr) {
-        qemu_chr_fe_claim_no_fail(chr);
-        qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
-			      sh_serial_event, s);
+        qemu_chr_fe_init(&s->chr, chr, &error_abort);
+        qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1,
+                                 sh_serial_receive1,
+                                 sh_serial_event, s, NULL, true);
     }
 
     s->eri = eri_source;
diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c
index 9aeafc0c42..31822fed9a 100644
--- a/hw/char/spapr_vty.c
+++ b/hw/char/spapr_vty.c
@@ -11,7 +11,7 @@
 
 typedef struct VIOsPAPRVTYDevice {
     VIOsPAPRDevice sdev;
-    CharDriverState *chardev;
+    CharBackend chardev;
     uint32_t in, out;
     uint8_t buf[VTERM_BUFSIZE];
 } VIOsPAPRVTYDevice;
@@ -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_accept_input(dev->chardev);
+    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, 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) {
+    if (!qemu_chr_fe_get_driver(&dev->chardev)) {
         error_setg(errp, "chardev property not set");
         return;
     }
 
-    qemu_chr_add_handlers(dev->chardev, vty_can_receive,
-                          vty_receive, NULL, dev);
+    qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive,
+                             vty_receive, NULL, dev, NULL, true);
 }
 
 /* Forward declaration */
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 4c6640dbe9..59872e6d3b 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -97,17 +97,13 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr,
     case USART_SR:
         retvalue = s->usart_sr;
         s->usart_sr &= ~USART_SR_TC;
-        if (s->chr) {
-            qemu_chr_accept_input(s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         return retvalue;
     case USART_DR:
         DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr);
         s->usart_sr |= USART_SR_TXE;
         s->usart_sr &= ~USART_SR_RXNE;
-        if (s->chr) {
-            qemu_chr_accept_input(s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         qemu_set_irq(s->irq, 0);
         return s->usart_dr & 0x3FF;
     case USART_BRR:
@@ -152,11 +148,9 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
     case USART_DR:
         if (value < 0xF000) {
             ch = value;
-            if (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, &ch, 1);
-            }
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->usart_sr |= USART_SR_TC;
             s->usart_sr &= ~USART_SR_TXE;
         }
@@ -212,10 +206,8 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp)
 {
     STM32F2XXUsartState *s = STM32F2XX_USART(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data)
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index d44c18c128..776205b4a9 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -24,7 +24,7 @@
 typedef struct VirtConsole {
     VirtIOSerialPort parent_obj;
 
-    CharDriverState *chr;
+    CharBackend chr;
     guint watch;
 } VirtConsole;
 
@@ -49,12 +49,12 @@ static ssize_t flush_buf(VirtIOSerialPort *port,
     VirtConsole *vcon = VIRTIO_CONSOLE(port);
     ssize_t ret;
 
-    if (!vcon->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, buf, len);
+    ret = qemu_chr_fe_write(&vcon->chr, buf, len);
     trace_virtio_console_flush_buf(port->id, len, ret);
 
     if (ret < len) {
@@ -92,7 +92,7 @@ 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,
+                vcon->watch = qemu_chr_fe_add_watch(&vcon->chr,
                                                     G_IO_OUT|G_IO_HUP,
                                                     chr_write_unblocked, vcon);
             }
@@ -108,8 +108,8 @@ static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
     DeviceState *dev = DEVICE(port);
     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
-    if (vcon->chr && !k->is_console) {
-        qemu_chr_fe_set_open(vcon->chr, guest_connected);
+    if (!k->is_console) {
+        qemu_chr_fe_set_open(&vcon->chr, guest_connected);
     }
 
     if (dev->id) {
@@ -122,9 +122,7 @@ static void guest_writable(VirtIOSerialPort *port)
 {
     VirtConsole *vcon = VIRTIO_CONSOLE(port);
 
-    if (vcon->chr) {
-        qemu_chr_accept_input(vcon->chr);
-    }
+    qemu_chr_fe_accept_input(&vcon->chr);
 }
 
 /* Readiness of the guest to accept data on a port */
@@ -170,6 +168,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 +176,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp)
         return;
     }
 
-    if (vcon->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 +187,12 @@ static void virtconsole_realize(DeviceState *dev, Error **errp)
          * trigger open/close of the device
          */
         if (k->is_console) {
-            vcon->chr->explicit_fe_open = 0;
-            qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read,
-                                  NULL, vcon);
+            qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
+                                     NULL, vcon, NULL, true);
             virtio_serial_open(port);
         } else {
-            vcon->chr->explicit_fe_open = 1;
-            qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read,
-                                  chr_event, vcon);
+            qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
+                                     chr_event, vcon, NULL, false);
         }
     }
 }
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index 83108b0bdb..86cdc529a3 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -23,9 +23,11 @@
 #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"
+#include "qapi/error.h"
 
 #include <xen/io/console.h>
 
@@ -43,7 +45,7 @@ struct XenConsole {
     char              console[XEN_BUFSIZE];
     int               ring_ref;
     void              *sring;
-    CharDriverState   *chr;
+    CharBackend       chr;
     int               backlog;
 };
 
@@ -148,11 +150,13 @@ static void xencons_send(struct XenConsole *con)
     ssize_t len, size;
 
     size = con->buffer.size - con->buffer.consumed;
-    if (con->chr)
-        len = qemu_chr_fe_write(con->chr, con->buffer.data + con->buffer.consumed,
-                             size);
-    else
+    if (qemu_chr_fe_get_driver(&con->chr)) {
+        len = qemu_chr_fe_write(&con->chr,
+                                con->buffer.data + con->buffer.consumed,
+                                size);
+    } else {
         len = size;
+    }
     if (len < 1) {
 	if (!con->backlog) {
 	    con->backlog = 1;
@@ -196,13 +200,18 @@ static int con_init(struct XenDevice *xendev)
 
     /* no Xen override, use qemu output device */
     if (output == NULL) {
-        con->chr = serial_hds[con->xendev.dev];
+        if (con->xendev.dev) {
+            qemu_chr_fe_init(&con->chr, serial_hds[con->xendev.dev],
+                             &error_abort);
+        }
     } else {
         snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
-        con->chr = qemu_chr_new(label, output, NULL);
+        qemu_chr_fe_init(&con->chr,
+                         qemu_chr_new(label, output), &error_abort);
     }
 
-    xenstore_store_pv_console_info(con->xendev.dev, con->chr);
+    xenstore_store_pv_console_info(con->xendev.dev,
+                                   qemu_chr_fe_get_driver(&con->chr));
 
 out:
     g_free(type);
@@ -235,17 +244,8 @@ static int con_initialise(struct XenDevice *xendev)
 	return -1;
 
     xen_be_bind_evtchn(&con->xendev);
-    if (con->chr) {
-        if (qemu_chr_fe_claim(con->chr) == 0) {
-            qemu_chr_add_handlers(con->chr, xencons_can_receive,
-                                  xencons_receive, NULL, con);
-        } else {
-            xen_be_printf(xendev, 0,
-                          "xen_console_init error chardev %s already used\n",
-                          con->chr->label);
-            con->chr = NULL;
-        }
-    }
+    qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
+                             xencons_receive, NULL, con, NULL, true);
 
     xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
 		  con->ring_ref,
@@ -259,10 +259,7 @@ static void con_disconnect(struct XenDevice *xendev)
 {
     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
 
-    if (con->chr) {
-        qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(con->chr);
-    }
+    qemu_chr_fe_deinit(&con->chr);
     xen_be_unbind_evtchn(&con->xendev);
 
     if (con->sring) {
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
index 3766dc2c5b..37d313b429 100644
--- a/hw/char/xilinx_uartlite.c
+++ b/hw/char/xilinx_uartlite.c
@@ -55,7 +55,7 @@ typedef struct XilinxUARTLite {
     SysBusDevice parent_obj;
 
     MemoryRegion mmio;
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
 
     uint8_t rx_fifo[8];
@@ -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_accept_input(s->chr);
+            qemu_chr_fe_accept_input(&s->chr);
             break;
 
         default:
@@ -143,11 +143,9 @@ uart_write(void *opaque, hwaddr addr,
             break;
 
         case R_TX:
-            if (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, &ch, 1);
-
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->regs[addr] = value;
 
             /* hax.  */
@@ -213,8 +211,8 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp)
 {
     XilinxUARTLite *s = XILINX_UARTLITE(dev);
 
-    if (s->chr)
-        qemu_chr_add_handlers(s->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, true);
 }
 
 static void xilinx_uartlite_init(Object *obj)
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index e55afe6bf2..c35f0f59d6 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -160,55 +160,67 @@ PropertyInfo qdev_prop_drive = {
 
 /* --- character device --- */
 
-static void parse_chr(DeviceState *dev, const char *str, void **ptr,
-                      const char *propname, Error **errp)
+static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
 {
-    CharDriverState *chr = qemu_chr_find(str);
-    if (chr == NULL) {
-        error_setg(errp, "Property '%s.%s' can't find value '%s'",
-                   object_get_typename(OBJECT(dev)), propname, str);
-        return;
-    }
-    if (qemu_chr_fe_claim(chr) != 0) {
-        error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use",
-                  object_get_typename(OBJECT(dev)), propname, str);
-        return;
-    }
-    *ptr = chr;
+    DeviceState *dev = DEVICE(obj);
+    CharBackend *be = qdev_get_prop_ptr(dev, opaque);
+    char *p;
+
+    p = g_strdup(be->chr && be->chr->label ? be->chr->label : "");
+    visit_type_str(v, name, &p, errp);
+    g_free(p);
 }
 
-static void release_chr(Object *obj, const char *name, void *opaque)
+static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
+    Error *local_err = NULL;
     Property *prop = opaque;
-    CharDriverState **ptr = qdev_get_prop_ptr(dev, prop);
-    CharDriverState *chr = *ptr;
+    CharBackend *be = qdev_get_prop_ptr(dev, prop);
+    CharDriverState *s;
+    char *str;
 
-    if (chr) {
-        qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(chr);
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, name, &str, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
     }
-}
 
+    if (!*str) {
+        g_free(str);
+        be->chr = NULL;
+        return;
+    }
 
-static char *print_chr(void *ptr)
-{
-    CharDriverState *chr = ptr;
-    const char *val = chr->label ? chr->label : "";
+    s = qemu_chr_find(str);
+    g_free(str);
+    if (s == NULL) {
+        error_setg(errp, "Property '%s.%s' can't find value '%s'",
+                   object_get_typename(obj), prop->name, str);
+        return;
+    }
 
-    return g_strdup(val);
+    if (!qemu_chr_fe_init(be, s, errp)) {
+        error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
+                      object_get_typename(obj), prop->name, str);
+        return;
+    }
 }
 
-static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
-                    Error **errp)
+static void release_chr(Object *obj, const char *name, void *opaque)
 {
-    get_pointer(obj, v, opaque, print_chr, name, errp);
-}
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    CharBackend *be = qdev_get_prop_ptr(dev, prop);
 
-static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
-                    Error **errp)
-{
-    set_pointer(obj, v, opaque, parse_chr, name, errp);
+    qemu_chr_fe_deinit(be);
 }
 
 PropertyInfo qdev_prop_chr = {
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index f4b0cda707..fb8f29c34c 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1589,7 +1589,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
         pcspk_init(isa_bus, pit);
     }
 
-    serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
+    serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
     parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
 
     a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index d93e3f3426..4b310e5eff 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -62,7 +62,7 @@
 typedef struct IPMIBmcExtern {
     IPMIBmc parent;
 
-    CharDriverState *chr;
+    CharBackend chr;
 
     bool connected;
 
@@ -105,7 +105,7 @@ static void continue_send(IPMIBmcExtern *ibe)
         goto check_reset;
     }
  send:
-    ret = qemu_chr_fe_write(ibe->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) {
+    if (!qemu_chr_fe_get_driver(&ibe->chr)) {
         error_setg(errp, "IPMI external bmc requires chardev attribute");
         return;
     }
 
-    qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe);
+    qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
+                             chr_event, ibe, NULL, true);
 }
 
 static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c
index c3ebf3e7a0..b1c1a0acb1 100644
--- a/hw/isa/pc87312.c
+++ b/hw/isa/pc87312.c
@@ -283,7 +283,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp)
         /* FIXME use a qdev chardev prop instead of parallel_hds[] */
         chr = parallel_hds[0];
         if (chr == NULL) {
-            chr = qemu_chr_new("par0", "null", NULL);
+            chr = qemu_chr_new("par0", "null");
         }
         isa = isa_create(bus, "isa-parallel");
         d = DEVICE(isa);
@@ -303,7 +303,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp)
             chr = serial_hds[i];
             if (chr == NULL) {
                 snprintf(name, sizeof(name), "ser%d", i);
-                chr = qemu_chr_new(name, "null", NULL);
+                chr = qemu_chr_new(name, "null");
             }
             isa = isa_create(bus, "isa-serial");
             d = DEVICE(isa);
diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c
index 889cdc7ca7..9a4dae42d9 100644
--- a/hw/mips/mips_fulong2e.c
+++ b/hw/mips/mips_fulong2e.c
@@ -374,7 +374,7 @@ static void mips_fulong2e_init(MachineState *machine)
 
     rtc_init(isa_bus, 2000, NULL);
 
-    serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
+    serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
     parallel_hds_isa_init(isa_bus, 1);
 
     /* Sound card */
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index e90857ee0b..cf9bd3eb45 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -85,9 +85,10 @@ typedef struct {
     uint32_t i2coe;
     uint32_t i2cout;
     uint32_t i2csel;
-    CharDriverState *display;
+    CharBackend display;
     char display_text[9];
     SerialState *uart;
+    bool display_inited;
 } MaltaFPGAState;
 
 #define TYPE_MIPS_MALTA "mips-malta"
@@ -124,8 +125,10 @@ static void malta_fpga_update_display(void *opaque)
     }
     leds_text[8] = '\0';
 
-    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, "\n\n\n\n|\e[31m%-8.8s\e[00m|", s->display_text);
+    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, "\n\n\n\n|\e[31m%-8.8s\e[00m|",
+                       s->display_text);
 }
 
 /*
@@ -530,23 +533,29 @@ static void malta_fpga_reset(void *opaque)
     snprintf(s->display_text, 9, "        ");
 }
 
-static void malta_fpga_led_init(CharDriverState *chr)
+static void malta_fgpa_display_event(void *opaque, int event)
 {
-    qemu_chr_fe_printf(chr, "\e[HMalta LEDBAR\r\n");
-    qemu_chr_fe_printf(chr, "+--------+\r\n");
-    qemu_chr_fe_printf(chr, "+        +\r\n");
-    qemu_chr_fe_printf(chr, "+--------+\r\n");
-    qemu_chr_fe_printf(chr, "\n");
-    qemu_chr_fe_printf(chr, "Malta ASCII\r\n");
-    qemu_chr_fe_printf(chr, "+--------+\r\n");
-    qemu_chr_fe_printf(chr, "+        +\r\n");
-    qemu_chr_fe_printf(chr, "+--------+\r\n");
+    MaltaFPGAState *s = opaque;
+
+    if (event == CHR_EVENT_OPENED && !s->display_inited) {
+        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;
+    }
 }
 
 static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
          hwaddr base, qemu_irq uart_irq, CharDriverState *uart_chr)
 {
     MaltaFPGAState *s;
+    CharDriverState *chr;
 
     s = (MaltaFPGAState *)g_malloc0(sizeof(MaltaFPGAState));
 
@@ -560,7 +569,10 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
     memory_region_add_subregion(address_space, base, &s->iomem_lo);
     memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi);
 
-    s->display = qemu_chr_new("fpga", "vc:320x200", malta_fpga_led_init);
+    chr = qemu_chr_new("fpga", "vc:320x200");
+    qemu_chr_fe_init(&s->display, chr, NULL);
+    qemu_chr_fe_set_handlers(&s->display, NULL, NULL,
+                             malta_fgpa_display_event, s, NULL, true);
 
     s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq,
                              230400, uart_chr, DEVICE_NATIVE_ENDIAN);
@@ -1025,7 +1037,7 @@ void mips_malta_init(MachineState *machine)
         if (!serial_hds[i]) {
             char label[32];
             snprintf(label, sizeof(label), "serial%d", i);
-            serial_hds[i] = qemu_chr_new(label, "null", NULL);
+            serial_hds[i] = qemu_chr_new(label, "null");
         }
     }
 
@@ -1215,7 +1227,7 @@ void mips_malta_init(MachineState *machine)
     isa_create_simple(isa_bus, "i8042");
 
     rtc_init(isa_bus, 2000, NULL);
-    serial_hds_isa_init(isa_bus, 2);
+    serial_hds_isa_init(isa_bus, 0, 2);
     parallel_hds_isa_init(isa_bus, 1);
 
     for(i = 0; i < MAX_FD; i++) {
diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c
index 16a59c779c..27548c43b6 100644
--- a/hw/mips/mips_r4k.c
+++ b/hw/mips/mips_r4k.c
@@ -286,7 +286,7 @@ void mips_r4k_init(MachineState *machine)
 
     pit = pit_init(isa_bus, 0x40, 0, NULL);
 
-    serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
+    serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
 
     isa_vga_init(isa_bus);
 
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index f803dfd5b3..230e51b6e0 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -88,7 +88,7 @@ typedef struct IVShmemState {
 
     /* exactly one of these two may be set */
     HostMemoryBackend *hostmem; /* with interrupts */
-    CharDriverState *server_chr; /* without interrupts */
+    CharBackend server_chr; /* without interrupts */
 
     /* registers */
     uint32_t intrmask;
@@ -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);
+    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, (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);
+    *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);
+        CharDriverState *chr = qemu_chr_fe_get_driver(&s->server_chr);
+        assert(chr);
 
         IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
-                        s->server_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, ivshmem_can_receive,
-                              ivshmem_read, NULL, s);
+        qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive,
+                                 ivshmem_read, NULL, s, NULL, true);
 
         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) {
+    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 + !!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/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 3165e18eb7..7b8134ef51 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -824,7 +824,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
         i++;
     }
 
-    serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
+    serial_hds_isa_init(isa_bus, i, MAX_SERIAL_PORTS);
     parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
 
     for(i = 0; i < nb_nics; i++)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index e2b77dc3de..da8adfa443 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -607,6 +607,7 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
     XilinxSPIPS *s = XILINX_SPIPS(dev);
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
     XilinxSPIPSClass *xsc = XILINX_SPIPS_GET_CLASS(s);
+    qemu_irq *cs;
     int i;
 
     DB_PRINT_L(0, "realized spips\n");
@@ -619,8 +620,10 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
     }
 
     s->cs_lines = g_new0(qemu_irq, s->num_cs * s->num_busses);
-    ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi[0]);
-    ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi[1]);
+    for (i = 0, cs = s->cs_lines; i < s->num_busses; ++i, cs += s->num_cs) {
+        ssi_auto_connect_slaves(DEVICE(s), cs, s->spi[i]);
+    }
+
     sysbus_init_irq(sbd, &s->irq);
     for (i = 0; i < s->num_cs * s->num_busses; ++i) {
         sysbus_init_irq(sbd, &s->cs_lines[i]);
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 2eacea72f3..325129a2f6 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -48,7 +48,7 @@ typedef struct PassthruState PassthruState;
 
 struct PassthruState {
     CCIDCardState base;
-    CharDriverState *cs;
+    CharBackend cs;
     uint8_t  vscard_in_data[VSCARD_IN_SIZE];
     uint32_t vscard_in_pos;
     uint32_t vscard_in_hdr;
@@ -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, (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, payload, length);
+    qemu_chr_fe_write_all(&s->cs, payload, length);
 }
 
 static void ccid_card_vscard_send_apdu(PassthruState *s,
@@ -264,7 +264,10 @@ static void ccid_card_vscard_handle_message(PassthruState *card,
 
 static void ccid_card_vscard_drop_connection(PassthruState *card)
 {
-    qemu_chr_delete(card->cs);
+    CharDriverState *chr = qemu_chr_fe_get_driver(&card->cs);
+
+    qemu_chr_fe_deinit(&card->cs);
+    qemu_chr_delete(chr);
     card->vscard_in_pos = card->vscard_in_hdr = 0;
 }
 
@@ -309,8 +312,6 @@ static void ccid_card_vscard_event(void *opaque, int event)
     case CHR_EVENT_BREAK:
         card->vscard_in_pos = card->vscard_in_hdr = 0;
         break;
-    case CHR_EVENT_FOCUS:
-        break;
     case CHR_EVENT_OPENED:
         DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__);
         break;
@@ -324,7 +325,7 @@ static void passthru_apdu_from_guest(
 {
     PassthruState *card = PASSTHRU_CCID_CARD(base);
 
-    if (!card->cs) {
+    if (!qemu_chr_fe_get_driver(&card->cs)) {
         printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
         return;
     }
@@ -345,12 +346,12 @@ static int passthru_initfn(CCIDCardState *base)
 
     card->vscard_in_pos = 0;
     card->vscard_in_hdr = 0;
-    if (card->cs) {
+    if (qemu_chr_fe_get_driver(&card->cs)) {
         DPRINTF(card, D_INFO, "initing chardev\n");
-        qemu_chr_add_handlers(card->cs,
+        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, true);
         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 966ad84b90..6066d9b0f7 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -103,7 +103,7 @@ typedef struct {
     uint8_t event_trigger;
     QEMUSerialSetParams params;
     int latency;        /* ms */
-    CharDriverState *cs;
+    CharBackend cs;
 } USBSerialState;
 
 #define TYPE_USB_SERIAL "usb-serial-dev"
@@ -209,8 +209,10 @@ static uint8_t usb_get_modem_lines(USBSerialState *s)
     int flags;
     uint8_t ret;
 
-    if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP)
+    if (qemu_chr_fe_ioctl(&s->cs,
+                          CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
         return FTDI_CTS|FTDI_DSR|FTDI_RLSD;
+    }
 
     ret = 0;
     if (flags & CHR_TIOCM_CTS)
@@ -260,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_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;
@@ -273,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_IOCTL_SERIAL_SET_TIOCM, &flags);
+        qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
         break;
     }
     case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
@@ -292,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_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:
@@ -321,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_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:
@@ -368,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, 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;
@@ -464,8 +466,6 @@ static void usb_serial_event(void *opaque, int event)
         case CHR_EVENT_BREAK:
             s->event_trigger |= FTDI_BI;
             break;
-        case CHR_EVENT_FOCUS:
-            break;
         case CHR_EVENT_OPENED:
             if (!s->dev.attached) {
                 usb_device_attach(&s->dev, &error_abort);
@@ -483,12 +483,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) {
+    if (!chr) {
         error_setg(errp, "Property chardev is required");
         return;
     }
@@ -499,11 +500,11 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
         return;
     }
 
-    qemu_chr_add_handlers(s->cs, 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, true);
     usb_serial_handle_reset(dev);
 
-    if (s->cs->be_open && !dev->attached) {
+    if (chr->be_open && !dev->attached) {
         usb_device_attach(dev, &error_abort);
     }
 }
@@ -547,7 +548,7 @@ static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
     filename++;
 
     snprintf(label, sizeof(label), "usbserial%d", index++);
-    cdrv = qemu_chr_new(label, filename, NULL);
+    cdrv = qemu_chr_new(label, filename);
     if (!cdrv)
         return NULL;
 
@@ -565,7 +566,7 @@ static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
     USBDevice *dev;
     CharDriverState *cdrv;
 
-    cdrv = qemu_chr_new("braille", "braille", NULL);
+    cdrv = qemu_chr_new("braille", "braille");
     if (!cdrv)
         return NULL;
 
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index d4ca026f00..528081e581 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -105,7 +105,7 @@ struct PacketIdQueue {
 struct USBRedirDevice {
     USBDevice dev;
     /* Properties */
-    CharDriverState *cs;
+    CharBackend cs;
     uint8_t debug;
     char *filter_str;
     int32_t bootindex;
@@ -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->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, 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, 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 == 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, 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, true);
 
     qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
 }
@@ -1426,9 +1428,11 @@ 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_fe_deinit(&dev->cs);
+    qemu_chr_delete(chr);
 
-    qemu_chr_delete(dev->cs);
-    dev->cs = NULL;
     /* Note must be done after qemu_chr_close, as that causes a close event */
     qemu_bh_delete(dev->chardev_close_bh);
     qemu_bh_delete(dev->device_reject_bh);
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index b57454a4b7..7ee92b32c5 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/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c
index ac75949484..dc6fdcc266 100644
--- a/hw/xtensa/xtfpga.c
+++ b/hw/xtensa/xtfpga.c
@@ -265,7 +265,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
     }
 
     if (!serial_hds[0]) {
-        serial_hds[0] = qemu_chr_new("serial0", "null", NULL);
+        serial_hds[0] = qemu_chr_new("serial0", "null");
     }
 
     serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0),