summary refs log tree commit diff stats
path: root/hw/char
diff options
context:
space:
mode:
Diffstat (limited to 'hw/char')
-rw-r--r--hw/char/bcm2835_aux.c12
-rw-r--r--hw/char/cadence_uart.c26
-rw-r--r--hw/char/debugcon.c8
-rw-r--r--hw/char/digic-uart.c8
-rw-r--r--hw/char/escc.c21
-rw-r--r--hw/char/etraxfs_ser.c8
-rw-r--r--hw/char/exynos4210_uart.c10
-rw-r--r--hw/char/grlib_apbuart.c8
-rw-r--r--hw/char/imx_serial.c19
-rw-r--r--hw/char/ipoctal232.c14
-rw-r--r--hw/char/lm32_juart.c11
-rw-r--r--hw/char/lm32_uart.c12
-rw-r--r--hw/char/milkymist-uart.c12
-rw-r--r--hw/char/parallel.c46
-rw-r--r--hw/char/pl011.c15
-rw-r--r--hw/char/sclpconsole-lm.c13
-rw-r--r--hw/char/sclpconsole.c10
-rw-r--r--hw/char/serial.c34
-rw-r--r--hw/char/spapr_vty.c10
-rw-r--r--hw/char/stm32f2xx_usart.c16
-rw-r--r--hw/char/virtio-console.c28
-rw-r--r--hw/char/xilinx_uartlite.c15
22 files changed, 185 insertions, 171 deletions
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index f7a845d3e2..4bc5d02135 100644
--- a/hw/char/bcm2835_aux.c
+++ b/hw/char/bcm2835_aux.c
@@ -79,8 +79,8 @@ 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);
+        if (s->chr.chr) {
+            qemu_chr_accept_input(s->chr.chr);
         }
         bcm2835_aux_update(s);
         return c;
@@ -168,10 +168,10 @@ 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) {
+        if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr, &ch, 1);
+            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
         }
         break;
 
@@ -282,8 +282,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,
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, bcm2835_aux_can_receive,
                               bcm2835_aux_receive, NULL, s);
     }
 }
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index e3bc52f7df..d5687dd61f 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -142,8 +142,8 @@ static void uart_rx_reset(CadenceUARTState *s)
 {
     s->rx_wpos = 0;
     s->rx_count = 0;
-    if (s->chr) {
-        qemu_chr_accept_input(s->chr);
+    if (s->chr.chr) {
+        qemu_chr_accept_input(s->chr.chr);
     }
 }
 
@@ -156,8 +156,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,
+    if (s->chr.chr) {
+        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_BREAK,
                                    &break_enabled);
     }
 }
@@ -210,8 +210,8 @@ 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);
+    if (s->chr.chr) {
+        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
     }
 }
 
@@ -278,7 +278,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
     int ret;
 
     /* instant drain the fifo when there's no back-end */
-    if (!s->chr) {
+    if (!s->chr.chr) {
         s->tx_count = 0;
         return FALSE;
     }
@@ -287,7 +287,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
         return FALSE;
     }
 
-    ret = qemu_chr_fe_write(s->chr, s->tx_fifo, s->tx_count);
+    ret = qemu_chr_fe_write(s->chr.chr, s->tx_fifo, s->tx_count);
 
     if (ret >= 0) {
         s->tx_count -= ret;
@@ -295,7 +295,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
     }
 
     if (s->tx_count) {
-        guint r = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP,
+        guint r = qemu_chr_fe_add_watch(s->chr.chr, G_IO_OUT | G_IO_HUP,
                                         cadence_uart_xmit, s);
         if (!r) {
             s->tx_count = 0;
@@ -368,8 +368,8 @@ 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);
+        if (s->chr.chr) {
+            qemu_chr_accept_input(s->chr.chr);
         }
     } else {
         *c = 0;
@@ -474,8 +474,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,
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, uart_can_receive, uart_receive,
                               uart_event, s);
     }
 }
diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c
index 4402033861..b405109eb5 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.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 (!s->chr.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_add_handlers(s->chr.chr, NULL, NULL, NULL, s);
 }
 
 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..fb4969fc50 100644
--- a/hw/char/digic-uart.c
+++ b/hw/char/digic-uart.c
@@ -76,10 +76,10 @@ static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value,
 
     switch (addr) {
     case R_TX:
-        if (s->chr) {
+        if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr, &ch, 1);
+            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
         }
         break;
 
@@ -147,8 +147,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);
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, uart_can_rx, uart_rx, uart_event, s);
     }
 }
 
diff --git a/hw/char/escc.c b/hw/char/escc.c
index aa1739762b..ae69b392a4 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 (!s->chr.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, 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 (s->chr.chr) {
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(s->chr, &s->tx, 1);
-            else if (s->type == kbd && !s->disabled) {
+                qemu_chr_fe_write_all(s->chr.chr, &s->tx, 1);
+            } else if (s->type == kbd && !s->disabled) {
                 handle_kbd_command(s, val);
             }
         }
@@ -599,8 +599,9 @@ 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);
+        if (s->chr.chr) {
+            qemu_chr_accept_input(s->chr.chr);
+        }
         return ret;
     default:
         break;
@@ -1013,9 +1014,9 @@ 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 (s->chn[i].chr.chr) {
             s->chn[i].clock = s->frequency / 2;
-            qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
+            qemu_chr_add_handlers(s->chn[i].chr.chr, serial_can_receive,
                                   serial_receive1, serial_event, &s->chn[i]);
         }
     }
diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c
index c99cc5d130..99c4801ba6 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.chr, &ch, 1);
             s->regs[R_INTR] |= 3;
             s->pending_tx = 1;
             s->regs[addr] = value;
@@ -231,8 +231,8 @@ static void etraxfs_ser_realize(DeviceState *dev, Error **errp)
 {
     ETRAXSerial *s = ETRAX_SERIAL(dev);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->chr,
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr,
                               serial_can_receive, serial_receive,
                               serial_event, s);
     }
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index 66e630409b..3f71059eee 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, 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 (s->chr.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.chr, &ch, 1);
 #if DEBUG_Tx_DATA
             fprintf(stderr, "%c", ch);
 #endif
@@ -640,7 +640,7 @@ static int exynos4210_uart_init(SysBusDevice *dev)
 
     sysbus_init_irq(dev, &s->irq);
 
-    qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive,
+    qemu_chr_add_handlers(s->chr.chr, exynos4210_uart_can_receive,
                           exynos4210_uart_receive, exynos4210_uart_event, s);
 
     return 0;
diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c
index 778148a15e..13c9455ed6 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,11 @@ 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 (uart->chr.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.chr, &c, 1);
             /* Generate interrupt */
             if (uart->control & UART_TRANSMIT_INTERRUPT) {
                 qemu_irq_pulse(uart->irq);
@@ -242,7 +242,7 @@ static int grlib_apbuart_init(SysBusDevice *dev)
 {
     UART *uart = GRLIB_APB_UART(dev);
 
-    qemu_chr_add_handlers(uart->chr,
+    qemu_chr_add_handlers(uart->chr.chr,
                           grlib_apbuart_can_receive,
                           grlib_apbuart_receive,
                           grlib_apbuart_event,
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 5c3fa61e4c..5c11de2cbe 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -121,8 +121,8 @@ 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);
+            if (s->chr.chr) {
+                qemu_chr_accept_input(s->chr.chr);
             }
         }
         return c;
@@ -175,16 +175,17 @@ static void imx_serial_write(void *opaque, hwaddr offset,
     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,
+            s->chr.chr ? s->chr.chr->label : "NODEV");
 
     switch (offset >> 2) {
     case 0x10: /* UTXD */
         ch = value;
         if (s->ucr2 & UCR2_TXEN) {
-            if (s->chr) {
+            if (s->chr.chr) {
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(s->chr, &ch, 1);
+                qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
             }
             s->usr1 &= ~USR1_TRDY;
             imx_update(s);
@@ -214,8 +215,8 @@ 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);
+                if (s->chr.chr) {
+                    qemu_chr_accept_input(s->chr.chr);
                 }
             }
         }
@@ -318,8 +319,8 @@ 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,
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, imx_can_receive, imx_receive,
                               imx_event, s);
     } else {
         DPRINTF("No char dev for uart\n");
diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c
index 2859fdd7fb..0c9dea322e 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,8 +288,8 @@ 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);
+                if (ch->dev.chr) {
+                    qemu_chr_accept_input(ch->dev.chr);
                 }
             } else {
                 ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
@@ -358,11 +358,11 @@ static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
     case REG_THRb:
         if (ch->sr & SR_TXRDY) {
             DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
-            if (ch->dev) {
+            if (ch->dev.chr) {
                 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);
+                qemu_chr_fe_write_all(ch->dev.chr, &thr, 1);
             }
         } else {
             DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
@@ -546,8 +546,8 @@ 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,
+        if (ch->dev.chr) {
+            qemu_chr_add_handlers(ch->dev.chr, hostdev_can_receive,
                                   hostdev_receive, hostdev_event, ch);
             DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label);
         } else {
diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c
index cb1ac76731..a0eb3127b3 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,10 +75,10 @@ void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx)
     trace_lm32_juart_set_jtx(s->jtx);
 
     s->jtx = jtx;
-    if (s->chr) {
+    if (s->chr.chr) {
         /* XXX this blocks entire thread. Rewrite to use
          * qemu_chr_fe_write and background I/O callbacks */
-        qemu_chr_fe_write_all(s->chr, &ch, 1);
+        qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
     }
 }
 
@@ -120,8 +120,9 @@ 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);
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, juart_can_rx,
+                              juart_rx, juart_event, s);
     }
 }
 
diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c
index be93697a39..72fc41c90f 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_accept_input(s->chr.chr);
         break;
     case R_IIR:
     case R_LSR:
@@ -177,10 +177,10 @@ static void uart_write(void *opaque, hwaddr addr,
     addr >>= 2;
     switch (addr) {
     case R_RXTX:
-        if (s->chr) {
+        if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr, &ch, 1);
+            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
         }
         break;
     case R_IER:
@@ -267,8 +267,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);
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, uart_can_rx, uart_rx, uart_event, s);
     }
 }
 
diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c
index baddb37648..a6518e62b0 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,8 +124,8 @@ 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);
+        if (s->chr.chr) {
+            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
         }
         s->regs[R_STAT] |= STAT_TX_EVT;
         break;
@@ -138,7 +138,7 @@ static void uart_write(void *opaque, hwaddr addr, uint64_t value,
     case R_STAT:
         /* write one to clear bits */
         s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT));
-        qemu_chr_accept_input(s->chr);
+        qemu_chr_accept_input(s->chr.chr);
         break;
 
     default:
@@ -200,8 +200,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);
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, uart_can_rx, uart_rx, uart_event, s);
     }
 }
 
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index da22e36356..80576afc1d 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.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, 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, 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, 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,
+                                  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, 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, 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, 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, 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, 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, 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,
+                                  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, 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, 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, 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 (!s->chr.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, 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..9645195f90 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,8 +87,8 @@ 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);
+        if (s->chr.chr) {
+            qemu_chr_accept_input(s->chr.chr);
         }
         r = c;
         break;
@@ -168,10 +168,11 @@ static void pl011_write(void *opaque, hwaddr offset,
     case 0: /* UARTDR */
         /* ??? Check if transmitter is enabled.  */
         ch = value;
-        if (s->chr)
+        if (s->chr.chr) {
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr, &ch, 1);
+            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+        }
         s->int_level |= PL011_INT_TX;
         pl011_update(s);
         break;
@@ -331,8 +332,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,
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, pl011_can_receive, pl011_receive,
                               pl011_event, s);
     }
 }
diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c
index 9a563269e6..3ef1517274 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.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 (!scon->chr.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.chr, buf, len);
 }
 
 static int process_mdb(SCLPEvent *event, MDBO *mdbo)
@@ -312,8 +312,9 @@ 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);
+    if (scon->chr.chr) {
+        qemu_chr_add_handlers(scon->chr.chr, chr_can_read,
+                              chr_read, NULL, scon);
     }
 
     return 0;
diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c
index a75ad4f60a..bb51a2c28d 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 (!scon->chr.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.chr, buf, len);
 }
 
 static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
@@ -227,8 +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,
+    if (scon->chr.chr) {
+        qemu_chr_add_handlers(scon->chr.chr, chr_can_read,
                               chr_read, NULL, scon);
     }
 
diff --git a/hw/char/serial.c b/hw/char/serial.c
index eec72b7b9e..18c748231c 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -182,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, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 
     DPRINTF("speed=%d parity=%c data=%d stop=%d\n",
            speed, parity, data_bits, stop_bits);
@@ -195,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, CHR_IOCTL_SERIAL_GET_TIOCM,
+                          &flags) == -ENOTSUP) {
         s->poll_msl = -1;
         return;
     }
@@ -260,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.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.chr, G_IO_OUT | G_IO_HUP,
+                                      serial_watch_cb, s);
             if (s->watch_tag > 0) {
                 s->tsr_retry++;
                 return;
@@ -417,7 +419,7 @@ 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,
+                qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_BREAK,
                                &break_enable);
             }
         }
@@ -432,7 +434,8 @@ 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,
+                                  CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
 
                 flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR);
 
@@ -441,7 +444,8 @@ 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,
+                                  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);
@@ -486,7 +490,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_accept_input(s->chr.chr);
             }
         }
         break;
@@ -659,7 +663,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.chr, G_IO_OUT | G_IO_HUP,
                                              serial_watch_cb, s);
     } else {
         /* tsr_retry == 0 implies LSR.TEMT = 1 (transmitter empty).  */
@@ -884,7 +888,7 @@ static void serial_reset(void *opaque)
 
 void serial_realize_core(SerialState *s, Error **errp)
 {
-    if (!s->chr) {
+    if (!s->chr.chr) {
         error_setg(errp, "Can't create serial device, empty char device");
         return;
     }
@@ -894,7 +898,7 @@ 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,
+    qemu_chr_add_handlers(s->chr.chr, serial_can_receive1, serial_receive1,
                           serial_event, s);
     fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH);
     fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH);
@@ -903,7 +907,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_add_handlers(s->chr.chr, NULL, NULL, NULL, NULL);
     qemu_unregister_reset(serial_reset, s);
 }
 
@@ -933,7 +937,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);
@@ -990,7 +994,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/spapr_vty.c b/hw/char/spapr_vty.c
index 9aeafc0c42..dcb4a85d97 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_accept_input(dev->chardev.chr);
 
     return n;
 }
@@ -62,19 +62,19 @@ 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.chr, buf, len);
 }
 
 static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp)
 {
     VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev);
 
-    if (!dev->chardev) {
+    if (!dev->chardev.chr) {
         error_setg(errp, "chardev property not set");
         return;
     }
 
-    qemu_chr_add_handlers(dev->chardev, vty_can_receive,
+    qemu_chr_add_handlers(dev->chardev.chr, vty_can_receive,
                           vty_receive, NULL, dev);
 }
 
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 4c6640dbe9..8cc27372f0 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -97,16 +97,16 @@ 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);
+        if (s->chr.chr) {
+            qemu_chr_accept_input(s->chr.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);
+        if (s->chr.chr) {
+            qemu_chr_accept_input(s->chr.chr);
         }
         qemu_set_irq(s->irq, 0);
         return s->usart_dr & 0x3FF;
@@ -152,10 +152,10 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
     case USART_DR:
         if (value < 0xF000) {
             ch = value;
-            if (s->chr) {
+            if (s->chr.chr) {
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(s->chr, &ch, 1);
+                qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
             }
             s->usart_sr |= USART_SR_TC;
             s->usart_sr &= ~USART_SR_TXE;
@@ -212,8 +212,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,
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, stm32f2xx_usart_can_receive,
                               stm32f2xx_usart_receive, NULL, s);
     }
 }
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index d44c18c128..a4730baafb 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 (!vcon->chr.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.chr, buf, len);
     trace_virtio_console_flush_buf(port->id, len, ret);
 
     if (ret < len) {
@@ -92,8 +92,8 @@ static ssize_t flush_buf(VirtIOSerialPort *port,
         if (!k->is_console) {
             virtio_serial_throttle_port(port, true);
             if (!vcon->watch) {
-                vcon->watch = qemu_chr_fe_add_watch(vcon->chr,
-                                                    G_IO_OUT|G_IO_HUP,
+                vcon->watch = qemu_chr_fe_add_watch(vcon->chr.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 (vcon->chr.chr && !k->is_console) {
+        qemu_chr_fe_set_open(vcon->chr.chr, guest_connected);
     }
 
     if (dev->id) {
@@ -122,8 +122,8 @@ static void guest_writable(VirtIOSerialPort *port)
 {
     VirtConsole *vcon = VIRTIO_CONSOLE(port);
 
-    if (vcon->chr) {
-        qemu_chr_accept_input(vcon->chr);
+    if (vcon->chr.chr) {
+        qemu_chr_accept_input(vcon->chr.chr);
     }
 }
 
@@ -177,7 +177,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp)
         return;
     }
 
-    if (vcon->chr) {
+    if (vcon->chr.chr) {
         /*
          * For consoles we don't block guest data transfer just
          * because nothing is connected - we'll just let it go
@@ -188,13 +188,13 @@ 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,
+            vcon->chr.chr->explicit_fe_open = 0;
+            qemu_chr_add_handlers(vcon->chr.chr, chr_can_read, chr_read,
                                   NULL, vcon);
             virtio_serial_open(port);
         } else {
-            vcon->chr->explicit_fe_open = 1;
-            qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read,
+            vcon->chr.chr->explicit_fe_open = 1;
+            qemu_chr_add_handlers(vcon->chr.chr, chr_can_read, chr_read,
                                   chr_event, vcon);
         }
     }
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
index 3766dc2c5b..0e809d52fe 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_accept_input(s->chr.chr);
             break;
 
         default:
@@ -143,11 +143,11 @@ uart_write(void *opaque, hwaddr addr,
             break;
 
         case R_TX:
-            if (s->chr)
+            if (s->chr.chr) {
                 /* XXX this blocks entire thread. Rewrite to use
                  * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(s->chr, &ch, 1);
-
+                qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+            }
             s->regs[addr] = value;
 
             /* hax.  */
@@ -213,8 +213,9 @@ 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);
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, uart_can_rx, uart_rx, uart_event, s);
+    }
 }
 
 static void xilinx_uartlite_init(Object *obj)