qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression")
@ 2016-10-22  9:52 Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 01/38] rng: remove unused included header Marc-André Lureau
                   ` (25 more replies)
  0 siblings, 26 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Hi,

This is a followup of the series "[PATCH 0/9] Fix mux regression
(commit 949055a2)". Paolo suggested a new API for qemu_chr_fe_* taking
a new CharBackend* structure as argument, and modifying properties to
hold such structure. I followed his advise in the series, and it turns
out to bring some nice clean-up benefits. I continue a bit the
cleaning, and there is 3 structures after this series: CharBackend (to
hold backend users), CharDriverState (instance of char), CharDriver
(the char "class"), it would be easy to come up with better naming,
and I would rather name CharBackend->CharFrontend, but Paolo prefered
the former. I started investigating switching to QOM object, that will
be for a later series as this one is large enough.

While doing this work, I found a few issues that are at the beginning
of this series.

Marc-André Lureau (38):
  rng: remove unused included header
  char: remove use-after-free on win-stdio
  ringbuf: fix chr_write return value
  sun4uv: fix serial initialization regression
  malta: replace chr init by CHR_EVENT_OPENED handler
  char: remove init callback
  xilinx: fix buffer overflow on realize
  mux: split mux_chr_update_read_handler()
  char: introduce CharBackend
  char: start converting mux driver to use CharBackend
  char: replace PROP_CHR with CharBackend
  char: remaining switch to CharBackend in frontend
  char: rename some frontend functions
  colo: claim in find_and_check_chardev
  char: use qemu_chr_fe* functions with CharBackend argument
  char: fold qemu_chr_set_handlers in qemu_chr_fe_set_handlers
  vhost-user: only initialize queue 0 CharBackend
  char: replace qemu_chr_claim/release with qemu_chr_fe_init/deinit
  char: make some qemu_chr_fe skip if no driver
  tests: start chardev unit tests
  char: move front end handlers in CharBackend
  char: rename chr_close/chr_free
  char: remove explicit_fe_open, use a set_handlers argument
  char: move fe_open in CharBackend
  char: remove unused CHR_EVENT_FOCUS
  char: use an enum for CHR_EVENT
  char: remove unused qemu_chr_fe_event
  char: replace avail_connections
  char: use common error path in qmp_chardev_add
  char: remove explicit_be_open from CharDriverState
  char: use a const CharDriver
  char: use a static array for backends
  char: move callbacks in CharDriver
  char: fold single-user functions in caller
  char: introduce generic qemu_chr_get_kind()
  char: use a feature bit for replay
  char: allocate CharDriverState as a single object
  bt: use qemu_chr_alloc()

 backends/baum.c                   |   44 +-
 backends/msmouse.c                |   39 +-
 backends/rng-egd.c                |   29 +-
 backends/testdev.c                |   35 +-
 gdbstub.c                         |   39 +-
 hmp.c                             |    2 +-
 hw/alpha/dp264.c                  |    2 +-
 hw/arm/fsl-imx25.c                |    2 +-
 hw/arm/fsl-imx31.c                |    2 +-
 hw/arm/fsl-imx6.c                 |    2 +-
 hw/arm/omap2.c                    |   16 +-
 hw/arm/pxa2xx.c                   |   19 +-
 hw/arm/strongarm.c                |   22 +-
 hw/bt/hci-csr.c                   |   46 +-
 hw/char/bcm2835_aux.c             |   18 +-
 hw/char/cadence_uart.c            |   30 +-
 hw/char/debugcon.c                |    8 +-
 hw/char/digic-uart.c              |   13 +-
 hw/char/escc.c                    |   22 +-
 hw/char/etraxfs_ser.c             |   12 +-
 hw/char/exynos4210_uart.c         |   15 +-
 hw/char/grlib_apbuart.c           |   17 +-
 hw/char/imx_serial.c              |   29 +-
 hw/char/ipoctal232.c              |   23 +-
 hw/char/lm32_juart.c              |   15 +-
 hw/char/lm32_uart.c               |   17 +-
 hw/char/mcf_uart.c                |   20 +-
 hw/char/milkymist-uart.c          |   13 +-
 hw/char/omap_uart.c               |    4 +-
 hw/char/parallel.c                |   46 +-
 hw/char/pl011.c                   |   19 +-
 hw/char/sclpconsole-lm.c          |   13 +-
 hw/char/sclpconsole.c             |   12 +-
 hw/char/serial-isa.c              |    7 +-
 hw/char/serial.c                  |   36 +-
 hw/char/sh_serial.c               |   16 +-
 hw/char/spapr_vty.c               |   12 +-
 hw/char/stm32f2xx_usart.c         |   22 +-
 hw/char/virtio-console.c          |   29 +-
 hw/char/xen_console.c             |   43 +-
 hw/char/xilinx_uartlite.c         |   16 +-
 hw/core/qdev-properties-system.c  |   80 +-
 hw/i386/pc.c                      |    2 +-
 hw/ipmi/ipmi_bmc_extern.c         |    9 +-
 hw/isa/pc87312.c                  |    4 +-
 hw/mips/mips_fulong2e.c           |    2 +-
 hw/mips/mips_malta.c              |   44 +-
 hw/mips/mips_r4k.c                |    2 +-
 hw/misc/ivshmem.c                 |   23 +-
 hw/sparc64/sun4u.c                |    2 +-
 hw/ssi/xilinx_spips.c             |    2 +-
 hw/usb/ccid-card-passthru.c       |   21 +-
 hw/usb/dev-serial.c               |   31 +-
 hw/usb/redirect.c                 |   22 +-
 hw/virtio/vhost-user.c            |    4 +-
 hw/xtensa/xtfpga.c                |    2 +-
 monitor.c                         |   31 +-
 net/colo-compare.c                |   59 +-
 net/filter-mirror.c               |   64 +-
 net/slirp.c                       |   25 +-
 net/vhost-user.c                  |   49 +-
 qemu-char.c                       | 1445 +++++++++++++++++++++----------------
 qtest.c                           |   29 +-
 spice-qemu-char.c                 |  102 +--
 tests/test-char.c                 |  253 +++++++
 tests/vhost-user-test.c           |   27 +-
 ui/console.c                      |   56 +-
 ui/gtk.c                          |   28 +-
 vl.c                              |   13 +-
 tests/Makefile.include            |    4 +
 include/hw/char/bcm2835_aux.h     |    2 +-
 include/hw/char/cadence_uart.h    |    2 +-
 include/hw/char/digic-uart.h      |    3 +-
 include/hw/char/imx_serial.h      |    3 +-
 include/hw/char/serial.h          |    6 +-
 include/hw/char/stm32f2xx_usart.h |    2 +-
 include/hw/qdev-properties.h      |    2 +-
 include/sysemu/char.h             |  290 ++++----
 include/ui/gtk.h                  |    3 +
 79 files changed, 2064 insertions(+), 1510 deletions(-)
 create mode 100644 tests/test-char.c

-- 
2.10.0

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 01/38] rng: remove unused included header
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 02/38] char: remove use-after-free on win-stdio Marc-André Lureau
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

DEFINE_PROP_CHR is not used (rng is not of TYPE_DEVICE)

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/rng-egd.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index ba17c07..0f6d0af 100644
--- a/backends/rng-egd.c
+++ b/backends/rng-egd.c
@@ -15,7 +15,6 @@
 #include "sysemu/char.h"
 #include "qapi/error.h"
 #include "qapi/qmp/qerror.h"
-#include "hw/qdev.h" /* just for DEFINE_PROP_CHR */
 
 #define TYPE_RNG_EGD "rng-egd"
 #define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 02/38] char: remove use-after-free on win-stdio
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 01/38] rng: remove unused included header Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 03/38] ringbuf: fix chr_write return value Marc-André Lureau
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Found by reviewing the code, win_stdio_close() is called by
qemu_chr_free() which then call qemu_chr_free_common() taking care of
freeing CharDriverState*.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/qemu-char.c b/qemu-char.c
index d83a896..9165051 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2435,7 +2435,6 @@ static void win_stdio_close(CharDriverState *chr)
     }
 
     g_free(chr->opaque);
-    g_free(chr);
 }
 
 static CharDriverState *qemu_chr_open_stdio(const char *id,
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 03/38] ringbuf: fix chr_write return value
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 01/38] rng: remove unused included header Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 02/38] char: remove use-after-free on win-stdio Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 04/38] sun4uv: fix serial initialization regression Marc-André Lureau
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

It should return the number of written bytes.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/qemu-char.c b/qemu-char.c
index 9165051..650943d 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3328,7 +3328,7 @@ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
         }
     }
 
-    return 0;
+    return len;
 }
 
 static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 04/38] sun4uv: fix serial initialization regression
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (2 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 03/38] ringbuf: fix chr_write return value Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 05/38] malta: replace chr init by CHR_EVENT_OPENED handler Marc-André Lureau
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Since commit b6607a1a204d, serial_hds_isa_init() was introduced to
factor out serial_isa_init() loops. However, sun4uv shouldn't start from
0 when there is a mm serial on 0 already. Add a "from" argument to
serial_hds_isa_init().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/alpha/dp264.c         | 2 +-
 hw/char/serial-isa.c     | 7 ++++---
 hw/i386/pc.c             | 2 +-
 hw/mips/mips_fulong2e.c  | 2 +-
 hw/mips/mips_malta.c     | 2 +-
 hw/mips/mips_r4k.c       | 2 +-
 hw/sparc64/sun4u.c       | 2 +-
 include/hw/char/serial.h | 2 +-
 8 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c
index f1267b5..d6431fd 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/char/serial-isa.c b/hw/char/serial-isa.c
index 1594ec4..54d3a12 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/i386/pc.c b/hw/i386/pc.c
index f4b0cda..fb8f29c 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/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c
index 889cdc7..9a4dae4 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 e90857e..bae60be 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -1215,7 +1215,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 16a59c7..27548c4 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/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 3165e18..7b8134e 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/include/hw/char/serial.h b/include/hw/char/serial.h
index a4fd3d5..4f3b73c 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -94,6 +94,6 @@ SerialState *serial_mm_init(MemoryRegion *address_space,
 
 /* serial-isa.c */
 #define TYPE_ISA_SERIAL "isa-serial"
-void serial_hds_isa_init(ISABus *bus, int n);
+void serial_hds_isa_init(ISABus *bus, int from, int to);
 
 #endif
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 05/38] malta: replace chr init by CHR_EVENT_OPENED handler
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (3 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 04/38] sun4uv: fix serial initialization regression Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 06/38] char: remove init callback Marc-André Lureau
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

The CharDriverState.init() callback was introduced in commit
ceecf1d158. It is only called from text_console_do_init(), but it is no
longer set since commit a61ae7f88 (init assignment has been removed by
accident).

It seems correct to use an event callback instead and print the console
text on CHR_EVENT_OPENED. That way we can remove the single user of
CharDriverState init().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/mips/mips_malta.c | 30 +++++++++++++++++++-----------
 1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index bae60be..3aec6d8 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -88,6 +88,7 @@ typedef struct {
     CharDriverState *display;
     char display_text[9];
     SerialState *uart;
+    bool display_inited;
 } MaltaFPGAState;
 
 #define TYPE_MIPS_MALTA "mips-malta"
@@ -530,17 +531,22 @@ 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,
@@ -560,7 +566,9 @@ 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);
+    s->display = qemu_chr_new("fpga", "vc:320x200", NULL);
+    qemu_chr_add_handlers(s->display, NULL, NULL,
+                          malta_fgpa_display_event, s);
 
     s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq,
                              230400, uart_chr, DEVICE_NATIVE_ENDIAN);
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 06/38] char: remove init callback
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (4 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 05/38] malta: replace chr init by CHR_EVENT_OPENED handler Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 07/38] xilinx: fix buffer overflow on realize Marc-André Lureau
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

The CharDriverState.init() callback is no longer set since commit
a61ae7f88ce and thus unused. The only user, the malta FGPA display has
been converted to use an event "opened" callback instead.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 gdbstub.c                 |  2 +-
 hmp.c                     |  2 +-
 hw/arm/fsl-imx25.c        |  2 +-
 hw/arm/fsl-imx31.c        |  2 +-
 hw/arm/fsl-imx6.c         |  2 +-
 hw/arm/omap2.c            |  2 +-
 hw/char/exynos4210_uart.c |  2 +-
 hw/char/omap_uart.c       |  4 ++--
 hw/char/xen_console.c     |  2 +-
 hw/isa/pc87312.c          |  4 ++--
 hw/mips/mips_malta.c      |  4 ++--
 hw/usb/dev-serial.c       |  4 ++--
 hw/xtensa/xtfpga.c        |  2 +-
 net/slirp.c               |  2 +-
 qemu-char.c               | 12 +++++-------
 qtest.c                   |  2 +-
 tests/vhost-user-test.c   |  2 +-
 ui/console.c              |  2 --
 ui/gtk.c                  |  3 ---
 vl.c                      | 12 ++++++------
 include/sysemu/char.h     | 13 +++----------
 21 files changed, 34 insertions(+), 48 deletions(-)

diff --git a/gdbstub.c b/gdbstub.c
index ecea8c4..2fe71ca 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1745,7 +1745,7 @@ int gdbserver_start(const char *device)
             sigaction(SIGINT, &act, NULL);
         }
 #endif
-        chr = qemu_chr_new_noreplay("gdb", device, NULL);
+        chr = qemu_chr_new_noreplay("gdb", device);
         if (!chr)
             return -1;
 
diff --git a/hmp.c b/hmp.c
index 80f7f1f..3d60259 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2002,7 +2002,7 @@ void hmp_chardev_add(Monitor *mon, const QDict *qdict)
     if (opts == NULL) {
         error_setg(&err, "Parsing chardev args failed");
     } else {
-        qemu_chr_new_from_opts(opts, NULL, &err);
+        qemu_chr_new_from_opts(opts, &err);
         qemu_opts_del(opts);
     }
     hmp_handle_error(mon, &err);
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index b4e358d..7bb7be7 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 fe204ac..f23672b 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 6a1bf26..e93532f 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 7e11c65..0b2a355 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -798,7 +798,7 @@ 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);
+    s->chr = chr ?: qemu_chr_new("null", "null");
 
     memory_region_init_io(&s->iomem, NULL, &omap_sti_ops, s, "omap.sti",
                           omap_l4_region_size(ta, 0));
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index 1107578..66e6304 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -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);
diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c
index 415bec5..893ab10 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/xen_console.c b/hw/char/xen_console.c
index 83108b0..11bf6a4 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -199,7 +199,7 @@ static int con_init(struct XenDevice *xendev)
         con->chr = serial_hds[con->xendev.dev];
     } else {
         snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
-        con->chr = qemu_chr_new(label, output, NULL);
+        con->chr = qemu_chr_new(label, output);
     }
 
     xenstore_store_pv_console_info(con->xendev.dev, con->chr);
diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c
index c3ebf3e..b1c1a0a 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_malta.c b/hw/mips/mips_malta.c
index 3aec6d8..ed0850c 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -566,7 +566,7 @@ 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", NULL);
+    s->display = qemu_chr_new("fpga", "vc:320x200");
     qemu_chr_add_handlers(s->display, NULL, NULL,
                           malta_fgpa_display_event, s);
 
@@ -1033,7 +1033,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");
         }
     }
 
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 966ad84..61452b5 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -547,7 +547,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 +565,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/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c
index ac75949..dc6fdcc 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),
diff --git a/net/slirp.c b/net/slirp.c
index b60893f..f9fdff5 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -747,7 +747,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
         }
     } else {
         fwd = g_new(struct GuestFwd, 1);
-        fwd->hd = qemu_chr_new(buf, p, NULL);
+        fwd->hd = qemu_chr_new(buf, p);
         if (!fwd->hd) {
             error_report("could not open guest forwarding device '%s'", buf);
             g_free(fwd);
diff --git a/qemu-char.c b/qemu-char.c
index 650943d..22504cc 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3909,8 +3909,7 @@ void register_char_driver(const char *name, ChardevBackendKind kind,
 }
 
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
-                                    void (*init)(struct CharDriverState *s),
-                                    Error **errp)
+                                        Error **errp)
 {
     Error *local_err = NULL;
     CharDriver *cd;
@@ -4007,8 +4006,7 @@ err:
     return NULL;
 }
 
-CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
-                                       void (*init)(struct CharDriverState *s))
+CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename)
 {
     const char *p;
     CharDriverState *chr;
@@ -4023,7 +4021,7 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
     if (!opts)
         return NULL;
 
-    chr = qemu_chr_new_from_opts(opts, init, &err);
+    chr = qemu_chr_new_from_opts(opts, &err);
     if (err) {
         error_report_err(err);
     }
@@ -4035,10 +4033,10 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
     return chr;
 }
 
-CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
+CharDriverState *qemu_chr_new(const char *label, const char *filename)
 {
     CharDriverState *chr;
-    chr = qemu_chr_new_noreplay(label, filename, init);
+    chr = qemu_chr_new_noreplay(label, filename);
     if (chr) {
         chr->replay = replay_mode != REPLAY_MODE_NONE;
         if (chr->replay && chr->chr_ioctl) {
diff --git a/qtest.c b/qtest.c
index b53b39c..2d9a021 100644
--- a/qtest.c
+++ b/qtest.c
@@ -670,7 +670,7 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp)
 {
     CharDriverState *chr;
 
-    chr = qemu_chr_new("qtest", qtest_chrdev, NULL);
+    chr = qemu_chr_new("qtest", qtest_chrdev);
 
     if (chr == NULL) {
         error_setg(errp, "Failed to initialize device for qtest: \"%s\"",
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index d7c48c5..edf30ac 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -455,7 +455,7 @@ static void test_server_create_chr(TestServer *server, const gchar *opt)
     gchar *chr_path;
 
     chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
-    server->chr = qemu_chr_new(server->chr_name, chr_path, NULL);
+    server->chr = qemu_chr_new(server->chr_name, chr_path);
     g_free(chr_path);
 
     qemu_chr_add_handlers(server->chr, chr_can_read, chr_read,
diff --git a/ui/console.c b/ui/console.c
index fa3e658..19adac7 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2033,8 +2033,6 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
     }
 
     qemu_chr_be_generic_open(chr);
-    if (chr->init)
-        chr->init(chr);
 }
 
 static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
diff --git a/ui/gtk.c b/ui/gtk.c
index 58d20ee..83984f6 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1789,9 +1789,6 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
                              gtk_label_new(vc->label));
 
     qemu_chr_be_generic_open(vc->vte.chr);
-    if (vc->vte.chr->init) {
-        vc->vte.chr->init(vc->vte.chr);
-    }
 
     return group;
 }
diff --git a/vl.c b/vl.c
index ebd47af..caa5f3b 100644
--- a/vl.c
+++ b/vl.c
@@ -2369,7 +2369,7 @@ static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp)
 {
     Error *local_err = NULL;
 
-    qemu_chr_new_from_opts(opts, NULL, &local_err);
+    qemu_chr_new_from_opts(opts, &local_err);
     if (local_err) {
         error_report_err(local_err);
         return -1;
@@ -2514,7 +2514,7 @@ static int serial_parse(const char *devname)
         exit(1);
     }
     snprintf(label, sizeof(label), "serial%d", index);
-    serial_hds[index] = qemu_chr_new(label, devname, NULL);
+    serial_hds[index] = qemu_chr_new(label, devname);
     if (!serial_hds[index]) {
         error_report("could not connect serial device"
                      " to character backend '%s'", devname);
@@ -2536,7 +2536,7 @@ static int parallel_parse(const char *devname)
         exit(1);
     }
     snprintf(label, sizeof(label), "parallel%d", index);
-    parallel_hds[index] = qemu_chr_new(label, devname, NULL);
+    parallel_hds[index] = qemu_chr_new(label, devname);
     if (!parallel_hds[index]) {
         error_report("could not connect parallel device"
                      " to character backend '%s'", devname);
@@ -2567,7 +2567,7 @@ static int virtcon_parse(const char *devname)
     qemu_opt_set(dev_opts, "driver", "virtconsole", &error_abort);
 
     snprintf(label, sizeof(label), "virtcon%d", index);
-    virtcon_hds[index] = qemu_chr_new(label, devname, NULL);
+    virtcon_hds[index] = qemu_chr_new(label, devname);
     if (!virtcon_hds[index]) {
         error_report("could not connect virtio console"
                      " to character backend '%s'", devname);
@@ -2600,7 +2600,7 @@ static int sclp_parse(const char *devname)
     qemu_opt_set(dev_opts, "driver", "sclpconsole", &error_abort);
 
     snprintf(label, sizeof(label), "sclpcon%d", index);
-    sclp_hds[index] = qemu_chr_new(label, devname, NULL);
+    sclp_hds[index] = qemu_chr_new(label, devname);
     if (!sclp_hds[index]) {
         error_report("could not connect sclp console"
                      " to character backend '%s'", devname);
@@ -2616,7 +2616,7 @@ static int debugcon_parse(const char *devname)
 {
     QemuOpts *opts;
 
-    if (!qemu_chr_new("debugcon", devname, NULL)) {
+    if (!qemu_chr_new("debugcon", devname)) {
         exit(1);
     }
     opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 19dad3f..6a74752 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -75,7 +75,6 @@ typedef enum {
 
 struct CharDriverState {
     QemuMutex chr_write_lock;
-    void (*init)(struct CharDriverState *s);
     int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
     int (*chr_sync_read)(struct CharDriverState *s,
                          const uint8_t *buf, int len);
@@ -130,13 +129,11 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp);
  * Create a new character backend from a QemuOpts list.
  *
  * @opts see qemu-config.c for a list of valid options
- * @init not sure..
  *
  * Returns: a new character backend
  */
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
-                                    void (*init)(struct CharDriverState *s),
-                                    Error **errp);
+                                        Error **errp);
 
 /**
  * @qemu_chr_parse_common:
@@ -155,12 +152,10 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend);
  *
  * @label the name of the backend
  * @filename the URI
- * @init not sure..
  *
  * Returns: a new character backend
  */
-CharDriverState *qemu_chr_new(const char *label, const char *filename,
-                              void (*init)(struct CharDriverState *s));
+CharDriverState *qemu_chr_new(const char *label, const char *filename);
 /**
  * @qemu_chr_disconnect:
  *
@@ -191,12 +186,10 @@ int qemu_chr_wait_connected(CharDriverState *chr, Error **errp);
  *
  * @label the name of the backend
  * @filename the URI
- * @init not sure..
  *
  * Returns: a new character backend
  */
-CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
-                                       void (*init)(struct CharDriverState *s));
+CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename);
 
 /**
  * @qemu_chr_delete:
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 07/38] xilinx: fix buffer overflow on realize
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (5 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 06/38] char: remove init callback Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-23 12:01   ` Paolo Bonzini
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 08/38] mux: split mux_chr_update_read_handler() Marc-André Lureau
                   ` (18 subsequent siblings)
  25 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

ASAN complains about buffer overflow when running:
aarch64-softmmu/qemu-system-aarch64 -machine xilinx-zynq-a9

==476==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000035e38 at pc 0x000000f75253 bp 0x7ffc597e0ec0 sp 0x7ffc597e0eb0
READ of size 8 at 0x602000035e38 thread T0
    #0 0xf75252 in xilinx_spips_realize hw/ssi/xilinx_spips.c:623
    #1 0xb9ef6c in device_set_realized hw/core/qdev.c:918
    #2 0x129ae01 in property_set_bool qom/object.c:1854
    #3 0x1296e70 in object_property_set qom/object.c:1088
    #4 0x129dd1b in object_property_set_qobject qom/qom-qobject.c:27
    #5 0x1297168 in object_property_set_bool qom/object.c:1157
    #6 0xb9aeac in qdev_init_nofail hw/core/qdev.c:358
    #7 0x78a5bf in zynq_init_spi_flashes /home/elmarco/src/qemu/hw/arm/xilinx_zynq.c:125
    #8 0x78af60 in zynq_init /home/elmarco/src/qemu/hw/arm/xilinx_zynq.c:238
    #9 0x998eac in main /home/elmarco/src/qemu/vl.c:4534
    #10 0x7f96ed692730 in __libc_start_main (/lib64/libc.so.6+0x20730)
    #11 0x41d0a8 in _start (/home/elmarco/src/qemu/aarch64-softmmu/qemu-system-aarch64+0x41d0a8)

0x602000035e38 is located 0 bytes to the right of 8-byte region [0x602000035e30,0x602000035e38)
allocated by thread T0 here:
    #0 0x7f970b014e60 in malloc (/lib64/libasan.so.3+0xc6e60)
    #1 0x7f96f15b0e18 in g_malloc (/lib64/libglib-2.0.so.0+0x4ee18)
    #2 0xb9ef6c in device_set_realized hw/core/qdev.c:918
    #3 0x129ae01 in property_set_bool qom/object.c:1854
    #4 0x1296e70 in object_property_set qom/object.c:1088
    #5 0x129dd1b in object_property_set_qobject qom/qom-qobject.c:27
    #6 0x1297168 in object_property_set_bool qom/object.c:1157
    #7 0xb9aeac in qdev_init_nofail hw/core/qdev.c:358
    #8 0x78a5bf in zynq_init_spi_flashes /home/elmarco/src/qemu/hw/arm/xilinx_zynq.c:125
    #9 0x78af60 in zynq_init /home/elmarco/src/qemu/hw/arm/xilinx_zynq.c:238
    #10 0x998eac in main /home/elmarco/src/qemu/vl.c:4534
    #11 0x7f96ed692730 in __libc_start_main (/lib64/libc.so.6+0x20730)

s->spi is allocated with the size of num_busses which may be 1 (by
default).

It looks like ssi_auto_connect_slaves() connects all devices children to
a s->spi bus. Since there can be only one parent bus, remove the second call.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/ssi/xilinx_spips.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index e2b77dc..ab7fa6f 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -620,7 +620,7 @@ 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]);
+
     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]);
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 08/38] mux: split mux_chr_update_read_handler()
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (6 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 07/38] xilinx: fix buffer overflow on realize Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-24 19:51   ` Eric Blake
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 09/38] char: introduce CharBackend Marc-André Lureau
                   ` (17 subsequent siblings)
  25 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Make qemu_chr_add_handlers_full() aware of mux handling. This allows
introduction of a tag associated with the fe handlers and a
qemu_chr_set_handlers() function to set the handler for a particular
tag. That will allow to get rid of qemu_chr_add_handlers*() in later
changes, in favor of qemu_chr_fe_set_handler().

To this end, chr_update_read_handler callback is enhanced with a tag
argument, and mux_chr_update_read_handler() is splitted in new
functions: mux_chr_new_handler_tag(), mux_chr_set_handlers(),
mux_set_focus().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c           | 139 +++++++++++++++++++++++++++++++++++---------------
 include/sysemu/char.h |   2 +-
 2 files changed, 99 insertions(+), 42 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 22504cc..29b339f 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -89,6 +89,8 @@
 #define READ_RETRIES 10
 #define TCP_MAX_FDS 16
 
+typedef struct MuxDriver MuxDriver;
+
 /***********************************************************/
 /* Socket address helpers */
 
@@ -449,12 +451,14 @@ void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...)
 
 static void remove_fd_in_watch(CharDriverState *chr);
 
-void qemu_chr_add_handlers_full(CharDriverState *s,
-                                IOCanReadHandler *fd_can_read,
-                                IOReadHandler *fd_read,
-                                IOEventHandler *fd_event,
-                                void *opaque,
-                                GMainContext *context)
+static void
+qemu_chr_set_handlers(CharDriverState *s,
+                      IOCanReadHandler *fd_can_read,
+                      IOReadHandler *fd_read,
+                      IOEventHandler *fd_event,
+                      void *opaque,
+                      GMainContext *context,
+                      int tag)
 {
     int fe_open;
 
@@ -469,7 +473,7 @@ void qemu_chr_add_handlers_full(CharDriverState *s,
     s->chr_event = fd_event;
     s->handler_opaque = opaque;
     if (s->chr_update_read_handler) {
-        s->chr_update_read_handler(s, context);
+        s->chr_update_read_handler(s, context, tag);
     }
 
     if (!s->explicit_fe_open) {
@@ -483,6 +487,34 @@ void qemu_chr_add_handlers_full(CharDriverState *s,
     }
 }
 
+static int mux_chr_new_handler_tag(CharDriverState *chr, Error **errp);
+static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context);
+static void mux_set_focus(MuxDriver *d, int focus);
+
+void qemu_chr_add_handlers_full(CharDriverState *s,
+                                IOCanReadHandler *fd_can_read,
+                                IOReadHandler *fd_read,
+                                IOEventHandler *fd_event,
+                                void *opaque,
+                                GMainContext *context)
+{
+    int tag = 0;
+
+    if (s->is_mux) {
+        tag = mux_chr_new_handler_tag(s, &error_abort);
+        if (tag == 0) {
+            mux_chr_set_handlers(s, context);
+        }
+    }
+
+    qemu_chr_set_handlers(s, fd_can_read, fd_read,
+                          fd_event, opaque, context, tag);
+
+    if (s->is_mux) {
+        mux_set_focus(s->opaque, tag);
+    }
+}
+
 void qemu_chr_add_handlers(CharDriverState *s,
                            IOCanReadHandler *fd_can_read,
                            IOReadHandler *fd_read,
@@ -519,7 +551,7 @@ static CharDriverState *qemu_chr_open_null(const char *id,
 #define MAX_MUX 4
 #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
 #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
-typedef struct {
+struct MuxDriver {
     IOCanReadHandler *chr_can_read[MAX_MUX];
     IOReadHandler *chr_read[MAX_MUX];
     IOEventHandler *chr_event[MAX_MUX];
@@ -540,8 +572,7 @@ typedef struct {
     /* Protected by the CharDriverState chr_write_lock.  */
     int linestart;
     int64_t timestamps_start;
-} MuxDriver;
-
+};
 
 /* Called with chr_write_lock held.  */
 static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
@@ -655,12 +686,9 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
             qemu_chr_be_event(chr, CHR_EVENT_BREAK);
             break;
         case 'c':
+            assert(d->mux_cnt > 0); /* handler registered with first fe */
             /* Switch to the next registered device */
-            mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
-            d->focus++;
-            if (d->focus >= d->mux_cnt)
-                d->focus = 0;
-            mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
+            mux_set_focus(d, (d->focus + 1) % d->mux_cnt);
             break;
         case 't':
             d->timestamps = !d->timestamps;
@@ -735,31 +763,18 @@ static void mux_chr_event(void *opaque, int event)
 }
 
 static void mux_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context)
+                                        GMainContext *context,
+                                        int tag)
 {
     MuxDriver *d = chr->opaque;
 
-    if (d->mux_cnt >= MAX_MUX) {
-        fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n");
-        return;
-    }
-    d->ext_opaque[d->mux_cnt] = chr->handler_opaque;
-    d->chr_can_read[d->mux_cnt] = chr->chr_can_read;
-    d->chr_read[d->mux_cnt] = chr->chr_read;
-    d->chr_event[d->mux_cnt] = chr->chr_event;
-    /* Fix up the real driver with mux routines */
-    if (d->mux_cnt == 0) {
-        qemu_chr_add_handlers_full(d->drv, mux_chr_can_read,
-                                   mux_chr_read,
-                                   mux_chr_event,
-                                   chr, context);
-    }
-    if (d->focus != -1) {
-        mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
-    }
-    d->focus = d->mux_cnt;
-    d->mux_cnt++;
-    mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
+    assert(tag >= 0);
+    assert(tag < d->mux_cnt);
+
+    d->ext_opaque[tag] = chr->handler_opaque;
+    d->chr_can_read[tag] = chr->chr_can_read;
+    d->chr_read[tag] = chr->chr_read;
+    d->chr_event[tag] = chr->chr_event;
 }
 
 static bool muxes_realized;
@@ -815,6 +830,44 @@ static void mux_chr_close(struct CharDriverState *chr)
     g_free(d);
 }
 
+static int mux_chr_new_handler_tag(CharDriverState *chr, Error **errp)
+{
+    MuxDriver *d = chr->opaque;
+
+    if (d->mux_cnt >= MAX_MUX) {
+        fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n");
+        return -1;
+    }
+
+    return d->mux_cnt++;
+}
+
+static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
+{
+    MuxDriver *d = chr->opaque;
+
+    /* Fix up the real driver with mux routines */
+    qemu_chr_add_handlers_full(d->drv,
+                               mux_chr_can_read,
+                               mux_chr_read,
+                               mux_chr_event,
+                               chr,
+                               context);
+}
+
+static void mux_set_focus(MuxDriver *d, int focus)
+{
+    assert(focus >= 0);
+    assert(focus < d->mux_cnt);
+
+    if (d->focus != -1) {
+        mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
+    }
+
+    d->focus = focus;
+    mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
+}
+
 static CharDriverState *qemu_chr_open_mux(const char *id,
                                           ChardevBackend *backend,
                                           ChardevReturn *ret, Error **errp)
@@ -1085,7 +1138,8 @@ static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 }
 
 static void fd_chr_update_read_handler(CharDriverState *chr,
-                                       GMainContext *context)
+                                       GMainContext *context,
+                                       int tag)
 {
     FDCharDriver *s = chr->opaque;
 
@@ -1342,7 +1396,8 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr)
 }
 
 static void pty_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context)
+                                        GMainContext *context,
+                                        int tag)
 {
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_update_read_handler_locked(chr);
@@ -2589,7 +2644,8 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 }
 
 static void udp_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context)
+                                        GMainContext *context,
+                                        int tag)
 {
     NetCharDriver *s = chr->opaque;
 
@@ -3008,7 +3064,8 @@ static void tcp_chr_connect(void *opaque)
 }
 
 static void tcp_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context)
+                                        GMainContext *context,
+                                        int tag)
 {
     TCPCharDriver *s = chr->opaque;
 
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 6a74752..f0abbac 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -80,7 +80,7 @@ struct CharDriverState {
                          const uint8_t *buf, int len);
     GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
     void (*chr_update_read_handler)(struct CharDriverState *s,
-                                    GMainContext *context);
+                                    GMainContext *context, int tag);
     int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
     int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
     int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 09/38] char: introduce CharBackend
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (7 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 08/38] mux: split mux_chr_update_read_handler() Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-23 12:03   ` Paolo Bonzini
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 10/38] char: start converting mux driver to use CharBackend Marc-André Lureau
                   ` (16 subsequent siblings)
  25 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

This new structure is meant to keep the details associated with a char
driver usage. On initialization, it gets a tag from the mux backend.
It can change its handlers thanks to qemu_chr_fe_set_handlers().

This structure is introduced so that all frontend will be moved to hold
and use a CharBackend. This will allow to better track char usage and
allocation, and help prevent some memory leaks or corruption.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c           | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/sysemu/char.h | 50 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/qemu-char.c b/qemu-char.c
index 29b339f..9722fb6 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -910,6 +910,63 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
     return chr;
 }
 
+CharDriverState *qemu_chr_fe_get_driver(CharBackend *be)
+{
+    assert(be);
+
+    return be->chr;
+}
+
+bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
+{
+    int tag = 0;
+
+    assert(b);
+    assert(s);
+
+    if (s->is_mux) {
+        tag = mux_chr_new_handler_tag(s, errp);
+        if (tag < 0) {
+            return false;
+        }
+    }
+
+    b->tag = tag;
+    b->chr = s;
+
+    return true;
+}
+
+void qemu_chr_fe_set_handlers(CharBackend *b,
+                              IOCanReadHandler *fd_can_read,
+                              IOReadHandler *fd_read,
+                              IOEventHandler *fd_event,
+                              void *opaque,
+                              GMainContext *context)
+{
+    assert(b);
+
+    if (!b->chr) {
+        return;
+    }
+
+    qemu_chr_set_handlers(b->chr, fd_can_read, fd_read,
+                          fd_event, opaque, context, b->tag);
+
+    if (b->chr->is_mux) {
+        mux_chr_set_handlers(b->chr, context);
+    }
+}
+
+void qemu_chr_fe_take_focus(CharBackend *b)
+{
+    assert(b);
+    assert(b->chr);
+
+    if (b->chr->is_mux) {
+        mux_set_focus(b->chr->opaque, b->tag);
+    }
+}
 
 typedef struct IOWatchPoll
 {
@@ -4184,6 +4241,9 @@ void qemu_chr_disconnect(CharDriverState *chr)
 
 static void qemu_chr_free_common(CharDriverState *chr)
 {
+    if (chr->be) {
+        chr->be->chr = NULL;
+    }
     g_free(chr->filename);
     g_free(chr->label);
     if (chr->logfd != -1) {
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index f0abbac..01cf0cc 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -72,6 +72,12 @@ typedef enum {
     QEMU_CHAR_FEATURE_LAST,
 } CharDriverFeature;
 
+/* This is the backend as seen by frontend, the actual backend is
+ * CharDriverState */
+typedef struct CharBackend {
+    CharDriverState *chr;
+    int tag;
+} CharBackend;
 
 struct CharDriverState {
     QemuMutex chr_write_lock;
@@ -156,6 +162,8 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend);
  * Returns: a new character backend
  */
 CharDriverState *qemu_chr_new(const char *label, const char *filename);
+
+
 /**
  * @qemu_chr_disconnect:
  *
@@ -425,6 +433,48 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len);
  */
 void qemu_chr_be_event(CharDriverState *s, int event);
 
+/**
+ * @qemu_chr_fe_init:
+ *
+ * Initializes a front end for the given CharBackend and CharDriver.
+ *
+ * Returns: false on error.
+ */
+bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp);
+
+/**
+ * @qemu_chr_fe_get_driver:
+ *
+ * Returns the driver associated with a CharBackend or NULL.
+ */
+CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
+
+/**
+ * @qemu_chr_fe_set_handlers:
+ * @b: a CharBackend
+ * @fd_can_read: callback to get the amount of data the frontend may
+ *               receive
+ * @fd_read: callback to receive data from char
+ * @fd_event: event callback
+ * @opaque: an opaque pointer for the callbacks
+ * @context: a main loop context or NULL for the default
+ *
+ * Set the front end char handlers.
+ */
+void qemu_chr_fe_set_handlers(CharBackend *b,
+                              IOCanReadHandler *fd_can_read,
+                              IOReadHandler *fd_read,
+                              IOEventHandler *fd_event,
+                              void *opaque,
+                              GMainContext *context);
+
+/**
+ * @qemu_chr_fe_take_focus:
+ *
+ * Take the focus (if the front end is muxed)
+ */
+void qemu_chr_fe_take_focus(CharBackend *b);
+
 void qemu_chr_add_handlers(CharDriverState *s,
                            IOCanReadHandler *fd_can_read,
                            IOReadHandler *fd_read,
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 10/38] char: start converting mux driver to use CharBackend
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (8 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 09/38] char: introduce CharBackend Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 11/38] char: replace PROP_CHR with CharBackend Marc-André Lureau
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Start using qemu_chr_fe* CharBackend functions:
initialize a CharBackend and use qemu_chr_fe_set_handlers().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 9722fb6..b44e5ba 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -502,9 +502,7 @@ void qemu_chr_add_handlers_full(CharDriverState *s,
 
     if (s->is_mux) {
         tag = mux_chr_new_handler_tag(s, &error_abort);
-        if (tag == 0) {
-            mux_chr_set_handlers(s, context);
-        }
+        mux_chr_set_handlers(s, context);
     }
 
     qemu_chr_set_handlers(s, fd_can_read, fd_read,
@@ -557,6 +555,7 @@ struct MuxDriver {
     IOEventHandler *chr_event[MAX_MUX];
     void *ext_opaque[MAX_MUX];
     CharDriverState *drv;
+    CharBackend chr;
     int focus;
     int mux_cnt;
     int term_got_escape;
@@ -847,12 +846,12 @@ static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
     MuxDriver *d = chr->opaque;
 
     /* Fix up the real driver with mux routines */
-    qemu_chr_add_handlers_full(d->drv,
-                               mux_chr_can_read,
-                               mux_chr_read,
-                               mux_chr_event,
-                               chr,
-                               context);
+    qemu_chr_fe_set_handlers(&d->chr,
+                             mux_chr_can_read,
+                             mux_chr_read,
+                             mux_chr_event,
+                             chr,
+                             context);
 }
 
 static void mux_set_focus(MuxDriver *d, int focus)
@@ -906,6 +905,10 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
      */
     chr->explicit_be_open = muxes_realized ? 0 : 1;
     chr->is_mux = 1;
+    if (!qemu_chr_fe_init(&d->chr, d->drv, errp)) {
+        qemu_chr_free(chr);
+        return NULL;
+    }
 
     return chr;
 }
@@ -4241,9 +4244,6 @@ void qemu_chr_disconnect(CharDriverState *chr)
 
 static void qemu_chr_free_common(CharDriverState *chr)
 {
-    if (chr->be) {
-        chr->be->chr = NULL;
-    }
     g_free(chr->filename);
     g_free(chr->label);
     if (chr->logfd != -1) {
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 11/38] char: replace PROP_CHR with CharBackend
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (9 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 10/38] char: start converting mux driver to use CharBackend Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 12/38] char: remaining switch to CharBackend in frontend Marc-André Lureau
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Store the property in a CharBackend instead of CharDriverState*.  This
also replace systematically chr by chr.chr to access the
CharDriverState*. The following patches will replace it with calls to
qemu_chr_fe CharBackend functions.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/arm/pxa2xx.c                   | 18 +++++----
 hw/arm/strongarm.c                | 14 +++----
 hw/char/bcm2835_aux.c             | 12 +++---
 hw/char/cadence_uart.c            | 26 ++++++------
 hw/char/debugcon.c                |  8 ++--
 hw/char/digic-uart.c              |  8 ++--
 hw/char/escc.c                    | 21 +++++-----
 hw/char/etraxfs_ser.c             |  8 ++--
 hw/char/exynos4210_uart.c         | 10 ++---
 hw/char/grlib_apbuart.c           |  8 ++--
 hw/char/imx_serial.c              | 19 ++++-----
 hw/char/ipoctal232.c              | 14 +++----
 hw/char/lm32_juart.c              | 11 ++---
 hw/char/lm32_uart.c               | 12 +++---
 hw/char/milkymist-uart.c          | 12 +++---
 hw/char/parallel.c                | 46 +++++++++++----------
 hw/char/pl011.c                   | 15 +++----
 hw/char/sclpconsole-lm.c          | 13 +++---
 hw/char/sclpconsole.c             | 10 ++---
 hw/char/serial.c                  | 34 +++++++++-------
 hw/char/spapr_vty.c               | 10 ++---
 hw/char/stm32f2xx_usart.c         | 16 ++++----
 hw/char/virtio-console.c          | 28 ++++++-------
 hw/char/xilinx_uartlite.c         | 15 +++----
 hw/core/qdev-properties-system.c  | 84 +++++++++++++++++++++++----------------
 hw/ipmi/ipmi_bmc_extern.c         |  8 ++--
 hw/misc/ivshmem.c                 | 18 ++++-----
 hw/usb/ccid-card-passthru.c       | 14 +++----
 hw/usb/dev-serial.c               | 22 +++++-----
 hw/usb/redirect.c                 | 16 ++++----
 include/hw/char/bcm2835_aux.h     |  2 +-
 include/hw/char/cadence_uart.h    |  2 +-
 include/hw/char/digic-uart.h      |  3 +-
 include/hw/char/imx_serial.h      |  3 +-
 include/hw/char/serial.h          |  3 +-
 include/hw/char/stm32f2xx_usart.h |  2 +-
 include/hw/qdev-properties.h      |  2 +-
 37 files changed, 302 insertions(+), 265 deletions(-)

diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 9898287..27f112c 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->chr.chr && s->enable && (s->control[0] & (1 << 3))) { /* TXE */
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(s->chr, &ch, 1);
+            qemu_chr_fe_write_all(s->chr.chr, &ch, 1);
+        }
         break;
     case ICSR0:
         s->status[0] &= ~(value & 0x66);
@@ -1973,9 +1975,9 @@ 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,
+    if (s->chr.chr) {
+        qemu_chr_fe_claim_no_fail(s->chr.chr);
+        qemu_chr_add_handlers(s->chr.chr, pxa2xx_fir_is_empty,
                         pxa2xx_fir_rx, pxa2xx_fir_event, s);
     }
 }
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index 021cbf9..d3e8aff 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,8 +1020,8 @@ 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);
+    if (s->chr.chr) {
+        qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
     }
 
     DPRINTF(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
@@ -1107,10 +1107,10 @@ static void strongarm_uart_tx(void *opaque)
 
     if (s->utcr3 & UTCR3_LBM) /* loopback */ {
         strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
-    } else if (s->chr) {
+    } else 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_fifo[s->tx_start], 1);
+        qemu_chr_fe_write_all(s->chr.chr, &s->tx_fifo[s->tx_start], 1);
     }
 
     s->tx_start = (s->tx_start + 1) % 8;
@@ -1239,8 +1239,8 @@ 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,
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr,
                         strongarm_uart_can_receive,
                         strongarm_uart_receive,
                         strongarm_uart_event,
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index f7a845d..4bc5d02 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 e3bc52f..d5687dd 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 4402033..b405109 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 e96a9b2..fb4969f 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 aa17397..ae69b39 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 c99cc5d..99c4801 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 66e6304..3f71059 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 778148a..13c9455 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 5c3fa61..5c11de2 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 2859fdd..0c9dea3 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 cb1ac76..a0eb312 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 be93697..72fc41c 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 baddb37..a6518e6 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 da22e36..80576af 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 1a7911f..9645195 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 9a56326..3ef1517 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 a75ad4f..bb51a2c 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 3442f47..b5fa8e1 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -181,7 +181,7 @@ static void serial_update_parameters(SerialState *s)
     ssp.data_bits = data_bits;
     ssp.stop_bits = stop_bits;
     s->char_transmit_time =  (NANOSECONDS_PER_SECOND / speed) * frame_size;
-    qemu_chr_fe_ioctl(s->chr, CHR_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);
@@ -194,7 +194,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;
     }
@@ -259,11 +260,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;
@@ -416,7 +418,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);
             }
         }
@@ -431,7 +433,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);
 
@@ -440,7 +443,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);
@@ -485,7 +489,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;
@@ -658,7 +662,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).  */
@@ -883,7 +887,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;
     }
@@ -893,7 +897,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);
@@ -902,7 +906,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);
 }
 
@@ -932,7 +936,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 +993,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 9aeafc0..dcb4a85 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 4c6640d..8cc2737 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 d44c18c..a4730ba 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 3766dc2..0e809d5 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)
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index e55afe6..6d45d32 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -160,55 +160,71 @@ 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;
+    }
 
-static char *print_chr(void *ptr)
-{
-    CharDriverState *chr = ptr;
-    const char *val = chr->label ? chr->label : "";
+    if (!*str) {
+        g_free(str);
+        be->chr = NULL;
+        return;
+    }
 
-    return g_strdup(val);
-}
+    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;
+    }
+    if (qemu_chr_fe_claim(s) != 0) {
+        error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use",
+                  object_get_typename(obj), prop->name, str);
+        return;
+    }
 
-static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
-                    Error **errp)
-{
-    get_pointer(obj, v, opaque, print_chr, name, errp);
+    qemu_chr_fe_init(be, s, errp);
 }
 
-static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
-                    Error **errp)
+static void release_chr(Object *obj, const char *name, void *opaque)
 {
-    set_pointer(obj, v, opaque, parse_chr, name, errp);
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    CharBackend *be = qdev_get_prop_ptr(dev, prop);
+
+    if (be->chr) {
+        qemu_chr_fe_set_handlers(be, NULL, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(be->chr);
+    }
 }
 
 PropertyInfo qdev_prop_chr = {
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index d93e3f3..af9b6f3 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.chr, ibe->outbuf + ibe->outpos,
                             ibe->outlen - ibe->outpos);
     if (ret > 0) {
         ibe->outpos += ret;
@@ -442,12 +442,12 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
 {
     IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
 
-    if (!ibe->chr) {
+    if (!ibe->chr.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_add_handlers(ibe->chr.chr, can_receive, receive, chr_event, ibe);
 }
 
 static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index f803dfd..efca8b0 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.chr);
 
     process_msg(s, msg, fd, &err);
     if (err) {
@@ -642,7 +642,7 @@ 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,
+        ret = qemu_chr_fe_read_all(s->server_chr.chr, (uint8_t *)&msg + n,
                                  sizeof(msg) - n);
         if (ret < 0 && ret != -EINTR) {
             error_setg_errno(errp, -ret, "read from server failed");
@@ -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.chr);
     return msg;
 }
 
@@ -868,10 +868,10 @@ 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);
+        assert(s->server_chr.chr);
 
         IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
-                        s->server_chr->filename);
+                        s->server_chr.chr->filename);
 
         /* we allocate enough space for 16 peers and grow as needed */
         resize_peers(s, 16);
@@ -893,7 +893,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
             return;
         }
 
-        qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive,
+        qemu_chr_add_handlers(s->server_chr.chr, ivshmem_can_receive,
                               ivshmem_read, NULL, s);
 
         if (ivshmem_setup_interrupts(s) < 0) {
@@ -1121,7 +1121,7 @@ static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp)
 {
     IVShmemState *s = IVSHMEM_COMMON(dev);
 
-    if (!s->server_chr) {
+    if (!s->server_chr.chr) {
         error_setg(errp, "You must specify a 'chardev'");
         return;
     }
@@ -1250,7 +1250,7 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp)
                      " or ivshmem-doorbell instead");
     }
 
-    if (!!s->server_chr + !!s->shmobj != 1) {
+    if (!!s->server_chr.chr + !!s->shmobj != 1) {
         error_setg(errp, "You must specify either 'shm' or 'chardev'");
         return;
     }
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 2eacea7..ebe3942 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.chr, (uint8_t *)&scr_msg_header,
                           sizeof(VSCMsgHeader));
-    qemu_chr_fe_write_all(s->cs, payload, length);
+    qemu_chr_fe_write_all(s->cs.chr, payload, length);
 }
 
 static void ccid_card_vscard_send_apdu(PassthruState *s,
@@ -264,7 +264,7 @@ static void ccid_card_vscard_handle_message(PassthruState *card,
 
 static void ccid_card_vscard_drop_connection(PassthruState *card)
 {
-    qemu_chr_delete(card->cs);
+    qemu_chr_delete(card->cs.chr);
     card->vscard_in_pos = card->vscard_in_hdr = 0;
 }
 
@@ -324,7 +324,7 @@ static void passthru_apdu_from_guest(
 {
     PassthruState *card = PASSTHRU_CCID_CARD(base);
 
-    if (!card->cs) {
+    if (!card->cs.chr) {
         printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
         return;
     }
@@ -345,9 +345,9 @@ static int passthru_initfn(CCIDCardState *base)
 
     card->vscard_in_pos = 0;
     card->vscard_in_hdr = 0;
-    if (card->cs) {
+    if (card->cs.chr) {
         DPRINTF(card, D_INFO, "initing chardev\n");
-        qemu_chr_add_handlers(card->cs,
+        qemu_chr_add_handlers(card->cs.chr,
             ccid_card_vscard_can_read,
             ccid_card_vscard_read,
             ccid_card_vscard_event, card);
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 61452b5..b8774b4 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,
+                          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, 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, 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, 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, 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.chr, iov->iov_base, iov->iov_len);
         }
         p->actual_length = p->iov.size;
         break;
@@ -488,7 +490,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
     usb_desc_init(dev);
     dev->auto_attach = 0;
 
-    if (!s->cs) {
+    if (!s->cs.chr) {
         error_setg(errp, "Property chardev is required");
         return;
     }
@@ -499,11 +501,11 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
         return;
     }
 
-    qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read,
+    qemu_chr_add_handlers(s->cs.chr, usb_serial_can_read, usb_serial_read,
                           usb_serial_event, s);
     usb_serial_handle_reset(dev);
 
-    if (s->cs->be_open && !dev->attached) {
+    if (s->cs.chr->be_open && !dev->attached) {
         usb_device_attach(dev, &error_abort);
     }
 }
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index d4ca026..0b21804 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;
@@ -285,7 +285,7 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
     USBRedirDevice *dev = priv;
     int r;
 
-    if (!dev->cs->be_open) {
+    if (!dev->cs.chr->be_open) {
         return 0;
     }
 
@@ -294,10 +294,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.chr, 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.chr, G_IO_OUT | G_IO_HUP,
                                                usbredir_write_unblocked, dev);
         }
         if (r < 0) {
@@ -1375,7 +1375,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp)
     USBRedirDevice *dev = USB_REDIRECT(udev);
     int i;
 
-    if (dev->cs == NULL) {
+    if (dev->cs.chr == NULL) {
         error_setg(errp, QERR_MISSING_PARAMETER, "chardev");
         return;
     }
@@ -1406,7 +1406,7 @@ 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,
+    qemu_chr_add_handlers(dev->cs.chr, usbredir_chardev_can_read,
                           usbredir_chardev_read, usbredir_chardev_event, dev);
 
     qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
@@ -1427,8 +1427,8 @@ static void usbredir_handle_destroy(USBDevice *udev)
 {
     USBRedirDevice *dev = USB_REDIRECT(udev);
 
-    qemu_chr_delete(dev->cs);
-    dev->cs = NULL;
+    qemu_chr_delete(dev->cs.chr);
+    dev->cs.chr = NULL;
     /* Note must be done after qemu_chr_close, as that causes a close event */
     qemu_bh_delete(dev->chardev_close_bh);
     qemu_bh_delete(dev->device_reject_bh);
diff --git a/include/hw/char/bcm2835_aux.h b/include/hw/char/bcm2835_aux.h
index 42f0ee7..6865f15 100644
--- a/include/hw/char/bcm2835_aux.h
+++ b/include/hw/char/bcm2835_aux.h
@@ -22,7 +22,7 @@ typedef struct {
     /*< public >*/
 
     MemoryRegion iomem;
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
 
     uint8_t read_fifo[BCM2835_AUX_RX_FIFO_LEN];
diff --git a/include/hw/char/cadence_uart.h b/include/hw/char/cadence_uart.h
index a12773c..ca75eb5 100644
--- a/include/hw/char/cadence_uart.h
+++ b/include/hw/char/cadence_uart.h
@@ -44,7 +44,7 @@ typedef struct {
     uint32_t rx_count;
     uint32_t tx_count;
     uint64_t char_tx_time;
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
     QEMUTimer *fifo_trigger_handle;
 } CadenceUARTState;
diff --git a/include/hw/char/digic-uart.h b/include/hw/char/digic-uart.h
index 7b3f145..340c8e1 100644
--- a/include/hw/char/digic-uart.h
+++ b/include/hw/char/digic-uart.h
@@ -19,6 +19,7 @@
 #define HW_CHAR_DIGIC_UART_H
 
 #include "hw/sysbus.h"
+#include "sysemu/char.h"
 
 #define TYPE_DIGIC_UART "digic-uart"
 #define DIGIC_UART(obj) \
@@ -37,7 +38,7 @@ typedef struct DigicUartState {
     /*< public >*/
 
     MemoryRegion regs_region;
-    CharDriverState *chr;
+    CharBackend chr;
 
     uint32_t reg_rx;
     uint32_t reg_st;
diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h
index 6cd75c0..4cc3fbc 100644
--- a/include/hw/char/imx_serial.h
+++ b/include/hw/char/imx_serial.h
@@ -19,6 +19,7 @@
 #define IMX_SERIAL_H
 
 #include "hw/sysbus.h"
+#include "sysemu/char.h"
 
 #define TYPE_IMX_SERIAL "imx.serial"
 #define IMX_SERIAL(obj) OBJECT_CHECK(IMXSerialState, (obj), TYPE_IMX_SERIAL)
@@ -96,7 +97,7 @@ typedef struct IMXSerialState {
     uint32_t ucr3;
 
     qemu_irq irq;
-    CharDriverState *chr;
+    CharBackend chr;
 } IMXSerialState;
 
 #endif
diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
index 4f3b73c..c3312fb 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -30,6 +30,7 @@
 #include "sysemu/sysemu.h"
 #include "exec/memory.h"
 #include "qemu/fifo8.h"
+#include "sysemu/char.h"
 
 #define UART_FIFO_LENGTH    16      /* 16550A Fifo Length */
 
@@ -52,7 +53,7 @@ struct SerialState {
        it can be reset while reading iir */
     int thr_ipending;
     qemu_irq irq;
-    CharDriverState *chr;
+    CharBackend chr;
     int last_break_enable;
     int it_shift;
     int baudbase;
diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h
index b97f192..3267523 100644
--- a/include/hw/char/stm32f2xx_usart.h
+++ b/include/hw/char/stm32f2xx_usart.h
@@ -67,7 +67,7 @@ typedef struct {
     uint32_t usart_cr3;
     uint32_t usart_gtpr;
 
-    CharDriverState *chr;
+    CharBackend chr;
     qemu_irq irq;
 } STM32F2XXUsartState;
 #endif /* HW_STM32F2XX_USART_H */
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 2a9d2f9..306bbab 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -146,7 +146,7 @@ extern PropertyInfo qdev_prop_arraylen;
     DEFINE_PROP(_n, _s, _f, qdev_prop_ptr, void*)
 
 #define DEFINE_PROP_CHR(_n, _s, _f)             \
-    DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharDriverState*)
+    DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharBackend)
 #define DEFINE_PROP_STRING(_n, _s, _f)             \
     DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*)
 #define DEFINE_PROP_NETDEV(_n, _s, _f)             \
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 12/38] char: remaining switch to CharBackend in frontend
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (10 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 11/38] char: replace PROP_CHR with CharBackend Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 13/38] char: rename some frontend functions Marc-André Lureau
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Similar to previous change, for the remaining CharDriverState front ends
users.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/rng-egd.c      | 29 ++++++++++++++-----------
 gdbstub.c               | 22 +++++++++++--------
 hw/arm/omap2.c          | 11 +++++-----
 hw/char/mcf_uart.c      | 12 +++++-----
 hw/char/sh_serial.c     | 10 ++++-----
 hw/char/xen_console.c   | 39 +++++++++++++++++++--------------
 hw/mips/mips_malta.c    | 32 +++++++++++++++------------
 monitor.c               | 19 ++++++++--------
 net/colo-compare.c      | 46 +++++++++++++++++++--------------------
 net/filter-mirror.c     | 58 +++++++++++++++++++++++++++++--------------------
 net/slirp.c             | 23 ++++++++++++++------
 net/vhost-user.c        | 27 +++++++++++++----------
 qtest.c                 |  8 +++----
 tests/vhost-user-test.c | 39 ++++++++++++++++++---------------
 14 files changed, 211 insertions(+), 164 deletions(-)

diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index 0f6d0af..e2f8189 100644
--- a/backends/rng-egd.c
+++ b/backends/rng-egd.c
@@ -23,7 +23,7 @@ typedef struct RngEgd
 {
     RngBackend parent;
 
-    CharDriverState *chr;
+    CharBackend chr;
     char *chr_name;
 } RngEgd;
 
@@ -42,7 +42,7 @@ static void rng_egd_request_entropy(RngBackend *b, RngRequest *req)
 
         /* XXX this blocks entire thread. Rewrite to use
          * qemu_chr_fe_write and background I/O callbacks */
-        qemu_chr_fe_write_all(s->chr, header, sizeof(header));
+        qemu_chr_fe_write_all(s->chr.chr, header, sizeof(header));
 
         size -= len;
     }
@@ -86,6 +86,7 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
 static void rng_egd_opened(RngBackend *b, Error **errp)
 {
     RngEgd *s = RNG_EGD(b);
+    CharDriverState *chr;
 
     if (s->chr_name == NULL) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
@@ -93,21 +94,23 @@ static void rng_egd_opened(RngBackend *b, Error **errp)
         return;
     }
 
-    s->chr = qemu_chr_find(s->chr_name);
-    if (s->chr == NULL) {
+    chr = qemu_chr_find(s->chr_name);
+    if (chr == NULL) {
         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                   "Device '%s' not found", s->chr_name);
         return;
     }
-
-    if (qemu_chr_fe_claim(s->chr) != 0) {
+    if (qemu_chr_fe_claim(chr) != 0) {
         error_setg(errp, QERR_DEVICE_IN_USE, s->chr_name);
         return;
     }
+    if (!qemu_chr_fe_init(&s->chr, chr, errp)) {
+        return;
+    }
 
     /* FIXME we should resubmit pending requests when the CDS reconnects. */
-    qemu_chr_add_handlers(s->chr, rng_egd_chr_can_read, rng_egd_chr_read,
-                          NULL, s);
+    qemu_chr_add_handlers(s->chr.chr, rng_egd_chr_can_read,
+                          rng_egd_chr_read, NULL, s);
 }
 
 static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
@@ -127,8 +130,8 @@ static char *rng_egd_get_chardev(Object *obj, Error **errp)
 {
     RngEgd *s = RNG_EGD(obj);
 
-    if (s->chr && s->chr->label) {
-        return g_strdup(s->chr->label);
+    if (s->chr.chr && s->chr.chr->label) {
+        return g_strdup(s->chr.chr->label);
     }
 
     return NULL;
@@ -145,9 +148,9 @@ static void rng_egd_finalize(Object *obj)
 {
     RngEgd *s = RNG_EGD(obj);
 
-    if (s->chr) {
-        qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr);
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(s->chr.chr);
     }
 
     g_free(s->chr_name);
diff --git a/gdbstub.c b/gdbstub.c
index 2fe71ca..347bd4d 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -303,7 +303,7 @@ typedef struct GDBState {
     int fd;
     int running_state;
 #else
-    CharDriverState *chr;
+    CharBackend chr;
     CharDriverState *mon_chr;
 #endif
     char syscall_buf[256];
@@ -404,7 +404,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len)
 #else
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(s->chr, buf, len);
+    qemu_chr_fe_write_all(s->chr.chr, buf, len);
 #endif
 }
 
@@ -1481,7 +1481,7 @@ void gdb_exit(CPUArchState *env, int code)
       return;
   }
 #else
-  if (!s->chr) {
+  if (!s->chr.chr) {
       return;
   }
 #endif
@@ -1490,7 +1490,7 @@ void gdb_exit(CPUArchState *env, int code)
   put_packet(s, buf);
 
 #ifndef CONFIG_USER_ONLY
-  qemu_chr_delete(s->chr);
+  qemu_chr_delete(s->chr.chr);
 #endif
 }
 
@@ -1750,8 +1750,6 @@ int gdbserver_start(const char *device)
             return -1;
 
         qemu_chr_fe_claim_no_fail(chr);
-        qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
-                              gdb_chr_event, NULL);
     }
 
     s = gdbserver_state;
@@ -1766,14 +1764,20 @@ int gdbserver_start(const char *device)
         mon_chr->chr_write = gdb_monitor_write;
         monitor_init(mon_chr, 0);
     } else {
-        if (s->chr)
-            qemu_chr_delete(s->chr);
+        if (s->chr.chr) {
+            qemu_chr_delete(s->chr.chr);
+        }
         mon_chr = s->mon_chr;
         memset(s, 0, sizeof(GDBState));
+        s->mon_chr = mon_chr;
     }
     s->c_cpu = first_cpu;
     s->g_cpu = first_cpu;
-    s->chr = chr;
+    if (chr) {
+        qemu_chr_fe_init(&s->chr, chr, &error_abort);
+        qemu_chr_add_handlers(s->chr.chr, gdb_chr_can_receive, gdb_chr_receive,
+                              gdb_chr_event, NULL);
+    }
     s->state = chr ? RS_IDLE : RS_INACTIVE;
     s->mon_chr = mon_chr;
     s->current_syscall_cb = NULL;
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index 0b2a355..43d9c4b 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,14 @@ 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.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);
+            qemu_chr_fe_write_all(s->chr.chr, (const uint8_t *) "\n", 1);
         else
-            qemu_chr_fe_write_all(s->chr, &byte, 1);
+            qemu_chr_fe_write_all(s->chr.chr, &byte, 1);
     }
 }
 
@@ -798,7 +798,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");
+    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/char/mcf_uart.c b/hw/char/mcf_uart.c
index c184859..436e1b0 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_accept_input(s->chr.chr);
             return val;
         }
     case 0x10:
@@ -113,10 +114,11 @@ 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)
+        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, (unsigned char *)&s->tb, 1);
+            qemu_chr_fe_write_all(s->chr.chr, (unsigned char *)&s->tb, 1);
+        }
         s->sr |= MCF_UART_TxEMP;
     }
     if (s->tx_enabled) {
@@ -280,9 +282,9 @@ 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_init(&s->chr, chr, &error_abort);
         qemu_chr_fe_claim_no_fail(chr);
         qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive,
                               mcf_uart_event, s);
diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
index 97ce562..c8b91bb 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 (s->chr.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.chr, &ch, 1);
 	}
 	s->dr = val;
 	s->flags &= ~SH_SERIAL_FLAG_TDE;
@@ -395,10 +396,9 @@ 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_fe_init(&s->chr, chr, &error_abort);
         qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
 			      sh_serial_event, s);
     }
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index 11bf6a4..c1d36dc 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -26,6 +26,7 @@
 #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 +44,7 @@ struct XenConsole {
     char              console[XEN_BUFSIZE];
     int               ring_ref;
     void              *sring;
-    CharDriverState   *chr;
+    CharBackend       chr;
     int               backlog;
 };
 
@@ -148,11 +149,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 (con->chr.chr) {
+        len = qemu_chr_fe_write(con->chr.chr,
+                                con->buffer.data + con->buffer.consumed,
+                                size);
+    } else {
         len = size;
+    }
     if (len < 1) {
 	if (!con->backlog) {
 	    con->backlog = 1;
@@ -196,13 +199,17 @@ 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);
+        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, con->chr.chr);
 
 out:
     g_free(type);
@@ -235,15 +242,15 @@ 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,
+    if (con->chr.chr) {
+        if (qemu_chr_fe_claim(con->chr.chr) == 0) {
+            qemu_chr_add_handlers(con->chr.chr, xencons_can_receive,
                                   xencons_receive, NULL, con);
         } else {
             xen_be_printf(xendev, 0,
                           "xen_console_init error chardev %s already used\n",
-                          con->chr->label);
-            con->chr = NULL;
+                          con->chr.chr->label);
+            con->chr.chr = NULL;
         }
     }
 
@@ -259,9 +266,9 @@ 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);
+    if (con->chr.chr) {
+        qemu_chr_add_handlers(con->chr.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(con->chr.chr);
     }
     xen_be_unbind_evtchn(&con->xendev);
 
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index ed0850c..d5601b1 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -85,7 +85,7 @@ typedef struct {
     uint32_t i2coe;
     uint32_t i2cout;
     uint32_t i2csel;
-    CharDriverState *display;
+    CharBackend display;
     char display_text[9];
     SerialState *uart;
     bool display_inited;
@@ -125,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.chr, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n",
+                       leds_text);
+    qemu_chr_fe_printf(s->display.chr, "\n\n\n\n|\e[31m%-8.8s\e[00m|",
+                       s->display_text);
 }
 
 /*
@@ -536,15 +538,15 @@ static void malta_fgpa_display_event(void *opaque, int event)
     MaltaFPGAState *s = opaque;
 
     if (event == CHR_EVENT_OPENED && !s->display_inited) {
-        qemu_chr_fe_printf(s->display, "\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");
+        qemu_chr_fe_printf(s->display.chr, "\e[HMalta LEDBAR\r\n");
+        qemu_chr_fe_printf(s->display.chr, "+--------+\r\n");
+        qemu_chr_fe_printf(s->display.chr, "+        +\r\n");
+        qemu_chr_fe_printf(s->display.chr, "+--------+\r\n");
+        qemu_chr_fe_printf(s->display.chr, "\n");
+        qemu_chr_fe_printf(s->display.chr, "Malta ASCII\r\n");
+        qemu_chr_fe_printf(s->display.chr, "+--------+\r\n");
+        qemu_chr_fe_printf(s->display.chr, "+        +\r\n");
+        qemu_chr_fe_printf(s->display.chr, "+--------+\r\n");
         s->display_inited = true;
     }
 }
@@ -553,6 +555,7 @@ 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));
 
@@ -566,8 +569,9 @@ 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");
-    qemu_chr_add_handlers(s->display, NULL, NULL,
+    chr = qemu_chr_new("fpga", "vc:320x200");
+    qemu_chr_fe_init(&s->display, chr, &error_abort);
+    qemu_chr_add_handlers(s->display.chr, NULL, NULL,
                           malta_fgpa_display_event, s);
 
     s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq,
diff --git a/monitor.c b/monitor.c
index 8728dd9..40712f7 100644
--- a/monitor.c
+++ b/monitor.c
@@ -186,7 +186,7 @@ typedef struct {
 } MonitorQAPIEventConf;
 
 struct Monitor {
-    CharDriverState *chr;
+    CharBackend chr;
     int reset_seen;
     int flags;
     int suspend_cnt;
@@ -297,7 +297,7 @@ static void monitor_flush_locked(Monitor *mon)
     len = qstring_get_length(mon->outbuf);
 
     if (len && !mon->mux_out) {
-        rc = qemu_chr_fe_write(mon->chr, (const uint8_t *) buf, len);
+        rc = qemu_chr_fe_write(mon->chr.chr, (const uint8_t *) buf, len);
         if ((rc < 0 && errno != EAGAIN) || (rc == len)) {
             /* all flushed or error */
             QDECREF(mon->outbuf);
@@ -311,8 +311,9 @@ static void monitor_flush_locked(Monitor *mon)
             mon->outbuf = tmp;
         }
         if (mon->out_watch == 0) {
-            mon->out_watch = qemu_chr_fe_add_watch(mon->chr, G_IO_OUT|G_IO_HUP,
-                                                   monitor_unblocked, mon);
+            mon->out_watch =
+                qemu_chr_fe_add_watch(mon->chr.chr, G_IO_OUT | G_IO_HUP,
+                                      monitor_unblocked, mon);
         }
     }
 }
@@ -581,8 +582,8 @@ static void monitor_data_init(Monitor *mon)
 
 static void monitor_data_destroy(Monitor *mon)
 {
-    if (mon->chr) {
-        qemu_chr_add_handlers(mon->chr, NULL, NULL, NULL, NULL);
+    if (mon->chr.chr) {
+        qemu_chr_add_handlers(mon->chr.chr, NULL, NULL, NULL, NULL);
     }
     if (monitor_is_qmp(mon)) {
         json_message_parser_destroy(&mon->qmp.parser);
@@ -1745,7 +1746,7 @@ void qmp_getfd(const char *fdname, Error **errp)
     mon_fd_t *monfd;
     int fd;
 
-    fd = qemu_chr_fe_get_msgfd(cur_mon->chr);
+    fd = qemu_chr_fe_get_msgfd(cur_mon->chr.chr);
     if (fd == -1) {
         error_setg(errp, QERR_FD_NOT_SUPPLIED);
         return;
@@ -1870,7 +1871,7 @@ AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque,
     Monitor *mon = cur_mon;
     AddfdInfo *fdinfo;
 
-    fd = qemu_chr_fe_get_msgfd(mon->chr);
+    fd = qemu_chr_fe_get_msgfd(mon->chr.chr);
     if (fd == -1) {
         error_setg(errp, QERR_FD_NOT_SUPPLIED);
         goto error;
@@ -3977,7 +3978,7 @@ void monitor_init(CharDriverState *chr, int flags)
     mon = g_malloc(sizeof(*mon));
     monitor_data_init(mon);
 
-    mon->chr = chr;
+    qemu_chr_fe_init(&mon->chr, chr, &error_abort);
     mon->flags = flags;
     if (flags & MONITOR_USE_READLINE) {
         mon->rs = readline_init(monitor_readline_printf,
diff --git a/net/colo-compare.c b/net/colo-compare.c
index 47703c5..efcd15e 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -68,9 +68,9 @@ typedef struct CompareState {
     char *pri_indev;
     char *sec_indev;
     char *outdev;
-    CharDriverState *chr_pri_in;
-    CharDriverState *chr_sec_in;
-    CharDriverState *chr_out;
+    CharBackend chr_pri_in;
+    CharBackend chr_sec_in;
+    CharBackend chr_out;
     SocketReadState pri_rs;
     SocketReadState sec_rs;
 
@@ -385,7 +385,7 @@ static void colo_compare_connection(void *opaque, void *user_data)
         }
 
         if (result) {
-            ret = compare_chr_send(s->chr_out, pkt->data, pkt->size);
+            ret = compare_chr_send(s->chr_out.chr, pkt->data, pkt->size);
             if (ret < 0) {
                 error_report("colo_send_primary_packet failed");
             }
@@ -451,7 +451,7 @@ static void compare_pri_chr_in(void *opaque, const uint8_t *buf, int size)
 
     ret = net_fill_rstate(&s->pri_rs, buf, size);
     if (ret == -1) {
-        qemu_chr_add_handlers(s->chr_pri_in, NULL, NULL, NULL, NULL);
+        qemu_chr_add_handlers(s->chr_pri_in.chr, NULL, NULL, NULL, NULL);
         error_report("colo-compare primary_in error");
     }
 }
@@ -467,7 +467,7 @@ static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size)
 
     ret = net_fill_rstate(&s->sec_rs, buf, size);
     if (ret == -1) {
-        qemu_chr_add_handlers(s->chr_sec_in, NULL, NULL, NULL, NULL);
+        qemu_chr_add_handlers(s->chr_sec_in.chr, NULL, NULL, NULL, NULL);
         error_report("colo-compare secondary_in error");
     }
 }
@@ -480,9 +480,9 @@ static void *colo_compare_thread(void *opaque)
 
     worker_context = g_main_context_new();
 
-    qemu_chr_add_handlers_full(s->chr_pri_in, compare_chr_can_read,
+    qemu_chr_add_handlers_full(s->chr_pri_in.chr, compare_chr_can_read,
                           compare_pri_chr_in, NULL, s, worker_context);
-    qemu_chr_add_handlers_full(s->chr_sec_in, compare_chr_can_read,
+    qemu_chr_add_handlers_full(s->chr_sec_in.chr, compare_chr_can_read,
                           compare_sec_chr_in, NULL, s, worker_context);
 
     compare_loop = g_main_loop_new(worker_context, FALSE);
@@ -545,7 +545,7 @@ static void compare_pri_rs_finalize(SocketReadState *pri_rs)
 
     if (packet_enqueue(s, PRIMARY_IN)) {
         trace_colo_compare_main("primary: unsupported packet in");
-        compare_chr_send(s->chr_out, pri_rs->buf, pri_rs->packet_len);
+        compare_chr_send(s->chr_out.chr, pri_rs->buf, pri_rs->packet_len);
     } else {
         /* compare connection */
         g_queue_foreach(&s->conn_list, colo_compare_connection, s);
@@ -634,23 +634,23 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp)
         return;
     }
 
-    if (find_and_check_chardev(&s->chr_pri_in, s->pri_indev, errp)) {
+    if (find_and_check_chardev(&s->chr_pri_in.chr, s->pri_indev, errp)) {
         return;
     }
 
-    if (find_and_check_chardev(&s->chr_sec_in, s->sec_indev, errp)) {
+    if (find_and_check_chardev(&s->chr_sec_in.chr, s->sec_indev, errp)) {
         return;
     }
 
-    if (find_and_check_chardev(&s->chr_out, s->outdev, errp)) {
+    if (find_and_check_chardev(&s->chr_out.chr, s->outdev, errp)) {
         return;
     }
 
-    qemu_chr_fe_claim_no_fail(s->chr_pri_in);
+    qemu_chr_fe_claim_no_fail(s->chr_pri_in.chr);
 
-    qemu_chr_fe_claim_no_fail(s->chr_sec_in);
+    qemu_chr_fe_claim_no_fail(s->chr_sec_in.chr);
 
-    qemu_chr_fe_claim_no_fail(s->chr_out);
+    qemu_chr_fe_claim_no_fail(s->chr_out.chr);
 
     net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize);
     net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize);
@@ -702,16 +702,16 @@ static void colo_compare_finalize(Object *obj)
 {
     CompareState *s = COLO_COMPARE(obj);
 
-    if (s->chr_pri_in) {
-        qemu_chr_add_handlers(s->chr_pri_in, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr_pri_in);
+    if (s->chr_pri_in.chr) {
+        qemu_chr_add_handlers(s->chr_pri_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(s->chr_pri_in.chr);
     }
-    if (s->chr_sec_in) {
-        qemu_chr_add_handlers(s->chr_sec_in, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr_sec_in);
+    if (s->chr_sec_in.chr) {
+        qemu_chr_add_handlers(s->chr_sec_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(s->chr_sec_in.chr);
     }
-    if (s->chr_out) {
-        qemu_chr_fe_release(s->chr_out);
+    if (s->chr_out.chr) {
+        qemu_chr_fe_release(s->chr_out.chr);
     }
 
     g_queue_free(&s->conn_list);
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index 0ee58d9..425e146 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -38,8 +38,8 @@ typedef struct MirrorState {
     NetFilterState parent_obj;
     char *indev;
     char *outdev;
-    CharDriverState *chr_in;
-    CharDriverState *chr_out;
+    CharBackend chr_in;
+    CharBackend chr_out;
     SocketReadState rs;
 } MirrorState;
 
@@ -110,7 +110,7 @@ static void redirector_chr_read(void *opaque, const uint8_t *buf, int size)
     ret = net_fill_rstate(&s->rs, buf, size);
 
     if (ret == -1) {
-        qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL);
+        qemu_chr_add_handlers(s->chr_in.chr, NULL, NULL, NULL, NULL);
     }
 }
 
@@ -121,7 +121,7 @@ static void redirector_chr_event(void *opaque, int event)
 
     switch (event) {
     case CHR_EVENT_CLOSED:
-        qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL);
+        qemu_chr_add_handlers(s->chr_in.chr, NULL, NULL, NULL, NULL);
         break;
     default:
         break;
@@ -138,7 +138,7 @@ static ssize_t filter_mirror_receive_iov(NetFilterState *nf,
     MirrorState *s = FILTER_MIRROR(nf);
     int ret;
 
-    ret = filter_mirror_send(s->chr_out, iov, iovcnt);
+    ret = filter_mirror_send(s->chr_out.chr, iov, iovcnt);
     if (ret) {
         error_report("filter_mirror_send failed(%s)", strerror(-ret));
     }
@@ -160,8 +160,8 @@ static ssize_t filter_redirector_receive_iov(NetFilterState *nf,
     MirrorState *s = FILTER_REDIRECTOR(nf);
     int ret;
 
-    if (s->chr_out) {
-        ret = filter_mirror_send(s->chr_out, iov, iovcnt);
+    if (s->chr_out.chr) {
+        ret = filter_mirror_send(s->chr_out.chr, iov, iovcnt);
         if (ret) {
             error_report("filter_mirror_send failed(%s)", strerror(-ret));
         }
@@ -175,8 +175,8 @@ static void filter_mirror_cleanup(NetFilterState *nf)
 {
     MirrorState *s = FILTER_MIRROR(nf);
 
-    if (s->chr_out) {
-        qemu_chr_fe_release(s->chr_out);
+    if (s->chr_out.chr) {
+        qemu_chr_fe_release(s->chr_out.chr);
     }
 }
 
@@ -184,18 +184,19 @@ static void filter_redirector_cleanup(NetFilterState *nf)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
 
-    if (s->chr_in) {
-        qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr_in);
+    if (s->chr_in.chr) {
+        qemu_chr_add_handlers(s->chr_in.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(s->chr_in.chr);
     }
-    if (s->chr_out) {
-        qemu_chr_fe_release(s->chr_out);
+    if (s->chr_out.chr) {
+        qemu_chr_fe_release(s->chr_out.chr);
     }
 }
 
 static void filter_mirror_setup(NetFilterState *nf, Error **errp)
 {
     MirrorState *s = FILTER_MIRROR(nf);
+    CharDriverState *chr;
 
     if (!s->outdev) {
         error_setg(errp, "filter mirror needs 'outdev' "
@@ -203,17 +204,19 @@ static void filter_mirror_setup(NetFilterState *nf, Error **errp)
         return;
     }
 
-    s->chr_out = qemu_chr_find(s->outdev);
-    if (s->chr_out == NULL) {
+    chr = qemu_chr_find(s->outdev);
+    if (chr == NULL) {
         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                   "Device '%s' not found", s->outdev);
         return;
     }
 
-    if (qemu_chr_fe_claim(s->chr_out) != 0) {
+    if (qemu_chr_fe_claim(chr) != 0) {
         error_setg(errp, QERR_DEVICE_IN_USE, s->outdev);
         return;
     }
+
+    qemu_chr_fe_init(&s->chr_out, chr, errp);
 }
 
 static void redirector_rs_finalize(SocketReadState *rs)
@@ -227,6 +230,7 @@ static void redirector_rs_finalize(SocketReadState *rs)
 static void filter_redirector_setup(NetFilterState *nf, Error **errp)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
+    CharDriverState *chr;
 
     if (!s->indev && !s->outdev) {
         error_setg(errp, "filter redirector needs 'indev' or "
@@ -243,26 +247,32 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp)
     net_socket_rs_init(&s->rs, redirector_rs_finalize);
 
     if (s->indev) {
-        s->chr_in = qemu_chr_find(s->indev);
-        if (s->chr_in == NULL) {
+        chr = qemu_chr_find(s->indev);
+        if (chr == NULL) {
             error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                       "IN Device '%s' not found", s->indev);
             return;
         }
 
-        qemu_chr_fe_claim_no_fail(s->chr_in);
-        qemu_chr_add_handlers(s->chr_in, redirector_chr_can_read,
+        qemu_chr_fe_claim_no_fail(chr);
+        if (!qemu_chr_fe_init(&s->chr_in, chr, errp)) {
+            return;
+        }
+        qemu_chr_add_handlers(s->chr_in.chr, redirector_chr_can_read,
                               redirector_chr_read, redirector_chr_event, nf);
     }
 
     if (s->outdev) {
-        s->chr_out = qemu_chr_find(s->outdev);
-        if (s->chr_out == NULL) {
+        chr = qemu_chr_find(s->outdev);
+        if (chr == NULL) {
             error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                       "OUT Device '%s' not found", s->outdev);
             return;
         }
-        qemu_chr_fe_claim_no_fail(s->chr_out);
+        qemu_chr_fe_claim_no_fail(chr);
+        if (!qemu_chr_fe_init(&s->chr_out, chr, errp)) {
+            return;
+        }
     }
 }
 
diff --git a/net/slirp.c b/net/slirp.c
index f9fdff5..407e8aa 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -40,6 +40,7 @@
 #include "sysemu/char.h"
 #include "sysemu/sysemu.h"
 #include "qemu/cutils.h"
+#include "qapi/error.h"
 
 static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
 {
@@ -682,7 +683,7 @@ int net_slirp_smb(const char *exported_dir)
 #endif /* !defined(_WIN32) */
 
 struct GuestFwd {
-    CharDriverState *hd;
+    CharBackend hd;
     struct in_addr server;
     int port;
     Slirp *slirp;
@@ -746,15 +747,23 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
             return -1;
         }
     } else {
-        fwd = g_new(struct GuestFwd, 1);
-        fwd->hd = qemu_chr_new(buf, p);
-        if (!fwd->hd) {
+        Error *err = NULL;
+        CharDriverState *chr = qemu_chr_new(buf, p);
+
+        if (!chr) {
             error_report("could not open guest forwarding device '%s'", buf);
+            return -1;
+        }
+
+        fwd = g_new(struct GuestFwd, 1);
+        qemu_chr_fe_init(&fwd->hd, chr, &err);
+        if (err) {
+            error_report_err(err);
             g_free(fwd);
             return -1;
         }
 
-        if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
+        if (slirp_add_exec(s->slirp, 3, fwd->hd.chr, &server, port) < 0) {
             error_report("conflicting/invalid host:port in guest forwarding "
                          "rule '%s'", config_str);
             g_free(fwd);
@@ -764,8 +773,8 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
         fwd->port = port;
         fwd->slirp = s->slirp;
 
-        qemu_chr_fe_claim_no_fail(fwd->hd);
-        qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
+        qemu_chr_fe_claim_no_fail(fwd->hd.chr);
+        qemu_chr_add_handlers(fwd->hd.chr, guestfwd_can_read, guestfwd_read,
                               NULL, fwd);
     }
     return 0;
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 5b94c84..957459f 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -20,7 +20,7 @@
 
 typedef struct VhostUserState {
     NetClientState nc;
-    CharDriverState *chr;
+    CharBackend chr;
     VHostNetState *vhost_net;
     guint watch;
     uint64_t acked_features;
@@ -78,7 +78,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[])
         s = DO_UPCAST(VhostUserState, nc, ncs[i]);
 
         options.net_backend = ncs[i];
-        options.opaque      = s->chr;
+        options.opaque      = s->chr.chr;
         options.busyloop_timeout = 0;
         net = vhost_net_init(&options);
         if (!net) {
@@ -150,10 +150,10 @@ static void vhost_user_cleanup(NetClientState *nc)
         g_free(s->vhost_net);
         s->vhost_net = NULL;
     }
-    if (s->chr) {
-        qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr);
-        s->chr = NULL;
+    if (s->chr.chr) {
+        qemu_chr_add_handlers(s->chr.chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(s->chr.chr);
+        s->chr.chr = NULL;
     }
 
     qemu_purge_queued_packets(nc);
@@ -187,7 +187,7 @@ static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond,
 {
     VhostUserState *s = opaque;
 
-    qemu_chr_disconnect(s->chr);
+    qemu_chr_disconnect(s->chr.chr);
 
     return FALSE;
 }
@@ -206,13 +206,13 @@ static void net_vhost_user_event(void *opaque, int event)
     assert(queues < MAX_QUEUE_NUM);
 
     s = DO_UPCAST(VhostUserState, nc, ncs[0]);
-    trace_vhost_user_event(s->chr->label, event);
+    trace_vhost_user_event(s->chr.chr->label, event);
     switch (event) {
     case CHR_EVENT_OPENED:
-        s->watch = qemu_chr_fe_add_watch(s->chr, G_IO_HUP,
+        s->watch = qemu_chr_fe_add_watch(s->chr.chr, G_IO_HUP,
                                          net_vhost_user_watch, s);
         if (vhost_user_start(queues, ncs) < 0) {
-            qemu_chr_disconnect(s->chr);
+            qemu_chr_disconnect(s->chr.chr);
             return;
         }
         qmp_set_link(name, true, &err);
@@ -235,6 +235,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
                                const char *name, CharDriverState *chr,
                                int queues)
 {
+    Error *err = NULL;
     NetClientState *nc, *nc0 = NULL;
     VhostUserState *s;
     int i;
@@ -254,12 +255,14 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
         nc->queue_index = i;
 
         s = DO_UPCAST(VhostUserState, nc, nc);
-        s->chr = chr;
+        if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
+            error_report_err(err);
+            return -1;
+        }
     }
 
     s = DO_UPCAST(VhostUserState, nc, nc0);
     do {
-        Error *err = NULL;
         if (qemu_chr_wait_connected(chr, &err) < 0) {
             error_report_err(err);
             return -1;
diff --git a/qtest.c b/qtest.c
index 2d9a021..3fb3c11 100644
--- a/qtest.c
+++ b/qtest.c
@@ -38,7 +38,7 @@ bool qtest_allowed;
 
 static DeviceState *irq_intercept_dev;
 static FILE *qtest_log_fp;
-static CharDriverState *qtest_chr;
+static CharBackend qtest_chr;
 static GString *inbuf;
 static int irq_levels[MAX_IRQ];
 static qemu_timeval start_time;
@@ -249,7 +249,7 @@ static void qtest_irq_handler(void *opaque, int n, int level)
     qemu_set_irq(old_irq, level);
 
     if (irq_levels[n] != level) {
-        CharDriverState *chr = qtest_chr;
+        CharDriverState *chr = qtest_chr.chr;
         irq_levels[n] = level;
         qtest_send_prefix(chr);
         qtest_sendf(chr, "IRQ %s %d\n",
@@ -690,12 +690,12 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp)
     qemu_chr_fe_set_echo(chr, true);
 
     inbuf = g_string_new("");
-    qtest_chr = chr;
+    qemu_chr_fe_init(&qtest_chr, chr, errp);
 }
 
 bool qtest_driver(void)
 {
-    return qtest_chr;
+    return qtest_chr.chr != NULL;
 }
 
 static void qtest_accel_class_init(ObjectClass *oc, void *data)
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index edf30ac..52fbc03 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -19,6 +19,7 @@
 #include "libqos/libqos.h"
 #include "libqos/pci-pc.h"
 #include "libqos/virtio-pci.h"
+#include "qapi/error.h"
 
 #include "libqos/pci-pc.h"
 #include "libqos/virtio-pci.h"
@@ -141,7 +142,7 @@ typedef struct TestServer {
     gchar *socket_path;
     gchar *mig_path;
     gchar *chr_name;
-    CharDriverState *chr;
+    CharBackend chr;
     int fds_num;
     int fds[VHOST_MEMORY_MAX_NREGIONS];
     VhostUserMemory memory;
@@ -261,13 +262,13 @@ static int chr_can_read(void *opaque)
 static void chr_read(void *opaque, const uint8_t *buf, int size)
 {
     TestServer *s = opaque;
-    CharDriverState *chr = s->chr;
+    CharBackend *chr = &s->chr;
     VhostUserMsg msg;
     uint8_t *p = (uint8_t *) &msg;
     int fd;
 
     if (s->test_fail) {
-        qemu_chr_disconnect(chr);
+        qemu_chr_disconnect(chr->chr);
         /* now switch to non-failure */
         s->test_fail = false;
     }
@@ -282,7 +283,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
 
     if (msg.size) {
         p += VHOST_USER_HDR_SIZE;
-        size = qemu_chr_fe_read_all(chr, p, msg.size);
+        size = qemu_chr_fe_read_all(chr->chr, p, msg.size);
         if (size != msg.size) {
             g_test_message("Wrong message size received %d != %d\n",
                            size, msg.size);
@@ -305,14 +306,14 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
             s->test_flags = TEST_FLAGS_END;
         }
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
+        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE + msg.size);
         break;
 
     case VHOST_USER_SET_FEATURES:
 	g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
 			!=, 0ULL);
         if (s->test_flags == TEST_FLAGS_DISCONNECT) {
-            qemu_chr_disconnect(chr);
+            qemu_chr_disconnect(chr->chr);
             s->test_flags = TEST_FLAGS_BAD;
         }
         break;
@@ -326,7 +327,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
             msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ;
         }
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
+        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE + msg.size);
         break;
 
     case VHOST_USER_GET_VRING_BASE:
@@ -335,7 +336,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
         msg.size = sizeof(m.payload.state);
         msg.payload.state.num = 0;
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
+        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE + msg.size);
 
         assert(msg.payload.state.index < s->queues * 2);
         s->rings &= ~(0x1ULL << msg.payload.state.index);
@@ -344,7 +345,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
     case VHOST_USER_SET_MEM_TABLE:
         /* received the mem table */
         memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
-        s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, G_N_ELEMENTS(s->fds));
+        s->fds_num = qemu_chr_fe_get_msgfds(chr->chr, s->fds,
+                                            G_N_ELEMENTS(s->fds));
 
         /* signal the test that it can continue */
         g_cond_signal(&s->data_cond);
@@ -353,7 +355,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
     case VHOST_USER_SET_VRING_KICK:
     case VHOST_USER_SET_VRING_CALL:
         /* consume the fd */
-        qemu_chr_fe_get_msgfds(chr, &fd, 1);
+        qemu_chr_fe_get_msgfds(chr->chr, &fd, 1);
         /*
          * This is a non-blocking eventfd.
          * The receive function forces it to be blocking,
@@ -367,11 +369,11 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
             close(s->log_fd);
             s->log_fd = -1;
         }
-        qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
+        qemu_chr_fe_get_msgfds(chr->chr, &s->log_fd, 1);
         msg.flags |= VHOST_USER_REPLY_MASK;
         msg.size = 0;
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
+        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE);
 
         g_cond_signal(&s->data_cond);
         break;
@@ -386,7 +388,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
         msg.size = sizeof(m.payload.u64);
         msg.payload.u64 = s->queues;
         p = (uint8_t *) &msg;
-        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
+        qemu_chr_fe_write_all(chr->chr, p, VHOST_USER_HDR_SIZE + msg.size);
         break;
 
     default:
@@ -453,12 +455,13 @@ static void chr_event(void *opaque, int event)
 static void test_server_create_chr(TestServer *server, const gchar *opt)
 {
     gchar *chr_path;
-
+    CharDriverState *chr;
     chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
-    server->chr = qemu_chr_new(server->chr_name, chr_path);
+    chr = qemu_chr_new(server->chr_name, chr_path);
+    qemu_chr_fe_init(&server->chr, chr, &error_abort);
     g_free(chr_path);
 
-    qemu_chr_add_handlers(server->chr, chr_can_read, chr_read,
+    qemu_chr_add_handlers(server->chr.chr, chr_can_read, chr_read,
                           chr_event, server);
 }
 
@@ -484,7 +487,7 @@ static gboolean _test_server_free(TestServer *server)
 {
     int i;
 
-    qemu_chr_delete(server->chr);
+    qemu_chr_delete(server->chr.chr);
 
     for (i = 0; i < server->fds_num; i++) {
         close(server->fds[i]);
@@ -721,7 +724,7 @@ reconnect_cb(gpointer user_data)
 {
     TestServer *s = user_data;
 
-    qemu_chr_disconnect(s->chr);
+    qemu_chr_disconnect(s->chr.chr);
 
     return FALSE;
 }
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 13/38] char: rename some frontend functions
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (11 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 12/38] char: remaining switch to CharBackend in frontend Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 14/38] colo: claim in find_and_check_chardev Marc-André Lureau
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

qemu_chr_accept_input() and qemu_chr_disconnect() are only used by
frontend, so use qemu_chr_fe prefix.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/char/bcm2835_aux.c     | 2 +-
 hw/char/cadence_uart.c    | 4 ++--
 hw/char/escc.c            | 2 +-
 hw/char/imx_serial.c      | 4 ++--
 hw/char/ipoctal232.c      | 2 +-
 hw/char/lm32_uart.c       | 2 +-
 hw/char/mcf_uart.c        | 2 +-
 hw/char/milkymist-uart.c  | 2 +-
 hw/char/pl011.c           | 2 +-
 hw/char/serial.c          | 2 +-
 hw/char/spapr_vty.c       | 2 +-
 hw/char/stm32f2xx_usart.c | 4 ++--
 hw/char/virtio-console.c  | 2 +-
 hw/char/xilinx_uartlite.c | 2 +-
 net/vhost-user.c          | 4 ++--
 qemu-char.c               | 4 ++--
 tests/vhost-user-test.c   | 6 +++---
 include/sysemu/char.h     | 6 +++---
 18 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index 4bc5d02..30f4ea5 100644
--- a/hw/char/bcm2835_aux.c
+++ b/hw/char/bcm2835_aux.c
@@ -80,7 +80,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
             }
         }
         if (s->chr.chr) {
-            qemu_chr_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(s->chr.chr);
         }
         bcm2835_aux_update(s);
         return c;
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index d5687dd..c027235 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -143,7 +143,7 @@ static void uart_rx_reset(CadenceUARTState *s)
     s->rx_wpos = 0;
     s->rx_count = 0;
     if (s->chr.chr) {
-        qemu_chr_accept_input(s->chr.chr);
+        qemu_chr_fe_accept_input(s->chr.chr);
     }
 }
 
@@ -369,7 +369,7 @@ static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t *c)
         s->rx_count--;
 
         if (s->chr.chr) {
-            qemu_chr_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(s->chr.chr);
         }
     } else {
         *c = 0;
diff --git a/hw/char/escc.c b/hw/char/escc.c
index ae69b39..f1e8fac 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -600,7 +600,7 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr,
             ret = s->rx;
         trace_escc_mem_readb_data(CHN_C(s), ret);
         if (s->chr.chr) {
-            qemu_chr_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(s->chr.chr);
         }
         return ret;
     default:
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 5c11de2..2e39d31 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -122,7 +122,7 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
             s->uts1 |= UTS1_RXEMPTY;
             imx_update(s);
             if (s->chr.chr) {
-                qemu_chr_accept_input(s->chr.chr);
+                qemu_chr_fe_accept_input(s->chr.chr);
             }
         }
         return c;
@@ -216,7 +216,7 @@ static void imx_serial_write(void *opaque, hwaddr offset,
         if (value & UCR2_RXEN) {
             if (!(s->ucr2 & UCR2_RXEN)) {
                 if (s->chr.chr) {
-                    qemu_chr_accept_input(s->chr.chr);
+                    qemu_chr_fe_accept_input(s->chr.chr);
                 }
             }
         }
diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c
index 0c9dea3..875fe3b 100644
--- a/hw/char/ipoctal232.c
+++ b/hw/char/ipoctal232.c
@@ -289,7 +289,7 @@ static uint16_t io_read(IPackDevice *ip, uint8_t addr)
                 ch->sr &= ~SR_RXRDY;
                 blk->isr &= ~ISR_RXRDY(channel);
                 if (ch->dev.chr) {
-                    qemu_chr_accept_input(ch->dev.chr);
+                    qemu_chr_fe_accept_input(ch->dev.chr);
                 }
             } else {
                 ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c
index 72fc41c..a8d4a2d 100644
--- a/hw/char/lm32_uart.c
+++ b/hw/char/lm32_uart.c
@@ -142,7 +142,7 @@ static uint64_t uart_read(void *opaque, hwaddr addr,
         r = s->regs[R_RXTX];
         s->regs[R_LSR] &= ~LSR_DR;
         uart_update_irq(s);
-        qemu_chr_accept_input(s->chr.chr);
+        qemu_chr_fe_accept_input(s->chr.chr);
         break;
     case R_IIR:
     case R_LSR:
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index 436e1b0..57b47c6 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -93,7 +93,7 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr,
             if (s->fifo_len == 0)
                 s->sr &= ~MCF_UART_RxRDY;
             mcf_uart_update(s);
-            qemu_chr_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(s->chr.chr);
             return val;
         }
     case 0x10:
diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c
index a6518e6..c75bcb5 100644
--- a/hw/char/milkymist-uart.c
+++ b/hw/char/milkymist-uart.c
@@ -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.chr);
+        qemu_chr_fe_accept_input(s->chr.chr);
         break;
 
     default:
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 9645195..29fb725 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -88,7 +88,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
         s->rsr = c >> 8;
         pl011_update(s);
         if (s->chr.chr) {
-            qemu_chr_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(s->chr.chr);
         }
         r = c;
         break;
diff --git a/hw/char/serial.c b/hw/char/serial.c
index b5fa8e1..c9a9eeb 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -489,7 +489,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.chr);
+                qemu_chr_fe_accept_input(s->chr.chr);
             }
         }
         break;
diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c
index dcb4a85..7afc6a5 100644
--- a/hw/char/spapr_vty.c
+++ b/hw/char/spapr_vty.c
@@ -51,7 +51,7 @@ static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max)
         buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE];
     }
 
-    qemu_chr_accept_input(dev->chardev.chr);
+    qemu_chr_fe_accept_input(dev->chardev.chr);
 
     return n;
 }
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 8cc2737..8619d10 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -98,7 +98,7 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr,
         retvalue = s->usart_sr;
         s->usart_sr &= ~USART_SR_TC;
         if (s->chr.chr) {
-            qemu_chr_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(s->chr.chr);
         }
         return retvalue;
     case USART_DR:
@@ -106,7 +106,7 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr,
         s->usart_sr |= USART_SR_TXE;
         s->usart_sr &= ~USART_SR_RXNE;
         if (s->chr.chr) {
-            qemu_chr_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(s->chr.chr);
         }
         qemu_set_irq(s->irq, 0);
         return s->usart_dr & 0x3FF;
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index a4730ba..93acbec 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -123,7 +123,7 @@ static void guest_writable(VirtIOSerialPort *port)
     VirtConsole *vcon = VIRTIO_CONSOLE(port);
 
     if (vcon->chr.chr) {
-        qemu_chr_accept_input(vcon->chr.chr);
+        qemu_chr_fe_accept_input(vcon->chr.chr);
     }
 }
 
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
index 0e809d5..185c63b 100644
--- a/hw/char/xilinx_uartlite.c
+++ b/hw/char/xilinx_uartlite.c
@@ -107,7 +107,7 @@ uart_read(void *opaque, hwaddr addr, unsigned int size)
                 s->rx_fifo_len--;
             uart_update_status(s);
             uart_update_irq(s);
-            qemu_chr_accept_input(s->chr.chr);
+            qemu_chr_fe_accept_input(s->chr.chr);
             break;
 
         default:
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 957459f..4578247 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -187,7 +187,7 @@ static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond,
 {
     VhostUserState *s = opaque;
 
-    qemu_chr_disconnect(s->chr.chr);
+    qemu_chr_fe_disconnect(s->chr.chr);
 
     return FALSE;
 }
@@ -212,7 +212,7 @@ static void net_vhost_user_event(void *opaque, int event)
         s->watch = qemu_chr_fe_add_watch(s->chr.chr, G_IO_HUP,
                                          net_vhost_user_watch, s);
         if (vhost_user_start(queues, ncs) < 0) {
-            qemu_chr_disconnect(s->chr.chr);
+            qemu_chr_fe_disconnect(s->chr.chr);
             return;
         }
         qmp_set_link(name, true, &err);
diff --git a/qemu-char.c b/qemu-char.c
index b44e5ba..a7e8547 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -430,7 +430,7 @@ int qemu_chr_add_client(CharDriverState *s, int fd)
     return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
 }
 
-void qemu_chr_accept_input(CharDriverState *s)
+void qemu_chr_fe_accept_input(CharDriverState *s)
 {
     if (s->chr_accept_input)
         s->chr_accept_input(s);
@@ -4235,7 +4235,7 @@ void qemu_chr_fe_release(CharDriverState *s)
     s->avail_connections++;
 }
 
-void qemu_chr_disconnect(CharDriverState *chr)
+void qemu_chr_fe_disconnect(CharDriverState *chr)
 {
     if (chr->chr_disconnect) {
         chr->chr_disconnect(chr);
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 52fbc03..6e5383d 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -268,7 +268,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
     int fd;
 
     if (s->test_fail) {
-        qemu_chr_disconnect(chr->chr);
+        qemu_chr_fe_disconnect(chr->chr);
         /* now switch to non-failure */
         s->test_fail = false;
     }
@@ -313,7 +313,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
 	g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
 			!=, 0ULL);
         if (s->test_flags == TEST_FLAGS_DISCONNECT) {
-            qemu_chr_disconnect(chr->chr);
+            qemu_chr_fe_disconnect(chr->chr);
             s->test_flags = TEST_FLAGS_BAD;
         }
         break;
@@ -724,7 +724,7 @@ reconnect_cb(gpointer user_data)
 {
     TestServer *s = user_data;
 
-    qemu_chr_disconnect(s->chr.chr);
+    qemu_chr_fe_disconnect(s->chr.chr);
 
     return FALSE;
 }
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 01cf0cc..d72d4f5 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -165,11 +165,11 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename);
 
 
 /**
- * @qemu_chr_disconnect:
+ * @qemu_chr_fe_disconnect:
  *
  * Close a fd accpeted by character backend.
  */
-void qemu_chr_disconnect(CharDriverState *chr);
+void qemu_chr_fe_disconnect(CharDriverState *chr);
 
 /**
  * @qemu_chr_cleanup:
@@ -490,7 +490,7 @@ void qemu_chr_add_handlers_full(CharDriverState *s,
                                 GMainContext *context);
 
 void qemu_chr_be_generic_open(CharDriverState *s);
-void qemu_chr_accept_input(CharDriverState *s);
+void qemu_chr_fe_accept_input(CharDriverState *s);
 int qemu_chr_add_client(CharDriverState *s, int fd);
 CharDriverState *qemu_chr_find(const char *name);
 bool chr_is_ringbuf(const CharDriverState *chr);
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 14/38] colo: claim in find_and_check_chardev
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (12 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 13/38] char: rename some frontend functions Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 15/38] char: use qemu_chr_fe* functions with CharBackend argument Marc-André Lureau
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

This factors out claiming of chardev, and changes the call to
non-fatal to return an error like the rest of the chardev checks.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 net/colo-compare.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/net/colo-compare.c b/net/colo-compare.c
index efcd15e..b115465 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -589,6 +589,13 @@ static int find_and_check_chardev(CharDriverState **chr,
                    chr_name);
         return 1;
     }
+
+    if (qemu_chr_fe_claim(*chr) < 0) {
+        error_setg(errp, "chardev \"%s\" cannot be claimed",
+                   chr_name);
+        return 1;
+    }
+
     return 0;
 }
 
@@ -646,12 +653,6 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp)
         return;
     }
 
-    qemu_chr_fe_claim_no_fail(s->chr_pri_in.chr);
-
-    qemu_chr_fe_claim_no_fail(s->chr_sec_in.chr);
-
-    qemu_chr_fe_claim_no_fail(s->chr_out.chr);
-
     net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize);
     net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize);
 
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 15/38] char: use qemu_chr_fe* functions with CharBackend argument
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (13 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 14/38] colo: claim in find_and_check_chardev Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 16/38] char: fold qemu_chr_set_handlers in qemu_chr_fe_set_handlers Marc-André Lureau
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

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

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

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

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 16/38] char: fold qemu_chr_set_handlers in qemu_chr_fe_set_handlers
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (14 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 15/38] char: use qemu_chr_fe* functions with CharBackend argument Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 17/38] vhost-user: only initialize queue 0 CharBackend Marc-André Lureau
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

qemu_chr_add_handlers*() have been removed in previous change, so the
common qemu_chr_set_handlers() is no longer needed.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c           | 78 ++++++++++++++++++++++-----------------------------
 include/sysemu/char.h |  3 +-
 2 files changed, 35 insertions(+), 46 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index b10db09..1ed69d0 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -467,46 +467,6 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
 }
 
 static void remove_fd_in_watch(CharDriverState *chr);
-
-static void
-qemu_chr_set_handlers(CharBackend *be,
-                      IOCanReadHandler *fd_can_read,
-                      IOReadHandler *fd_read,
-                      IOEventHandler *fd_event,
-                      void *opaque,
-                      GMainContext *context)
-{
-    CharDriverState *s = be->chr;
-    int fe_open;
-
-    if (!opaque && !fd_can_read && !fd_read && !fd_event) {
-        fe_open = 0;
-        remove_fd_in_watch(s);
-    } else {
-        fe_open = 1;
-    }
-    s->chr_can_read = fd_can_read;
-    s->chr_read = fd_read;
-    s->chr_event = fd_event;
-    s->handler_opaque = opaque;
-    if (s->chr_update_read_handler) {
-        s->chr_update_read_handler(s, context, be->tag);
-    }
-
-    if (!s->explicit_fe_open) {
-        qemu_chr_fe_set_open(be, fe_open);
-    }
-
-    /* We're connecting to an already opened device, so let's make sure we
-       also get the open event */
-    if (fe_open) {
-        qemu_chr_fe_take_focus(be);
-        if (s->be_open) {
-            qemu_chr_be_generic_open(s);
-        }
-    }
-}
-
 static int mux_chr_new_handler_tag(CharDriverState *chr, Error **errp);
 static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context);
 static void mux_set_focus(MuxDriver *d, int focus);
@@ -936,17 +896,45 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
                               void *opaque,
                               GMainContext *context)
 {
+    CharDriverState *s;
+    int fe_open;
+
     assert(b);
 
-    if (!b->chr) {
+    s = b->chr;
+    if (!s) {
         return;
     }
 
-    qemu_chr_set_handlers(b, fd_can_read, fd_read,
-                          fd_event, opaque, context);
+    if (!opaque && !fd_can_read && !fd_read && !fd_event) {
+        fe_open = 0;
+        remove_fd_in_watch(s);
+    } else {
+        fe_open = 1;
+    }
+    s->chr_can_read = fd_can_read;
+    s->chr_read = fd_read;
+    s->chr_event = fd_event;
+    s->handler_opaque = opaque;
+    if (s->chr_update_read_handler) {
+        s->chr_update_read_handler(s, context, b->tag);
+    }
+
+    if (!s->explicit_fe_open) {
+        qemu_chr_fe_set_open(b, fe_open);
+    }
 
-    if (b->chr->is_mux) {
-        mux_chr_set_handlers(b->chr, context);
+    if (fe_open) {
+        qemu_chr_fe_take_focus(b);
+        /* We're connecting to an already opened device, so let's make sure we
+           also get the open event */
+        if (s->be_open) {
+            qemu_chr_be_generic_open(s);
+        }
+    }
+
+    if (s->is_mux) {
+        mux_chr_set_handlers(s, context);
     }
 }
 
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0bd7506..6737a8a 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -460,7 +460,8 @@ CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
  * @opaque: an opaque pointer for the callbacks
  * @context: a main loop context or NULL for the default
  *
- * Set the front end char handlers.
+ * Set the front end char handlers. The front end takes the focus if
+ * any of the handler is non-NULL.
  */
 void qemu_chr_fe_set_handlers(CharBackend *b,
                               IOCanReadHandler *fd_can_read,
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 17/38] vhost-user: only initialize queue 0 CharBackend
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (15 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 16/38] char: fold qemu_chr_set_handlers in qemu_chr_fe_set_handlers Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 18/38] char: replace qemu_chr_claim/release with qemu_chr_fe_init/deinit Marc-André Lureau
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

All the queues share the same chardev. Initialize only the first queue
CharBackend, and pass it to other queues. This will allow to claim the
chardev only once in a later change.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 net/vhost-user.c | 29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/net/vhost-user.c b/net/vhost-user.c
index 8b7e98d..357b500 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -20,7 +20,7 @@
 
 typedef struct VhostUserState {
     NetClientState nc;
-    CharBackend chr;
+    CharBackend chr; /* only queue index 0 */
     VHostNetState *vhost_net;
     guint watch;
     uint64_t acked_features;
@@ -62,7 +62,7 @@ static void vhost_user_stop(int queues, NetClientState *ncs[])
     }
 }
 
-static int vhost_user_start(int queues, NetClientState *ncs[])
+static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be)
 {
     VhostNetOptions options;
     struct vhost_net *net = NULL;
@@ -78,7 +78,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[])
         s = DO_UPCAST(VhostUserState, nc, ncs[i]);
 
         options.net_backend = ncs[i];
-        options.opaque      = &s->chr;
+        options.opaque      = be;
         options.busyloop_timeout = 0;
         net = vhost_net_init(&options);
         if (!net) {
@@ -150,7 +150,7 @@ static void vhost_user_cleanup(NetClientState *nc)
         g_free(s->vhost_net);
         s->vhost_net = NULL;
     }
-    if (s->chr.chr) {
+    if (nc->queue_index == 0 && s->chr.chr) {
         qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL);
         qemu_chr_fe_release(s->chr.chr);
         s->chr.chr = NULL;
@@ -213,7 +213,7 @@ static void net_vhost_user_event(void *opaque, int event)
     case CHR_EVENT_OPENED:
         s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
                                          net_vhost_user_watch, s);
-        if (vhost_user_start(queues, ncs) < 0) {
+        if (vhost_user_start(queues, ncs, &s->chr) < 0) {
             qemu_chr_fe_disconnect(&s->chr);
             return;
         }
@@ -247,21 +247,18 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
 
     for (i = 0; i < queues; i++) {
         nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
-        if (!nc0) {
-            nc0 = nc;
-        }
-
         snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
                  i, chr->label);
-
         nc->queue_index = i;
-
-        s = DO_UPCAST(VhostUserState, nc, nc);
-
-        if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
-            error_report_err(err);
-            return -1;
+        if (!nc0) {
+            nc0 = nc;
+            s = DO_UPCAST(VhostUserState, nc, nc);
+            if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
+                error_report_err(err);
+                return -1;
+            }
         }
+
     }
 
     s = DO_UPCAST(VhostUserState, nc, nc0);
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 18/38] char: replace qemu_chr_claim/release with qemu_chr_fe_init/deinit
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (16 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 17/38] vhost-user: only initialize queue 0 CharBackend Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 19/38] char: make some qemu_chr_fe skip if no driver Marc-André Lureau
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Now that all front end use qemu_chr_fe_init(), we can move chardev
claiming in init(), and add a function deinit() to release the chardev
and cleanup handlers.

The qemu_chr_fe_claim_no_fail() for property are gone, since the
property will raise an error instead. In other cases, where there is
already an error path, an error is raised instead. Finally, other cases
are handled by &error_abort in qemu_chr_fe_init().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/rng-egd.c               | 10 +---------
 gdbstub.c                        |  3 +--
 hw/arm/pxa2xx.c                  |  1 -
 hw/char/mcf_uart.c               |  1 -
 hw/char/serial.c                 |  2 +-
 hw/char/sh_serial.c              |  1 -
 hw/char/xen_console.c            | 16 +++------------
 hw/core/qdev-properties-system.c | 14 +++++---------
 hw/usb/ccid-card-passthru.c      |  1 +
 hw/usb/redirect.c                |  2 +-
 monitor.c                        |  4 +---
 net/colo-compare.c               | 20 +++----------------
 net/filter-mirror.c              | 20 +++----------------
 net/slirp.c                      |  1 -
 net/vhost-user.c                 |  8 ++------
 qemu-char.c                      | 42 +++++++++++++++++-----------------------
 tests/vhost-user-test.c          |  1 +
 vl.c                             |  1 -
 include/sysemu/char.h            | 39 +++++++++----------------------------
 19 files changed, 50 insertions(+), 137 deletions(-)

diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index d9e50bb..433f583 100644
--- a/backends/rng-egd.c
+++ b/backends/rng-egd.c
@@ -100,10 +100,6 @@ static void rng_egd_opened(RngBackend *b, Error **errp)
                   "Device '%s' not found", s->chr_name);
         return;
     }
-    if (qemu_chr_fe_claim(chr) != 0) {
-        error_setg(errp, QERR_DEVICE_IN_USE, s->chr_name);
-        return;
-    }
     if (!qemu_chr_fe_init(&s->chr, chr, errp)) {
         return;
     }
@@ -149,11 +145,7 @@ static void rng_egd_finalize(Object *obj)
 {
     RngEgd *s = RNG_EGD(obj);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr.chr);
-    }
-
+    qemu_chr_fe_deinit(&s->chr);
     g_free(s->chr_name);
 }
 
diff --git a/gdbstub.c b/gdbstub.c
index 33b056e..5944494 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1494,6 +1494,7 @@ void gdb_exit(CPUArchState *env, int code)
   put_packet(s, buf);
 
 #ifndef CONFIG_USER_ONLY
+  qemu_chr_fe_deinit(&s->chr);
   qemu_chr_delete(chr);
 #endif
 }
@@ -1752,8 +1753,6 @@ int gdbserver_start(const char *device)
         chr = qemu_chr_new_noreplay("gdb", device);
         if (!chr)
             return -1;
-
-        qemu_chr_fe_claim_no_fail(chr);
     }
 
     s = gdbserver_state;
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 798c05b..cd98379 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1976,7 +1976,6 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
     PXA2xxFIrState *s = PXA2XX_FIR(dev);
 
     if (s->chr.chr) {
-        qemu_chr_fe_claim_no_fail(s->chr.chr);
         qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty,
                                  pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL);
     }
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index cc3db13..b505343 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -285,7 +285,6 @@ void *mcf_uart_init(qemu_irq irq, CharDriverState *chr)
     s->irq = irq;
     if (chr) {
         qemu_chr_fe_init(&s->chr, chr, &error_abort);
-        qemu_chr_fe_claim_no_fail(chr);
         qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive,
                                  mcf_uart_receive, mcf_uart_event, s, NULL);
     }
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 257be46..afa6248 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -904,7 +904,7 @@ void serial_realize_core(SerialState *s, Error **errp)
 
 void serial_exit_core(SerialState *s)
 {
-    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL);
+    qemu_chr_fe_deinit(&s->chr);
     qemu_unregister_reset(serial_reset, s);
 }
 
diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
index 8d82986..8bb45db 100644
--- a/hw/char/sh_serial.c
+++ b/hw/char/sh_serial.c
@@ -397,7 +397,6 @@ void sh_serial_init(MemoryRegion *sysmem,
     memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
 
     if (chr) {
-        qemu_chr_fe_claim_no_fail(chr);
         qemu_chr_fe_init(&s->chr, chr, &error_abort);
         qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1,
                                  sh_serial_receive1,
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index 5e5aca1..f664366 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -245,15 +245,8 @@ static int con_initialise(struct XenDevice *xendev)
 
     xen_be_bind_evtchn(&con->xendev);
     if (con->chr.chr) {
-        if (qemu_chr_fe_claim(con->chr.chr) == 0) {
-            qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
-                                     xencons_receive, NULL, con, NULL);
-        } else {
-            xen_be_printf(xendev, 0,
-                          "xen_console_init error chardev %s already used\n",
-                          con->chr.chr->label);
-            con->chr.chr = NULL;
-        }
+        qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
+                                 xencons_receive, NULL, con, NULL);
     }
 
     xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
@@ -268,10 +261,7 @@ static void con_disconnect(struct XenDevice *xendev)
 {
     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
 
-    if (con->chr.chr) {
-        qemu_chr_fe_set_handlers(&con->chr, NULL, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(con->chr.chr);
-    }
+    qemu_chr_fe_deinit(&con->chr);
     xen_be_unbind_evtchn(&con->xendev);
 
     if (con->sring) {
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 6d45d32..c35f0f5 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -206,13 +206,12 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
                    object_get_typename(obj), prop->name, str);
         return;
     }
-    if (qemu_chr_fe_claim(s) != 0) {
-        error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use",
-                  object_get_typename(obj), prop->name, str);
+
+    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;
     }
-
-    qemu_chr_fe_init(be, s, errp);
 }
 
 static void release_chr(Object *obj, const char *name, void *opaque)
@@ -221,10 +220,7 @@ static void release_chr(Object *obj, const char *name, void *opaque)
     Property *prop = opaque;
     CharBackend *be = qdev_get_prop_ptr(dev, prop);
 
-    if (be->chr) {
-        qemu_chr_fe_set_handlers(be, NULL, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(be->chr);
-    }
+    qemu_chr_fe_deinit(be);
 }
 
 PropertyInfo qdev_prop_chr = {
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index a8c8684..369a8f1 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -266,6 +266,7 @@ static void ccid_card_vscard_drop_connection(PassthruState *card)
 {
     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;
 }
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 0fb2e9a..6f5ad20 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1430,9 +1430,9 @@ 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);
 
-    dev->cs.chr = NULL;
     /* Note must be done after qemu_chr_close, as that causes a close event */
     qemu_bh_delete(dev->chardev_close_bh);
     qemu_bh_delete(dev->device_reject_bh);
diff --git a/monitor.c b/monitor.c
index 1c6f28f..5c349c5 100644
--- a/monitor.c
+++ b/monitor.c
@@ -582,9 +582,7 @@ static void monitor_data_init(Monitor *mon)
 
 static void monitor_data_destroy(Monitor *mon)
 {
-    if (mon->chr.chr) {
-        qemu_chr_fe_set_handlers(&mon->chr, NULL, NULL, NULL, NULL, NULL);
-    }
+    qemu_chr_fe_deinit(&mon->chr);
     if (monitor_is_qmp(mon)) {
         json_message_parser_destroy(&mon->qmp.parser);
     }
diff --git a/net/colo-compare.c b/net/colo-compare.c
index 63d92cb..3083681 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -590,12 +590,6 @@ static int find_and_check_chardev(CharDriverState **chr,
         return 1;
     }
 
-    if (qemu_chr_fe_claim(*chr) < 0) {
-        error_setg(errp, "chardev \"%s\" cannot be claimed",
-                   chr_name);
-        return 1;
-    }
-
     return 0;
 }
 
@@ -707,17 +701,9 @@ static void colo_compare_finalize(Object *obj)
 {
     CompareState *s = COLO_COMPARE(obj);
 
-    if (s->chr_pri_in.chr) {
-        qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr_pri_in.chr);
-    }
-    if (s->chr_sec_in.chr) {
-        qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr_sec_in.chr);
-    }
-    if (s->chr_out.chr) {
-        qemu_chr_fe_release(s->chr_out.chr);
-    }
+    qemu_chr_fe_deinit(&s->chr_pri_in);
+    qemu_chr_fe_deinit(&s->chr_sec_in);
+    qemu_chr_fe_deinit(&s->chr_out);
 
     g_queue_free(&s->conn_list);
 
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index 12d79cd..1864c81 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -175,22 +175,15 @@ static void filter_mirror_cleanup(NetFilterState *nf)
 {
     MirrorState *s = FILTER_MIRROR(nf);
 
-    if (s->chr_out.chr) {
-        qemu_chr_fe_release(s->chr_out.chr);
-    }
+    qemu_chr_fe_deinit(&s->chr_out);
 }
 
 static void filter_redirector_cleanup(NetFilterState *nf)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
 
-    if (s->chr_in.chr) {
-        qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr_in.chr);
-    }
-    if (s->chr_out.chr) {
-        qemu_chr_fe_release(s->chr_out.chr);
-    }
+    qemu_chr_fe_deinit(&s->chr_in);
+    qemu_chr_fe_deinit(&s->chr_out);
 }
 
 static void filter_mirror_setup(NetFilterState *nf, Error **errp)
@@ -211,11 +204,6 @@ static void filter_mirror_setup(NetFilterState *nf, Error **errp)
         return;
     }
 
-    if (qemu_chr_fe_claim(chr) != 0) {
-        error_setg(errp, QERR_DEVICE_IN_USE, s->outdev);
-        return;
-    }
-
     qemu_chr_fe_init(&s->chr_out, chr, errp);
 }
 
@@ -254,7 +242,6 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp)
             return;
         }
 
-        qemu_chr_fe_claim_no_fail(chr);
         if (!qemu_chr_fe_init(&s->chr_in, chr, errp)) {
             return;
         }
@@ -271,7 +258,6 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp)
                       "OUT Device '%s' not found", s->outdev);
             return;
         }
-        qemu_chr_fe_claim_no_fail(chr);
         if (!qemu_chr_fe_init(&s->chr_out, chr, errp)) {
             return;
         }
diff --git a/net/slirp.c b/net/slirp.c
index f9f6fc6..0e67535 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -774,7 +774,6 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
         fwd->port = port;
         fwd->slirp = s->slirp;
 
-        qemu_chr_fe_claim_no_fail(fwd->hd.chr);
         qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read,
                                  NULL, fwd, NULL);
     }
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 357b500..140a4e0 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -150,10 +150,8 @@ static void vhost_user_cleanup(NetClientState *nc)
         g_free(s->vhost_net);
         s->vhost_net = NULL;
     }
-    if (nc->queue_index == 0 && s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(s->chr.chr);
-        s->chr.chr = NULL;
+    if (nc->queue_index == 0) {
+        qemu_chr_fe_deinit(&s->chr);
     }
 
     qemu_purge_queued_packets(nc);
@@ -297,8 +295,6 @@ static CharDriverState *net_vhost_claim_chardev(
         return NULL;
     }
 
-    qemu_chr_fe_claim_no_fail(chr);
-
     return chr;
 }
 
diff --git a/qemu-char.c b/qemu-char.c
index 1ed69d0..40c5b50 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -776,6 +776,7 @@ static void mux_chr_close(struct CharDriverState *chr)
 {
     MuxDriver *d = chr->opaque;
 
+    qemu_chr_fe_deinit(&d->chr);
     g_free(d);
 }
 
@@ -876,6 +877,12 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
     assert(b);
     assert(s);
 
+    if (s->avail_connections < 1) {
+        error_setg(errp, QERR_DEVICE_IN_USE, s->label);
+        return false;
+    }
+    s->avail_connections--;
+
     if (s->is_mux) {
         tag = mux_chr_new_handler_tag(s, errp);
         if (tag < 0) {
@@ -889,6 +896,17 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
     return true;
 }
 
+void qemu_chr_fe_deinit(CharBackend *b)
+{
+    assert(b);
+
+    if (b->chr) {
+        qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL);
+        b->chr->avail_connections++;
+        b->chr = NULL;
+    }
+}
+
 void qemu_chr_fe_set_handlers(CharBackend *b,
                               IOCanReadHandler *fd_can_read,
                               IOReadHandler *fd_read,
@@ -4124,7 +4142,6 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename)
         error_report_err(err);
     }
     if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
-        qemu_chr_fe_claim_no_fail(chr);
         monitor_init(chr, MONITOR_USE_READLINE);
     }
     qemu_opts_del(opts);
@@ -4200,29 +4217,6 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
     return tag;
 }
 
-int qemu_chr_fe_claim(CharDriverState *s)
-{
-    if (s->avail_connections < 1) {
-        return -1;
-    }
-    s->avail_connections--;
-    return 0;
-}
-
-void qemu_chr_fe_claim_no_fail(CharDriverState *s)
-{
-    if (qemu_chr_fe_claim(s) != 0) {
-        fprintf(stderr, "%s: error chardev \"%s\" already used\n",
-                __func__, s->label);
-        exit(1);
-    }
-}
-
-void qemu_chr_fe_release(CharDriverState *s)
-{
-    s->avail_connections++;
-}
-
 void qemu_chr_fe_disconnect(CharBackend *be)
 {
     CharDriverState *chr = be->chr;
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 2d86de1..24c2323 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -490,6 +490,7 @@ static gboolean _test_server_free(TestServer *server)
     int i;
     CharDriverState *chr = qemu_chr_fe_get_driver(&server->chr);
 
+    qemu_chr_fe_deinit(&server->chr);
     qemu_chr_delete(chr);
 
     for (i = 0; i < server->fds_num; i++) {
diff --git a/vl.c b/vl.c
index caa5f3b..43f1457 100644
--- a/vl.c
+++ b/vl.c
@@ -2417,7 +2417,6 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
         exit(1);
     }
 
-    qemu_chr_fe_claim_no_fail(chr);
     monitor_init(chr, flags);
     return 0;
 }
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 6737a8a..b81dbcc 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -361,35 +361,6 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int num);
  */
 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num);
 
-/**
- * @qemu_chr_fe_claim:
- *
- * Claim a backend before using it, should be called before calling
- * qemu_chr_fe_set_handlers().
- *
- * Returns: -1 if the backend is already in use by another frontend, 0 on
- *          success.
- */
-int qemu_chr_fe_claim(CharDriverState *s);
-
-/**
- * @qemu_chr_fe_claim_no_fail:
- *
- * Like qemu_chr_fe_claim, but will exit qemu with an error when the
- * backend is already in use.
- */
-void qemu_chr_fe_claim_no_fail(CharDriverState *s);
-
-/**
- * @qemu_chr_fe_claim:
- *
- * Release a backend for use by another frontend.
- *
- * Returns: -1 if the backend is already in use by another frontend, 0 on
- *          success.
- */
-void qemu_chr_fe_release(CharDriverState *s);
-
 /**
  * @qemu_chr_be_can_write:
  *
@@ -437,7 +408,8 @@ void qemu_chr_be_event(CharDriverState *s, int event);
  * @qemu_chr_fe_init:
  *
  * Initializes a front end for the given CharBackend and
- * CharDriver.
+ * CharDriver. Call qemu_chr_fe_deinit() to remove the association and
+ * release the driver.
  *
  * Returns: false on error.
  */
@@ -450,6 +422,13 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp);
  */
 CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
 
+/**
+ * @qemu_chr_fe_deinit:
+ *
+ * Dissociate the CharBackend from the CharDriver.
+ */
+void qemu_chr_fe_deinit(CharBackend *b);
+
 /**
  * @qemu_chr_fe_set_handlers:
  * @b: a CharBackend
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 19/38] char: make some qemu_chr_fe skip if no driver
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (17 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 18/38] char: replace qemu_chr_claim/release with qemu_chr_fe_init/deinit Marc-André Lureau
@ 2016-10-22  9:52 ` Marc-André Lureau
  2023-02-15 22:13   ` Philippe Mathieu-Daudé
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 20/38] tests: start chardev unit tests Marc-André Lureau
                   ` (6 subsequent siblings)
  25 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

In most cases, front ends do not care about the side effect of
CharBackend, so we can simply skip the checks and call the qemu_chr_fe
functions even without associated CharDriver.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/arm/pxa2xx.c           |  8 +++-----
 hw/arm/strongarm.c        | 16 ++++++---------
 hw/char/bcm2835_aux.c     | 18 ++++++-----------
 hw/char/cadence_uart.c    | 24 +++++++---------------
 hw/char/digic-uart.c      | 14 +++++--------
 hw/char/escc.c            |  4 +---
 hw/char/etraxfs_ser.c     |  8 +++-----
 hw/char/imx_serial.c      | 26 +++++++++---------------
 hw/char/ipoctal232.c      | 14 +++++--------
 hw/char/lm32_juart.c      | 14 +++++--------
 hw/char/lm32_uart.c       | 14 +++++--------
 hw/char/mcf_uart.c        |  8 +++-----
 hw/char/milkymist-uart.c  | 10 +++-------
 hw/char/pl011.c           | 18 ++++++-----------
 hw/char/sclpconsole-lm.c  |  6 ++----
 hw/char/sclpconsole.c     |  6 ++----
 hw/char/stm32f2xx_usart.c | 22 +++++++-------------
 hw/char/virtio-console.c  |  6 ++----
 hw/char/xen_console.c     |  6 ++----
 hw/char/xilinx_uartlite.c | 14 +++++--------
 qemu-char.c               | 51 ++++++++++++++++++++++++++++++++++++++---------
 include/sysemu/char.h     | 40 +++++++++++++++++++++++++------------
 22 files changed, 156 insertions(+), 191 deletions(-)

diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index cd98379..c9f4503 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1903,7 +1903,7 @@ static void pxa2xx_fir_write(void *opaque, hwaddr addr,
         } else {
             ch = ~value;
         }
-        if (s->chr.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);
@@ -1975,10 +1975,8 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
 {
     PXA2xxFIrState *s = PXA2XX_FIR(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty,
-                                 pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty,
+                             pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL);
 }
 
 static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id)
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index fd13a39..370198a 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -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.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);
@@ -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.chr) {
-        qemu_chr_fe_set_handlers(&s->chr,
-                                 strongarm_uart_can_receive,
-                                 strongarm_uart_receive,
-                                 strongarm_uart_event,
-                                 s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr,
+                             strongarm_uart_can_receive,
+                             strongarm_uart_receive,
+                             strongarm_uart_event,
+                             s, NULL);
 }
 
 static void strongarm_uart_reset(DeviceState *dev)
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index c49ec8c..af329aa 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.chr) {
-            qemu_chr_fe_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.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.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
-                                 bcm2835_aux_receive, NULL, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
+                             bcm2835_aux_receive, NULL, s, NULL);
 }
 
 static Property bcm2835_aux_props[] = {
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index 4459b2d..291818e 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.chr) {
-        qemu_chr_fe_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.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.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)
@@ -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.chr) {
-            qemu_chr_fe_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.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
+                             uart_event, s, NULL);
 }
 
 static void cadence_uart_init(Object *obj)
diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c
index c7b3db6..2955e19 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.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,10 +145,8 @@ static void digic_uart_realize(DeviceState *dev, Error **errp)
 {
     DigicUartState *s = DIGIC_UART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                             uart_event, s, NULL);
 }
 
 static void digic_uart_init(Object *obj)
diff --git a/hw/char/escc.c b/hw/char/escc.c
index 4578a46..c71b0a8 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -599,9 +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.chr) {
-            qemu_chr_fe_accept_input(&s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         return ret;
     default:
         break;
diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c
index d812954..18c374b 100644
--- a/hw/char/etraxfs_ser.c
+++ b/hw/char/etraxfs_ser.c
@@ -231,11 +231,9 @@ static void etraxfs_ser_realize(DeviceState *dev, Error **errp)
 {
     ETRAXSerial *s = ETRAX_SERIAL(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr,
-                                 serial_can_receive, serial_receive,
-                                 serial_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr,
+                             serial_can_receive, serial_receive,
+                             serial_event, s, NULL);
 }
 
 static void etraxfs_ser_class_init(ObjectClass *klass, void *data)
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index d9a0a25..8dbb7b2 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.chr) {
-                qemu_chr_fe_accept_input(&s->chr);
-            }
+            qemu_chr_fe_accept_input(&s->chr);
         }
         return c;
 
@@ -182,11 +180,9 @@ static void imx_serial_write(void *opaque, hwaddr offset,
     case 0x10: /* UTXD */
         ch = value;
         if (s->ucr2 & UCR2_TXEN) {
-            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);
-            }
+            /* 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;
@@ -215,9 +211,7 @@ static void imx_serial_write(void *opaque, hwaddr offset,
         }
         if (value & UCR2_RXEN) {
             if (!(s->ucr2 & UCR2_RXEN)) {
-                if (s->chr.chr) {
-                    qemu_chr_fe_accept_input(&s->chr);
-                }
+                qemu_chr_fe_accept_input(&s->chr);
             }
         }
         s->ucr2 = value & 0xffff;
@@ -319,12 +313,10 @@ static void imx_serial_realize(DeviceState *dev, Error **errp)
 {
     IMXSerialState *s = IMX_SERIAL(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive,
-                                 imx_event, s, NULL);
-    } 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);
 }
 
 static void imx_serial_init(Object *obj)
diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c
index d504721..6f150e0 100644
--- a/hw/char/ipoctal232.c
+++ b/hw/char/ipoctal232.c
@@ -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.chr) {
-                    qemu_chr_fe_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.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);
-            }
+            /* 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);
         }
diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c
index 9629e9e..febb412 100644
--- a/hw/char/lm32_juart.c
+++ b/hw/char/lm32_juart.c
@@ -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.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,10 +118,8 @@ static void lm32_juart_realize(DeviceState *dev, Error **errp)
 {
     LM32JuartState *s = LM32_JUART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx,
-                                 juart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx,
+                             juart_event, s, NULL);
 }
 
 static const VMStateDescription vmstate_lm32_juart = {
diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c
index e325b91..1b2746f 100644
--- a/hw/char/lm32_uart.c
+++ b/hw/char/lm32_uart.c
@@ -177,11 +177,9 @@ static void uart_write(void *opaque, hwaddr addr,
     addr >>= 2;
     switch (addr) {
     case R_RXTX:
-        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);
-        }
+        /* 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,10 +265,8 @@ static void lm32_uart_realize(DeviceState *dev, Error **errp)
 {
     LM32UartState *s = LM32_UART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                             uart_event, s, NULL);
 }
 
 static const VMStateDescription vmstate_lm32_uart = {
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index b505343..456591a 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -114,11 +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.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) {
diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c
index 0a4c617..dec0a8c 100644
--- a/hw/char/milkymist-uart.c
+++ b/hw/char/milkymist-uart.c
@@ -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.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:
@@ -200,10 +198,8 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp)
 {
     MilkymistUartState *s = MILKYMIST_UART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                             uart_event, s, NULL);
 }
 
 static void milkymist_uart_init(Object *obj)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 52ec866..900ee5d 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -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.chr) {
-            qemu_chr_fe_accept_input(&s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         r = c;
         break;
     case 1: /* UARTRSR */
@@ -168,11 +166,9 @@ static void pl011_write(void *opaque, hwaddr offset,
     case 0: /* UARTDR */
         /* ??? Check if transmitter is enabled.  */
         ch = value;
-        if (s->chr.chr) {
-            /* XXX this blocks entire thread. Rewrite to use
-             * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(&s->chr, &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;
@@ -332,10 +328,8 @@ static void pl011_realize(DeviceState *dev, Error **errp)
 {
     PL011State *s = PL011(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
-                                 pl011_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
+                             pl011_event, s, NULL);
 }
 
 static void pl011_class_init(ObjectClass *oc, void *data)
diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c
index 0660cbc..7191717 100644
--- a/hw/char/sclpconsole-lm.c
+++ b/hw/char/sclpconsole-lm.c
@@ -312,10 +312,8 @@ static int console_init(SCLPEvent *event)
     }
     console_available = true;
 
-    if (scon->chr.chr) {
-        qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
-                                 chr_read, NULL, scon, NULL);
-    }
+    qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
+                             chr_read, NULL, scon, NULL);
 
     return 0;
 }
diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c
index 0559208..27a6034 100644
--- a/hw/char/sclpconsole.c
+++ b/hw/char/sclpconsole.c
@@ -227,10 +227,8 @@ static int console_init(SCLPEvent *event)
         return -1;
     }
     console_available = true;
-    if (scon->chr.chr) {
-        qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
-                                 chr_read, NULL, scon, NULL);
-    }
+    qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
+                             chr_read, NULL, scon, NULL);
 
     return 0;
 }
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 03ccc52..8e9852b 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.chr) {
-            qemu_chr_fe_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.chr) {
-            qemu_chr_fe_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.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.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive,
-                                 stm32f2xx_usart_receive, NULL, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive,
+                             stm32f2xx_usart_receive, NULL, s, NULL);
 }
 
 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 135f589..378c1c1 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -108,7 +108,7 @@ 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.chr && !k->is_console) {
+    if (!k->is_console) {
         qemu_chr_fe_set_open(&vcon->chr, guest_connected);
     }
 
@@ -122,9 +122,7 @@ static void guest_writable(VirtIOSerialPort *port)
 {
     VirtConsole *vcon = VIRTIO_CONSOLE(port);
 
-    if (vcon->chr.chr) {
-        qemu_chr_fe_accept_input(&vcon->chr);
-    }
+    qemu_chr_fe_accept_input(&vcon->chr);
 }
 
 /* Readiness of the guest to accept data on a port */
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index f664366..785bf86 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -244,10 +244,8 @@ static int con_initialise(struct XenDevice *xendev)
 	return -1;
 
     xen_be_bind_evtchn(&con->xendev);
-    if (con->chr.chr) {
-        qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
-                                 xencons_receive, NULL, con, NULL);
-    }
+    qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
+                             xencons_receive, NULL, con, NULL);
 
     xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
 		  con->ring_ref,
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
index d6df643..c7888f7 100644
--- a/hw/char/xilinx_uartlite.c
+++ b/hw/char/xilinx_uartlite.c
@@ -143,11 +143,9 @@ uart_write(void *opaque, hwaddr addr,
             break;
 
         case R_TX:
-            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);
-            }
+            /* 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,10 +211,8 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp)
 {
     XilinxUARTLite *s = XILINX_UARTLITE(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                             uart_event, s, NULL);
 }
 
 static void xilinx_uartlite_init(Object *obj)
diff --git a/qemu-char.c b/qemu-char.c
index 40c5b50..dee2ced 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -273,6 +273,10 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
     CharDriverState *s = be->chr;
     int ret;
 
+    if (!s) {
+        return 0;
+    }
+
     if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
         int offset;
         replay_char_write_event_load(&ret, &offset);
@@ -325,6 +329,10 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
 {
     CharDriverState *s = be->chr;
 
+    if (!s) {
+        return 0;
+    }
+
     return qemu_chr_write_all(s, buf, len);
 }
 
@@ -334,10 +342,10 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
     int offset = 0, counter = 10;
     int res;
 
-    if (!s->chr_sync_read) {
+    if (!s || !s->chr_sync_read) {
         return 0;
     }
-    
+
     if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
         return replay_char_read_all_load(buf);
     }
@@ -378,7 +386,8 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
 {
     CharDriverState *s = be->chr;
     int res;
-    if (!s->chr_ioctl || s->replay) {
+
+    if (!s || !s->chr_ioctl || s->replay) {
         res = -ENOTSUP;
     } else {
         res = s->chr_ioctl(s, cmd, arg);
@@ -418,7 +427,7 @@ int qemu_chr_fe_get_msgfd(CharBackend *be)
     CharDriverState *s = be->chr;
     int fd;
     int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
-    if (s->replay) {
+    if (s && s->replay) {
         fprintf(stderr,
                 "Replay: get msgfd is not supported for serial devices yet\n");
         exit(1);
@@ -430,6 +439,10 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
 {
     CharDriverState *s = be->chr;
 
+    if (!s) {
+        return -1;
+    }
+
     return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
 }
 
@@ -437,6 +450,10 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
 {
     CharDriverState *s = be->chr;
 
+    if (!s) {
+        return -1;
+    }
+
     return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1;
 }
 
@@ -449,6 +466,10 @@ void qemu_chr_fe_accept_input(CharBackend *be)
 {
     CharDriverState *s = be->chr;
 
+    if (!s) {
+        return;
+    }
+
     if (s->chr_accept_input)
         s->chr_accept_input(s);
     qemu_notify_event();
@@ -959,7 +980,10 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
 void qemu_chr_fe_take_focus(CharBackend *b)
 {
     assert(b);
-    assert(b->chr);
+
+    if (!b->chr) {
+        return;
+    }
 
     if (b->chr->is_mux) {
         mux_set_focus(b->chr->opaque, b->tag);
@@ -3363,6 +3387,11 @@ static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
 
 int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
 {
+    if (!be->chr) {
+        error_setg(errp, "missing associated backend");
+        return -1;
+    }
+
     return qemu_chr_wait_connected(be->chr, errp);
 }
 
@@ -4167,7 +4196,7 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr->chr_set_echo) {
+    if (chr && chr->chr_set_echo) {
         chr->chr_set_echo(chr, echo);
     }
 }
@@ -4176,6 +4205,10 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 {
     CharDriverState *chr = be->chr;
 
+    if (!chr) {
+        return;
+    }
+
     if (chr->fe_open == fe_open) {
         return;
     }
@@ -4189,7 +4222,7 @@ void qemu_chr_fe_event(CharBackend *be, int event)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr->chr_fe_event) {
+    if (chr && chr->chr_fe_event) {
         chr->chr_fe_event(chr, event);
     }
 }
@@ -4201,7 +4234,7 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
     GSource *src;
     guint tag;
 
-    if (s->chr_add_watch == NULL) {
+    if (!s || s->chr_add_watch == NULL) {
         return 0;
     }
 
@@ -4221,7 +4254,7 @@ void qemu_chr_fe_disconnect(CharBackend *be)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr->chr_disconnect) {
+    if (chr && chr->chr_disconnect) {
         chr->chr_disconnect(chr);
     }
 }
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index b81dbcc..2f60a10 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -168,6 +168,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename);
  * @qemu_chr_fe_disconnect:
  *
  * Close a fd accpeted by character backend.
+ * Without associated CharDriver, do nothing.
  */
 void qemu_chr_fe_disconnect(CharBackend *be);
 
@@ -181,7 +182,8 @@ void qemu_chr_cleanup(void);
 /**
  * @qemu_chr_fe_wait_connected:
  *
- * Wait for characted backend to be connected.
+ * Wait for characted backend to be connected, return < 0 on error or
+ * if no assicated CharDriver.
  */
 int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp);
 
@@ -220,6 +222,7 @@ void qemu_chr_free(CharDriverState *chr);
  * Ask the backend to override its normal echo setting.  This only really
  * applies to the stdio backend and is used by the QMP server such that you
  * can see what you type if you try to type QMP commands.
+ * Without associated CharDriver, do nothing.
  *
  * @echo true to enable echo, false to disable echo
  */
@@ -230,13 +233,15 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
  *
  * Set character frontend open status.  This is an indication that the
  * front end is ready (or not) to begin doing I/O.
+ * Without associated CharDriver, do nothing.
  */
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open);
 
 /**
  * @qemu_chr_fe_event:
  *
- * Send an event from the front end to the back end.
+ * Send an event from the front end to the back end. It does nothing
+ * without associated CharDriver.
  *
  * @event the event to send
  */
@@ -245,8 +250,9 @@ void qemu_chr_fe_event(CharBackend *be, int event);
 /**
  * @qemu_chr_fe_printf:
  *
- * Write to a character backend using a printf style interface.
- * This function is thread-safe.
+ * Write to a character backend using a printf style interface.  This
+ * function is thread-safe. It does nothing without associated
+ * CharDriver.
  *
  * @fmt see #printf
  */
@@ -259,7 +265,7 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
  * If the backend is connected, create and add a #GSource that fires
  * when the given condition (typically G_IO_OUT|G_IO_HUP or G_IO_HUP)
  * is active; return the #GSource's tag.  If it is disconnected,
- * return 0.
+ * or without associated CharDriver, return 0.
  *
  * @cond the condition to poll for
  * @func the function to call when the condition happens
@@ -278,7 +284,7 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
  * @buf the data
  * @len the number of bytes to send
  *
- * Returns: the number of bytes consumed
+ * Returns: the number of bytes consumed (0 if no assicated CharDriver)
  */
 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
 
@@ -293,7 +299,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
  * @buf the data
  * @len the number of bytes to send
  *
- * Returns: the number of bytes consumed
+ * Returns: the number of bytes consumed (0 if no assicated CharDriver)
  */
 int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
 
@@ -305,7 +311,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
  * @buf the data buffer
  * @len the number of bytes to read
  *
- * Returns: the number of bytes read
+ * Returns: the number of bytes read (0 if no assicated CharDriver)
  */
 int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
 
@@ -317,8 +323,9 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
  * @cmd see CHR_IOCTL_*
  * @arg the data associated with @cmd
  *
- * Returns: if @cmd is not supported by the backend, -ENOTSUP, otherwise the
- *          return value depends on the semantics of @cmd
+ * Returns: if @cmd is not supported by the backend or there is no
+ *          associated CharDriver, -ENOTSUP, otherwise the return
+ *          value depends on the semantics of @cmd
  */
 int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg);
 
@@ -357,7 +364,7 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int num);
  * result in overwriting the fd array with the new value without being send.
  * Upon writing the message the fd array is freed.
  *
- * Returns: -1 if fd passing isn't supported.
+ * Returns: -1 if fd passing isn't supported or no associated CharDriver.
  */
 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num);
 
@@ -418,7 +425,8 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp);
 /**
  * @qemu_chr_fe_get_driver:
  *
- * Returns the driver associated with a CharBackend or NULL.
+ * Returns the driver associated with a CharBackend or NULL if no
+ * associated CharDriver.
  */
 CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
 
@@ -426,6 +434,8 @@ CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
  * @qemu_chr_fe_deinit:
  *
  * Dissociate the CharBackend from the CharDriver.
+ *
+ * Safe to call without associated CharDriver.
  */
 void qemu_chr_fe_deinit(CharBackend *b);
 
@@ -441,6 +451,8 @@ void qemu_chr_fe_deinit(CharBackend *b);
  *
  * Set the front end char handlers. The front end takes the focus if
  * any of the handler is non-NULL.
+ *
+ * Without associated CharDriver, nothing is changed.
  */
 void qemu_chr_fe_set_handlers(CharBackend *b,
                               IOCanReadHandler *fd_can_read,
@@ -452,7 +464,9 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
 /**
  * @qemu_chr_fe_take_focus:
  *
- * Take the focus (if the front end is muxed)
+ * Take the focus (if the front end is muxed).
+ *
+ * Without associated CharDriver, nothing is changed.
  */
 void qemu_chr_fe_take_focus(CharBackend *b);
 
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 20/38] tests: start chardev unit tests
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (18 preceding siblings ...)
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 19/38] char: make some qemu_chr_fe skip if no driver Marc-André Lureau
@ 2016-10-22  9:53 ` Marc-André Lureau
  2016-10-27 18:27   ` Eric Blake
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 21/38] char: move front end handlers in CharBackend Marc-André Lureau
                   ` (5 subsequent siblings)
  25 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:53 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/test-char.c      | 253 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/Makefile.include |   4 +
 2 files changed, 257 insertions(+)
 create mode 100644 tests/test-char.c

diff --git a/tests/test-char.c b/tests/test-char.c
new file mode 100644
index 0000000..13c55b8
--- /dev/null
+++ b/tests/test-char.c
@@ -0,0 +1,253 @@
+#include "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+#include "sysemu/char.h"
+#include "sysemu/sysemu.h"
+#include "qapi/error.h"
+#include "qmp-commands.h"
+
+typedef struct FeHandler {
+    int read_count;
+    int last_event;
+    char read_buf[128];
+} FeHandler;
+
+static int fe_can_read(void *opaque)
+{
+    FeHandler *h = opaque;
+
+    return sizeof(h->read_buf) - h->read_count;
+}
+
+static void fe_read(void *opaque, const uint8_t *buf, int size)
+{
+    FeHandler *h = opaque;
+
+    g_assert_cmpint(size, <=, fe_can_read(opaque));
+
+    memcpy(h->read_buf + h->read_count, buf, size);
+    h->read_count += size;
+}
+
+static void fe_event(void *opaque, int event)
+{
+    FeHandler *h = opaque;
+
+    h->last_event = event;
+}
+
+#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
+static void char_stdio_test_subprocess(void)
+{
+    CharDriverState *chr;
+    CharBackend be;
+    int ret;
+
+    chr = qemu_chr_new("label", "stdio");
+    g_assert_nonnull(chr);
+
+    qemu_chr_fe_init(&be, chr, &error_abort);
+    qemu_chr_fe_set_open(&be, true);
+    ret = qemu_chr_fe_write(&be, (void *)"buf", 4);
+    g_assert_cmpint(ret, ==, 4);
+
+    qemu_chr_fe_deinit(&be);
+    qemu_chr_delete(chr);
+}
+
+static void char_stdio_test(void)
+{
+    g_test_trap_subprocess("/char/stdio/subprocess", 0, 0);
+    g_test_trap_assert_passed();
+    g_test_trap_assert_stdout("buf");
+}
+#endif
+
+
+static void char_ringbuf_test(void)
+{
+    QemuOpts *opts;
+    CharDriverState *chr;
+    CharBackend be;
+    char *data;
+    int ret;
+
+    opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label",
+                            1, &error_abort);
+    qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
+
+    qemu_opt_set(opts, "size", "5", &error_abort);
+    chr = qemu_chr_new_from_opts(opts, NULL);
+    g_assert_null(chr);
+    qemu_opts_del(opts);
+
+    opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label",
+                            1, &error_abort);
+    qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
+    qemu_opt_set(opts, "size", "2", &error_abort);
+    chr = qemu_chr_new_from_opts(opts, &error_abort);
+    g_assert_nonnull(chr);
+    qemu_opts_del(opts);
+
+    qemu_chr_fe_init(&be, chr, &error_abort);
+    ret = qemu_chr_fe_write(&be, (void *)"buff", 4);
+    g_assert_cmpint(ret, ==, 4);
+
+    data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort);
+    g_assert_cmpstr(data, ==, "ff");
+    g_free(data);
+
+    data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort);
+    g_assert_cmpstr(data, ==, "");
+    g_free(data);
+
+    qemu_chr_fe_deinit(&be);
+    qemu_chr_delete(chr);
+}
+
+static void char_mux_test(void)
+{
+    QemuOpts *opts;
+    CharDriverState *chr, *base;
+    char *data;
+    FeHandler h1 = { 0, }, h2 = { 0, };
+    CharBackend chr_be1, chr_be2;
+
+    opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
+                            1, &error_abort);
+    qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
+    qemu_opt_set(opts, "size", "128", &error_abort);
+    qemu_opt_set(opts, "mux", "on", &error_abort);
+    chr = qemu_chr_new_from_opts(opts, &error_abort);
+    g_assert_nonnull(chr);
+    qemu_opts_del(opts);
+
+    qemu_chr_fe_init(&chr_be1, chr, &error_abort);
+    qemu_chr_fe_set_handlers(&chr_be1,
+                             fe_can_read,
+                             fe_read,
+                             fe_event,
+                             &h1,
+                             NULL);
+
+    qemu_chr_fe_init(&chr_be2, chr, &error_abort);
+    qemu_chr_fe_set_handlers(&chr_be2,
+                             fe_can_read,
+                             fe_read,
+                             fe_event,
+                             &h2,
+                             NULL);
+    qemu_chr_fe_take_focus(&chr_be2);
+
+    base = qemu_chr_find("mux-label-base");
+    g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0);
+
+    qemu_chr_be_write(base, (void *)"hello", 6);
+    g_assert_cmpint(h1.read_count, ==, 0);
+    g_assert_cmpint(h2.read_count, ==, 6);
+    g_assert_cmpstr(h2.read_buf, ==, "hello");
+    h2.read_count = 0;
+
+    /* switch focus */
+    qemu_chr_be_write(base, (void *)"\1c", 2);
+
+    qemu_chr_be_write(base, (void *)"hello", 6);
+    g_assert_cmpint(h2.read_count, ==, 0);
+    g_assert_cmpint(h1.read_count, ==, 6);
+    g_assert_cmpstr(h1.read_buf, ==, "hello");
+    h1.read_count = 0;
+
+    /* remove first handler */
+    qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL);
+    qemu_chr_be_write(base, (void *)"hello", 6);
+    g_assert_cmpint(h1.read_count, ==, 0);
+    g_assert_cmpint(h2.read_count, ==, 0);
+
+    qemu_chr_be_write(base, (void *)"\1c", 2);
+    qemu_chr_be_write(base, (void *)"hello", 6);
+    g_assert_cmpint(h1.read_count, ==, 0);
+    g_assert_cmpint(h2.read_count, ==, 6);
+    g_assert_cmpstr(h2.read_buf, ==, "hello");
+    h2.read_count = 0;
+
+    /* print help */
+    qemu_chr_be_write(base, (void *)"\1?", 2);
+    data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort);
+    g_assert_cmpint(strlen(data), !=, 0);
+    g_free(data);
+
+    qemu_chr_fe_deinit(&chr_be1);
+    qemu_chr_fe_deinit(&chr_be2);
+    qemu_chr_delete(chr);
+}
+
+static void char_null_test(void)
+{
+    Error *err = NULL;
+    CharDriverState *chr;
+    CharBackend be;
+    int ret;
+
+    chr = qemu_chr_find("label-null");
+    g_assert_null(chr);
+
+    chr = qemu_chr_new("label-null", "null");
+    chr = qemu_chr_find("label-null");
+    g_assert_nonnull(chr);
+
+    g_assert(qemu_chr_has_feature(chr,
+                 QEMU_CHAR_FEATURE_FD_PASS) == false);
+    g_assert(qemu_chr_has_feature(chr,
+                 QEMU_CHAR_FEATURE_RECONNECTABLE) == false);
+
+    /* check max avail */
+    qemu_chr_fe_init(&be, chr, &error_abort);
+    qemu_chr_fe_init(&be, chr, &err);
+    error_free_or_abort(&err);
+
+    /* deinit & reinit */
+    qemu_chr_fe_deinit(&be);
+    qemu_chr_fe_init(&be, chr, &error_abort);
+
+    qemu_chr_fe_set_open(&be, true);
+
+    qemu_chr_fe_set_handlers(&be,
+                             fe_can_read,
+                             fe_read,
+                             fe_event,
+                             NULL, NULL);
+
+    ret = qemu_chr_fe_write(&be, (void *)"buf", 4);
+    g_assert_cmpint(ret, ==, 4);
+
+    qemu_chr_fe_deinit(&be);
+    qemu_chr_delete(chr);
+}
+
+static void char_invalid_test(void)
+{
+    CharDriverState *chr;
+
+    chr = qemu_chr_new("label-invalid", "invalid");
+    g_assert_null(chr);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    module_call_init(MODULE_INIT_QOM);
+    qemu_add_opts(&qemu_chardev_opts);
+
+    g_test_add_func("/char/null", char_null_test);
+    g_test_add_func("/char/invalid", char_invalid_test);
+    g_test_add_func("/char/ringbuf", char_ringbuf_test);
+    g_test_add_func("/char/mux", char_mux_test);
+#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
+    g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess);
+    g_test_add_func("/char/stdio", char_stdio_test);
+#endif
+
+    return g_test_run();
+}
diff --git a/tests/Makefile.include b/tests/Makefile.include
index cbe38ad..e65e9f7 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -8,6 +8,8 @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \
 
 check-unit-y = tests/check-qdict$(EXESUF)
 gcov-files-check-qdict-y = qobject/qdict.c
+check-unit-y = tests/test-char$(EXESUF)
+gcov-files-check-qdict-y = qemu-char.c
 check-unit-y += tests/check-qfloat$(EXESUF)
 gcov-files-check-qfloat-y = qobject/qfloat.c
 check-unit-y += tests/check-qint$(EXESUF)
@@ -481,6 +483,8 @@ tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y)
 tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
 tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
 tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
+
+tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y)
 tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
 tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
 tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y)
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 21/38] char: move front end handlers in CharBackend
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (19 preceding siblings ...)
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 20/38] tests: start chardev unit tests Marc-André Lureau
@ 2016-10-22  9:53 ` Marc-André Lureau
  2016-10-24 13:40   ` Paolo Bonzini
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 22/38] char: rename chr_close/chr_free Marc-André Lureau
                   ` (4 subsequent siblings)
  25 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:53 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Since the hanlders are associated with a CharBackend, rather than the
CharDriverState, it is more appropriate to store in CharBackend. This
avoids the handler copy dance in qemu_chr_fe_set_handlers() then
mux_chr_update_read_handler(), by storing the CharBackend pointer
directly.

Also a mux CharDriver should go through mux->backends[focused], since
chr->be will stay NULL. Before that, it was possible to call
chr->handler by mistake with surprising results, for ex through
qemu_chr_be_can_write(), which would result in calling the last set
handler front end, not the one with focus.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/bt/hci-csr.c       |  12 +++--
 qemu-char.c           | 131 +++++++++++++++++++++++++++-----------------------
 ui/console.c          |   4 +-
 include/sysemu/char.h |  11 +++--
 4 files changed, 88 insertions(+), 70 deletions(-)

diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index b77c036..cdf52a9 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;
diff --git a/qemu-char.c b/qemu-char.c
index dee2ced..1c6f39b 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -192,6 +192,8 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp)
 
 void qemu_chr_be_event(CharDriverState *s, int event)
 {
+    CharBackend *be = s->be;
+
     /* Keep track if the char device is open */
     switch (event) {
         case CHR_EVENT_OPENED:
@@ -202,9 +204,11 @@ void qemu_chr_be_event(CharDriverState *s, int event)
             break;
     }
 
-    if (!s->chr_event)
+    if (!be || !be->chr_event) {
         return;
-    s->chr_event(s->handler_opaque, event);
+    }
+
+    be->chr_event(be->opaque, event);
 }
 
 void qemu_chr_be_generic_open(CharDriverState *s)
@@ -398,15 +402,21 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
 
 int qemu_chr_be_can_write(CharDriverState *s)
 {
-    if (!s->chr_can_read)
+    CharBackend *be = s->be;
+
+    if (!be || !be->chr_can_read) {
         return 0;
-    return s->chr_can_read(s->handler_opaque);
+    }
+
+    return be->chr_can_read(be->opaque);
 }
 
 void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
 {
-    if (s->chr_read) {
-        s->chr_read(s->handler_opaque, buf, len);
+    CharBackend *be = s->be;
+
+    if (be && be->chr_read) {
+        be->chr_read(be->opaque, buf, len);
     }
 }
 
@@ -488,7 +498,6 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
 }
 
 static void remove_fd_in_watch(CharDriverState *chr);
-static int mux_chr_new_handler_tag(CharDriverState *chr, Error **errp);
 static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context);
 static void mux_set_focus(MuxDriver *d, int focus);
 
@@ -519,10 +528,7 @@ static CharDriverState *qemu_chr_open_null(const char *id,
 #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
 #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
 struct MuxDriver {
-    IOCanReadHandler *chr_can_read[MAX_MUX];
-    IOReadHandler *chr_read[MAX_MUX];
-    IOEventHandler *chr_event[MAX_MUX];
-    void *ext_opaque[MAX_MUX];
+    CharBackend *backends[MAX_MUX];
     CharBackend chr;
     int focus;
     int mux_cnt;
@@ -625,8 +631,11 @@ static void mux_print_help(CharDriverState *chr)
 
 static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event)
 {
-    if (d->chr_event[mux_nr])
-        d->chr_event[mux_nr](d->ext_opaque[mux_nr], event);
+    CharBackend *be = d->backends[mux_nr];
+
+    if (be && be->chr_event) {
+        be->chr_event(be->opaque, event);
+    }
 }
 
 static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
@@ -677,12 +686,12 @@ static void mux_chr_accept_input(CharDriverState *chr)
 {
     MuxDriver *d = chr->opaque;
     int m = d->focus;
+    CharBackend *be = d->backends[m];
 
-    while (d->prod[m] != d->cons[m] &&
-           d->chr_can_read[m] &&
-           d->chr_can_read[m](d->ext_opaque[m])) {
-        d->chr_read[m](d->ext_opaque[m],
-                       &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
+    while (be && d->prod[m] != d->cons[m] &&
+           be->chr_can_read && be->chr_can_read(be->opaque)) {
+        be->chr_read(be->opaque,
+                     &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
     }
 }
 
@@ -691,11 +700,16 @@ static int mux_chr_can_read(void *opaque)
     CharDriverState *chr = opaque;
     MuxDriver *d = chr->opaque;
     int m = d->focus;
+    CharBackend *be = d->backends[m];
 
-    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE)
+    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
         return 1;
-    if (d->chr_can_read[m])
-        return d->chr_can_read[m](d->ext_opaque[m]);
+    }
+
+    if (be && be->chr_can_read) {
+        return be->chr_can_read(be->opaque);
+    }
+
     return 0;
 }
 
@@ -704,16 +718,17 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
     CharDriverState *chr = opaque;
     MuxDriver *d = chr->opaque;
     int m = d->focus;
+    CharBackend *be = d->backends[m];
     int i;
 
-    mux_chr_accept_input (opaque);
+    mux_chr_accept_input(opaque);
 
-    for(i = 0; i < size; i++)
+    for (i = 0; i < size; i++)
         if (mux_proc_byte(chr, d, buf[i])) {
             if (d->prod[m] == d->cons[m] &&
-                d->chr_can_read[m] &&
-                d->chr_can_read[m](d->ext_opaque[m]))
-                d->chr_read[m](d->ext_opaque[m], &buf[i], 1);
+                be && be->chr_can_read &&
+                be->chr_can_read(be->opaque))
+                be->chr_read(be->opaque, &buf[i], 1);
             else
                 d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
         }
@@ -730,21 +745,6 @@ static void mux_chr_event(void *opaque, int event)
         mux_chr_send_event(d, i, event);
 }
 
-static void mux_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context,
-                                        int tag)
-{
-    MuxDriver *d = chr->opaque;
-
-    assert(tag >= 0);
-    assert(tag < d->mux_cnt);
-
-    d->ext_opaque[tag] = chr->handler_opaque;
-    d->chr_can_read[tag] = chr->chr_can_read;
-    d->chr_read[tag] = chr->chr_read;
-    d->chr_event[tag] = chr->chr_event;
-}
-
 static bool muxes_realized;
 
 /**
@@ -796,12 +796,19 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
 static void mux_chr_close(struct CharDriverState *chr)
 {
     MuxDriver *d = chr->opaque;
+    int i;
 
+    for (i = 0; i < d->mux_cnt; i++) {
+        CharBackend *be = d->backends[i];
+        if (be) {
+            be->chr = NULL;
+        }
+    }
     qemu_chr_fe_deinit(&d->chr);
     g_free(d);
 }
 
-static int mux_chr_new_handler_tag(CharDriverState *chr, Error **errp)
+static int mux_chr_new_fe(CharDriverState *chr, CharBackend *be, Error **errp)
 {
     MuxDriver *d = chr->opaque;
 
@@ -810,6 +817,8 @@ static int mux_chr_new_handler_tag(CharDriverState *chr, Error **errp)
         return -1;
     }
 
+    d->backends[d->mux_cnt] = be;
+
     return d->mux_cnt++;
 }
 
@@ -864,7 +873,6 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
     d->focus = -1;
     chr->chr_close = mux_chr_close;
     chr->chr_write = mux_chr_write;
-    chr->chr_update_read_handler = mux_chr_update_read_handler;
     chr->chr_accept_input = mux_chr_accept_input;
     /* Frontend guest-open / -close notification is not support with muxes */
     chr->chr_set_fe_open = NULL;
@@ -905,10 +913,12 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
     s->avail_connections--;
 
     if (s->is_mux) {
-        tag = mux_chr_new_handler_tag(s, errp);
+        tag = mux_chr_new_fe(s, b, errp);
         if (tag < 0) {
             return false;
         }
+    } else {
+        s->be = b;
     }
 
     b->tag = tag;
@@ -924,6 +934,11 @@ void qemu_chr_fe_deinit(CharBackend *b)
     if (b->chr) {
         qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL);
         b->chr->avail_connections++;
+        b->chr->be = NULL;
+        if (b->chr->is_mux) {
+            MuxDriver *d = b->chr->opaque;
+            d->backends[b->tag] = NULL;
+        }
         b->chr = NULL;
     }
 }
@@ -951,12 +966,12 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
     } else {
         fe_open = 1;
     }
-    s->chr_can_read = fd_can_read;
-    s->chr_read = fd_read;
-    s->chr_event = fd_event;
-    s->handler_opaque = opaque;
+    b->chr_can_read = fd_can_read;
+    b->chr_read = fd_read;
+    b->chr_event = fd_event;
+    b->opaque = opaque;
     if (s->chr_update_read_handler) {
-        s->chr_update_read_handler(s, context, b->tag);
+        s->chr_update_read_handler(s, context);
     }
 
     if (!s->explicit_fe_open) {
@@ -1217,8 +1232,7 @@ static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 }
 
 static void fd_chr_update_read_handler(CharDriverState *chr,
-                                       GMainContext *context,
-                                       int tag)
+                                       GMainContext *context)
 {
     FDCharDriver *s = chr->opaque;
 
@@ -1475,8 +1489,7 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr)
 }
 
 static void pty_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context,
-                                        int tag)
+                                        GMainContext *context)
 {
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_update_read_handler_locked(chr);
@@ -2722,8 +2735,7 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 }
 
 static void udp_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context,
-                                        int tag)
+                                        GMainContext *context)
 {
     NetCharDriver *s = chr->opaque;
 
@@ -3142,8 +3154,7 @@ static void tcp_chr_connect(void *opaque)
 }
 
 static void tcp_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context,
-                                        int tag)
+                                        GMainContext *context)
 {
     TCPCharDriver *s = chr->opaque;
 
@@ -4261,6 +4272,9 @@ void qemu_chr_fe_disconnect(CharBackend *be)
 
 static void qemu_chr_free_common(CharDriverState *chr)
 {
+    if (chr->be) {
+        chr->be->chr = NULL;
+    }
     g_free(chr->filename);
     g_free(chr->label);
     if (chr->logfd != -1) {
@@ -4805,8 +4819,7 @@ void qmp_chardev_remove(const char *id, Error **errp)
         error_setg(errp, "Chardev '%s' not found", id);
         return;
     }
-    if (chr->chr_can_read || chr->chr_read ||
-        chr->chr_event || chr->handler_opaque) {
+    if (chr->be) {
         error_setg(errp, "Chardev '%s' is busy", id);
         return;
     }
diff --git a/ui/console.c b/ui/console.c
index 19adac7..f490346 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1083,6 +1083,7 @@ static void kbd_send_chars(void *opaque)
 void kbd_put_keysym_console(QemuConsole *s, int keysym)
 {
     uint8_t buf[16], *q;
+    CharBackend *be;
     int c;
 
     if (!s || (s->console_type == GRAPHIC_CONSOLE))
@@ -1125,7 +1126,8 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym)
         if (s->echo) {
             console_puts(s->chr, buf, q - buf);
         }
-        if (s->chr->chr_read) {
+        be = s->chr->be;
+        if (be && be->chr_read) {
             qemu_fifo_write(&s->out_fifo, buf, q - buf);
             kbd_send_chars(s);
         }
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 2f60a10..7187c3e 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -76,6 +76,10 @@ typedef enum {
  * CharDriverState */
 typedef struct CharBackend {
     CharDriverState *chr;
+    IOEventHandler *chr_event;
+    IOCanReadHandler *chr_can_read;
+    IOReadHandler *chr_read;
+    void *opaque;
     int tag;
 } CharBackend;
 
@@ -86,22 +90,19 @@ struct CharDriverState {
                          const uint8_t *buf, int len);
     GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
     void (*chr_update_read_handler)(struct CharDriverState *s,
-                                    GMainContext *context, int tag);
+                                    GMainContext *context);
     int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
     int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
     int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
     int (*chr_add_client)(struct CharDriverState *chr, int fd);
     int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
-    IOEventHandler *chr_event;
-    IOCanReadHandler *chr_can_read;
-    IOReadHandler *chr_read;
-    void *handler_opaque;
     void (*chr_close)(struct CharDriverState *chr);
     void (*chr_disconnect)(struct CharDriverState *chr);
     void (*chr_accept_input)(struct CharDriverState *chr);
     void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
     void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
     void (*chr_fe_event)(struct CharDriverState *chr, int event);
+    CharBackend *be;
     void *opaque;
     char *label;
     char *filename;
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 22/38] char: rename chr_close/chr_free
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (20 preceding siblings ...)
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 21/38] char: move front end handlers in CharBackend Marc-André Lureau
@ 2016-10-22  9:53 ` Marc-André Lureau
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 23/38] char: remove explicit_fe_open, use a set_handlers argument Marc-André Lureau
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:53 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

The function is used to free the backend opaque pointer, let's name it
accordingly.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/baum.c       |  4 ++--
 backends/msmouse.c    |  4 ++--
 backends/testdev.c    |  4 ++--
 qemu-char.c           | 58 +++++++++++++++++++++++++--------------------------
 spice-qemu-char.c     |  4 ++--
 include/sysemu/char.h |  2 +-
 6 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index c537141..a516434 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -551,7 +551,7 @@ static void baum_chr_read(void *opaque)
     }
 }
 
-static void baum_close(struct CharDriverState *chr)
+static void baum_free(struct CharDriverState *chr)
 {
     BaumDriverState *baum = chr->opaque;
 
@@ -589,7 +589,7 @@ static CharDriverState *chr_baum_init(const char *id,
     chr->opaque = baum;
     chr->chr_write = baum_write;
     chr->chr_accept_input = baum_accept_input;
-    chr->chr_close = baum_close;
+    chr->chr_free = baum_free;
 
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 85d08f7..448369f 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -133,7 +133,7 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int
     return len;
 }
 
-static void msmouse_chr_close (struct CharDriverState *chr)
+static void msmouse_chr_free(struct CharDriverState *chr)
 {
     MouseState *mouse = chr->opaque;
 
@@ -162,7 +162,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
         return NULL;
     }
     chr->chr_write = msmouse_chr_write;
-    chr->chr_close = msmouse_chr_close;
+    chr->chr_free = msmouse_chr_free;
     chr->chr_accept_input = msmouse_chr_accept_input;
     chr->explicit_be_open = true;
 
diff --git a/backends/testdev.c b/backends/testdev.c
index 3ab1c90..1ba8bf2 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -102,7 +102,7 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
     return orig_len;
 }
 
-static void testdev_close(struct CharDriverState *chr)
+static void testdev_free(struct CharDriverState *chr)
 {
     TestdevCharState *testdev = chr->opaque;
 
@@ -122,7 +122,7 @@ static CharDriverState *chr_testdev_init(const char *id,
 
     chr->opaque = testdev;
     chr->chr_write = testdev_write;
-    chr->chr_close = testdev_close;
+    chr->chr_free = testdev_free;
 
     return chr;
 }
diff --git a/qemu-char.c b/qemu-char.c
index 1c6f39b..45d601f 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -793,7 +793,7 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
     return chr->chr_add_watch(chr, cond);
 }
 
-static void mux_chr_close(struct CharDriverState *chr)
+static void mux_chr_free(struct CharDriverState *chr)
 {
     MuxDriver *d = chr->opaque;
     int i;
@@ -871,7 +871,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
 
     chr->opaque = d;
     d->focus = -1;
-    chr->chr_close = mux_chr_close;
+    chr->chr_free = mux_chr_free;
     chr->chr_write = mux_chr_write;
     chr->chr_accept_input = mux_chr_accept_input;
     /* Frontend guest-open / -close notification is not support with muxes */
@@ -1245,7 +1245,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr,
     }
 }
 
-static void fd_chr_close(struct CharDriverState *chr)
+static void fd_chr_free(struct CharDriverState *chr)
 {
     FDCharDriver *s = chr->opaque;
 
@@ -1281,7 +1281,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
     chr->chr_add_watch = fd_chr_add_watch;
     chr->chr_write = fd_chr_write;
     chr->chr_update_read_handler = fd_chr_update_read_handler;
-    chr->chr_close = fd_chr_close;
+    chr->chr_free = fd_chr_free;
 
     return chr;
 }
@@ -1362,10 +1362,10 @@ static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
     tcsetattr (0, TCSANOW, &tty);
 }
 
-static void qemu_chr_close_stdio(struct CharDriverState *chr)
+static void qemu_chr_free_stdio(struct CharDriverState *chr)
 {
     term_exit();
-    fd_chr_close(chr);
+    fd_chr_free(chr);
 }
 
 static CharDriverState *qemu_chr_open_stdio(const char *id,
@@ -1402,7 +1402,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     if (!chr) {
         return NULL;
     }
-    chr->chr_close = qemu_chr_close_stdio;
+    chr->chr_free = qemu_chr_free_stdio;
     chr->chr_set_echo = qemu_chr_set_echo_stdio;
     if (opts->has_signal) {
         stdio_allow_signal = opts->signal;
@@ -1599,7 +1599,7 @@ static void pty_chr_state(CharDriverState *chr, int connected)
     }
 }
 
-static void pty_chr_close(struct CharDriverState *chr)
+static void pty_chr_free(struct CharDriverState *chr)
 {
     PtyCharDriver *s = chr->opaque;
 
@@ -1652,7 +1652,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     chr->opaque = s;
     chr->chr_write = pty_chr_write;
     chr->chr_update_read_handler = pty_chr_update_read_handler;
-    chr->chr_close = pty_chr_close;
+    chr->chr_free = pty_chr_free;
     chr->chr_add_watch = pty_chr_add_watch;
     chr->explicit_be_open = true;
 
@@ -1847,9 +1847,9 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-static void qemu_chr_close_tty(CharDriverState *chr)
+static void qemu_chr_free_tty(CharDriverState *chr)
 {
-    fd_chr_close(chr);
+    fd_chr_free(chr);
 }
 
 static CharDriverState *qemu_chr_open_tty_fd(int fd,
@@ -1864,7 +1864,7 @@ static CharDriverState *qemu_chr_open_tty_fd(int fd,
         return NULL;
     }
     chr->chr_ioctl = tty_serial_ioctl;
-    chr->chr_close = qemu_chr_close_tty;
+    chr->chr_free = qemu_chr_free_tty;
     return chr;
 }
 #endif /* __linux__ || __sun__ */
@@ -1970,7 +1970,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-static void pp_close(CharDriverState *chr)
+static void pp_free(CharDriverState *chr)
 {
     ParallelCharDriver *drv = chr->opaque;
     int fd = drv->fd;
@@ -2004,7 +2004,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd,
     chr->opaque = drv;
     chr->chr_write = null_chr_write;
     chr->chr_ioctl = pp_ioctl;
-    chr->chr_close = pp_close;
+    chr->chr_free = pp_free;
 
     drv->fd = fd;
     drv->mode = IEEE1284_MODE_COMPAT;
@@ -2103,7 +2103,7 @@ typedef struct {
 static int win_chr_poll(void *opaque);
 static int win_chr_pipe_poll(void *opaque);
 
-static void win_chr_close(CharDriverState *chr)
+static void win_chr_free(CharDriverState *chr)
 {
     WinCharState *s = chr->opaque;
 
@@ -2190,7 +2190,7 @@ static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp
     return 0;
 
  fail:
-    win_chr_close(chr);
+    win_chr_free(chr);
     return -1;
 }
 
@@ -2302,7 +2302,7 @@ static CharDriverState *qemu_chr_open_win_path(const char *filename,
     s = g_new0(WinCharState, 1);
     chr->opaque = s;
     chr->chr_write = win_chr_write;
-    chr->chr_close = win_chr_close;
+    chr->chr_free = win_chr_free;
 
     if (win_chr_init(chr, filename, errp) < 0) {
         g_free(s);
@@ -2388,7 +2388,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
     return 0;
 
  fail:
-    win_chr_close(chr);
+    win_chr_free(chr);
     return -1;
 }
 
@@ -2411,7 +2411,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
     s = g_new0(WinCharState, 1);
     chr->opaque = s;
     chr->chr_write = win_chr_write;
-    chr->chr_close = win_chr_close;
+    chr->chr_free = win_chr_free;
 
     if (win_chr_pipe_init(chr, filename, errp) < 0) {
         g_free(s);
@@ -2567,7 +2567,7 @@ static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
     }
 }
 
-static void win_stdio_close(CharDriverState *chr)
+static void win_stdio_free(CharDriverState *chr)
 {
     WinStdioCharState *stdio = chr->opaque;
 
@@ -2611,7 +2611,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
 
     chr->opaque    = stdio;
     chr->chr_write = win_stdio_write;
-    chr->chr_close = win_stdio_close;
+    chr->chr_free = win_stdio_free;
 
     if (is_console) {
         if (qemu_add_wait_object(stdio->hStdIn,
@@ -2748,7 +2748,7 @@ static void udp_chr_update_read_handler(CharDriverState *chr,
     }
 }
 
-static void udp_chr_close(CharDriverState *chr)
+static void udp_chr_free(CharDriverState *chr)
 {
     NetCharDriver *s = chr->opaque;
 
@@ -2779,7 +2779,7 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
     chr->opaque = s;
     chr->chr_write = udp_chr_write;
     chr->chr_update_read_handler = udp_chr_update_read_handler;
-    chr->chr_close = udp_chr_close;
+    chr->chr_free = udp_chr_free;
     /* be isn't opened until we get a connection */
     chr->explicit_be_open = true;
     return chr;
@@ -3406,7 +3406,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
     return qemu_chr_wait_connected(be->chr, errp);
 }
 
-static void tcp_chr_close(CharDriverState *chr)
+static void tcp_chr_free(CharDriverState *chr)
 {
     TCPCharDriver *s = chr->opaque;
 
@@ -3501,7 +3501,7 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
     return i;
 }
 
-static void ringbuf_chr_close(struct CharDriverState *chr)
+static void ringbuf_chr_free(struct CharDriverState *chr)
 {
     RingBufCharDriver *d = chr->opaque;
 
@@ -3540,7 +3540,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
 
     chr->opaque = d;
     chr->chr_write = ringbuf_chr_write;
-    chr->chr_close = ringbuf_chr_close;
+    chr->chr_free = ringbuf_chr_free;
 
     return chr;
 
@@ -4286,8 +4286,8 @@ static void qemu_chr_free_common(CharDriverState *chr)
 
 void qemu_chr_free(CharDriverState *chr)
 {
-    if (chr->chr_close) {
-        chr->chr_close(chr);
+    if (chr->chr_free) {
+        chr->chr_free(chr);
     }
     qemu_chr_free_common(chr);
 }
@@ -4666,7 +4666,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     chr->chr_wait_connected = tcp_chr_wait_connected;
     chr->chr_write = tcp_chr_write;
     chr->chr_sync_read = tcp_chr_sync_read;
-    chr->chr_close = tcp_chr_close;
+    chr->chr_free = tcp_chr_free;
     chr->chr_disconnect = tcp_chr_disconnect;
     chr->get_msgfds = tcp_get_msgfds;
     chr->set_msgfds = tcp_set_msgfds;
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 351fcaa..930b8c5 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -199,7 +199,7 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return read_bytes;
 }
 
-static void spice_chr_close(struct CharDriverState *chr)
+static void spice_chr_free(struct CharDriverState *chr)
 {
     SpiceCharDriver *s = chr->opaque;
 
@@ -289,7 +289,7 @@ static CharDriverState *chr_open(const char *subtype,
     chr->opaque = s;
     chr->chr_write = spice_chr_write;
     chr->chr_add_watch = spice_chr_add_watch;
-    chr->chr_close = spice_chr_close;
+    chr->chr_free = spice_chr_free;
     chr->chr_set_fe_open = set_fe_open;
     chr->explicit_be_open = true;
     chr->chr_fe_event = spice_chr_fe_event;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 7187c3e..d029d54 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -96,7 +96,7 @@ struct CharDriverState {
     int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
     int (*chr_add_client)(struct CharDriverState *chr, int fd);
     int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
-    void (*chr_close)(struct CharDriverState *chr);
+    void (*chr_free)(struct CharDriverState *chr);
     void (*chr_disconnect)(struct CharDriverState *chr);
     void (*chr_accept_input)(struct CharDriverState *chr);
     void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 23/38] char: remove explicit_fe_open, use a set_handlers argument
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (21 preceding siblings ...)
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 22/38] char: rename chr_close/chr_free Marc-André Lureau
@ 2016-10-22  9:53 ` Marc-André Lureau
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22  9:53 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

No need to keep explicit_fe_open around if it affects only a
qemu_chr_fe_set_handlers(). Use an additional argument instead.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/rng-egd.c          |  2 +-
 gdbstub.c                   |  2 +-
 hw/arm/pxa2xx.c             |  2 +-
 hw/arm/strongarm.c          |  2 +-
 hw/char/bcm2835_aux.c       |  2 +-
 hw/char/cadence_uart.c      |  2 +-
 hw/char/debugcon.c          |  2 +-
 hw/char/digic-uart.c        |  2 +-
 hw/char/escc.c              |  2 +-
 hw/char/etraxfs_ser.c       |  2 +-
 hw/char/exynos4210_uart.c   |  2 +-
 hw/char/grlib_apbuart.c     |  2 +-
 hw/char/imx_serial.c        |  2 +-
 hw/char/ipoctal232.c        |  3 ++-
 hw/char/lm32_juart.c        |  2 +-
 hw/char/lm32_uart.c         |  2 +-
 hw/char/mcf_uart.c          |  3 ++-
 hw/char/milkymist-uart.c    |  2 +-
 hw/char/pl011.c             |  2 +-
 hw/char/sclpconsole-lm.c    |  2 +-
 hw/char/sclpconsole.c       |  2 +-
 hw/char/serial.c            |  2 +-
 hw/char/sh_serial.c         |  2 +-
 hw/char/spapr_vty.c         |  2 +-
 hw/char/stm32f2xx_usart.c   |  2 +-
 hw/char/virtio-console.c    |  6 ++----
 hw/char/xen_console.c       |  2 +-
 hw/char/xilinx_uartlite.c   |  2 +-
 hw/ipmi/ipmi_bmc_extern.c   |  2 +-
 hw/mips/mips_malta.c        |  2 +-
 hw/misc/ivshmem.c           |  2 +-
 hw/usb/ccid-card-passthru.c |  2 +-
 hw/usb/dev-serial.c         |  2 +-
 hw/usb/redirect.c           |  2 +-
 monitor.c                   |  4 ++--
 net/colo-compare.c          | 10 ++++++----
 net/filter-mirror.c         |  8 +++++---
 net/slirp.c                 |  2 +-
 net/vhost-user.c            |  2 +-
 qemu-char.c                 |  9 +++++----
 qtest.c                     |  2 +-
 tests/test-char.c           |  8 ++++----
 tests/vhost-user-test.c     |  2 +-
 include/sysemu/char.h       |  6 ++++--
 44 files changed, 67 insertions(+), 60 deletions(-)

diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index 433f583..69c04b1 100644
--- a/backends/rng-egd.c
+++ b/backends/rng-egd.c
@@ -106,7 +106,7 @@ static void rng_egd_opened(RngBackend *b, Error **errp)
 
     /* FIXME we should resubmit pending requests when the CDS reconnects. */
     qemu_chr_fe_set_handlers(&s->chr, rng_egd_chr_can_read,
-                             rng_egd_chr_read, NULL, s, NULL);
+                             rng_egd_chr_read, NULL, s, NULL, true);
 }
 
 static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
diff --git a/gdbstub.c b/gdbstub.c
index 5944494..b2e1b79 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1779,7 +1779,7 @@ int gdbserver_start(const char *device)
     if (chr) {
         qemu_chr_fe_init(&s->chr, chr, &error_abort);
         qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive,
-                                 gdb_chr_event, NULL, NULL);
+                                 gdb_chr_event, NULL, NULL, true);
     }
     s->state = chr ? RS_IDLE : RS_INACTIVE;
     s->mon_chr = mon_chr;
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index c9f4503..42cdde0 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1976,7 +1976,7 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
     PXA2xxFIrState *s = PXA2XX_FIR(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty,
-                             pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL);
+                             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 370198a..85db1e2 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -1241,7 +1241,7 @@ static void strongarm_uart_init(Object *obj)
                              strongarm_uart_can_receive,
                              strongarm_uart_receive,
                              strongarm_uart_event,
-                             s, NULL);
+                             s, NULL, true);
 }
 
 static void strongarm_uart_reset(DeviceState *dev)
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index af329aa..4d46ad6 100644
--- a/hw/char/bcm2835_aux.c
+++ b/hw/char/bcm2835_aux.c
@@ -279,7 +279,7 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
     BCM2835AuxState *s = BCM2835_AUX(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
-                             bcm2835_aux_receive, NULL, s, NULL);
+                             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 291818e..c2b9154 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -467,7 +467,7 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
                                           fifo_trigger_update, s);
 
     qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
-                             uart_event, s, NULL);
+                             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 2009c3e..80dce07 100644
--- a/hw/char/debugcon.c
+++ b/hw/char/debugcon.c
@@ -92,7 +92,7 @@ static void debugcon_realize_core(DebugconState *s, Error **errp)
         return;
     }
 
-    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, s, NULL);
+    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 2955e19..029f5bb 100644
--- a/hw/char/digic-uart.c
+++ b/hw/char/digic-uart.c
@@ -146,7 +146,7 @@ static void digic_uart_realize(DeviceState *dev, Error **errp)
     DigicUartState *s = DIGIC_UART(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                             uart_event, s, NULL);
+                             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 c71b0a8..d6662dc 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -1016,7 +1016,7 @@ static void escc_realize(DeviceState *dev, Error **errp)
             s->chn[i].clock = s->frequency / 2;
             qemu_chr_fe_set_handlers(&s->chn[i].chr, serial_can_receive,
                                      serial_receive1, serial_event,
-                                     &s->chn[i], NULL);
+                                     &s->chn[i], NULL, true);
         }
     }
 
diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c
index 18c374b..5438387 100644
--- a/hw/char/etraxfs_ser.c
+++ b/hw/char/etraxfs_ser.c
@@ -233,7 +233,7 @@ static void etraxfs_ser_realize(DeviceState *dev, Error **errp)
 
     qemu_chr_fe_set_handlers(&s->chr,
                              serial_can_receive, serial_receive,
-                             serial_event, s, NULL);
+                             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 48216b1..571c324 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -642,7 +642,7 @@ static int exynos4210_uart_init(SysBusDevice *dev)
 
     qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive,
                              exynos4210_uart_receive, exynos4210_uart_event,
-                             s, NULL);
+                             s, NULL, true);
 
     return 0;
 }
diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c
index e50d65b..db686e6 100644
--- a/hw/char/grlib_apbuart.c
+++ b/hw/char/grlib_apbuart.c
@@ -247,7 +247,7 @@ static int grlib_apbuart_init(SysBusDevice *dev)
                              grlib_apbuart_can_receive,
                              grlib_apbuart_receive,
                              grlib_apbuart_event,
-                             uart, NULL);
+                             uart, NULL, true);
 
     sysbus_init_irq(dev, &uart->irq);
 
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 8dbb7b2..99545fc 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -316,7 +316,7 @@ static void imx_serial_realize(DeviceState *dev, Error **errp)
     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);
+                             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 6f150e0..93929c2 100644
--- a/hw/char/ipoctal232.c
+++ b/hw/char/ipoctal232.c
@@ -544,7 +544,8 @@ static void ipoctal_realize(DeviceState *dev, Error **errp)
         /* Redirect IP-Octal channels to host character devices */
         if (qemu_chr_fe_get_driver(&ch->dev)) {
             qemu_chr_fe_set_handlers(&ch->dev, hostdev_can_receive,
-                                     hostdev_receive, hostdev_event, ch, NULL);
+                                     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 febb412..f8c1e0d 100644
--- a/hw/char/lm32_juart.c
+++ b/hw/char/lm32_juart.c
@@ -119,7 +119,7 @@ static void lm32_juart_realize(DeviceState *dev, Error **errp)
     LM32JuartState *s = LM32_JUART(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx,
-                             juart_event, s, NULL);
+                             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 1b2746f..7f3597c 100644
--- a/hw/char/lm32_uart.c
+++ b/hw/char/lm32_uart.c
@@ -266,7 +266,7 @@ static void lm32_uart_realize(DeviceState *dev, Error **errp)
     LM32UartState *s = LM32_UART(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                             uart_event, s, NULL);
+                             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 456591a..ecaa091 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -284,7 +284,8 @@ void *mcf_uart_init(qemu_irq irq, CharDriverState *chr)
     if (chr) {
         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);
+                                 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 dec0a8c..ae8e2f3 100644
--- a/hw/char/milkymist-uart.c
+++ b/hw/char/milkymist-uart.c
@@ -199,7 +199,7 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp)
     MilkymistUartState *s = MILKYMIST_UART(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                             uart_event, s, NULL);
+                             uart_event, s, NULL, true);
 }
 
 static void milkymist_uart_init(Object *obj)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 900ee5d..24ea973 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -329,7 +329,7 @@ static void pl011_realize(DeviceState *dev, Error **errp)
     PL011State *s = PL011(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
-                             pl011_event, s, NULL);
+                             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 7191717..07d6ebd 100644
--- a/hw/char/sclpconsole-lm.c
+++ b/hw/char/sclpconsole-lm.c
@@ -313,7 +313,7 @@ static int console_init(SCLPEvent *event)
     console_available = true;
 
     qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
-                             chr_read, NULL, scon, NULL);
+                             chr_read, NULL, scon, NULL, true);
 
     return 0;
 }
diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c
index 27a6034..b78f240 100644
--- a/hw/char/sclpconsole.c
+++ b/hw/char/sclpconsole.c
@@ -228,7 +228,7 @@ static int console_init(SCLPEvent *event)
     }
     console_available = true;
     qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
-                             chr_read, NULL, scon, NULL);
+                             chr_read, NULL, scon, NULL, true);
 
     return 0;
 }
diff --git a/hw/char/serial.c b/hw/char/serial.c
index afa6248..d210260 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -896,7 +896,7 @@ void serial_realize_core(SerialState *s, Error **errp)
     qemu_register_reset(serial_reset, s);
 
     qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1,
-                             serial_event, s, NULL);
+                             serial_event, s, NULL, true);
     fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH);
     fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH);
     serial_reset(s);
diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
index 8bb45db..9d35564 100644
--- a/hw/char/sh_serial.c
+++ b/hw/char/sh_serial.c
@@ -400,7 +400,7 @@ void sh_serial_init(MemoryRegion *sysmem,
         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);
+                                 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 8d39d40..31822fe 100644
--- a/hw/char/spapr_vty.c
+++ b/hw/char/spapr_vty.c
@@ -75,7 +75,7 @@ static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp)
     }
 
     qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive,
-                             vty_receive, NULL, dev, NULL);
+                             vty_receive, NULL, dev, NULL, true);
 }
 
 /* Forward declaration */
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 8e9852b..59872e6 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -207,7 +207,7 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp)
     STM32F2XXUsartState *s = STM32F2XX_USART(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive,
-                             stm32f2xx_usart_receive, NULL, s, NULL);
+                             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 378c1c1..776205b 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -187,14 +187,12 @@ static void virtconsole_realize(DeviceState *dev, Error **errp)
          * trigger open/close of the device
          */
         if (k->is_console) {
-            chr->explicit_fe_open = 0;
             qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
-                                     NULL, vcon, NULL);
+                                     NULL, vcon, NULL, true);
             virtio_serial_open(port);
         } else {
-            chr->explicit_fe_open = 1;
             qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
-                                     chr_event, vcon, NULL);
+                                     chr_event, vcon, NULL, false);
         }
     }
 }
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index 785bf86..86cdc52 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -245,7 +245,7 @@ static int con_initialise(struct XenDevice *xendev)
 
     xen_be_bind_evtchn(&con->xendev);
     qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
-                             xencons_receive, NULL, con, NULL);
+                             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,
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
index c7888f7..37d313b 100644
--- a/hw/char/xilinx_uartlite.c
+++ b/hw/char/xilinx_uartlite.c
@@ -212,7 +212,7 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp)
     XilinxUARTLite *s = XILINX_UARTLITE(dev);
 
     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                             uart_event, s, NULL);
+                             uart_event, s, NULL, true);
 }
 
 static void xilinx_uartlite_init(Object *obj)
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index 5530870..4b310e5 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -448,7 +448,7 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
     }
 
     qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
-                             chr_event, ibe, NULL);
+                             chr_event, ibe, NULL, true);
 }
 
 static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index 273ec6d..cf9bd3e 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -572,7 +572,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
     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);
+                             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);
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index bb70704..230e51b 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -895,7 +895,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
         }
 
         qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive,
-                                 ivshmem_read, NULL, s, NULL);
+                                 ivshmem_read, NULL, s, NULL, true);
 
         if (ivshmem_setup_interrupts(s) < 0) {
             error_setg(errp, "failed to initialize interrupts");
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 369a8f1..1faef0e 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -353,7 +353,7 @@ static int passthru_initfn(CCIDCardState *base)
         qemu_chr_fe_set_handlers(&card->cs,
             ccid_card_vscard_can_read,
             ccid_card_vscard_read,
-            ccid_card_vscard_event, card, NULL);
+            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 a69b9a3..ef1e1ad 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -503,7 +503,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
     }
 
     qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read,
-                             usb_serial_event, s, NULL);
+                             usb_serial_event, s, NULL, true);
     usb_serial_handle_reset(dev);
 
     if (chr->be_open && !dev->attached) {
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 6f5ad20..528081e 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1409,7 +1409,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp)
     /* Let the backend know we are ready */
     qemu_chr_fe_set_handlers(&dev->cs, usbredir_chardev_can_read,
                              usbredir_chardev_read, usbredir_chardev_event,
-                             dev, NULL);
+                             dev, NULL, true);
 
     qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
 }
diff --git a/monitor.c b/monitor.c
index 5c349c5..b73a999 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3988,12 +3988,12 @@ void monitor_init(CharDriverState *chr, int flags)
 
     if (monitor_is_qmp(mon)) {
         qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read,
-                                 monitor_qmp_event, mon, NULL);
+                                 monitor_qmp_event, mon, NULL, true);
         qemu_chr_fe_set_echo(&mon->chr, true);
         json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
     } else {
         qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
-                                 monitor_event, mon, NULL);
+                                 monitor_event, mon, NULL, true);
     }
 
     qemu_mutex_lock(&monitor_lock);
diff --git a/net/colo-compare.c b/net/colo-compare.c
index 3083681..109990f 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -451,7 +451,8 @@ static void compare_pri_chr_in(void *opaque, const uint8_t *buf, int size)
 
     ret = net_fill_rstate(&s->pri_rs, buf, size);
     if (ret == -1) {
-        qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL,
+                                 NULL, NULL, true);
         error_report("colo-compare primary_in error");
     }
 }
@@ -467,7 +468,8 @@ static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size)
 
     ret = net_fill_rstate(&s->sec_rs, buf, size);
     if (ret == -1) {
-        qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL,
+                                 NULL, NULL, true);
         error_report("colo-compare secondary_in error");
     }
 }
@@ -481,9 +483,9 @@ static void *colo_compare_thread(void *opaque)
     worker_context = g_main_context_new();
 
     qemu_chr_fe_set_handlers(&s->chr_pri_in, compare_chr_can_read,
-                             compare_pri_chr_in, NULL, s, worker_context);
+                             compare_pri_chr_in, NULL, s, worker_context, true);
     qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read,
-                             compare_sec_chr_in, NULL, s, worker_context);
+                             compare_sec_chr_in, NULL, s, worker_context, true);
 
     compare_loop = g_main_loop_new(worker_context, FALSE);
 
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index 1864c81..b7d6456 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -110,7 +110,8 @@ static void redirector_chr_read(void *opaque, const uint8_t *buf, int size)
     ret = net_fill_rstate(&s->rs, buf, size);
 
     if (ret == -1) {
-        qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL,
+                                 NULL, NULL, true);
     }
 }
 
@@ -121,7 +122,8 @@ static void redirector_chr_event(void *opaque, int event)
 
     switch (event) {
     case CHR_EVENT_CLOSED:
-        qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL,
+                                 NULL, NULL, true);
         break;
     default:
         break;
@@ -248,7 +250,7 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp)
 
         qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read,
                                  redirector_chr_read, redirector_chr_event,
-                                 nf, NULL);
+                                 nf, NULL, true);
     }
 
     if (s->outdev) {
diff --git a/net/slirp.c b/net/slirp.c
index 0e67535..64dd325 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -775,7 +775,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
         fwd->slirp = s->slirp;
 
         qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read,
-                                 NULL, fwd, NULL);
+                                 NULL, fwd, NULL, true);
     }
     return 0;
 
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 140a4e0..7aff77e 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -266,7 +266,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
             return -1;
         }
         qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
-                                 net_vhost_user_event, nc0->name, NULL);
+                                 net_vhost_user_event, nc0->name, NULL, true);
     } while (!s->started);
 
     assert(s->vhost_net);
diff --git a/qemu-char.c b/qemu-char.c
index 45d601f..e52c3af 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -832,7 +832,7 @@ static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
                              mux_chr_read,
                              mux_chr_event,
                              chr,
-                             context);
+                             context, true);
 }
 
 static void mux_set_focus(MuxDriver *d, int focus)
@@ -932,7 +932,7 @@ void qemu_chr_fe_deinit(CharBackend *b)
     assert(b);
 
     if (b->chr) {
-        qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
         b->chr->avail_connections++;
         b->chr->be = NULL;
         if (b->chr->is_mux) {
@@ -948,7 +948,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
                               IOReadHandler *fd_read,
                               IOEventHandler *fd_event,
                               void *opaque,
-                              GMainContext *context)
+                              GMainContext *context,
+                              bool set_open)
 {
     CharDriverState *s;
     int fe_open;
@@ -974,7 +975,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         s->chr_update_read_handler(s, context);
     }
 
-    if (!s->explicit_fe_open) {
+    if (set_open) {
         qemu_chr_fe_set_open(b, fe_open);
     }
 
diff --git a/qtest.c b/qtest.c
index 1a6c8b1..46b99ae 100644
--- a/qtest.c
+++ b/qtest.c
@@ -688,7 +688,7 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp)
 
     qemu_chr_fe_init(&qtest_chr, chr, errp);
     qemu_chr_fe_set_handlers(&qtest_chr, qtest_can_read, qtest_read,
-                             qtest_event, &qtest_chr, NULL);
+                             qtest_event, &qtest_chr, NULL, true);
     qemu_chr_fe_set_echo(&qtest_chr, true);
 
     inbuf = g_string_new("");
diff --git a/tests/test-char.c b/tests/test-char.c
index 13c55b8..241685a 100644
--- a/tests/test-char.c
+++ b/tests/test-char.c
@@ -129,7 +129,7 @@ static void char_mux_test(void)
                              fe_read,
                              fe_event,
                              &h1,
-                             NULL);
+                             NULL, true);
 
     qemu_chr_fe_init(&chr_be2, chr, &error_abort);
     qemu_chr_fe_set_handlers(&chr_be2,
@@ -137,7 +137,7 @@ static void char_mux_test(void)
                              fe_read,
                              fe_event,
                              &h2,
-                             NULL);
+                             NULL, true);
     qemu_chr_fe_take_focus(&chr_be2);
 
     base = qemu_chr_find("mux-label-base");
@@ -159,7 +159,7 @@ static void char_mux_test(void)
     h1.read_count = 0;
 
     /* remove first handler */
-    qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL);
+    qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, true);
     qemu_chr_be_write(base, (void *)"hello", 6);
     g_assert_cmpint(h1.read_count, ==, 0);
     g_assert_cmpint(h2.read_count, ==, 0);
@@ -216,7 +216,7 @@ static void char_null_test(void)
                              fe_can_read,
                              fe_read,
                              fe_event,
-                             NULL, NULL);
+                             NULL, NULL, true);
 
     ret = qemu_chr_fe_write(&be, (void *)"buf", 4);
     g_assert_cmpint(ret, ==, 4);
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 24c2323..a7f0629 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -464,7 +464,7 @@ static void test_server_create_chr(TestServer *server, const gchar *opt)
 
     qemu_chr_fe_init(&server->chr, chr, &error_abort);
     qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
-                             chr_event, server, NULL);
+                             chr_event, server, NULL, true);
 }
 
 static void test_server_listen(TestServer *server)
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index d029d54..ae32e1c 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -109,7 +109,6 @@ struct CharDriverState {
     int logfd;
     int be_open;
     int fe_open;
-    int explicit_fe_open;
     int explicit_be_open;
     int avail_connections;
     int is_mux;
@@ -449,6 +448,8 @@ void qemu_chr_fe_deinit(CharBackend *b);
  * @fd_event: event callback
  * @opaque: an opaque pointer for the callbacks
  * @context: a main loop context or NULL for the default
+ * @set_open: whether to call qemu_chr_fe_set_open() implicitely when
+ * any of the handler is non-NULL
  *
  * Set the front end char handlers. The front end takes the focus if
  * any of the handler is non-NULL.
@@ -460,7 +461,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
                               IOReadHandler *fd_read,
                               IOEventHandler *fd_event,
                               void *opaque,
-                              GMainContext *context);
+                              GMainContext *context,
+                              bool set_open);
 
 /**
  * @qemu_chr_fe_take_focus:
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (22 preceding siblings ...)
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 23/38] char: remove explicit_fe_open, use a set_handlers argument Marc-André Lureau
@ 2016-10-22 10:09 ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 25/38] char: remove unused CHR_EVENT_FOCUS Marc-André Lureau
                     ` (11 more replies)
  2016-10-22 10:16 ` [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object Marc-André Lureau
  2016-10-23 18:15 ` [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Paolo Bonzini
  25 siblings, 12 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

The fe_open state belongs to front end.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c           | 7 ++++---
 include/sysemu/char.h | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index e52c3af..81a593b 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -921,6 +921,7 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
         s->be = b;
     }
 
+    b->fe_open = false;
     b->tag = tag;
     b->chr = s;
 
@@ -4221,10 +4222,10 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
         return;
     }
 
-    if (chr->fe_open == fe_open) {
+    if (be->fe_open == fe_open) {
         return;
     }
-    chr->fe_open = fe_open;
+    be->fe_open = fe_open;
     if (chr->chr_set_fe_open) {
         chr->chr_set_fe_open(chr, fe_open);
     }
@@ -4309,7 +4310,7 @@ ChardevInfoList *qmp_query_chardev(Error **errp)
         info->value = g_malloc0(sizeof(*info->value));
         info->value->label = g_strdup(chr->label);
         info->value->filename = g_strdup(chr->filename);
-        info->value->frontend_open = chr->fe_open;
+        info->value->frontend_open = chr->be && chr->be->fe_open;
 
         info->next = chr_list;
         chr_list = info;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index ae32e1c..2c3060c 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -81,6 +81,7 @@ typedef struct CharBackend {
     IOReadHandler *chr_read;
     void *opaque;
     int tag;
+    int fe_open;
 } CharBackend;
 
 struct CharDriverState {
@@ -108,7 +109,6 @@ struct CharDriverState {
     char *filename;
     int logfd;
     int be_open;
-    int fe_open;
     int explicit_be_open;
     int avail_connections;
     int is_mux;
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 25/38] char: remove unused CHR_EVENT_FOCUS
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 26/38] char: use an enum for CHR_EVENT Marc-André Lureau
                     ` (10 subsequent siblings)
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Usage has long been removed, since commit f220174de8d9.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/usb/ccid-card-passthru.c | 2 --
 hw/usb/dev-serial.c         | 2 --
 include/sysemu/char.h       | 1 -
 3 files changed, 5 deletions(-)

diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 1faef0e..325129a 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -312,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;
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index ef1e1ad..6066d9b 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -466,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);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 2c3060c..43da4ac 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -14,7 +14,6 @@
 /* character device */
 
 #define CHR_EVENT_BREAK   0 /* serial break char */
-#define CHR_EVENT_FOCUS   1 /* focus to this terminal (modal input needed) */
 #define CHR_EVENT_OPENED  2 /* new connection established */
 #define CHR_EVENT_MUX_IN  3 /* mux-focus was set to this terminal */
 #define CHR_EVENT_MUX_OUT 4 /* mux-focus will move on */
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 26/38] char: use an enum for CHR_EVENT
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 25/38] char: remove unused CHR_EVENT_FOCUS Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 27/38] char: remove unused qemu_chr_fe_event Marc-André Lureau
                     ` (9 subsequent siblings)
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

This may help to catch unhandled cases, and avoid having to maintain
numbering.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/sysemu/char.h | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 43da4ac..de0d99b 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -13,11 +13,13 @@
 
 /* character device */
 
-#define CHR_EVENT_BREAK   0 /* serial break char */
-#define CHR_EVENT_OPENED  2 /* new connection established */
-#define CHR_EVENT_MUX_IN  3 /* mux-focus was set to this terminal */
-#define CHR_EVENT_MUX_OUT 4 /* mux-focus will move on */
-#define CHR_EVENT_CLOSED  5 /* connection closed */
+typedef enum {
+    CHR_EVENT_BREAK, /* serial break char */
+    CHR_EVENT_OPENED, /* new connection established */
+    CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */
+    CHR_EVENT_MUX_OUT, /* mux-focus will move on */
+    CHR_EVENT_CLOSED /* connection closed */
+} QEMUChrEvent;
 
 
 #define CHR_IOCTL_SERIAL_SET_PARAMS   1
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 27/38] char: remove unused qemu_chr_fe_event
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 25/38] char: remove unused CHR_EVENT_FOCUS Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 26/38] char: use an enum for CHR_EVENT Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 28/38] char: replace avail_connections Marc-André Lureau
                     ` (8 subsequent siblings)
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

I introduced this function in d61b0c9a2f7f, but it isn't
used. Furthermore, it was incomplete, as it would need to translate QEMU
chr events to Spice port events.

(presumably it was used in the follow-up NBD-spice series that was not
completed: http://lists.gnu.org/archive/html/qemu-devel/2013-11/msg02024.html)

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c           |  9 ---------
 spice-qemu-char.c     | 10 ----------
 include/sysemu/char.h | 11 -----------
 3 files changed, 30 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 81a593b..36ee7a7 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -4231,15 +4231,6 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
     }
 }
 
-void qemu_chr_fe_event(CharBackend *be, int event)
-{
-    CharDriverState *chr = be->chr;
-
-    if (chr && chr->chr_fe_event) {
-        chr->chr_fe_event(chr, event);
-    }
-}
-
 guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
                             GIOFunc func, void *user_data)
 {
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 930b8c5..89fae6d 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -236,15 +236,6 @@ static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
 #endif
 }
 
-static void spice_chr_fe_event(struct CharDriverState *chr, int event)
-{
-#if SPICE_SERVER_VERSION >= 0x000c02
-    SpiceCharDriver *s = chr->opaque;
-
-    spice_server_port_event(&s->sin, event);
-#endif
-}
-
 static void print_allowed_subtypes(void)
 {
     const char** psubtype;
@@ -292,7 +283,6 @@ static CharDriverState *chr_open(const char *subtype,
     chr->chr_free = spice_chr_free;
     chr->chr_set_fe_open = set_fe_open;
     chr->explicit_be_open = true;
-    chr->chr_fe_event = spice_chr_fe_event;
     chr->chr_accept_input = spice_chr_accept_input;
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index de0d99b..6bad856 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -103,7 +103,6 @@ struct CharDriverState {
     void (*chr_accept_input)(struct CharDriverState *chr);
     void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
     void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
-    void (*chr_fe_event)(struct CharDriverState *chr, int event);
     CharBackend *be;
     void *opaque;
     char *label;
@@ -238,16 +237,6 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
  */
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open);
 
-/**
- * @qemu_chr_fe_event:
- *
- * Send an event from the front end to the back end. It does nothing
- * without associated CharDriver.
- *
- * @event the event to send
- */
-void qemu_chr_fe_event(CharBackend *be, int event);
-
 /**
  * @qemu_chr_fe_printf:
  *
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 28/38] char: replace avail_connections
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (2 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 27/38] char: remove unused qemu_chr_fe_event Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 29/38] char: use common error path in qmp_chardev_add Marc-André Lureau
                     ` (7 subsequent siblings)
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

No need to count the users of a CharDriverState, it can rely on the fact
of whether there is a CharBackend associated or if there is enough space
in the muxer.

Simplify and fold chr_mux_new_fe() in qemu_chr_fe_init() since there is
a single user now. Also switch from fprintf to raising error instead.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/bt/hci-csr.c       |  1 -
 qemu-char.c           | 40 +++++++++++++---------------------------
 include/sysemu/char.h |  1 -
 3 files changed, 13 insertions(+), 29 deletions(-)

diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index cdf52a9..fbb3109 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -468,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/qemu-char.c b/qemu-char.c
index 36ee7a7..f386dcd 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -808,20 +808,6 @@ static void mux_chr_free(struct CharDriverState *chr)
     g_free(d);
 }
 
-static int mux_chr_new_fe(CharDriverState *chr, CharBackend *be, Error **errp)
-{
-    MuxDriver *d = chr->opaque;
-
-    if (d->mux_cnt >= MAX_MUX) {
-        fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n");
-        return -1;
-    }
-
-    d->backends[d->mux_cnt] = be;
-
-    return d->mux_cnt++;
-}
-
 static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
 {
     MuxDriver *d = chr->opaque;
@@ -906,17 +892,17 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
     assert(b);
     assert(s);
 
-    if (s->avail_connections < 1) {
-        error_setg(errp, QERR_DEVICE_IN_USE, s->label);
-        return false;
-    }
-    s->avail_connections--;
-
     if (s->is_mux) {
-        tag = mux_chr_new_fe(s, b, errp);
-        if (tag < 0) {
-            return false;
+        MuxDriver *d = s->opaque;
+
+        if (d->mux_cnt >= MAX_MUX) {
+            goto unavailable;
         }
+
+        d->backends[d->mux_cnt] = b;
+        tag = d->mux_cnt++;
+    } else if (s->be) {
+        goto unavailable;
     } else {
         s->be = b;
     }
@@ -924,8 +910,11 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
     b->fe_open = false;
     b->tag = tag;
     b->chr = s;
-
     return true;
+
+unavailable:
+    error_setg(errp, QERR_DEVICE_IN_USE, s->label);
+    return false;
 }
 
 void qemu_chr_fe_deinit(CharBackend *b)
@@ -934,7 +923,6 @@ void qemu_chr_fe_deinit(CharBackend *b)
 
     if (b->chr) {
         qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
-        b->chr->avail_connections++;
         b->chr->be = NULL;
         if (b->chr->is_mux) {
             MuxDriver *d = b->chr->opaque;
@@ -4787,8 +4775,6 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
     }
 
     chr->label = g_strdup(id);
-    chr->avail_connections =
-        (backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
     if (!chr->filename) {
         chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
     }
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 6bad856..0628b14 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -110,7 +110,6 @@ struct CharDriverState {
     int logfd;
     int be_open;
     int explicit_be_open;
-    int avail_connections;
     int is_mux;
     guint fd_in_tag;
     bool replay;
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 29/38] char: use common error path in qmp_chardev_add
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (3 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 28/38] char: replace avail_connections Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 30/38] char: remove explicit_be_open from CharDriverState Marc-André Lureau
                     ` (6 subsequent siblings)
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index f386dcd..2408e97 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -4751,8 +4751,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
     chr = qemu_chr_find(id);
     if (chr) {
         error_setg(errp, "Chardev '%s' already exists", id);
-        g_free(ret);
-        return NULL;
+        goto out_error;
     }
 
     for (i = backends; i; i = i->next) {
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 30/38] char: remove explicit_be_open from CharDriverState
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (4 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 29/38] char: use common error path in qmp_chardev_add Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 31/38] char: use a const CharDriver Marc-André Lureau
                     ` (5 subsequent siblings)
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

It's only used in qmp_chardev_add(), so use a create() argument instead.

Also switched to typedef functions for CharDriverParse/CharDriverCreate.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/baum.c       |  1 +
 backends/msmouse.c    |  3 ++-
 backends/testdev.c    |  1 +
 qemu-char.c           | 55 ++++++++++++++++++++++++++++++++++-----------------
 spice-qemu-char.c     |  5 ++++-
 ui/console.c          | 11 ++++++-----
 ui/gtk.c              |  3 ---
 include/sysemu/char.h | 12 +++++++----
 8 files changed, 59 insertions(+), 32 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index a516434..919844e 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -566,6 +566,7 @@ static void baum_free(struct CharDriverState *chr)
 static CharDriverState *chr_baum_init(const char *id,
                                       ChardevBackend *backend,
                                       ChardevReturn *ret,
+                                      bool *be_opened,
                                       Error **errp)
 {
     ChardevCommon *common = backend->u.braille.data;
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 448369f..733ca80 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -151,6 +151,7 @@ static QemuInputHandler msmouse_handler = {
 static CharDriverState *qemu_chr_open_msmouse(const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
+                                              bool *be_opened,
                                               Error **errp)
 {
     ChardevCommon *common = backend->u.msmouse.data;
@@ -164,7 +165,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
     chr->chr_write = msmouse_chr_write;
     chr->chr_free = msmouse_chr_free;
     chr->chr_accept_input = msmouse_chr_accept_input;
-    chr->explicit_be_open = true;
+    *be_opened = false;
 
     mouse = g_new0(MouseState, 1);
     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
diff --git a/backends/testdev.c b/backends/testdev.c
index 1ba8bf2..60156e3 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -112,6 +112,7 @@ static void testdev_free(struct CharDriverState *chr)
 static CharDriverState *chr_testdev_init(const char *id,
                                          ChardevBackend *backend,
                                          ChardevReturn *ret,
+                                         bool *be_opened,
                                          Error **errp)
 {
     TestdevCharState *testdev;
diff --git a/qemu-char.c b/qemu-char.c
index 2408e97..8e05c8c 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -509,6 +509,7 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 static CharDriverState *qemu_chr_open_null(const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
+                                           bool *be_opened,
                                            Error **errp)
 {
     CharDriverState *chr;
@@ -519,7 +520,7 @@ static CharDriverState *qemu_chr_open_null(const char *id,
         return NULL;
     }
     chr->chr_write = null_chr_write;
-    chr->explicit_be_open = true;
+    *be_opened = false;
     return chr;
 }
 
@@ -836,7 +837,9 @@ static void mux_set_focus(MuxDriver *d, int focus)
 
 static CharDriverState *qemu_chr_open_mux(const char *id,
                                           ChardevBackend *backend,
-                                          ChardevReturn *ret, Error **errp)
+                                          ChardevReturn *ret,
+                                          bool *be_opened,
+                                          Error **errp)
 {
     ChardevMux *mux = backend->u.mux.data;
     CharDriverState *chr, *drv;
@@ -868,7 +871,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
     /* only default to opened state if we've realized the initial
      * set of muxes
      */
-    chr->explicit_be_open = muxes_realized ? 0 : 1;
+    *be_opened = muxes_realized;
     chr->is_mux = 1;
     if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
         qemu_chr_free(chr);
@@ -1279,6 +1282,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
 static CharDriverState *qemu_chr_open_pipe(const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
+                                           bool *be_opened,
                                            Error **errp)
 {
     ChardevHostdev *opts = backend->u.pipe.data;
@@ -1361,6 +1365,7 @@ static void qemu_chr_free_stdio(struct CharDriverState *chr)
 static CharDriverState *qemu_chr_open_stdio(const char *id,
                                             ChardevBackend *backend,
                                             ChardevReturn *ret,
+                                            bool *be_opened,
                                             Error **errp)
 {
     ChardevStdio *opts = backend->u.stdio.data;
@@ -1608,6 +1613,7 @@ static void pty_chr_free(struct CharDriverState *chr)
 static CharDriverState *qemu_chr_open_pty(const char *id,
                                           ChardevBackend *backend,
                                           ChardevReturn *ret,
+                                          bool *be_opened,
                                           Error **errp)
 {
     CharDriverState *chr;
@@ -1644,7 +1650,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     chr->chr_update_read_handler = pty_chr_update_read_handler;
     chr->chr_free = pty_chr_free;
     chr->chr_add_watch = pty_chr_add_watch;
-    chr->explicit_be_open = true;
+    *be_opened = false;
 
     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
     s->timer_tag = 0;
@@ -1844,6 +1850,7 @@ static void qemu_chr_free_tty(CharDriverState *chr)
 
 static CharDriverState *qemu_chr_open_tty_fd(int fd,
                                              ChardevCommon *backend,
+                                             bool *be_opened,
                                              Error **errp)
 {
     CharDriverState *chr;
@@ -1974,6 +1981,7 @@ static void pp_free(CharDriverState *chr)
 
 static CharDriverState *qemu_chr_open_pp_fd(int fd,
                                             ChardevCommon *backend,
+                                            bool *be_opened,
                                             Error **errp)
 {
     CharDriverState *chr;
@@ -2046,6 +2054,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 
 static CharDriverState *qemu_chr_open_pp_fd(int fd,
                                             ChardevCommon *backend,
+                                            bool *be_opened,
                                             Error **errp)
 {
     CharDriverState *chr;
@@ -2057,7 +2066,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd,
     chr->opaque = (void *)(intptr_t)fd;
     chr->chr_write = null_chr_write;
     chr->chr_ioctl = pp_ioctl;
-    chr->explicit_be_open = true;
+    *be_opened = false;
     return chr;
 }
 #endif
@@ -2386,6 +2395,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
 static CharDriverState *qemu_chr_open_pipe(const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
+                                           bool *be_opened,
                                            Error **errp)
 {
     ChardevHostdev *opts = backend->u.pipe.data;
@@ -2432,6 +2442,7 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out,
 static CharDriverState *qemu_chr_open_win_con(const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
+                                              bool *be_opened,
                                               Error **errp)
 {
     ChardevCommon *common = backend->u.console.data;
@@ -2577,6 +2588,7 @@ static void win_stdio_free(CharDriverState *chr)
 static CharDriverState *qemu_chr_open_stdio(const char *id,
                                             ChardevBackend *backend,
                                             ChardevReturn *ret,
+                                            bool *be_opened,
                                             Error **errp)
 {
     CharDriverState   *chr;
@@ -2752,6 +2764,7 @@ static void udp_chr_free(CharDriverState *chr)
 
 static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
                                           ChardevCommon *backend,
+                                          bool *be_opened,
                                           Error **errp)
 {
     CharDriverState *chr = NULL;
@@ -2771,7 +2784,7 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
     chr->chr_update_read_handler = udp_chr_update_read_handler;
     chr->chr_free = udp_chr_free;
     /* be isn't opened until we get a connection */
-    chr->explicit_be_open = true;
+    *be_opened = false;
     return chr;
 }
 
@@ -3503,6 +3516,7 @@ static void ringbuf_chr_free(struct CharDriverState *chr)
 static CharDriverState *qemu_chr_open_ringbuf(const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
+                                              bool *be_opened,
                                               Error **errp)
 {
     ChardevRingbuf *opts = backend->u.ringbuf.data;
@@ -4031,17 +4045,14 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
 typedef struct CharDriver {
     const char *name;
     ChardevBackendKind kind;
-    void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
-    CharDriverState *(*create)(const char *id, ChardevBackend *backend,
-                               ChardevReturn *ret, Error **errp);
+    CharDriverParse *parse;
+    CharDriverCreate *create;
 } CharDriver;
 
 static GSList *backends;
 
 void register_char_driver(const char *name, ChardevBackendKind kind,
-        void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp),
-        CharDriverState *(*create)(const char *id, ChardevBackend *backend,
-                                   ChardevReturn *ret, Error **errp))
+                          CharDriverParse *parse, CharDriverCreate *create)
 {
     CharDriver *s;
 
@@ -4428,6 +4439,7 @@ QemuOptsList qemu_chardev_opts = {
 static CharDriverState *qmp_chardev_open_file(const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
+                                              bool *be_opened,
                                               Error **errp)
 {
     ChardevFile *file = backend->u.file.data;
@@ -4463,6 +4475,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
 static CharDriverState *qmp_chardev_open_serial(const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
+                                                bool *be_opened,
                                                 Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial.data;
@@ -4487,6 +4500,7 @@ static int qmp_chardev_open_file_source(char *src, int flags,
 static CharDriverState *qmp_chardev_open_file(const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
+                                              bool *be_opened,
                                               Error **errp)
 {
     ChardevFile *file = backend->u.file.data;
@@ -4521,6 +4535,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
 static CharDriverState *qmp_chardev_open_serial(const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
+                                                bool *be_opened,
                                                 Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial.data;
@@ -4532,7 +4547,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
         return NULL;
     }
     qemu_set_nonblock(fd);
-    return qemu_chr_open_tty_fd(fd, common, errp);
+    return qemu_chr_open_tty_fd(fd, common, be_opened, errp);
 }
 #endif
 
@@ -4540,6 +4555,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
 static CharDriverState *qmp_chardev_open_parallel(const char *id,
                                                   ChardevBackend *backend,
                                                   ChardevReturn *ret,
+                                                  bool *be_opened,
                                                   Error **errp)
 {
     ChardevHostdev *parallel = backend->u.parallel.data;
@@ -4550,7 +4566,7 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id,
     if (fd < 0) {
         return NULL;
     }
-    return qemu_chr_open_pp_fd(fd, common, errp);
+    return qemu_chr_open_pp_fd(fd, common, be_opened, errp);
 }
 #endif
 
@@ -4579,6 +4595,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
 static CharDriverState *qmp_chardev_open_socket(const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
+                                                bool *be_opened,
                                                 Error **errp)
 {
     CharDriverState *chr;
@@ -4655,7 +4672,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     chr->chr_add_watch = tcp_chr_add_watch;
     chr->chr_update_read_handler = tcp_chr_update_read_handler;
     /* be isn't opened until we get a connection */
-    chr->explicit_be_open = true;
+    *be_opened = false;
 
     chr->filename = SocketAddress_to_str("disconnected:",
                                          addr, is_listen, is_telnet);
@@ -4711,6 +4728,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
 static CharDriverState *qmp_chardev_open_udp(const char *id,
                                              ChardevBackend *backend,
                                              ChardevReturn *ret,
+                                             bool *be_opened,
                                              Error **errp)
 {
     ChardevUdp *udp = backend->u.udp.data;
@@ -4723,7 +4741,7 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
         object_unref(OBJECT(sioc));
         return NULL;
     }
-    return qemu_chr_open_udp(sioc, common, errp);
+    return qemu_chr_open_udp(sioc, common, be_opened, errp);
 }
 
 
@@ -4747,6 +4765,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
     Error *local_err = NULL;
     GSList *i;
     CharDriver *cd;
+    bool be_opened = true;
 
     chr = qemu_chr_find(id);
     if (chr) {
@@ -4758,7 +4777,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
         cd = i->data;
 
         if (cd->kind == backend->type) {
-            chr = cd->create(id, backend, ret, &local_err);
+            chr = cd->create(id, backend, ret, &be_opened, &local_err);
             if (local_err) {
                 error_propagate(errp, local_err);
                 goto out_error;
@@ -4777,7 +4796,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
     if (!chr->filename) {
         chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
     }
-    if (!chr->explicit_be_open) {
+    if (be_opened) {
         qemu_chr_be_event(chr, CHR_EVENT_OPENED);
     }
     QTAILQ_INSERT_TAIL(&chardevs, chr, next);
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 89fae6d..276c4ae 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -282,7 +282,6 @@ static CharDriverState *chr_open(const char *subtype,
     chr->chr_add_watch = spice_chr_add_watch;
     chr->chr_free = spice_chr_free;
     chr->chr_set_fe_open = set_fe_open;
-    chr->explicit_be_open = true;
     chr->chr_accept_input = spice_chr_accept_input;
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
@@ -293,6 +292,7 @@ static CharDriverState *chr_open(const char *subtype,
 static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
+                                                bool *be_opened,
                                                 Error **errp)
 {
     ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
@@ -311,6 +311,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
         return NULL;
     }
 
+    *be_opened = false;
     return chr_open(type, spice_vmc_set_fe_open, common, errp);
 }
 
@@ -318,6 +319,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
 static CharDriverState *qemu_chr_open_spice_port(const char *id,
                                                  ChardevBackend *backend,
                                                  ChardevReturn *ret,
+                                                 bool *be_opened,
                                                  Error **errp)
 {
     ChardevSpicePort *spiceport = backend->u.spiceport.data;
@@ -335,6 +337,7 @@ static CharDriverState *qemu_chr_open_spice_port(const char *id,
     if (!chr) {
         return NULL;
     }
+    *be_opened = false;
     s = chr->opaque;
     s->sin.portname = g_strdup(name);
 
diff --git a/ui/console.c b/ui/console.c
index f490346..ed888e5 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2079,10 +2079,6 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
     s->chr = chr;
     chr->opaque = s;
     chr->chr_set_echo = text_console_set_echo;
-    /* console/chardev init sometimes completes elsewhere in a 2nd
-     * stage, so defer OPENED events until they are fully initialized
-     */
-    chr->explicit_be_open = true;
 
     if (display_state) {
         text_console_do_init(chr, display_state);
@@ -2093,8 +2089,13 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 static VcHandler *vc_handler = text_console_init;
 
 static CharDriverState *vc_init(const char *id, ChardevBackend *backend,
-                                ChardevReturn *ret, Error **errp)
+                                ChardevReturn *ret, bool *be_opened,
+                                Error **errp)
 {
+    /* console/chardev init sometimes completes elsewhere in a 2nd
+     * stage, so defer OPENED events until they are fully initialized
+     */
+    *be_opened = false;
     return vc_handler(backend->u.vc.data, errp);
 }
 
diff --git a/ui/gtk.c b/ui/gtk.c
index 83984f6..25e6d99 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1685,9 +1685,6 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
     /* Temporary, until gd_vc_vte_init runs.  */
     chr->opaque = g_new0(VirtualConsole, 1);
 
-    /* defer OPENED events until our vc is fully initialized */
-    chr->explicit_be_open = true;
-
     vcs[nb_vcs++] = chr;
 
     return chr;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0628b14..0a14942 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -109,7 +109,6 @@ struct CharDriverState {
     char *filename;
     int logfd;
     int be_open;
-    int explicit_be_open;
     int is_mux;
     guint fd_in_tag;
     bool replay;
@@ -474,10 +473,15 @@ void qemu_chr_set_feature(CharDriverState *chr,
                           CharDriverFeature feature);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
+typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend,
+                             Error **errp);
+typedef CharDriverState *CharDriverCreate(const char *id,
+                                          ChardevBackend *backend,
+                                          ChardevReturn *ret, bool *be_opened,
+                                          Error **errp);
+
 void register_char_driver(const char *name, ChardevBackendKind kind,
-        void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp),
-        CharDriverState *(*create)(const char *id, ChardevBackend *backend,
-                                   ChardevReturn *ret, Error **errp));
+                          CharDriverParse *parse, CharDriverCreate *create);
 
 extern int term_escape_char;
 
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 31/38] char: use a const CharDriver
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (5 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 30/38] char: remove explicit_be_open from CharDriverState Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-23 12:24     ` Paolo Bonzini
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 32/38] char: use a static array for backends Marc-André Lureau
                     ` (4 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

No need to allocate & copy fileds, let's use static const struct
instead.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/baum.c       |  7 ++--
 backends/msmouse.c    |  6 ++--
 backends/testdev.c    |  6 ++--
 qemu-char.c           | 89 +++++++++++++++++++++++----------------------------
 spice-qemu-char.c     | 14 +++++---
 ui/console.c          |  8 +++--
 include/sysemu/char.h | 20 ++++++------
 7 files changed, 79 insertions(+), 71 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 919844e..4fe11de 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -643,8 +643,11 @@ fail_handle:
 
 static void register_types(void)
 {
-    register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL,
-                         chr_baum_init);
+    static const CharDriver driver = {
+        "braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init
+    };
+
+    register_char_driver(&driver);
 }
 
 type_init(register_types);
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 733ca80..d6ab4ca 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -179,8 +179,10 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
 
 static void register_types(void)
 {
-    register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL,
-                         qemu_chr_open_msmouse);
+    static const CharDriver driver = {
+        "msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL, qemu_chr_open_msmouse
+    };
+    register_char_driver(&driver);
 }
 
 type_init(register_types);
diff --git a/backends/testdev.c b/backends/testdev.c
index 60156e3..5936189 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -130,8 +130,10 @@ static CharDriverState *chr_testdev_init(const char *id,
 
 static void register_types(void)
 {
-    register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL,
-                         chr_testdev_init);
+    static const CharDriver driver = {
+        "testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init
+    };
+    register_char_driver(&driver);
 }
 
 type_init(register_types);
diff --git a/qemu-char.c b/qemu-char.c
index 8e05c8c..594e795 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -4042,27 +4042,11 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     }
 }
 
-typedef struct CharDriver {
-    const char *name;
-    ChardevBackendKind kind;
-    CharDriverParse *parse;
-    CharDriverCreate *create;
-} CharDriver;
-
 static GSList *backends;
 
-void register_char_driver(const char *name, ChardevBackendKind kind,
-                          CharDriverParse *parse, CharDriverCreate *create)
+void register_char_driver(const CharDriver *driver)
 {
-    CharDriver *s;
-
-    s = g_malloc0(sizeof(*s));
-    s->name = g_strdup(name);
-    s->kind = kind;
-    s->parse = parse;
-    s->create = create;
-
-    backends = g_slist_append(backends, s);
+    backends = g_slist_append(backends, (void *)driver);
 }
 
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
@@ -4839,45 +4823,52 @@ void qemu_chr_cleanup(void)
 
 static void register_types(void)
 {
-    register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL,
-                         qemu_chr_open_null);
-    register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET,
-                         qemu_chr_parse_socket, qmp_chardev_open_socket);
-    register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
-                         qmp_chardev_open_udp);
-    register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
-                         qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
-    register_char_driver("file", CHARDEV_BACKEND_KIND_FILE,
-                         qemu_chr_parse_file_out, qmp_chardev_open_file);
-    register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO,
-                         qemu_chr_parse_stdio, qemu_chr_open_stdio);
+    int i;
+    static const CharDriver drivers[] = {
+        { "null", CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null },
+        { "socket", CHARDEV_BACKEND_KIND_SOCKET,
+          qemu_chr_parse_socket, qmp_chardev_open_socket },
+        { "udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
+          qmp_chardev_open_udp },
+        { "ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
+          qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
+        { "file", CHARDEV_BACKEND_KIND_FILE,
+          qemu_chr_parse_file_out, qmp_chardev_open_file },
+        { "stdio", CHARDEV_BACKEND_KIND_STDIO,
+          qemu_chr_parse_stdio, qemu_chr_open_stdio },
 #if defined HAVE_CHARDEV_SERIAL
-    register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL,
-                         qemu_chr_parse_serial, qmp_chardev_open_serial);
-    register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL,
-                         qemu_chr_parse_serial, qmp_chardev_open_serial);
+        { "serial", CHARDEV_BACKEND_KIND_SERIAL,
+          qemu_chr_parse_serial, qmp_chardev_open_serial },
+        { "tty", CHARDEV_BACKEND_KIND_SERIAL,
+          qemu_chr_parse_serial, qmp_chardev_open_serial },
 #endif
 #ifdef HAVE_CHARDEV_PARPORT
-    register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
-                         qemu_chr_parse_parallel, qmp_chardev_open_parallel);
-    register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL,
-                         qemu_chr_parse_parallel, qmp_chardev_open_parallel);
+        { "parallel", CHARDEV_BACKEND_KIND_PARALLEL,
+          qemu_chr_parse_parallel, qmp_chardev_open_parallel },
+        { "parport", CHARDEV_BACKEND_KIND_PARALLEL,
+          qemu_chr_parse_parallel, qmp_chardev_open_parallel },
 #endif
 #ifdef HAVE_CHARDEV_PTY
-    register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL,
-                         qemu_chr_open_pty);
+        { "pty", CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty },
 #endif
 #ifdef _WIN32
-    register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
-                         qemu_chr_open_win_con);
+        { "console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
+          qemu_chr_open_win_con },
 #endif
-    register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE,
-                         qemu_chr_parse_pipe, qemu_chr_open_pipe);
-    register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
-                         qemu_chr_open_mux);
-    /* Bug-compatibility: */
-    register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
-                         qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
+        { "pipe", CHARDEV_BACKEND_KIND_PIPE,
+          qemu_chr_parse_pipe, qemu_chr_open_pipe },
+        { "mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
+          qemu_chr_open_mux },
+        /* Bug-compatibility: */
+        { "memory", CHARDEV_BACKEND_KIND_MEMORY,
+          qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
+    };
+
+
+    for (i = 0; i < ARRAY_SIZE(drivers); i++) {
+        register_char_driver(&drivers[i]);
+    }
+
     /* this must be done after machine init, since we register FEs with muxes
      * as part of realize functions like serial_isa_realizefn when -nographic
      * is specified
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 276c4ae..7ce8527 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -389,10 +389,16 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
 
 static void register_types(void)
 {
-    register_char_driver("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
-                         qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc);
-    register_char_driver("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
-                         qemu_chr_parse_spice_port, qemu_chr_open_spice_port);
+    static const CharDriver vmc_driver = {
+        "spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
+        qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc
+    };
+    static const CharDriver port_driver = {
+        "spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
+        qemu_chr_parse_spice_port, qemu_chr_open_spice_port
+    };
+    register_char_driver(&vmc_driver);
+    register_char_driver(&port_driver);
 }
 
 type_init(register_types);
diff --git a/ui/console.c b/ui/console.c
index ed888e5..a65223a 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2180,12 +2180,14 @@ static const TypeInfo qemu_console_info = {
     .class_size = sizeof(QemuConsoleClass),
 };
 
-
 static void register_types(void)
 {
+    static const CharDriver vc_driver = {
+        "vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init
+    };
+
     type_register_static(&qemu_console_info);
-    register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc,
-                         vc_init);
+    register_char_driver(&vc_driver);
 }
 
 type_init(register_types);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0a14942..6a4b3ef 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -473,15 +473,17 @@ void qemu_chr_set_feature(CharDriverState *chr,
                           CharDriverFeature feature);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
-typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend,
-                             Error **errp);
-typedef CharDriverState *CharDriverCreate(const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret, bool *be_opened,
-                                          Error **errp);
-
-void register_char_driver(const char *name, ChardevBackendKind kind,
-                          CharDriverParse *parse, CharDriverCreate *create);
+typedef struct CharDriver {
+    const char *name;
+    ChardevBackendKind kind;
+    void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
+    CharDriverState *(*create)(const char *id,
+                               ChardevBackend *backend,
+                               ChardevReturn *ret, bool *be_opened,
+                               Error **errp);
+} CharDriver;
+
+void register_char_driver(const CharDriver *driver);
 
 extern int term_escape_char;
 
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 32/38] char: use a static array for backends
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (6 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 31/38] char: use a const CharDriver Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-23 12:21     ` Paolo Bonzini
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 33/38] char: move callbacks in CharDriver Marc-André Lureau
                     ` (3 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Number and kinds of backends is known at compile-time, use a fixed-sized
static array to simplify iterations & lookups.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/baum.c       |   2 +-
 backends/msmouse.c    |   2 +-
 backends/testdev.c    |   2 +-
 qemu-char.c           | 117 +++++++++++++++++++++++++++-----------------------
 spice-qemu-char.c     |   4 +-
 ui/console.c          |   2 +-
 include/sysemu/char.h |   2 +-
 7 files changed, 70 insertions(+), 61 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 4fe11de..0a65c99 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -644,7 +644,7 @@ fail_handle:
 static void register_types(void)
 {
     static const CharDriver driver = {
-        "braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init
+        { "braille" }, CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init
     };
 
     register_char_driver(&driver);
diff --git a/backends/msmouse.c b/backends/msmouse.c
index d6ab4ca..3367d67 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -180,7 +180,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
 static void register_types(void)
 {
     static const CharDriver driver = {
-        "msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL, qemu_chr_open_msmouse
+        { "msmouse" }, CHARDEV_BACKEND_KIND_MSMOUSE, NULL, qemu_chr_open_msmouse
     };
     register_char_driver(&driver);
 }
diff --git a/backends/testdev.c b/backends/testdev.c
index 5936189..d41352a 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -131,7 +131,7 @@ static CharDriverState *chr_testdev_init(const char *id,
 static void register_types(void)
 {
     static const CharDriver driver = {
-        "testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init
+        { "testdev" }, CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init
     };
     register_char_driver(&driver);
 }
diff --git a/qemu-char.c b/qemu-char.c
index 594e795..7348cb0 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -4042,20 +4042,20 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     }
 }
 
-static GSList *backends;
+static const CharDriver *backends[CHARDEV_BACKEND_KIND__MAX];
 
 void register_char_driver(const CharDriver *driver)
 {
-    backends = g_slist_append(backends, (void *)driver);
+    backends[driver->kind] = driver;
 }
 
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
                                         Error **errp)
 {
     Error *local_err = NULL;
-    CharDriver *cd;
+    const CharDriver *cd;
     CharDriverState *chr;
-    GSList *i;
+    int i;
     ChardevReturn *ret = NULL;
     ChardevBackend *backend;
     const char *id = qemu_opts_id(opts);
@@ -4069,9 +4069,14 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
 
     if (is_help_option(qemu_opt_get(opts, "backend"))) {
         fprintf(stderr, "Available chardev backend types:\n");
-        for (i = backends; i; i = i->next) {
-            cd = i->data;
-            fprintf(stderr, "%s\n", cd->name);
+        for (i = 0; i < ARRAY_SIZE(backends); i++) {
+            cd = backends[i];
+            if (cd) {
+                fprintf(stderr, "%s\n", cd->name[0]);
+                if (cd->name[1]) {
+                    fprintf(stderr, "%s\n", cd->name[1]);
+                }
+            }
         }
         exit(!is_help_option(qemu_opt_get(opts, "backend")));
     }
@@ -4081,14 +4086,17 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         goto err;
     }
 
-    for (i = backends; i; i = i->next) {
-        cd = i->data;
+    cd = NULL;
+    for (i = 0; i < ARRAY_SIZE(backends); i++) {
+        const char *name = qemu_opt_get(opts, "backend");
+        cd = backends[i];
 
-        if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
+        if (cd && (g_str_equal(cd->name[0], name) ||
+                   (cd->name[1] && g_str_equal(cd->name[1], name)))) {
             break;
         }
     }
-    if (i == NULL) {
+    if (cd == NULL) {
         error_setg(errp, "chardev: backend \"%s\" not found",
                    qemu_opt_get(opts, "backend"));
         goto err;
@@ -4293,20 +4301,32 @@ ChardevInfoList *qmp_query_chardev(Error **errp)
     return chr_list;
 }
 
+static ChardevBackendInfoList *
+qmp_prepend_backend(ChardevBackendInfoList *list, const CharDriver *c,
+                    const char *name)
+{
+    ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
+    info->value = g_malloc0(sizeof(*info->value));
+    info->value->name = g_strdup(name);
+    info->next = list;
+    return info;
+
+}
+
 ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
 {
     ChardevBackendInfoList *backend_list = NULL;
-    CharDriver *c = NULL;
-    GSList *i = NULL;
-
-    for (i = backends; i; i = i->next) {
-        ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
-        c = i->data;
-        info->value = g_malloc0(sizeof(*info->value));
-        info->value->name = g_strdup(c->name);
+    const CharDriver *c;
+    int i;
 
-        info->next = backend_list;
-        backend_list = info;
+    for (i = 0; i < ARRAY_SIZE(backends); i++) {
+        c = backends[i];
+        if (c) {
+            backend_list = qmp_prepend_backend(backend_list, c, c->name[0]);
+            if (c->name[1]) {
+                backend_list = qmp_prepend_backend(backend_list, c, c->name[1]);
+            }
+        }
     }
 
     return backend_list;
@@ -4746,9 +4766,8 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
 {
     ChardevReturn *ret = g_new0(ChardevReturn, 1);
     CharDriverState *chr = NULL;
+    const CharDriver *cd;
     Error *local_err = NULL;
-    GSList *i;
-    CharDriver *cd;
     bool be_opened = true;
 
     chr = qemu_chr_find(id);
@@ -4757,22 +4776,16 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
         goto out_error;
     }
 
-    for (i = backends; i; i = i->next) {
-        cd = i->data;
-
-        if (cd->kind == backend->type) {
-            chr = cd->create(id, backend, ret, &be_opened, &local_err);
-            if (local_err) {
-                error_propagate(errp, local_err);
-                goto out_error;
-            }
-            break;
-        }
+    cd = (int)backend->type >= 0 && backend->type < ARRAY_SIZE(backends) ?
+        backends[backend->type] : NULL;
+    if (cd == NULL) {
+        error_setg(errp, "chardev backend not available");
+        goto out_error;
     }
 
-    if (chr == NULL) {
-        assert(!i);
-        error_setg(errp, "chardev backend not available");
+    chr = cd->create(id, backend, ret, &be_opened, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
         goto out_error;
     }
 
@@ -4825,42 +4838,38 @@ static void register_types(void)
 {
     int i;
     static const CharDriver drivers[] = {
-        { "null", CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null },
-        { "socket", CHARDEV_BACKEND_KIND_SOCKET,
+        { { "null" }, CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null },
+        { { "socket" }, CHARDEV_BACKEND_KIND_SOCKET,
           qemu_chr_parse_socket, qmp_chardev_open_socket },
-        { "udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
+        { { "udp" }, CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
           qmp_chardev_open_udp },
-        { "ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
+        { { "ringbuf" }, CHARDEV_BACKEND_KIND_RINGBUF,
           qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
-        { "file", CHARDEV_BACKEND_KIND_FILE,
+        { { "file" }, CHARDEV_BACKEND_KIND_FILE,
           qemu_chr_parse_file_out, qmp_chardev_open_file },
-        { "stdio", CHARDEV_BACKEND_KIND_STDIO,
+        { { "stdio" }, CHARDEV_BACKEND_KIND_STDIO,
           qemu_chr_parse_stdio, qemu_chr_open_stdio },
 #if defined HAVE_CHARDEV_SERIAL
-        { "serial", CHARDEV_BACKEND_KIND_SERIAL,
-          qemu_chr_parse_serial, qmp_chardev_open_serial },
-        { "tty", CHARDEV_BACKEND_KIND_SERIAL,
+        { { "serial", "tty" }, CHARDEV_BACKEND_KIND_SERIAL,
           qemu_chr_parse_serial, qmp_chardev_open_serial },
 #endif
 #ifdef HAVE_CHARDEV_PARPORT
-        { "parallel", CHARDEV_BACKEND_KIND_PARALLEL,
-          qemu_chr_parse_parallel, qmp_chardev_open_parallel },
-        { "parport", CHARDEV_BACKEND_KIND_PARALLEL,
+        { { "parallel", "parport" }, CHARDEV_BACKEND_KIND_PARALLEL,
           qemu_chr_parse_parallel, qmp_chardev_open_parallel },
 #endif
 #ifdef HAVE_CHARDEV_PTY
-        { "pty", CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty },
+        { { "pty" }, CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty },
 #endif
 #ifdef _WIN32
-        { "console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
+        { { "console" }, CHARDEV_BACKEND_KIND_CONSOLE, NULL,
           qemu_chr_open_win_con },
 #endif
-        { "pipe", CHARDEV_BACKEND_KIND_PIPE,
+        { { "pipe" }, CHARDEV_BACKEND_KIND_PIPE,
           qemu_chr_parse_pipe, qemu_chr_open_pipe },
-        { "mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
+        { { "mux" }, CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
           qemu_chr_open_mux },
         /* Bug-compatibility: */
-        { "memory", CHARDEV_BACKEND_KIND_MEMORY,
+        { { "memory" }, CHARDEV_BACKEND_KIND_MEMORY,
           qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
     };
 
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 7ce8527..3172461 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -390,11 +390,11 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
 static void register_types(void)
 {
     static const CharDriver vmc_driver = {
-        "spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
+        { "spicevmc" }, CHARDEV_BACKEND_KIND_SPICEVMC,
         qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc
     };
     static const CharDriver port_driver = {
-        "spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
+        { "spiceport" }, CHARDEV_BACKEND_KIND_SPICEPORT,
         qemu_chr_parse_spice_port, qemu_chr_open_spice_port
     };
     register_char_driver(&vmc_driver);
diff --git a/ui/console.c b/ui/console.c
index a65223a..4e31cf2 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2183,7 +2183,7 @@ static const TypeInfo qemu_console_info = {
 static void register_types(void)
 {
     static const CharDriver vc_driver = {
-        "vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init
+        { "vc" }, CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init
     };
 
     type_register_static(&qemu_console_info);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 6a4b3ef..ee5618b 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -474,7 +474,7 @@ void qemu_chr_set_feature(CharDriverState *chr,
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
 typedef struct CharDriver {
-    const char *name;
+    const char *name[2]; /* name & opt alias */
     ChardevBackendKind kind;
     void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
     CharDriverState *(*create)(const char *id,
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 33/38] char: move callbacks in CharDriver
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (7 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 32/38] char: use a static array for backends Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 34/38] char: fold single-user functions in caller Marc-André Lureau
                     ` (2 subsequent siblings)
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

This makes the code more declarative, and avoids to duplicate the
information on all instances.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/baum.c       |  13 +-
 backends/msmouse.c    |  14 +-
 backends/testdev.c    |  10 +-
 gdbstub.c             |   7 +-
 hw/bt/hci-csr.c       |   8 +-
 qemu-char.c           | 421 +++++++++++++++++++++++++++++++-------------------
 spice-qemu-char.c     |  36 +++--
 ui/console.c          |  20 +--
 ui/gtk.c              |  11 +-
 include/sysemu/char.h |  46 +++---
 10 files changed, 363 insertions(+), 223 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 0a65c99..b2d4719 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -563,7 +563,8 @@ static void baum_free(struct CharDriverState *chr)
     g_free(baum);
 }
 
-static CharDriverState *chr_baum_init(const char *id,
+static CharDriverState *chr_baum_init(const CharDriver *driver,
+                                      const char *id,
                                       ChardevBackend *backend,
                                       ChardevReturn *ret,
                                       bool *be_opened,
@@ -580,7 +581,7 @@ static CharDriverState *chr_baum_init(const char *id,
 #endif
     int tty;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -588,9 +589,6 @@ static CharDriverState *chr_baum_init(const char *id,
     baum->chr = chr;
 
     chr->opaque = baum;
-    chr->chr_write = baum_write;
-    chr->chr_accept_input = baum_accept_input;
-    chr->chr_free = baum_free;
 
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
@@ -644,7 +642,10 @@ fail_handle:
 static void register_types(void)
 {
     static const CharDriver driver = {
-        { "braille" }, CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init
+        { "braille" }, CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init,
+        .chr_write = baum_write,
+        .chr_accept_input = baum_accept_input,
+        .chr_free = baum_free,
     };
 
     register_char_driver(&driver);
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 3367d67..f71aab0 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -148,7 +148,8 @@ static QemuInputHandler msmouse_handler = {
     .sync  = msmouse_input_sync,
 };
 
-static CharDriverState *qemu_chr_open_msmouse(const char *id,
+static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
@@ -158,13 +159,10 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
     MouseState *mouse;
     CharDriverState *chr;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
-    chr->chr_write = msmouse_chr_write;
-    chr->chr_free = msmouse_chr_free;
-    chr->chr_accept_input = msmouse_chr_accept_input;
     *be_opened = false;
 
     mouse = g_new0(MouseState, 1);
@@ -180,7 +178,11 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
 static void register_types(void)
 {
     static const CharDriver driver = {
-        { "msmouse" }, CHARDEV_BACKEND_KIND_MSMOUSE, NULL, qemu_chr_open_msmouse
+        { "msmouse" }, CHARDEV_BACKEND_KIND_MSMOUSE,
+        NULL, qemu_chr_open_msmouse,
+        .chr_write = msmouse_chr_write,
+        .chr_accept_input = msmouse_chr_accept_input,
+        .chr_free = msmouse_chr_free,
     };
     register_char_driver(&driver);
 }
diff --git a/backends/testdev.c b/backends/testdev.c
index d41352a..774cd47 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -109,7 +109,8 @@ static void testdev_free(struct CharDriverState *chr)
     g_free(testdev);
 }
 
-static CharDriverState *chr_testdev_init(const char *id,
+static CharDriverState *chr_testdev_init(const CharDriver *driver,
+                                         const char *id,
                                          ChardevBackend *backend,
                                          ChardevReturn *ret,
                                          bool *be_opened,
@@ -121,9 +122,8 @@ static CharDriverState *chr_testdev_init(const char *id,
     testdev = g_new0(TestdevCharState, 1);
     testdev->chr = chr = g_new0(CharDriverState, 1);
 
+    chr->driver = driver;
     chr->opaque = testdev;
-    chr->chr_write = testdev_write;
-    chr->chr_free = testdev_free;
 
     return chr;
 }
@@ -131,7 +131,9 @@ static CharDriverState *chr_testdev_init(const char *id,
 static void register_types(void)
 {
     static const CharDriver driver = {
-        { "testdev" }, CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init
+        { "testdev" }, CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init,
+        .chr_write = testdev_write,
+        .chr_free = testdev_free,
     };
     register_char_driver(&driver);
 }
diff --git a/gdbstub.c b/gdbstub.c
index b2e1b79..8e43836 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1731,6 +1731,10 @@ int gdbserver_start(const char *device)
     CharDriverState *chr = NULL;
     CharDriverState *mon_chr;
     ChardevCommon common = { 0 };
+    static const CharDriver driver = {
+        .kind = -1,
+        .chr_write = gdb_monitor_write
+    };
 
     if (!device)
         return -1;
@@ -1763,8 +1767,7 @@ int gdbserver_start(const char *device)
         qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
 
         /* Initialize a monitor terminal for gdb */
-        mon_chr = qemu_chr_alloc(&common, &error_abort);
-        mon_chr->chr_write = gdb_monitor_write;
+        mon_chr = qemu_chr_alloc(&driver, &common, &error_abort);
         monitor_init(mon_chr, 0);
     } else {
         if (qemu_chr_fe_get_driver(&s->chr)) {
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index fbb3109..9c3fb3c 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -462,12 +462,16 @@ qemu_irq *csrhci_pins_get(CharDriverState *chr)
 
 CharDriverState *uart_hci_init(void)
 {
+    static const CharDriver hci_driver = {
+        .kind = -1,
+        .chr_write = csrhci_write,
+        .chr_ioctl = csrhci_ioctl,
+    };
     struct csrhci_s *s = (struct csrhci_s *)
             g_malloc0(sizeof(struct csrhci_s));
 
     s->chr.opaque = s;
-    s->chr.chr_write = csrhci_write;
-    s->chr.chr_ioctl = csrhci_ioctl;
+    s->chr.driver = &hci_driver;
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
diff --git a/qemu-char.c b/qemu-char.c
index 7348cb0..a52e0ba 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -162,11 +162,15 @@ static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
 
 static void qemu_chr_free_common(CharDriverState *chr);
 
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp)
+CharDriverState *qemu_chr_alloc(const CharDriver *driver,
+                                ChardevCommon *backend, Error **errp)
 {
     CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
     qemu_mutex_init(&chr->chr_write_lock);
 
+    assert(driver);
+    assert(driver->chr_write);
+
     if (backend->has_logfile) {
         int flags = O_WRONLY | O_CREAT;
         if (backend->has_logappend &&
@@ -186,6 +190,7 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp)
     } else {
         chr->logfd = -1;
     }
+    chr->driver = driver;
 
     return chr;
 }
@@ -252,7 +257,7 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int
     qemu_mutex_lock(&s->chr_write_lock);
     while (*offset < len) {
     retry:
-        res = s->chr_write(s, buf + *offset, len - *offset);
+        res = s->driver->chr_write(s, buf + *offset, len - *offset);
         if (res < 0 && errno == EAGAIN) {
             g_usleep(100);
             goto retry;
@@ -290,7 +295,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
     }
 
     qemu_mutex_lock(&s->chr_write_lock);
-    ret = s->chr_write(s, buf, len);
+    ret = s->driver->chr_write(s, buf, len);
 
     if (ret > 0) {
         qemu_chr_fe_write_log(s, buf, ret);
@@ -346,7 +351,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
     int offset = 0, counter = 10;
     int res;
 
-    if (!s || !s->chr_sync_read) {
+    if (!s || !s->driver->chr_sync_read) {
         return 0;
     }
 
@@ -356,7 +361,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
 
     while (offset < len) {
     retry:
-        res = s->chr_sync_read(s, buf + offset, len - offset);
+        res = s->driver->chr_sync_read(s, buf + offset, len - offset);
         if (res == -1 && errno == EAGAIN) {
             g_usleep(100);
             goto retry;
@@ -391,10 +396,10 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
     CharDriverState *s = be->chr;
     int res;
 
-    if (!s || !s->chr_ioctl || s->replay) {
+    if (!s || !s->driver->chr_ioctl || s->replay) {
         res = -ENOTSUP;
     } else {
-        res = s->chr_ioctl(s, cmd, arg);
+        res = s->driver->chr_ioctl(s, cmd, arg);
     }
 
     return res;
@@ -453,7 +458,7 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
         return -1;
     }
 
-    return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
+    return s->driver->get_msgfds ? s->driver->get_msgfds(s, fds, len) : -1;
 }
 
 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
@@ -464,12 +469,12 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
         return -1;
     }
 
-    return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1;
+    return s->driver->set_msgfds ? s->driver->set_msgfds(s, fds, num) : -1;
 }
 
 int qemu_chr_add_client(CharDriverState *s, int fd)
 {
-    return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
+    return s->driver->chr_add_client ? s->driver->chr_add_client(s, fd) : -1;
 }
 
 void qemu_chr_fe_accept_input(CharBackend *be)
@@ -480,8 +485,9 @@ void qemu_chr_fe_accept_input(CharBackend *be)
         return;
     }
 
-    if (s->chr_accept_input)
-        s->chr_accept_input(s);
+    if (s->driver->chr_accept_input) {
+        s->driver->chr_accept_input(s);
+    }
     qemu_notify_event();
 }
 
@@ -506,7 +512,8 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return len;
 }
 
-static CharDriverState *qemu_chr_open_null(const char *id,
+static CharDriverState *qemu_chr_open_null(const CharDriver *driver,
+                                           const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
                                            bool *be_opened,
@@ -515,15 +522,19 @@ static CharDriverState *qemu_chr_open_null(const char *id,
     CharDriverState *chr;
     ChardevCommon *common = backend->u.null.data;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
-    chr->chr_write = null_chr_write;
     *be_opened = false;
     return chr;
 }
 
+static const CharDriver null_driver = {
+    { "null" }, CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null,
+    .chr_write = null_chr_write
+};
+
 /* MUX driver for serial I/O splitting */
 #define MAX_MUX 4
 #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
@@ -791,7 +802,11 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
     MuxDriver *d = s->opaque;
     CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
 
-    return chr->chr_add_watch(chr, cond);
+    if (!chr->driver->chr_add_watch) {
+        return NULL;
+    }
+
+    return chr->driver->chr_add_watch(chr, cond);
 }
 
 static void mux_chr_free(struct CharDriverState *chr)
@@ -835,7 +850,8 @@ static void mux_set_focus(MuxDriver *d, int focus)
     mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 }
 
-static CharDriverState *qemu_chr_open_mux(const char *id,
+static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
+                                          const char *id,
                                           ChardevBackend *backend,
                                           ChardevReturn *ret,
                                           bool *be_opened,
@@ -852,7 +868,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
         return NULL;
     }
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -860,14 +876,6 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
 
     chr->opaque = d;
     d->focus = -1;
-    chr->chr_free = mux_chr_free;
-    chr->chr_write = mux_chr_write;
-    chr->chr_accept_input = mux_chr_accept_input;
-    /* Frontend guest-open / -close notification is not support with muxes */
-    chr->chr_set_fe_open = NULL;
-    if (drv->chr_add_watch) {
-        chr->chr_add_watch = mux_chr_add_watch;
-    }
     /* only default to opened state if we've realized the initial
      * set of muxes
      */
@@ -963,8 +971,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
     b->chr_read = fd_read;
     b->chr_event = fd_event;
     b->opaque = opaque;
-    if (s->chr_update_read_handler) {
-        s->chr_update_read_handler(s, context);
+    if (s->driver->chr_update_read_handler) {
+        s->driver->chr_update_read_handler(s, context);
     }
 
     if (set_open) {
@@ -1255,13 +1263,14 @@ static void fd_chr_free(struct CharDriverState *chr)
 }
 
 /* open a character device to a unix fd */
-static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
+static CharDriverState *qemu_chr_open_fd(const CharDriver *driver,
+                                         int fd_in, int fd_out,
                                          ChardevCommon *backend, Error **errp)
 {
     CharDriverState *chr;
     FDCharDriver *s;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
@@ -1271,15 +1280,12 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
     qemu_set_nonblock(fd_out);
     s->chr = chr;
     chr->opaque = s;
-    chr->chr_add_watch = fd_chr_add_watch;
-    chr->chr_write = fd_chr_write;
-    chr->chr_update_read_handler = fd_chr_update_read_handler;
-    chr->chr_free = fd_chr_free;
 
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_pipe(const char *id,
+static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
+                                           const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
                                            bool *be_opened,
@@ -1310,7 +1316,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
             return NULL;
         }
     }
-    return qemu_chr_open_fd(fd_in, fd_out, common, errp);
+    return qemu_chr_open_fd(driver, fd_in, fd_out, common, errp);
 }
 
 /* init terminal so that we can grab keys */
@@ -1362,7 +1368,8 @@ static void qemu_chr_free_stdio(struct CharDriverState *chr)
     fd_chr_free(chr);
 }
 
-static CharDriverState *qemu_chr_open_stdio(const char *id,
+static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
+                                            const char *id,
                                             ChardevBackend *backend,
                                             ChardevReturn *ret,
                                             bool *be_opened,
@@ -1393,12 +1400,10 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     act.sa_handler = term_stdio_handler;
     sigaction(SIGCONT, &act, NULL);
 
-    chr = qemu_chr_open_fd(0, 1, common, errp);
+    chr = qemu_chr_open_fd(driver, 0, 1, common, errp);
     if (!chr) {
         return NULL;
     }
-    chr->chr_free = qemu_chr_free_stdio;
-    chr->chr_set_echo = qemu_chr_set_echo_stdio;
     if (opts->has_signal) {
         stdio_allow_signal = opts->signal;
     }
@@ -1610,7 +1615,8 @@ static void pty_chr_free(struct CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_pty(const char *id,
+static CharDriverState *qemu_chr_open_pty(const CharDriver *driver,
+                                          const char *id,
                                           ChardevBackend *backend,
                                           ChardevReturn *ret,
                                           bool *be_opened,
@@ -1631,7 +1637,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     close(slave_fd);
     qemu_set_nonblock(master_fd);
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         close(master_fd);
         return NULL;
@@ -1646,10 +1652,6 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
 
     s = g_new0(PtyCharDriver, 1);
     chr->opaque = s;
-    chr->chr_write = pty_chr_write;
-    chr->chr_update_read_handler = pty_chr_update_read_handler;
-    chr->chr_free = pty_chr_free;
-    chr->chr_add_watch = pty_chr_add_watch;
     *be_opened = false;
 
     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
@@ -1658,6 +1660,14 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     return chr;
 }
 
+static const CharDriver pty_driver = {
+    { "pty" }, CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty,
+    .chr_write = pty_chr_write,
+    .chr_update_read_handler = pty_chr_update_read_handler,
+    .chr_add_watch = pty_chr_add_watch,
+    .chr_free = pty_chr_free,
+};
+
 static void tty_serial_init(int fd, int speed,
                             int parity, int data_bits, int stop_bits)
 {
@@ -1848,7 +1858,8 @@ static void qemu_chr_free_tty(CharDriverState *chr)
     fd_chr_free(chr);
 }
 
-static CharDriverState *qemu_chr_open_tty_fd(int fd,
+static CharDriverState *qemu_chr_open_tty_fd(const CharDriver *driver,
+                                             int fd,
                                              ChardevCommon *backend,
                                              bool *be_opened,
                                              Error **errp)
@@ -1856,12 +1867,10 @@ static CharDriverState *qemu_chr_open_tty_fd(int fd,
     CharDriverState *chr;
 
     tty_serial_init(fd, 115200, 'N', 8, 1);
-    chr = qemu_chr_open_fd(fd, fd, backend, errp);
+    chr = qemu_chr_open_fd(driver, fd, fd, backend, errp);
     if (!chr) {
         return NULL;
     }
-    chr->chr_ioctl = tty_serial_ioctl;
-    chr->chr_free = qemu_chr_free_tty;
     return chr;
 }
 #endif /* __linux__ || __sun__ */
@@ -1979,7 +1988,8 @@ static void pp_free(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_pp_fd(int fd,
+static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
+                                            int fd,
                                             ChardevCommon *backend,
                                             bool *be_opened,
                                             Error **errp)
@@ -1993,16 +2003,13 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd,
         return NULL;
     }
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
 
     drv = g_new0(ParallelCharDriver, 1);
     chr->opaque = drv;
-    chr->chr_write = null_chr_write;
-    chr->chr_ioctl = pp_ioctl;
-    chr->chr_free = pp_free;
 
     drv->fd = fd;
     drv->mode = IEEE1284_MODE_COMPAT;
@@ -2052,20 +2059,19 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_pp_fd(int fd,
+static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
+                                            int fd,
                                             ChardevCommon *backend,
                                             bool *be_opened,
                                             Error **errp)
 {
     CharDriverState *chr;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
     chr->opaque = (void *)(intptr_t)fd;
-    chr->chr_write = null_chr_write;
-    chr->chr_ioctl = pp_ioctl;
     *be_opened = false;
     return chr;
 }
@@ -2287,21 +2293,20 @@ static int win_chr_poll(void *opaque)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_win_path(const char *filename,
+static CharDriverState *qemu_chr_open_win_path(const CharDriver *driver,
+                                               const char *filename,
                                                ChardevCommon *backend,
                                                Error **errp)
 {
     CharDriverState *chr;
     WinCharState *s;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
     s = g_new0(WinCharState, 1);
     chr->opaque = s;
-    chr->chr_write = win_chr_write;
-    chr->chr_free = win_chr_free;
 
     if (win_chr_init(chr, filename, errp) < 0) {
         g_free(s);
@@ -2392,7 +2397,8 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
 }
 
 
-static CharDriverState *qemu_chr_open_pipe(const char *id,
+static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
+                                           const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
                                            bool *be_opened,
@@ -2404,14 +2410,12 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
     WinCharState *s;
     ChardevCommon *common = qapi_ChardevHostdev_base(opts);
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
     s = g_new0(WinCharState, 1);
     chr->opaque = s;
-    chr->chr_write = win_chr_write;
-    chr->chr_free = win_chr_free;
 
     if (win_chr_pipe_init(chr, filename, errp) < 0) {
         g_free(s);
@@ -2421,35 +2425,42 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out,
+static CharDriverState *qemu_chr_open_win_file(const CharDriver *driver,
+                                               HANDLE fd_out,
                                                ChardevCommon *backend,
                                                Error **errp)
 {
     CharDriverState *chr;
     WinCharState *s;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
     s = g_new0(WinCharState, 1);
     s->hcom = fd_out;
     chr->opaque = s;
-    chr->chr_write = win_chr_write;
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_win_con(const char *id,
+static CharDriverState *qemu_chr_open_win_con(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
                                               Error **errp)
 {
     ChardevCommon *common = backend->u.console.data;
-    return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE),
+    return qemu_chr_open_win_file(driver,
+                                  GetStdHandle(STD_OUTPUT_HANDLE),
                                   common, errp);
 }
 
+static const CharDriver console_driver = {
+    { "console" }, CHARDEV_BACKEND_KIND_CONSOLE, NULL, qemu_chr_open_win_con,
+    .chr_write = win_chr_write,
+};
+
 static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -2585,7 +2596,8 @@ static void win_stdio_free(CharDriverState *chr)
     g_free(chr->opaque);
 }
 
-static CharDriverState *qemu_chr_open_stdio(const char *id,
+static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
+                                            const char *id,
                                             ChardevBackend *backend,
                                             ChardevReturn *ret,
                                             bool *be_opened,
@@ -2597,7 +2609,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     int                is_console = 0;
     ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data);
 
-    chr   = qemu_chr_alloc(common, errp);
+    chr   = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -2612,8 +2624,6 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
 
     chr->opaque    = stdio;
-    chr->chr_write = win_stdio_write;
-    chr->chr_free = win_stdio_free;
 
     if (is_console) {
         if (qemu_add_wait_object(stdio->hStdIn,
@@ -2655,7 +2665,6 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
 
     SetConsoleMode(stdio->hStdIn, dwMode);
 
-    chr->chr_set_echo = qemu_chr_set_echo_win_stdio;
     qemu_chr_set_echo_win_stdio(chr, false);
 
     return chr;
@@ -2762,7 +2771,8 @@ static void udp_chr_free(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
+static CharDriverState *qemu_chr_open_udp(const CharDriver *driver,
+                                          QIOChannelSocket *sioc,
                                           ChardevCommon *backend,
                                           bool *be_opened,
                                           Error **errp)
@@ -2770,7 +2780,7 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
     CharDriverState *chr = NULL;
     NetCharDriver *s = NULL;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
@@ -2780,9 +2790,6 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
     s->bufcnt = 0;
     s->bufptr = 0;
     chr->opaque = s;
-    chr->chr_write = udp_chr_write;
-    chr->chr_update_read_handler = udp_chr_update_read_handler;
-    chr->chr_free = udp_chr_free;
     /* be isn't opened until we get a connection */
     *be_opened = false;
     return chr;
@@ -3392,8 +3399,8 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
 
 static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
 {
-    if (chr->chr_wait_connected) {
-        return chr->chr_wait_connected(chr, errp);
+    if (chr->driver->chr_wait_connected) {
+        return chr->driver->chr_wait_connected(chr, errp);
     }
 
     return 0;
@@ -3513,7 +3520,8 @@ static void ringbuf_chr_free(struct CharDriverState *chr)
     chr->opaque = NULL;
 }
 
-static CharDriverState *qemu_chr_open_ringbuf(const char *id,
+static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
@@ -3524,7 +3532,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
     CharDriverState *chr;
     RingBufCharDriver *d;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -3543,8 +3551,6 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
     d->cbuf = g_malloc0(d->size);
 
     chr->opaque = d;
-    chr->chr_write = ringbuf_chr_write;
-    chr->chr_free = ringbuf_chr_free;
 
     return chr;
 
@@ -3556,7 +3562,7 @@ fail:
 
 bool chr_is_ringbuf(const CharDriverState *chr)
 {
-    return chr->chr_write == ringbuf_chr_write;
+    return chr->driver->chr_write == ringbuf_chr_write;
 }
 
 void qmp_ringbuf_write(const char *device, const char *data,
@@ -3835,6 +3841,22 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
     stdio->signal = qemu_opt_get_bool(opts, "signal", true);
 }
 
+static const CharDriver stdio_driver = {
+    { "stdio" }, CHARDEV_BACKEND_KIND_STDIO,
+    qemu_chr_parse_stdio, qemu_chr_open_stdio,
+#ifdef _WIN32
+    .chr_write = win_stdio_write,
+    .chr_set_echo = qemu_chr_set_echo_win_stdio,
+    .chr_free = win_stdio_free,
+#else
+    .chr_add_watch = fd_chr_add_watch,
+    .chr_write = fd_chr_write,
+    .chr_update_read_handler = fd_chr_update_read_handler,
+    .chr_set_echo = qemu_chr_set_echo_stdio,
+    .chr_free = qemu_chr_free_stdio,
+#endif
+};
+
 #ifdef HAVE_CHARDEV_SERIAL
 static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
                                   Error **errp)
@@ -3884,6 +3906,20 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
     dev->device = g_strdup(device);
 }
 
+static const CharDriver pipe_driver = {
+    { "pipe" }, CHARDEV_BACKEND_KIND_PIPE,
+    qemu_chr_parse_pipe, qemu_chr_open_pipe,
+#ifdef _WIN32
+    .chr_write = win_chr_write,
+    .chr_free = win_chr_free,
+#else
+    .chr_add_watch = fd_chr_add_watch,
+    .chr_write = fd_chr_write,
+    .chr_update_read_handler = fd_chr_update_read_handler,
+    .chr_free = fd_chr_free,
+#endif
+};
+
 static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
                                    Error **errp)
 {
@@ -3900,6 +3936,21 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
     }
 }
 
+static const CharDriver ringbuf_driver = {
+    { "ringbuf" }, CHARDEV_BACKEND_KIND_RINGBUF,
+    qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf,
+    .chr_write = ringbuf_chr_write,
+    .chr_free = ringbuf_chr_free,
+};
+
+/* Bug-compatibility: */
+static const CharDriver memory_driver = {
+    { "memory" }, CHARDEV_BACKEND_KIND_MEMORY,
+    qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf,
+    .chr_write = ringbuf_chr_write,
+    .chr_free = ringbuf_chr_free,
+};
+
 static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
                                Error **errp)
 {
@@ -3915,6 +3966,14 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
     mux->chardev = g_strdup(chardev);
 }
 
+static const CharDriver mux_driver = {
+    { "mux" }, CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux, qemu_chr_open_mux,
+    .chr_free = mux_chr_free,
+    .chr_write = mux_chr_write,
+    .chr_accept_input = mux_chr_accept_input,
+    .chr_add_watch = mux_chr_add_watch,
+};
+
 static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
                                   Error **errp)
 {
@@ -4187,7 +4246,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename)
     chr = qemu_chr_new_noreplay(label, filename);
     if (chr) {
         chr->replay = replay_mode != REPLAY_MODE_NONE;
-        if (chr->replay && chr->chr_ioctl) {
+        if (chr->replay && chr->driver->chr_ioctl) {
             fprintf(stderr,
                     "Replay: ioctl is not supported for serial devices yet\n");
         }
@@ -4200,8 +4259,8 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr && chr->chr_set_echo) {
-        chr->chr_set_echo(chr, echo);
+    if (chr && chr->driver->chr_set_echo) {
+        chr->driver->chr_set_echo(chr, echo);
     }
 }
 
@@ -4217,8 +4276,8 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
         return;
     }
     be->fe_open = fe_open;
-    if (chr->chr_set_fe_open) {
-        chr->chr_set_fe_open(chr, fe_open);
+    if (chr->driver->chr_set_fe_open) {
+        chr->driver->chr_set_fe_open(chr, fe_open);
     }
 }
 
@@ -4229,11 +4288,11 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
     GSource *src;
     guint tag;
 
-    if (!s || s->chr_add_watch == NULL) {
+    if (!s || s->driver->chr_add_watch == NULL) {
         return 0;
     }
 
-    src = s->chr_add_watch(s, cond);
+    src = s->driver->chr_add_watch(s, cond);
     if (!src) {
         return 0;
     }
@@ -4249,8 +4308,8 @@ void qemu_chr_fe_disconnect(CharBackend *be)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr && chr->chr_disconnect) {
-        chr->chr_disconnect(chr);
+    if (chr && chr->driver->chr_disconnect) {
+        chr->driver->chr_disconnect(chr);
     }
 }
 
@@ -4270,8 +4329,8 @@ static void qemu_chr_free_common(CharDriverState *chr)
 
 void qemu_chr_free(CharDriverState *chr)
 {
-    if (chr->chr_free) {
-        chr->chr_free(chr);
+    if (chr->driver->chr_free) {
+        chr->driver->chr_free(chr);
     }
     qemu_chr_free_common(chr);
 }
@@ -4440,7 +4499,8 @@ QemuOptsList qemu_chardev_opts = {
 
 #ifdef _WIN32
 
-static CharDriverState *qmp_chardev_open_file(const char *id,
+static CharDriverState *qmp_chardev_open_file(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
@@ -4473,10 +4533,11 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
         error_setg(errp, "open %s failed", file->out);
         return NULL;
     }
-    return qemu_chr_open_win_file(out, common, errp);
+    return qemu_chr_open_win_file(driver, out, common, errp);
 }
 
-static CharDriverState *qmp_chardev_open_serial(const char *id,
+static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
+                                                const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
                                                 bool *be_opened,
@@ -4484,7 +4545,8 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
 {
     ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
-    return qemu_chr_open_win_path(serial->device, common, errp);
+
+    return qemu_chr_open_win_path(driver, serial->device, common, errp);
 }
 
 #else /* WIN32 */
@@ -4501,7 +4563,8 @@ static int qmp_chardev_open_file_source(char *src, int flags,
     return fd;
 }
 
-static CharDriverState *qmp_chardev_open_file(const char *id,
+static CharDriverState *qmp_chardev_open_file(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
@@ -4532,11 +4595,12 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
         }
     }
 
-    return qemu_chr_open_fd(in, out, common, errp);
+    return qemu_chr_open_fd(driver, in, out, common, errp);
 }
 
 #ifdef HAVE_CHARDEV_SERIAL
-static CharDriverState *qmp_chardev_open_serial(const char *id,
+static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
+                                                const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
                                                 bool *be_opened,
@@ -4551,12 +4615,14 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
         return NULL;
     }
     qemu_set_nonblock(fd);
-    return qemu_chr_open_tty_fd(fd, common, be_opened, errp);
+
+    return qemu_chr_open_tty_fd(driver, fd, common, be_opened, errp);
 }
 #endif
 
 #ifdef HAVE_CHARDEV_PARPORT
-static CharDriverState *qmp_chardev_open_parallel(const char *id,
+static CharDriverState *qmp_chardev_open_parallel(const CharDriver *driver,
+                                                  const char *id,
                                                   ChardevBackend *backend,
                                                   ChardevReturn *ret,
                                                   bool *be_opened,
@@ -4570,12 +4636,55 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id,
     if (fd < 0) {
         return NULL;
     }
-    return qemu_chr_open_pp_fd(fd, common, be_opened, errp);
+    return qemu_chr_open_pp_fd(driver, fd, common, be_opened, errp);
 }
+
+static const CharDriver parallel_driver = {
+    { "parallel", "parport" }, CHARDEV_BACKEND_KIND_PARALLEL,
+    qemu_chr_parse_parallel, qmp_chardev_open_parallel,
+#if defined(__linux__)
+    .chr_write = null_chr_write,
+    .chr_ioctl = pp_ioctl,
+    .chr_free = pp_free,
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+    .chr_write = null_chr_write;
+    .chr_ioctl = pp_ioctl;
+#endif
+};
 #endif
 
 #endif /* WIN32 */
 
+static const CharDriver file_driver = {
+    { "file" }, CHARDEV_BACKEND_KIND_FILE,
+    qemu_chr_parse_file_out, qmp_chardev_open_file,
+#ifdef _WIN32
+    .chr_write = win_chr_write,
+#else
+    .chr_add_watch = fd_chr_add_watch,
+    .chr_write = fd_chr_write,
+    .chr_update_read_handler = fd_chr_update_read_handler,
+    .chr_free = fd_chr_free,
+#endif
+};
+
+#ifdef HAVE_CHARDEV_SERIAL
+static const CharDriver serial_driver = {
+    { "serial", "tty" }, CHARDEV_BACKEND_KIND_SERIAL,
+    qemu_chr_parse_serial, qmp_chardev_open_serial,
+#ifdef _WIN32
+    .chr_write = win_chr_write,
+    .chr_free = win_chr_free,
+#else
+    .chr_add_watch = fd_chr_add_watch,
+    .chr_write = fd_chr_write,
+    .chr_update_read_handler = fd_chr_update_read_handler,
+    .chr_ioctl = tty_serial_ioctl,
+    .chr_free = qemu_chr_free_tty,
+#endif
+};
+#endif
+
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
     CharDriverState *chr = opaque;
@@ -4596,7 +4705,8 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
     return false;
 }
 
-static CharDriverState *qmp_chardev_open_socket(const char *id,
+static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
+                                                const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
                                                 bool *be_opened,
@@ -4614,7 +4724,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     ChardevCommon *common = qapi_ChardevSocket_base(sock);
     QIOChannelSocket *sioc = NULL;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -4665,16 +4775,6 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     }
 
     chr->opaque = s;
-    chr->chr_wait_connected = tcp_chr_wait_connected;
-    chr->chr_write = tcp_chr_write;
-    chr->chr_sync_read = tcp_chr_sync_read;
-    chr->chr_free = tcp_chr_free;
-    chr->chr_disconnect = tcp_chr_disconnect;
-    chr->get_msgfds = tcp_get_msgfds;
-    chr->set_msgfds = tcp_set_msgfds;
-    chr->chr_add_client = tcp_chr_add_client;
-    chr->chr_add_watch = tcp_chr_add_watch;
-    chr->chr_update_read_handler = tcp_chr_update_read_handler;
     /* be isn't opened until we get a connection */
     *be_opened = false;
 
@@ -4729,7 +4829,23 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     return NULL;
 }
 
-static CharDriverState *qmp_chardev_open_udp(const char *id,
+static const CharDriver socket_driver = {
+    { "socket" }, CHARDEV_BACKEND_KIND_SOCKET,
+    qemu_chr_parse_socket, qmp_chardev_open_socket,
+    .chr_wait_connected = tcp_chr_wait_connected,
+    .chr_write = tcp_chr_write,
+    .chr_sync_read = tcp_chr_sync_read,
+    .chr_disconnect = tcp_chr_disconnect,
+    .get_msgfds = tcp_get_msgfds,
+    .set_msgfds = tcp_set_msgfds,
+    .chr_add_client = tcp_chr_add_client,
+    .chr_add_watch = tcp_chr_add_watch,
+    .chr_update_read_handler = tcp_chr_update_read_handler,
+    .chr_free = tcp_chr_free,
+};
+
+static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
+                                             const char *id,
                                              ChardevBackend *backend,
                                              ChardevReturn *ret,
                                              bool *be_opened,
@@ -4745,9 +4861,17 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
         object_unref(OBJECT(sioc));
         return NULL;
     }
-    return qemu_chr_open_udp(sioc, common, be_opened, errp);
+
+    return qemu_chr_open_udp(driver, sioc, common, be_opened, errp);
 }
 
+static const CharDriver udp_driver = {
+    { "udp" }, CHARDEV_BACKEND_KIND_UDP,
+    qemu_chr_parse_udp, qmp_chardev_open_udp,
+    .chr_write = udp_chr_write,
+    .chr_update_read_handler = udp_chr_update_read_handler,
+    .chr_free = udp_chr_free,
+};
 
 bool qemu_chr_has_feature(CharDriverState *chr,
                           CharDriverFeature feature)
@@ -4783,7 +4907,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
         goto out_error;
     }
 
-    chr = cd->create(id, backend, ret, &be_opened, &local_err);
+    chr = cd->create(cd, id, backend, ret, &be_opened, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         goto out_error;
@@ -4836,46 +4960,33 @@ void qemu_chr_cleanup(void)
 
 static void register_types(void)
 {
-    int i;
-    static const CharDriver drivers[] = {
-        { { "null" }, CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null },
-        { { "socket" }, CHARDEV_BACKEND_KIND_SOCKET,
-          qemu_chr_parse_socket, qmp_chardev_open_socket },
-        { { "udp" }, CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
-          qmp_chardev_open_udp },
-        { { "ringbuf" }, CHARDEV_BACKEND_KIND_RINGBUF,
-          qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
-        { { "file" }, CHARDEV_BACKEND_KIND_FILE,
-          qemu_chr_parse_file_out, qmp_chardev_open_file },
-        { { "stdio" }, CHARDEV_BACKEND_KIND_STDIO,
-          qemu_chr_parse_stdio, qemu_chr_open_stdio },
-#if defined HAVE_CHARDEV_SERIAL
-        { { "serial", "tty" }, CHARDEV_BACKEND_KIND_SERIAL,
-          qemu_chr_parse_serial, qmp_chardev_open_serial },
+    static const CharDriver *drivers[] = {
+        &null_driver,
+        &socket_driver,
+        &udp_driver,
+        &ringbuf_driver,
+        &file_driver,
+        &stdio_driver,
+#ifdef HAVE_CHARDEV_SERIAL
+        &serial_driver,
 #endif
 #ifdef HAVE_CHARDEV_PARPORT
-        { { "parallel", "parport" }, CHARDEV_BACKEND_KIND_PARALLEL,
-          qemu_chr_parse_parallel, qmp_chardev_open_parallel },
+        &parallel_driver,
 #endif
 #ifdef HAVE_CHARDEV_PTY
-        { { "pty" }, CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty },
+        &pty_driver,
 #endif
 #ifdef _WIN32
-        { { "console" }, CHARDEV_BACKEND_KIND_CONSOLE, NULL,
-          qemu_chr_open_win_con },
+        &console_driver,
 #endif
-        { { "pipe" }, CHARDEV_BACKEND_KIND_PIPE,
-          qemu_chr_parse_pipe, qemu_chr_open_pipe },
-        { { "mux" }, CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
-          qemu_chr_open_mux },
-        /* Bug-compatibility: */
-        { { "memory" }, CHARDEV_BACKEND_KIND_MEMORY,
-          qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
+        &pipe_driver,
+        &mux_driver,
+        &memory_driver
     };
-
+    int i;
 
     for (i = 0; i < ARRAY_SIZE(drivers); i++) {
-        register_char_driver(&drivers[i]);
+        register_char_driver(drivers[i]);
     }
 
     /* this must be done after machine init, since we register FEs with muxes
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 3172461..14afd4a 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -260,16 +260,15 @@ static void spice_chr_accept_input(struct CharDriverState *chr)
     spice_server_char_device_wakeup(&s->sin);
 }
 
-static CharDriverState *chr_open(const char *subtype,
-                                 void (*set_fe_open)(struct CharDriverState *,
-                                                     int),
+static CharDriverState *chr_open(const CharDriver *driver,
+                                 const char *subtype,
                                  ChardevCommon *backend,
                                  Error **errp)
 {
     CharDriverState *chr;
     SpiceCharDriver *s;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
@@ -278,18 +277,14 @@ static CharDriverState *chr_open(const char *subtype,
     s->active = false;
     s->sin.subtype = g_strdup(subtype);
     chr->opaque = s;
-    chr->chr_write = spice_chr_write;
-    chr->chr_add_watch = spice_chr_add_watch;
-    chr->chr_free = spice_chr_free;
-    chr->chr_set_fe_open = set_fe_open;
-    chr->chr_accept_input = spice_chr_accept_input;
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
 
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
+static CharDriverState *qemu_chr_open_spice_vmc(const CharDriver *driver,
+                                                const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
                                                 bool *be_opened,
@@ -312,11 +307,12 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
     }
 
     *be_opened = false;
-    return chr_open(type, spice_vmc_set_fe_open, common, errp);
+    return chr_open(driver, type, common, errp);
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
-static CharDriverState *qemu_chr_open_spice_port(const char *id,
+static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
+                                                 const char *id,
                                                  ChardevBackend *backend,
                                                  ChardevReturn *ret,
                                                  bool *be_opened,
@@ -333,7 +329,7 @@ static CharDriverState *qemu_chr_open_spice_port(const char *id,
         return NULL;
     }
 
-    chr = chr_open("port", spice_port_set_fe_open, common, errp);
+    chr = chr_open(driver, "port", common, errp);
     if (!chr) {
         return NULL;
     }
@@ -391,11 +387,21 @@ static void register_types(void)
 {
     static const CharDriver vmc_driver = {
         { "spicevmc" }, CHARDEV_BACKEND_KIND_SPICEVMC,
-        qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc
+        qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc,
+        .chr_write = spice_chr_write,
+        .chr_add_watch = spice_chr_add_watch,
+        .chr_set_fe_open = spice_vmc_set_fe_open,
+        .chr_accept_input = spice_chr_accept_input,
+        .chr_free = spice_chr_free,
     };
     static const CharDriver port_driver = {
         { "spiceport" }, CHARDEV_BACKEND_KIND_SPICEPORT,
-        qemu_chr_parse_spice_port, qemu_chr_open_spice_port
+        qemu_chr_parse_spice_port, qemu_chr_open_spice_port,
+        .chr_write = spice_chr_write,
+        .chr_add_watch = spice_chr_add_watch,
+        .chr_set_fe_open = spice_port_set_fe_open,
+        .chr_accept_input = spice_chr_accept_input,
+        .chr_free = spice_chr_free,
     };
     register_char_driver(&vmc_driver);
     register_char_driver(&port_driver);
diff --git a/ui/console.c b/ui/console.c
index 4e31cf2..2944d22 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1989,8 +1989,6 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
 
     s = chr->opaque;
 
-    chr->chr_write = console_puts;
-
     s->out_fifo.buf = s->out_fifo_buf;
     s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
     s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
@@ -2037,6 +2035,8 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
     qemu_chr_be_generic_open(chr);
 }
 
+static const CharDriver vc_driver;
+
 static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 {
     ChardevCommon *common = qapi_ChardevVC_base(vc);
@@ -2045,7 +2045,7 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
     unsigned width = 0;
     unsigned height = 0;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(&vc_driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -2078,7 +2078,6 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 
     s->chr = chr;
     chr->opaque = s;
-    chr->chr_set_echo = text_console_set_echo;
 
     if (display_state) {
         text_console_do_init(chr, display_state);
@@ -2088,7 +2087,8 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 
 static VcHandler *vc_handler = text_console_init;
 
-static CharDriverState *vc_init(const char *id, ChardevBackend *backend,
+static CharDriverState *vc_init(const CharDriver *driver,
+                                const char *id, ChardevBackend *backend,
                                 ChardevReturn *ret, bool *be_opened,
                                 Error **errp)
 {
@@ -2180,12 +2180,14 @@ static const TypeInfo qemu_console_info = {
     .class_size = sizeof(QemuConsoleClass),
 };
 
+static const CharDriver vc_driver = {
+    { "vc" }, CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init,
+    .chr_write = console_puts,
+    .chr_set_echo = text_console_set_echo,
+};
+
 static void register_types(void)
 {
-    static const CharDriver vc_driver = {
-        { "vc" }, CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init
-    };
-
     type_register_static(&qemu_console_info);
     register_char_driver(&vc_driver);
 }
diff --git a/ui/gtk.c b/ui/gtk.c
index 25e6d99..1566faa 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1671,17 +1671,20 @@ static CharDriverState *vcs[MAX_VCS];
 
 static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
 {
+    static const CharDriver gd_vc_driver = {
+        { "vc" }, CHARDEV_BACKEND_KIND_VC,
+        .chr_write = gd_vc_chr_write,
+        .chr_set_echo = gd_vc_chr_set_echo,
+    };
+
     ChardevCommon *common = qapi_ChardevVC_base(vc);
     CharDriverState *chr;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(&gd_vc_driver, common, errp);
     if (!chr) {
         return NULL;
     }
 
-    chr->chr_write = gd_vc_chr_write;
-    chr->chr_set_echo = gd_vc_chr_set_echo;
-
     /* Temporary, until gd_vc_vte_init runs.  */
     chr->opaque = g_new0(VirtualConsole, 1);
 
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index ee5618b..fb2e6ff 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -85,24 +85,11 @@ typedef struct CharBackend {
     int fe_open;
 } CharBackend;
 
+typedef struct CharDriver CharDriver;
+
 struct CharDriverState {
+    const CharDriver *driver;
     QemuMutex chr_write_lock;
-    int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
-    int (*chr_sync_read)(struct CharDriverState *s,
-                         const uint8_t *buf, int len);
-    GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
-    void (*chr_update_read_handler)(struct CharDriverState *s,
-                                    GMainContext *context);
-    int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
-    int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
-    int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
-    int (*chr_add_client)(struct CharDriverState *chr, int fd);
-    int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
-    void (*chr_free)(struct CharDriverState *chr);
-    void (*chr_disconnect)(struct CharDriverState *chr);
-    void (*chr_accept_input)(struct CharDriverState *chr);
-    void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
-    void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
     CharBackend *be;
     void *opaque;
     char *label;
@@ -125,7 +112,8 @@ struct CharDriverState {
  *
  * Returns: a newly allocated CharDriverState, or NULL on error.
  */
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp);
+CharDriverState *qemu_chr_alloc(const CharDriver *driver,
+                                ChardevCommon *backend, Error **errp);
 
 /**
  * @qemu_chr_new_from_opts:
@@ -473,15 +461,33 @@ void qemu_chr_set_feature(CharDriverState *chr,
                           CharDriverFeature feature);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
-typedef struct CharDriver {
+struct CharDriver {
     const char *name[2]; /* name & opt alias */
     ChardevBackendKind kind;
     void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
-    CharDriverState *(*create)(const char *id,
+    CharDriverState *(*create)(const CharDriver *driver,
+                               const char *id,
                                ChardevBackend *backend,
                                ChardevReturn *ret, bool *be_opened,
                                Error **errp);
-} CharDriver;
+
+    int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
+    int (*chr_sync_read)(struct CharDriverState *s,
+                         const uint8_t *buf, int len);
+    GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
+    void (*chr_update_read_handler)(struct CharDriverState *s,
+                                    GMainContext *context);
+    int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
+    int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
+    int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
+    int (*chr_add_client)(struct CharDriverState *chr, int fd);
+    int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
+    void (*chr_free)(struct CharDriverState *chr);
+    void (*chr_disconnect)(struct CharDriverState *chr);
+    void (*chr_accept_input)(struct CharDriverState *chr);
+    void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
+    void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
+};
 
 void register_char_driver(const CharDriver *driver);
 
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 34/38] char: fold single-user functions in caller
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (8 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 33/38] char: move callbacks in CharDriver Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 35/38] char: introduce generic qemu_chr_get_kind() Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 36/38] char: use a feature bit for replay Marc-André Lureau
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

This shorten a bit the code.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c | 100 +++++++++++++++++++++---------------------------------------
 1 file changed, 34 insertions(+), 66 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index a52e0ba..735a518 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -1857,22 +1857,6 @@ static void qemu_chr_free_tty(CharDriverState *chr)
 {
     fd_chr_free(chr);
 }
-
-static CharDriverState *qemu_chr_open_tty_fd(const CharDriver *driver,
-                                             int fd,
-                                             ChardevCommon *backend,
-                                             bool *be_opened,
-                                             Error **errp)
-{
-    CharDriverState *chr;
-
-    tty_serial_init(fd, 115200, 'N', 8, 1);
-    chr = qemu_chr_open_fd(driver, fd, fd, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    return chr;
-}
 #endif /* __linux__ || __sun__ */
 
 #if defined(__linux__)
@@ -2293,29 +2277,6 @@ static int win_chr_poll(void *opaque)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_win_path(const CharDriver *driver,
-                                               const char *filename,
-                                               ChardevCommon *backend,
-                                               Error **errp)
-{
-    CharDriverState *chr;
-    WinCharState *s;
-
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(WinCharState, 1);
-    chr->opaque = s;
-
-    if (win_chr_init(chr, filename, errp) < 0) {
-        g_free(s);
-        qemu_chr_free_common(chr);
-        return NULL;
-    }
-    return chr;
-}
-
 static int win_chr_pipe_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
@@ -2771,30 +2732,6 @@ static void udp_chr_free(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_udp(const CharDriver *driver,
-                                          QIOChannelSocket *sioc,
-                                          ChardevCommon *backend,
-                                          bool *be_opened,
-                                          Error **errp)
-{
-    CharDriverState *chr = NULL;
-    NetCharDriver *s = NULL;
-
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(NetCharDriver, 1);
-
-    s->ioc = QIO_CHANNEL(sioc);
-    s->bufcnt = 0;
-    s->bufptr = 0;
-    chr->opaque = s;
-    /* be isn't opened until we get a connection */
-    *be_opened = false;
-    return chr;
-}
-
 /***********************************************************/
 /* TCP Net console */
 
@@ -4545,8 +4482,23 @@ static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
 {
     ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
+    CharDriverState *chr;
+    WinCharState *s;
+
+    chr = qemu_chr_alloc(driver, common, errp);
+    if (!chr) {
+        return NULL;
+    }
 
-    return qemu_chr_open_win_path(driver, serial->device, common, errp);
+    s = g_new0(WinCharState, 1);
+    chr->opaque = s;
+    if (win_chr_init(chr, serial->device, errp) < 0) {
+        g_free(s);
+        qemu_chr_free_common(chr);
+        return NULL;
+    }
+
+    return chr;
 }
 
 #else /* WIN32 */
@@ -4615,8 +4567,9 @@ static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
         return NULL;
     }
     qemu_set_nonblock(fd);
+    tty_serial_init(fd, 115200, 'N', 8, 1);
 
-    return qemu_chr_open_tty_fd(driver, fd, common, be_opened, errp);
+    return qemu_chr_open_fd(driver, fd, fd, common, errp);
 }
 #endif
 
@@ -4854,6 +4807,8 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
     ChardevUdp *udp = backend->u.udp.data;
     ChardevCommon *common = qapi_ChardevUdp_base(udp);
     QIOChannelSocket *sioc = qio_channel_socket_new();
+    CharDriverState *chr;
+    NetCharDriver *s;
 
     if (qio_channel_socket_dgram_sync(sioc,
                                       udp->local, udp->remote,
@@ -4862,7 +4817,20 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
         return NULL;
     }
 
-    return qemu_chr_open_udp(driver, sioc, common, be_opened, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
+    if (!chr) {
+        return NULL;
+    }
+
+    s = g_new0(NetCharDriver, 1);
+    s->ioc = QIO_CHANNEL(sioc);
+    s->bufcnt = 0;
+    s->bufptr = 0;
+    chr->opaque = s;
+    /* be isn't opened until we get a connection */
+    *be_opened = false;
+
+    return chr;
 }
 
 static const CharDriver udp_driver = {
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 35/38] char: introduce generic qemu_chr_get_kind()
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (9 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 34/38] char: fold single-user functions in caller Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 36/38] char: use a feature bit for replay Marc-André Lureau
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

This allows to remove the "is_mux" field from CharDriverState.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 monitor.c             |  2 +-
 qemu-char.c           | 19 +++++++++----------
 include/sysemu/char.h | 15 +++++++++++++--
 3 files changed, 23 insertions(+), 13 deletions(-)

diff --git a/monitor.c b/monitor.c
index b73a999..3084175 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3196,7 +3196,7 @@ static void ringbuf_completion(ReadLineState *rs, const char *str)
 
         if (!strncmp(chr_info->label, str, len)) {
             CharDriverState *chr = qemu_chr_find(chr_info->label);
-            if (chr && chr_is_ringbuf(chr)) {
+            if (chr && qemu_chr_is_ringbuf(chr)) {
                 readline_add_completion(rs, chr_info->label);
             }
         }
diff --git a/qemu-char.c b/qemu-char.c
index 735a518..654d6a6 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -776,7 +776,7 @@ static void muxes_realize_done(Notifier *notifier, void *unused)
     CharDriverState *chr;
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
-        if (chr->is_mux) {
+        if (qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MUX) {
             MuxDriver *d = chr->opaque;
             int i;
 
@@ -880,7 +880,6 @@ static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
      * set of muxes
      */
     *be_opened = muxes_realized;
-    chr->is_mux = 1;
     if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
         qemu_chr_free(chr);
         return NULL;
@@ -903,7 +902,7 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
     assert(b);
     assert(s);
 
-    if (s->is_mux) {
+    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
         MuxDriver *d = s->opaque;
 
         if (d->mux_cnt >= MAX_MUX) {
@@ -935,7 +934,7 @@ void qemu_chr_fe_deinit(CharBackend *b)
     if (b->chr) {
         qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
         b->chr->be = NULL;
-        if (b->chr->is_mux) {
+        if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
             MuxDriver *d = b->chr->opaque;
             d->backends[b->tag] = NULL;
         }
@@ -988,7 +987,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         }
     }
 
-    if (s->is_mux) {
+    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
         mux_chr_set_handlers(s, context);
     }
 }
@@ -1001,7 +1000,7 @@ void qemu_chr_fe_take_focus(CharBackend *b)
         return;
     }
 
-    if (b->chr->is_mux) {
+    if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
         mux_set_focus(b->chr->opaque, b->tag);
     }
 }
@@ -3497,9 +3496,9 @@ fail:
     return NULL;
 }
 
-bool chr_is_ringbuf(const CharDriverState *chr)
+ChardevBackendKind qemu_chr_get_kind(const CharDriverState *chr)
 {
-    return chr->driver->chr_write == ringbuf_chr_write;
+    return chr->driver->kind;
 }
 
 void qmp_ringbuf_write(const char *device, const char *data,
@@ -3517,7 +3516,7 @@ void qmp_ringbuf_write(const char *device, const char *data,
         return;
     }
 
-    if (!chr_is_ringbuf(chr)) {
+    if (!qemu_chr_is_ringbuf(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return;
     }
@@ -3561,7 +3560,7 @@ char *qmp_ringbuf_read(const char *device, int64_t size,
         return NULL;
     }
 
-    if (!chr_is_ringbuf(chr)) {
+    if (!qemu_chr_is_ringbuf(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return NULL;
     }
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index fb2e6ff..29ebf2e 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -96,7 +96,6 @@ struct CharDriverState {
     char *filename;
     int logfd;
     int be_open;
-    int is_mux;
     guint fd_in_tag;
     bool replay;
     DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
@@ -453,7 +452,19 @@ void qemu_chr_be_generic_open(CharDriverState *s);
 void qemu_chr_fe_accept_input(CharBackend *be);
 int qemu_chr_add_client(CharDriverState *s, int fd);
 CharDriverState *qemu_chr_find(const char *name);
-bool chr_is_ringbuf(const CharDriverState *chr);
+
+/**
+ * @qemu_chr_get_kind:
+ *
+ * Returns the kind of char backend, or -1 if unspecified.
+ */
+ChardevBackendKind qemu_chr_get_kind(const CharDriverState *chr);
+
+static inline bool qemu_chr_is_ringbuf(const CharDriverState *chr)
+{
+    return qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_RINGBUF ||
+        qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MEMORY;
+}
 
 bool qemu_chr_has_feature(CharDriverState *chr,
                           CharDriverFeature feature);
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 36/38] char: use a feature bit for replay
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
                     ` (10 preceding siblings ...)
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 35/38] char: introduce generic qemu_chr_get_kind() Marc-André Lureau
@ 2016-10-22 10:09   ` Marc-André Lureau
  11 siblings, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Use a feature flag rather than a structure field for "replay".

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c           | 33 ++++++++++++++++++++-------------
 include/sysemu/char.h |  3 ++-
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 654d6a6..fe8f717 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -277,6 +277,11 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int
     return res;
 }
 
+static bool qemu_chr_replay(CharDriverState *chr)
+{
+    return qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+}
+
 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 {
     CharDriverState *s = be->chr;
@@ -286,7 +291,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
         return 0;
     }
 
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
         int offset;
         replay_char_write_event_load(&ret, &offset);
         assert(offset <= len);
@@ -303,7 +308,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 
     qemu_mutex_unlock(&s->chr_write_lock);
     
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
         replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
     }
     
@@ -315,7 +320,7 @@ static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len)
     int offset;
     int res;
 
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
         replay_char_write_event_load(&res, &offset);
         assert(offset <= len);
         qemu_chr_fe_write_buffer(s, buf, offset, &offset);
@@ -324,7 +329,7 @@ static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len)
 
     res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
 
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
         replay_char_write_event_save(res, offset);
     }
 
@@ -355,7 +360,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
         return 0;
     }
 
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
         return replay_char_read_all_load(buf);
     }
 
@@ -372,7 +377,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
         }
 
         if (res < 0) {
-            if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+            if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
                 replay_char_read_all_save_error(res);
             }
             return res;
@@ -385,7 +390,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
         }
     }
 
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
         replay_char_read_all_save_buf(buf, offset);
     }
     return offset;
@@ -396,7 +401,7 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
     CharDriverState *s = be->chr;
     int res;
 
-    if (!s || !s->driver->chr_ioctl || s->replay) {
+    if (!s || !s->driver->chr_ioctl || qemu_chr_replay(s)) {
         res = -ENOTSUP;
     } else {
         res = s->driver->chr_ioctl(s, cmd, arg);
@@ -427,7 +432,7 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
 
 void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
 {
-    if (s->replay) {
+    if (qemu_chr_replay(s)) {
         if (replay_mode == REPLAY_MODE_PLAY) {
             return;
         }
@@ -442,7 +447,7 @@ int qemu_chr_fe_get_msgfd(CharBackend *be)
     CharDriverState *s = be->chr;
     int fd;
     int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
-    if (s && s->replay) {
+    if (s && qemu_chr_replay(s)) {
         fprintf(stderr,
                 "Replay: get msgfd is not supported for serial devices yet\n");
         exit(1);
@@ -4181,8 +4186,10 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename)
     CharDriverState *chr;
     chr = qemu_chr_new_noreplay(label, filename);
     if (chr) {
-        chr->replay = replay_mode != REPLAY_MODE_NONE;
-        if (chr->replay && chr->driver->chr_ioctl) {
+        if (replay_mode != REPLAY_MODE_NONE) {
+            qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+        }
+        if (qemu_chr_replay(chr) && chr->driver->chr_ioctl) {
             fprintf(stderr,
                     "Replay: ioctl is not supported for serial devices yet\n");
         }
@@ -4908,7 +4915,7 @@ void qmp_chardev_remove(const char *id, Error **errp)
         error_setg(errp, "Chardev '%s' is busy", id);
         return;
     }
-    if (chr->replay) {
+    if (qemu_chr_replay(chr)) {
         error_setg(errp,
             "Chardev '%s' cannot be unplugged in record/replay mode", id);
         return;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 29ebf2e..96b464a 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -69,6 +69,8 @@ typedef enum {
     /* Whether it is possible to send/recv file descriptors
      * over the data channel */
     QEMU_CHAR_FEATURE_FD_PASS,
+    /* Whether replay or record mode is enabled */
+    QEMU_CHAR_FEATURE_REPLAY,
 
     QEMU_CHAR_FEATURE_LAST,
 } CharDriverFeature;
@@ -97,7 +99,6 @@ struct CharDriverState {
     int logfd;
     int be_open;
     guint fd_in_tag;
-    bool replay;
     DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
     QTAILQ_ENTRY(CharDriverState) next;
 };
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (23 preceding siblings ...)
  2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
@ 2016-10-22 10:16 ` Marc-André Lureau
  2016-10-22 10:16   ` [Qemu-devel] [PATCH 38/38] bt: use qemu_chr_alloc() Marc-André Lureau
  2016-10-23 12:28   ` [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object Paolo Bonzini
  2016-10-23 18:15 ` [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Paolo Bonzini
  25 siblings, 2 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Use a single allocation for CharDriverState, this avoids extra
allocations & pointers, and is a step towards more object-oriented
CharDriver.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/baum.c       |  23 ++---
 backends/msmouse.c    |  16 +--
 backends/testdev.c    |  22 ++--
 gdbstub.c             |   1 +
 hw/bt/hci-csr.c       |  10 +-
 qemu-char.c           | 275 ++++++++++++++++++++++++++------------------------
 spice-qemu-char.c     |  39 +++----
 ui/console.c          |  21 ++--
 ui/gtk.c              |  11 +-
 include/sysemu/char.h |   2 +-
 include/ui/gtk.h      |   3 +
 11 files changed, 215 insertions(+), 208 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index b2d4719..45a6fd4 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -87,7 +87,7 @@
 #define BUF_SIZE 256
 
 typedef struct {
-    CharDriverState *chr;
+    CharDriverState parent;
 
     brlapi_handle_t *brlapi;
     int brlapi_fd;
@@ -219,7 +219,7 @@ static const uint8_t nabcc_translation[256] = {
 /* The serial port can receive more of our data */
 static void baum_accept_input(struct CharDriverState *chr)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumDriverState *baum = (BaumDriverState *)chr;
     int room, first;
 
     if (!baum->out_buf_used)
@@ -245,22 +245,23 @@ static void baum_accept_input(struct CharDriverState *chr)
 /* We want to send a packet */
 static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len)
 {
+    CharDriverState *chr = (CharDriverState *)baum;
     uint8_t io_buf[1 + 2 * len], *cur = io_buf;
     int room;
     *cur++ = ESC;
     while (len--)
         if ((*cur++ = *buf++) == ESC)
             *cur++ = ESC;
-    room = qemu_chr_be_can_write(baum->chr);
+    room = qemu_chr_be_can_write(chr);
     len = cur - io_buf;
     if (len <= room) {
         /* Fits */
-        qemu_chr_be_write(baum->chr, io_buf, len);
+        qemu_chr_be_write(chr, io_buf, len);
     } else {
         int first;
         uint8_t out;
         /* Can't fit all, send what can be, and store the rest. */
-        qemu_chr_be_write(baum->chr, io_buf, room);
+        qemu_chr_be_write(chr, io_buf, room);
         len -= room;
         cur = io_buf + room;
         if (len > BUF_SIZE - baum->out_buf_used) {
@@ -433,7 +434,7 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
 /* The other end is writing some data.  Store it and try to interpret */
 static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumDriverState *baum = (BaumDriverState *)chr;
     int tocopy, cur, eaten, orig_len = len;
 
     if (!len)
@@ -553,14 +554,13 @@ static void baum_chr_read(void *opaque)
 
 static void baum_free(struct CharDriverState *chr)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumDriverState *baum = (BaumDriverState *)chr;
 
     timer_free(baum->cellCount_timer);
     if (baum->brlapi) {
         brlapi__closeConnection(baum->brlapi);
         g_free(baum->brlapi);
     }
-    g_free(baum);
 }
 
 static CharDriverState *chr_baum_init(const CharDriver *driver,
@@ -585,10 +585,7 @@ static CharDriverState *chr_baum_init(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    baum = g_malloc0(sizeof(BaumDriverState));
-    baum->chr = chr;
-
-    chr->opaque = baum;
+    baum = (BaumDriverState *)chr;
 
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
@@ -635,7 +632,6 @@ fail:
 fail_handle:
     g_free(handle);
     g_free(chr);
-    g_free(baum);
     return NULL;
 }
 
@@ -643,6 +639,7 @@ static void register_types(void)
 {
     static const CharDriver driver = {
         { "braille" }, CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init,
+        sizeof(BaumDriverState),
         .chr_write = baum_write,
         .chr_accept_input = baum_accept_input,
         .chr_free = baum_free,
diff --git a/backends/msmouse.c b/backends/msmouse.c
index f71aab0..37e7b82 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -31,7 +31,8 @@
 #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
 
 typedef struct {
-    CharDriverState *chr;
+    CharDriverState parent;
+
     QemuInputHandlerState *hs;
     int axis[INPUT_AXIS__MAX];
     bool btns[INPUT_BUTTON__MAX];
@@ -42,7 +43,7 @@ typedef struct {
 
 static void msmouse_chr_accept_input(CharDriverState *chr)
 {
-    MouseState *mouse = chr->opaque;
+    MouseState *mouse = (MouseState *)chr;
     int len;
 
     len = qemu_chr_be_can_write(chr);
@@ -122,9 +123,10 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
 static void msmouse_input_sync(DeviceState *dev)
 {
     MouseState *mouse = (MouseState *)dev;
+    CharDriverState *chr = (CharDriverState *)dev;
 
     msmouse_queue_event(mouse);
-    msmouse_chr_accept_input(mouse->chr);
+    msmouse_chr_accept_input(chr);
 }
 
 static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
@@ -135,10 +137,9 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int
 
 static void msmouse_chr_free(struct CharDriverState *chr)
 {
-    MouseState *mouse = chr->opaque;
+    MouseState *mouse = (MouseState *)chr;
 
     qemu_input_handler_unregister(mouse->hs);
-    g_free(mouse);
 }
 
 static QemuInputHandler msmouse_handler = {
@@ -165,12 +166,10 @@ static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
     }
     *be_opened = false;
 
-    mouse = g_new0(MouseState, 1);
+    mouse = (MouseState *)chr;
     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
                                             &msmouse_handler);
 
-    mouse->chr = chr;
-    chr->opaque = mouse;
 
     return chr;
 }
@@ -180,6 +179,7 @@ static void register_types(void)
     static const CharDriver driver = {
         { "msmouse" }, CHARDEV_BACKEND_KIND_MSMOUSE,
         NULL, qemu_chr_open_msmouse,
+        sizeof(MouseState),
         .chr_write = msmouse_chr_write,
         .chr_accept_input = msmouse_chr_accept_input,
         .chr_free = msmouse_chr_free,
diff --git a/backends/testdev.c b/backends/testdev.c
index 774cd47..4b1953b 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -30,7 +30,8 @@
 #define BUF_SIZE 32
 
 typedef struct {
-    CharDriverState *chr;
+    CharDriverState parent;
+
     uint8_t in_buf[32];
     int in_buf_used;
 } TestdevCharState;
@@ -79,7 +80,7 @@ static int testdev_eat_packet(TestdevCharState *testdev)
 /* The other end is writing some data.  Store it and try to interpret */
 static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    TestdevCharState *testdev = chr->opaque;
+    TestdevCharState *testdev = (TestdevCharState *)chr;
     int tocopy, eaten, orig_len = len;
 
     while (len) {
@@ -102,13 +103,6 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
     return orig_len;
 }
 
-static void testdev_free(struct CharDriverState *chr)
-{
-    TestdevCharState *testdev = chr->opaque;
-
-    g_free(testdev);
-}
-
 static CharDriverState *chr_testdev_init(const CharDriver *driver,
                                          const char *id,
                                          ChardevBackend *backend,
@@ -116,14 +110,10 @@ static CharDriverState *chr_testdev_init(const CharDriver *driver,
                                          bool *be_opened,
                                          Error **errp)
 {
-    TestdevCharState *testdev;
-    CharDriverState *chr;
-
-    testdev = g_new0(TestdevCharState, 1);
-    testdev->chr = chr = g_new0(CharDriverState, 1);
+    TestdevCharState *testdev = g_new0(TestdevCharState, 1);;
+    CharDriverState *chr = (CharDriverState *)testdev;
 
     chr->driver = driver;
-    chr->opaque = testdev;
 
     return chr;
 }
@@ -132,8 +122,8 @@ static void register_types(void)
 {
     static const CharDriver driver = {
         { "testdev" }, CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init,
+        sizeof(TestdevCharState),
         .chr_write = testdev_write,
-        .chr_free = testdev_free,
     };
     register_char_driver(&driver);
 }
diff --git a/gdbstub.c b/gdbstub.c
index 8e43836..3a9f857 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1732,6 +1732,7 @@ int gdbserver_start(const char *device)
     CharDriverState *mon_chr;
     ChardevCommon common = { 0 };
     static const CharDriver driver = {
+        .instance_size = sizeof(CharDriverState),
         .kind = -1,
         .chr_write = gdb_monitor_write
     };
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index 9c3fb3c..bf2deb0 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -28,11 +28,11 @@
 #include "hw/bt.h"
 
 struct csrhci_s {
+    CharDriverState chr;
     int enable;
     qemu_irq *pins;
     int pin_state;
     int modem_state;
-    CharDriverState chr;
 #define FIFO_LEN	4096
     int out_start;
     int out_len;
@@ -314,7 +314,7 @@ static void csrhci_ready_for_next_inpkt(struct csrhci_s *s)
 static int csrhci_write(struct CharDriverState *chr,
                 const uint8_t *buf, int len)
 {
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *)chr;
     int total = 0;
 
     if (!s->enable)
@@ -387,7 +387,7 @@ static void csrhci_out_hci_packet_acl(void *opaque,
 static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
 {
     QEMUSerialSetParams *ssp;
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *) chr;
     int prev_state = s->modem_state;
 
     switch (cmd) {
@@ -455,7 +455,7 @@ static void csrhci_pins(void *opaque, int line, int level)
 
 qemu_irq *csrhci_pins_get(CharDriverState *chr)
 {
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *) chr;
 
     return s->pins;
 }
@@ -463,6 +463,7 @@ qemu_irq *csrhci_pins_get(CharDriverState *chr)
 CharDriverState *uart_hci_init(void)
 {
     static const CharDriver hci_driver = {
+        .instance_size = sizeof(struct csrhci_s),
         .kind = -1,
         .chr_write = csrhci_write,
         .chr_ioctl = csrhci_ioctl,
@@ -470,7 +471,6 @@ CharDriverState *uart_hci_init(void)
     struct csrhci_s *s = (struct csrhci_s *)
             g_malloc0(sizeof(struct csrhci_s));
 
-    s->chr.opaque = s;
     s->chr.driver = &hci_driver;
 
     s->hci = qemu_next_hci();
diff --git a/qemu-char.c b/qemu-char.c
index fe8f717..1da7244 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -165,12 +165,14 @@ static void qemu_chr_free_common(CharDriverState *chr);
 CharDriverState *qemu_chr_alloc(const CharDriver *driver,
                                 ChardevCommon *backend, Error **errp)
 {
-    CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
-    qemu_mutex_init(&chr->chr_write_lock);
+    CharDriverState *chr;
 
     assert(driver);
     assert(driver->chr_write);
+    assert(driver->instance_size >= sizeof(CharDriverState));
 
+    chr = g_malloc0(driver->instance_size);
+    qemu_mutex_init(&chr->chr_write_lock);
     if (backend->has_logfile) {
         int flags = O_WRONLY | O_CREAT;
         if (backend->has_logappend &&
@@ -537,6 +539,7 @@ static CharDriverState *qemu_chr_open_null(const CharDriver *driver,
 
 static const CharDriver null_driver = {
     { "null" }, CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null,
+    sizeof(CharDriverState),
     .chr_write = null_chr_write
 };
 
@@ -545,6 +548,7 @@ static const CharDriver null_driver = {
 #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
 #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
 struct MuxDriver {
+    CharDriverState parent;
     CharBackend *backends[MAX_MUX];
     CharBackend chr;
     int focus;
@@ -567,7 +571,7 @@ struct MuxDriver {
 /* Called with chr_write_lock held.  */
 static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
     int ret;
     if (!d->timestamps) {
         ret = qemu_chr_fe_write(&d->chr, buf, len);
@@ -701,7 +705,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
 
 static void mux_chr_accept_input(CharDriverState *chr)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -714,8 +718,7 @@ static void mux_chr_accept_input(CharDriverState *chr)
 
 static int mux_chr_can_read(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = opaque;
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -733,7 +736,7 @@ static int mux_chr_can_read(void *opaque)
 static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 {
     CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = opaque;
     int m = d->focus;
     CharBackend *be = d->backends[m];
     int i;
@@ -753,8 +756,7 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 
 static void mux_chr_event(void *opaque, int event)
 {
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = opaque;
     int i;
 
     /* Send the event to all registered listeners */
@@ -782,7 +784,7 @@ static void muxes_realize_done(Notifier *notifier, void *unused)
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
         if (qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxDriver *d = chr->opaque;
+            MuxDriver *d = (MuxDriver *)chr;
             int i;
 
             /* send OPENED to all already-attached FEs */
@@ -804,7 +806,7 @@ static Notifier muxes_realize_notify = {
 
 static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
 {
-    MuxDriver *d = s->opaque;
+    MuxDriver *d = (MuxDriver *)s;
     CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
 
     if (!chr->driver->chr_add_watch) {
@@ -816,7 +818,7 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
 
 static void mux_chr_free(struct CharDriverState *chr)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
     int i;
 
     for (i = 0; i < d->mux_cnt; i++) {
@@ -826,12 +828,11 @@ static void mux_chr_free(struct CharDriverState *chr)
         }
     }
     qemu_chr_fe_deinit(&d->chr);
-    g_free(d);
 }
 
 static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
 
     /* Fix up the real driver with mux routines */
     qemu_chr_fe_set_handlers(&d->chr,
@@ -877,9 +878,7 @@ static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    d = g_new0(MuxDriver, 1);
-
-    chr->opaque = d;
+    d = (MuxDriver *)chr;
     d->focus = -1;
     /* only default to opened state if we've realized the initial
      * set of muxes
@@ -908,7 +907,7 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
     assert(s);
 
     if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxDriver *d = s->opaque;
+        MuxDriver *d = (MuxDriver *)s;
 
         if (d->mux_cnt >= MAX_MUX) {
             goto unavailable;
@@ -940,7 +939,7 @@ void qemu_chr_fe_deinit(CharBackend *b)
         qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
         b->chr->be = NULL;
         if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxDriver *d = b->chr->opaque;
+            MuxDriver *d = (MuxDriver *)b->chr;
             d->backends[b->tag] = NULL;
         }
         b->chr = NULL;
@@ -1006,7 +1005,7 @@ void qemu_chr_fe_take_focus(CharBackend *b)
     }
 
     if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
-        mux_set_focus(b->chr->opaque, b->tag);
+        mux_set_focus((MuxDriver *)b->chr, b->tag);
     }
 }
 
@@ -1178,6 +1177,7 @@ static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
 
 
 typedef struct FDCharDriver {
+    CharDriverState parent;
     CharDriverState *chr;
     QIOChannel *ioc_in, *ioc_out;
     int max_size;
@@ -1186,15 +1186,15 @@ typedef struct FDCharDriver {
 /* Called with chr_write_lock held.  */
 static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    FDCharDriver *s = chr->opaque;
-    
+    FDCharDriver *s = (FDCharDriver *)chr;
+
     return io_channel_send(s->ioc_out, buf, len);
 }
 
 static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = opaque;
     int len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1224,7 +1224,7 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 static int fd_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = opaque;
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -1232,14 +1232,14 @@ static int fd_chr_read_poll(void *opaque)
 
 static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = (FDCharDriver *)chr;
     return qio_channel_create_watch(s->ioc_out, cond);
 }
 
 static void fd_chr_update_read_handler(CharDriverState *chr,
                                        GMainContext *context)
 {
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = (FDCharDriver *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1252,7 +1252,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr,
 
 static void fd_chr_free(struct CharDriverState *chr)
 {
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = (FDCharDriver *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1262,7 +1262,6 @@ static void fd_chr_free(struct CharDriverState *chr)
         object_unref(OBJECT(s->ioc_out));
     }
 
-    g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -1278,12 +1277,11 @@ static CharDriverState *qemu_chr_open_fd(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = g_new0(FDCharDriver, 1);
+    s = (FDCharDriver *)chr;
     s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
     s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
     qemu_set_nonblock(fd_out);
     s->chr = chr;
-    chr->opaque = s;
 
     return chr;
 }
@@ -1424,6 +1422,7 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
 #define HAVE_CHARDEV_PTY 1
 
 typedef struct {
+    CharDriverState parent;
     QIOChannel *ioc;
     int read_bytes;
 
@@ -1439,7 +1438,7 @@ static void pty_chr_state(CharDriverState *chr, int connected);
 static gboolean pty_chr_timer(gpointer opaque)
 {
     struct CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = opaque;
 
     qemu_mutex_lock(&chr->chr_write_lock);
     s->timer_tag = 0;
@@ -1455,7 +1454,7 @@ static gboolean pty_chr_timer(gpointer opaque)
 /* Called with chr_write_lock held.  */
 static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
 
     if (s->timer_tag) {
         g_source_remove(s->timer_tag);
@@ -1472,7 +1471,7 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
 /* Called with chr_write_lock held.  */
 static void pty_chr_update_read_handler_locked(CharDriverState *chr)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
     GPollFD pfd;
     int rc;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
@@ -1503,7 +1502,7 @@ static void pty_chr_update_read_handler(CharDriverState *chr,
 /* Called with chr_write_lock held.  */
 static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
 
     if (!s->connected) {
         /* guest sends data, check for (re-)connect */
@@ -1517,7 +1516,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
     if (!s->connected) {
         return NULL;
     }
@@ -1527,7 +1526,7 @@ static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 static int pty_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = opaque;
 
     s->read_bytes = qemu_chr_be_can_write(chr);
     return s->read_bytes;
@@ -1536,7 +1535,7 @@ static int pty_chr_read_poll(void *opaque)
 static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = opaque;
     gsize len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1561,7 +1560,7 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 {
     CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = opaque;
 
     s->open_tag = 0;
     qemu_chr_be_generic_open(chr);
@@ -1571,7 +1570,7 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 /* Called with chr_write_lock held.  */
 static void pty_chr_state(CharDriverState *chr, int connected)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
 
     if (!connected) {
         if (s->open_tag) {
@@ -1605,7 +1604,7 @@ static void pty_chr_state(CharDriverState *chr, int connected)
 
 static void pty_chr_free(struct CharDriverState *chr)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
 
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_state(chr, 0);
@@ -1615,7 +1614,6 @@ static void pty_chr_free(struct CharDriverState *chr)
         s->timer_tag = 0;
     }
     qemu_mutex_unlock(&chr->chr_write_lock);
-    g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -1654,18 +1652,17 @@ static CharDriverState *qemu_chr_open_pty(const CharDriver *driver,
     fprintf(stderr, "char device redirected to %s (label %s)\n",
             pty_name, id);
 
-    s = g_new0(PtyCharDriver, 1);
-    chr->opaque = s;
-    *be_opened = false;
-
+    s = (PtyCharDriver *)chr;
     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
     s->timer_tag = 0;
+    *be_opened = false;
 
     return chr;
 }
 
 static const CharDriver pty_driver = {
     { "pty" }, CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty,
+    sizeof(PtyCharDriver),
     .chr_write = pty_chr_write,
     .chr_update_read_handler = pty_chr_update_read_handler,
     .chr_add_watch = pty_chr_add_watch,
@@ -1789,7 +1786,7 @@ static void tty_serial_init(int fd, int speed,
 
 static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
 {
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = (FDCharDriver *)chr;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
 
     switch(cmd) {
@@ -1868,6 +1865,7 @@ static void qemu_chr_free_tty(CharDriverState *chr)
 #define HAVE_CHARDEV_PARPORT 1
 
 typedef struct {
+    CharDriverState parent;
     int fd;
     int mode;
 } ParallelCharDriver;
@@ -1885,7 +1883,7 @@ static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
 
 static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 {
-    ParallelCharDriver *drv = chr->opaque;
+    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
     int fd = drv->fd;
     uint8_t b;
 
@@ -1966,13 +1964,12 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 
 static void pp_free(CharDriverState *chr)
 {
-    ParallelCharDriver *drv = chr->opaque;
+    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
     int fd = drv->fd;
 
     pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
     ioctl(fd, PPRELEASE);
     close(fd);
-    g_free(drv);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -1996,9 +1993,7 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
         return NULL;
     }
 
-    drv = g_new0(ParallelCharDriver, 1);
-    chr->opaque = drv;
-
+    drv = (ParallelCharDriver *)chr;
     drv->fd = fd;
     drv->mode = IEEE1284_MODE_COMPAT;
 
@@ -2010,35 +2005,45 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
 
 #define HAVE_CHARDEV_PARPORT 1
 
+typedef struct {
+    CharDriverState parent;
+    int fd;
+} ParallelCharDriver;
+
 static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 {
-    int fd = (int)(intptr_t)chr->opaque;
+    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
     uint8_t b;
 
-    switch(cmd) {
+    switch (cmd) {
     case CHR_IOCTL_PP_READ_DATA:
-        if (ioctl(fd, PPIGDATA, &b) < 0)
+        if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
             return -ENOTSUP;
+        }
         *(uint8_t *)arg = b;
         break;
     case CHR_IOCTL_PP_WRITE_DATA:
         b = *(uint8_t *)arg;
-        if (ioctl(fd, PPISDATA, &b) < 0)
+        if (ioctl(drv->fd, PPISDATA, &b) < 0) {
             return -ENOTSUP;
+        }
         break;
     case CHR_IOCTL_PP_READ_CONTROL:
-        if (ioctl(fd, PPIGCTRL, &b) < 0)
+        if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
             return -ENOTSUP;
+        }
         *(uint8_t *)arg = b;
         break;
     case CHR_IOCTL_PP_WRITE_CONTROL:
         b = *(uint8_t *)arg;
-        if (ioctl(fd, PPISCTRL, &b) < 0)
+        if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
             return -ENOTSUP;
+        }
         break;
     case CHR_IOCTL_PP_READ_STATUS:
-        if (ioctl(fd, PPIGSTATUS, &b) < 0)
+        if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
             return -ENOTSUP;
+        }
         *(uint8_t *)arg = b;
         break;
     default:
@@ -2054,12 +2059,14 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
                                             Error **errp)
 {
     CharDriverState *chr;
+    ParallelCharDriver *drv;
 
     chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
-    chr->opaque = (void *)(intptr_t)fd;
+    drv = (ParallelCharDriver *)chr;
+    drv->fd = fd;
     *be_opened = false;
     return chr;
 }
@@ -2070,6 +2077,7 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
 #define HAVE_CHARDEV_SERIAL 1
 
 typedef struct {
+    CharDriverState parent;
     int max_size;
     HANDLE hcom, hrecv, hsend;
     OVERLAPPED orecv;
@@ -2081,6 +2089,7 @@ typedef struct {
 } WinCharState;
 
 typedef struct {
+    CharDriverState parent;
     HANDLE  hStdIn;
     HANDLE  hInputReadyEvent;
     HANDLE  hInputDoneEvent;
@@ -2098,7 +2107,7 @@ static int win_chr_pipe_poll(void *opaque);
 
 static void win_chr_free(CharDriverState *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
 
     if (s->hsend) {
         CloseHandle(s->hsend);
@@ -2122,7 +2131,7 @@ static void win_chr_free(CharDriverState *chr)
 
 static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
     COMMCONFIG comcfg;
     COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
     COMSTAT comstat;
@@ -2190,7 +2199,7 @@ static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp
 /* Called with chr_write_lock held.  */
 static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
     DWORD len, ret, size, err;
 
     len = len1;
@@ -2224,7 +2233,7 @@ static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
 
 static int win_chr_read_poll(CharDriverState *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -2232,7 +2241,7 @@ static int win_chr_read_poll(CharDriverState *chr)
 
 static void win_chr_readfile(CharDriverState *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
     int ret, err;
     uint8_t buf[READ_BUF_LEN];
     DWORD size;
@@ -2254,7 +2263,7 @@ static void win_chr_readfile(CharDriverState *chr)
 
 static void win_chr_read(CharDriverState *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
 
     if (s->len > s->max_size)
         s->len = s->max_size;
@@ -2267,7 +2276,7 @@ static void win_chr_read(CharDriverState *chr)
 static int win_chr_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    WinCharState *s = chr->opaque;
+    WinCharState *s = opaque;
     COMSTAT status;
     DWORD comerr;
 
@@ -2284,7 +2293,7 @@ static int win_chr_poll(void *opaque)
 static int win_chr_pipe_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    WinCharState *s = chr->opaque;
+    WinCharState *s = opaque;
     DWORD size;
 
     PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
@@ -2300,7 +2309,7 @@ static int win_chr_pipe_poll(void *opaque)
 static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
                              Error **errp)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
     OVERLAPPED ov;
     int ret;
     DWORD size;
@@ -2372,18 +2381,14 @@ static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
     ChardevHostdev *opts = backend->u.pipe.data;
     const char *filename = opts->device;
     CharDriverState *chr;
-    WinCharState *s;
     ChardevCommon *common = qapi_ChardevHostdev_base(opts);
 
     chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
-    s = g_new0(WinCharState, 1);
-    chr->opaque = s;
 
     if (win_chr_pipe_init(chr, filename, errp) < 0) {
-        g_free(s);
         qemu_chr_free_common(chr);
         return NULL;
     }
@@ -2402,9 +2407,8 @@ static CharDriverState *qemu_chr_open_win_file(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = g_new0(WinCharState, 1);
+    s = (WinCharState *)chr;
     s->hcom = fd_out;
-    chr->opaque = s;
     return chr;
 }
 
@@ -2423,6 +2427,7 @@ static CharDriverState *qemu_chr_open_win_con(const CharDriver *driver,
 
 static const CharDriver console_driver = {
     { "console" }, CHARDEV_BACKEND_KIND_CONSOLE, NULL, qemu_chr_open_win_con,
+    sizeof(WinCharState),
     .chr_write = win_chr_write,
 };
 
@@ -2448,7 +2453,7 @@ static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
 static void win_stdio_wait_func(void *opaque)
 {
     CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioCharState *stdio = opaque;
     INPUT_RECORD       buf[4];
     int                ret;
     DWORD              dwSize;
@@ -2481,8 +2486,7 @@ static void win_stdio_wait_func(void *opaque)
 
 static DWORD WINAPI win_stdio_thread(LPVOID param)
 {
-    CharDriverState   *chr   = param;
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioCharState *stdio = param;
     int                ret;
     DWORD              dwSize;
 
@@ -2521,7 +2525,7 @@ static DWORD WINAPI win_stdio_thread(LPVOID param)
 static void win_stdio_thread_wait_func(void *opaque)
 {
     CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioCharState *stdio = opaque;
 
     if (qemu_chr_be_can_write(chr)) {
         qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
@@ -2532,7 +2536,7 @@ static void win_stdio_thread_wait_func(void *opaque)
 
 static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
 {
-    WinStdioCharState *stdio  = chr->opaque;
+    WinStdioCharState *stdio  = (WinStdioCharState *)chr;
     DWORD              dwMode = 0;
 
     GetConsoleMode(stdio->hStdIn, &dwMode);
@@ -2546,7 +2550,7 @@ static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
 
 static void win_stdio_free(CharDriverState *chr)
 {
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioCharState *stdio = (WinStdioCharState *)chr;
 
     if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
         CloseHandle(stdio->hInputReadyEvent);
@@ -2557,8 +2561,6 @@ static void win_stdio_free(CharDriverState *chr)
     if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
         TerminateThread(stdio->hInputThread, 0);
     }
-
-    g_free(chr->opaque);
 }
 
 static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
@@ -2578,7 +2580,7 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    stdio = g_new0(WinStdioCharState, 1);
+    stdio = (WinStdioCharState *)chr;
 
     stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
     if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
@@ -2588,8 +2590,6 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
 
     is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
 
-    chr->opaque    = stdio;
-
     if (is_console) {
         if (qemu_add_wait_object(stdio->hStdIn,
                                  win_stdio_wait_func, chr)) {
@@ -2649,6 +2649,7 @@ err1:
 /* UDP Net console */
 
 typedef struct {
+    CharDriverState parent;
     QIOChannel *ioc;
     uint8_t buf[READ_BUF_LEN];
     int bufcnt;
@@ -2659,7 +2660,7 @@ typedef struct {
 /* Called with chr_write_lock held.  */
 static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = (NetCharDriver *)chr;
 
     return qio_channel_write(
         s->ioc, (const char *)buf, len, NULL);
@@ -2668,7 +2669,7 @@ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 static int udp_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = opaque;
 
     s->max_size = qemu_chr_be_can_write(chr);
 
@@ -2686,7 +2687,7 @@ static int udp_chr_read_poll(void *opaque)
 static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = opaque;
     ssize_t ret;
 
     if (s->max_size == 0) {
@@ -2713,7 +2714,7 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 static void udp_chr_update_read_handler(CharDriverState *chr,
                                         GMainContext *context)
 {
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = (NetCharDriver *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2726,13 +2727,12 @@ static void udp_chr_update_read_handler(CharDriverState *chr,
 
 static void udp_chr_free(CharDriverState *chr)
 {
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = (NetCharDriver *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
         object_unref(OBJECT(s->ioc));
     }
-    g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -2740,6 +2740,7 @@ static void udp_chr_free(CharDriverState *chr)
 /* TCP Net console */
 
 typedef struct {
+    CharDriverState parent;
     QIOChannel *ioc; /* Client I/O channel */
     QIOChannelSocket *sioc; /* Client master channel */
     QIOChannelSocket *listen_ioc;
@@ -2768,7 +2769,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque);
 
 static void qemu_chr_socket_restart_timer(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     assert(s->connected == 0);
     s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
                                                socket_reconnect_timeout, chr);
@@ -2777,7 +2778,7 @@ static void qemu_chr_socket_restart_timer(CharDriverState *chr)
 static void check_report_connect_error(CharDriverState *chr,
                                        Error *err)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     if (!s->connect_err_reported) {
         error_report("Unable to connect character device %s: %s",
@@ -2794,7 +2795,8 @@ static gboolean tcp_chr_accept(QIOChannel *chan,
 /* Called with chr_write_lock held.  */
 static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
+
     if (s->connected) {
         int ret =  io_channel_send_full(s->ioc, buf, len,
                                         s->write_msgfds,
@@ -2817,7 +2819,7 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 static int tcp_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = opaque;
     if (!s->connected)
         return 0;
     s->max_size = qemu_chr_be_can_write(chr);
@@ -2876,7 +2878,8 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
 
 static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
+
     int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
 
     assert(num <= TCP_MAX_FDS);
@@ -2901,7 +2904,7 @@ static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
 
 static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     /* clear old pending fd array */
     g_free(s->write_msgfds);
@@ -2926,7 +2929,7 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
 
 static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     struct iovec iov = { .iov_base = buf, .iov_len = len };
     int ret;
     size_t i;
@@ -2983,13 +2986,13 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
 
 static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     return qio_channel_create_watch(s->ioc, cond);
 }
 
 static void tcp_chr_free_connection(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     int i;
 
     if (!s->connected) {
@@ -3018,7 +3021,7 @@ static void tcp_chr_free_connection(CharDriverState *chr)
 
 static void tcp_chr_disconnect(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     if (!s->connected) {
         return;
@@ -3041,7 +3044,7 @@ static void tcp_chr_disconnect(CharDriverState *chr)
 static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = opaque;
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
@@ -3067,7 +3070,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 
 static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     int size;
 
     if (!s->connected) {
@@ -3086,7 +3089,7 @@ static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
 static void tcp_chr_connect(void *opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = opaque;
 
     g_free(chr->filename);
     chr->filename = sockaddr_to_str(
@@ -3107,7 +3110,7 @@ static void tcp_chr_connect(void *opaque)
 static void tcp_chr_update_read_handler(CharDriverState *chr,
                                         GMainContext *context)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     if (!s->connected) {
         return;
@@ -3158,7 +3161,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
 
 static void tcp_chr_telnet_init(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     TCPCharDriverTelnetInit *init =
         g_new0(TCPCharDriverTelnetInit, 1);
     size_t n = 0;
@@ -3194,7 +3197,7 @@ static void tcp_chr_tls_handshake(Object *source,
                                   gpointer user_data)
 {
     CharDriverState *chr = user_data;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = user_data;
 
     if (err) {
         tcp_chr_disconnect(chr);
@@ -3210,7 +3213,7 @@ static void tcp_chr_tls_handshake(Object *source,
 
 static void tcp_chr_tls_init(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     QIOChannelTLS *tioc;
     Error *err = NULL;
 
@@ -3242,7 +3245,8 @@ static void tcp_chr_tls_init(CharDriverState *chr)
 
 static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
+
     if (s->ioc != NULL) {
 	return -1;
     }
@@ -3312,7 +3316,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
 
 static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     QIOChannelSocket *sioc;
 
     /* It can't wait on s->connected, since it is set asynchronously
@@ -3359,7 +3363,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
 
 static void tcp_chr_free(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     tcp_chr_free_connection(chr);
 
@@ -3378,7 +3382,7 @@ static void tcp_chr_free(CharDriverState *chr)
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
-    g_free(s);
+
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -3387,7 +3391,7 @@ static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque)
 {
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(src);
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     if (err) {
         check_report_connect_error(chr, err);
@@ -3405,6 +3409,7 @@ static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque)
 /* Ring buffer chardev */
 
 typedef struct {
+    CharDriverState parent;
     size_t size;
     size_t prod;
     size_t cons;
@@ -3413,7 +3418,7 @@ typedef struct {
 
 static size_t ringbuf_count(const CharDriverState *chr)
 {
-    const RingBufCharDriver *d = chr->opaque;
+    const RingBufCharDriver *d = (RingBufCharDriver *)chr;
 
     return d->prod - d->cons;
 }
@@ -3421,7 +3426,7 @@ static size_t ringbuf_count(const CharDriverState *chr)
 /* Called with chr_write_lock held.  */
 static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    RingBufCharDriver *d = chr->opaque;
+    RingBufCharDriver *d = (RingBufCharDriver *)chr;
     int i;
 
     if (!buf || (len < 0)) {
@@ -3440,7 +3445,7 @@ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
 {
-    RingBufCharDriver *d = chr->opaque;
+    RingBufCharDriver *d = (RingBufCharDriver *)chr;
     int i;
 
     qemu_mutex_lock(&chr->chr_write_lock);
@@ -3454,11 +3459,9 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
 
 static void ringbuf_chr_free(struct CharDriverState *chr)
 {
-    RingBufCharDriver *d = chr->opaque;
+    RingBufCharDriver *d = (RingBufCharDriver *)chr;
 
     g_free(d->cbuf);
-    g_free(d);
-    chr->opaque = NULL;
 }
 
 static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
@@ -3477,7 +3480,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    d = g_malloc(sizeof(*d));
+    d = (RingBufCharDriver *)chr;
 
     d->size = opts->has_size ? opts->size : 65536;
 
@@ -3491,12 +3494,9 @@ static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
     d->cons = 0;
     d->cbuf = g_malloc0(d->size);
 
-    chr->opaque = d;
-
     return chr;
 
 fail:
-    g_free(d);
     qemu_chr_free_common(chr);
     return NULL;
 }
@@ -3786,10 +3786,12 @@ static const CharDriver stdio_driver = {
     { "stdio" }, CHARDEV_BACKEND_KIND_STDIO,
     qemu_chr_parse_stdio, qemu_chr_open_stdio,
 #ifdef _WIN32
+    sizeof(WinStdioCharState),
     .chr_write = win_stdio_write,
     .chr_set_echo = qemu_chr_set_echo_win_stdio,
     .chr_free = win_stdio_free,
 #else
+    sizeof(FDCharDriver),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -3851,9 +3853,11 @@ static const CharDriver pipe_driver = {
     { "pipe" }, CHARDEV_BACKEND_KIND_PIPE,
     qemu_chr_parse_pipe, qemu_chr_open_pipe,
 #ifdef _WIN32
+    sizeof(WinCharState),
     .chr_write = win_chr_write,
     .chr_free = win_chr_free,
 #else
+    sizeof(FDCharDriver),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -3880,6 +3884,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
 static const CharDriver ringbuf_driver = {
     { "ringbuf" }, CHARDEV_BACKEND_KIND_RINGBUF,
     qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf,
+    sizeof(RingBufCharDriver),
     .chr_write = ringbuf_chr_write,
     .chr_free = ringbuf_chr_free,
 };
@@ -3888,6 +3893,7 @@ static const CharDriver ringbuf_driver = {
 static const CharDriver memory_driver = {
     { "memory" }, CHARDEV_BACKEND_KIND_MEMORY,
     qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf,
+    sizeof(RingBufCharDriver),
     .chr_write = ringbuf_chr_write,
     .chr_free = ringbuf_chr_free,
 };
@@ -3909,6 +3915,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 
 static const CharDriver mux_driver = {
     { "mux" }, CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux, qemu_chr_open_mux,
+    sizeof(MuxDriver),
     .chr_free = mux_chr_free,
     .chr_write = mux_chr_write,
     .chr_accept_input = mux_chr_accept_input,
@@ -4489,17 +4496,13 @@ static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
     ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
     CharDriverState *chr;
-    WinCharState *s;
 
     chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
 
-    s = g_new0(WinCharState, 1);
-    chr->opaque = s;
     if (win_chr_init(chr, serial->device, errp) < 0) {
-        g_free(s);
         qemu_chr_free_common(chr);
         return NULL;
     }
@@ -4601,6 +4604,7 @@ static CharDriverState *qmp_chardev_open_parallel(const CharDriver *driver,
 static const CharDriver parallel_driver = {
     { "parallel", "parport" }, CHARDEV_BACKEND_KIND_PARALLEL,
     qemu_chr_parse_parallel, qmp_chardev_open_parallel,
+    sizeof(ParallelCharDriver),
 #if defined(__linux__)
     .chr_write = null_chr_write,
     .chr_ioctl = pp_ioctl,
@@ -4618,8 +4622,10 @@ static const CharDriver file_driver = {
     { "file" }, CHARDEV_BACKEND_KIND_FILE,
     qemu_chr_parse_file_out, qmp_chardev_open_file,
 #ifdef _WIN32
+    sizeof(WinCharState),
     .chr_write = win_chr_write,
 #else
+    sizeof(FDCharDriver),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -4632,9 +4638,11 @@ static const CharDriver serial_driver = {
     { "serial", "tty" }, CHARDEV_BACKEND_KIND_SERIAL,
     qemu_chr_parse_serial, qmp_chardev_open_serial,
 #ifdef _WIN32
+    sizeof(WinCharState),
     .chr_write = win_chr_write,
     .chr_free = win_chr_free,
 #else
+    sizeof(FDCharDriver),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -4647,7 +4655,7 @@ static const CharDriver serial_driver = {
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = opaque;
     QIOChannelSocket *sioc;
 
     s->reconnect_timer = 0;
@@ -4687,7 +4695,7 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = g_new0(TCPCharDriver, 1);
+    s = (TCPCharDriver *)chr;
 
     s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
@@ -4733,7 +4741,6 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
         qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
     }
 
-    chr->opaque = s;
     /* be isn't opened until we get a connection */
     *be_opened = false;
 
@@ -4783,7 +4790,6 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
-    g_free(s);
     qemu_chr_free_common(chr);
     return NULL;
 }
@@ -4791,6 +4797,7 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
 static const CharDriver socket_driver = {
     { "socket" }, CHARDEV_BACKEND_KIND_SOCKET,
     qemu_chr_parse_socket, qmp_chardev_open_socket,
+    sizeof(TCPCharDriver),
     .chr_wait_connected = tcp_chr_wait_connected,
     .chr_write = tcp_chr_write,
     .chr_sync_read = tcp_chr_sync_read,
@@ -4828,11 +4835,10 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
         return NULL;
     }
 
-    s = g_new0(NetCharDriver, 1);
+    s = (NetCharDriver *)chr;
     s->ioc = QIO_CHANNEL(sioc);
     s->bufcnt = 0;
     s->bufptr = 0;
-    chr->opaque = s;
     /* be isn't opened until we get a connection */
     *be_opened = false;
 
@@ -4842,6 +4848,7 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
 static const CharDriver udp_driver = {
     { "udp" }, CHARDEV_BACKEND_KIND_UDP,
     qemu_chr_parse_udp, qmp_chardev_open_udp,
+    sizeof(NetCharDriver),
     .chr_write = udp_chr_write,
     .chr_update_read_handler = udp_chr_update_read_handler,
     .chr_free = udp_chr_free,
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 14afd4a..bd8b898 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -7,7 +7,8 @@
 
 
 typedef struct SpiceCharDriver {
-    CharDriverState*      chr;
+    CharDriverState       parent;
+
     SpiceCharDeviceInstance     sin;
     bool                  active;
     bool                  blocked;
@@ -27,17 +28,18 @@ static QLIST_HEAD(, SpiceCharDriver) spice_chars =
 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 {
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    CharDriverState *chr = (CharDriverState *)scd;
     ssize_t out = 0;
     ssize_t last_out;
     uint8_t* p = (uint8_t*)buf;
 
     while (len > 0) {
-        int can_write = qemu_chr_be_can_write(scd->chr);
+        int can_write = qemu_chr_be_can_write(chr);
         last_out = MIN(len, can_write);
         if (last_out <= 0) {
             break;
         }
-        qemu_chr_be_write(scd->chr, p, last_out);
+        qemu_chr_be_write(chr, p, last_out);
         out += last_out;
         len -= last_out;
         p += last_out;
@@ -70,6 +72,7 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
 {
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    CharDriverState *chr = (CharDriverState *)scd;
     int chr_event;
 
     switch (event) {
@@ -81,20 +84,21 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
     }
 
     trace_spice_vmc_event(chr_event);
-    qemu_chr_be_event(scd->chr, chr_event);
+    qemu_chr_be_event(chr, chr_event);
 }
 #endif
 
 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
 {
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    CharDriverState *chr = (CharDriverState *)scd;
 
-    if ((scd->chr->be_open && connected) ||
-        (!scd->chr->be_open && !connected)) {
+    if ((chr->be_open && connected) ||
+        (!chr->be_open && !connected)) {
         return;
     }
 
-    qemu_chr_be_event(scd->chr,
+    qemu_chr_be_event(chr,
                       connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
 }
 
@@ -168,7 +172,7 @@ static GSourceFuncs SpiceCharSourceFuncs = {
 
 static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
-    SpiceCharDriver *scd = chr->opaque;
+    SpiceCharDriver *scd = (SpiceCharDriver *)chr;
     SpiceCharSource *src;
 
     assert(cond & G_IO_OUT);
@@ -182,7 +186,7 @@ static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 
 static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
     int read_bytes;
 
     assert(s->datalen == 0);
@@ -201,7 +205,7 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static void spice_chr_free(struct CharDriverState *chr)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
 
     vmc_unregister_interface(s);
     QLIST_REMOVE(s, next);
@@ -210,12 +214,11 @@ static void spice_chr_free(struct CharDriverState *chr)
 #if SPICE_SERVER_VERSION >= 0x000c02
     g_free((char *)s->sin.portname);
 #endif
-    g_free(s);
 }
 
 static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
     if (fe_open) {
         vmc_register_interface(s);
     } else {
@@ -226,7 +229,7 @@ static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
 static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
 {
 #if SPICE_SERVER_VERSION >= 0x000c02
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
 
     if (fe_open) {
         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
@@ -255,7 +258,7 @@ static void print_allowed_subtypes(void)
 
 static void spice_chr_accept_input(struct CharDriverState *chr)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
 
     spice_server_char_device_wakeup(&s->sin);
 }
@@ -272,11 +275,9 @@ static CharDriverState *chr_open(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = g_malloc0(sizeof(SpiceCharDriver));
-    s->chr = chr;
+    s = (SpiceCharDriver *)chr;
     s->active = false;
     s->sin.subtype = g_strdup(subtype);
-    chr->opaque = s;
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
 
@@ -334,7 +335,7 @@ static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
         return NULL;
     }
     *be_opened = false;
-    s = chr->opaque;
+    s = (SpiceCharDriver *)chr;
     s->sin.portname = g_strdup(name);
 
     return chr;
@@ -388,6 +389,7 @@ static void register_types(void)
     static const CharDriver vmc_driver = {
         { "spicevmc" }, CHARDEV_BACKEND_KIND_SPICEVMC,
         qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc,
+        sizeof(SpiceCharDriver),
         .chr_write = spice_chr_write,
         .chr_add_watch = spice_chr_add_watch,
         .chr_set_fe_open = spice_vmc_set_fe_open,
@@ -397,6 +399,7 @@ static void register_types(void)
     static const CharDriver port_driver = {
         { "spiceport" }, CHARDEV_BACKEND_KIND_SPICEPORT,
         qemu_chr_parse_spice_port, qemu_chr_open_spice_port,
+        sizeof(SpiceCharDriver),
         .chr_write = spice_chr_write,
         .chr_add_watch = spice_chr_add_watch,
         .chr_set_fe_open = spice_port_set_fe_open,
diff --git a/ui/console.c b/ui/console.c
index 2944d22..b8c6328 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1035,9 +1035,15 @@ void console_select(unsigned int index)
     }
 }
 
+typedef struct VCDriverState {
+    CharDriverState parent;
+    QemuConsole *console;
+} VCDriverState;
+
 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    QemuConsole *s = chr->opaque;
+    VCDriverState *drv = (VCDriverState *)chr;
+    QemuConsole *s = drv->console;
     int i;
 
     s->update_x0 = s->width * FONT_WIDTH;
@@ -1943,7 +1949,8 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
 
 static void text_console_set_echo(CharDriverState *chr, bool echo)
 {
-    QemuConsole *s = chr->opaque;
+    VCDriverState *drv = (VCDriverState *)chr;
+    QemuConsole *s = drv->console;
 
     s->echo = echo;
 }
@@ -1983,12 +1990,11 @@ static const GraphicHwOps text_console_ops = {
 
 static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
 {
-    QemuConsole *s;
+    VCDriverState *drv = (VCDriverState *)chr;
+    QemuConsole *s = drv->console;
     int g_width = 80 * FONT_WIDTH;
     int g_height = 24 * FONT_HEIGHT;
 
-    s = chr->opaque;
-
     s->out_fifo.buf = s->out_fifo_buf;
     s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
     s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
@@ -2041,6 +2047,7 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 {
     ChardevCommon *common = qapi_ChardevVC_base(vc);
     CharDriverState *chr;
+    VCDriverState *drv;
     QemuConsole *s;
     unsigned width = 0;
     unsigned height = 0;
@@ -2077,7 +2084,8 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
     }
 
     s->chr = chr;
-    chr->opaque = s;
+    drv = (VCDriverState *)chr;
+    drv->console = s;
 
     if (display_state) {
         text_console_do_init(chr, display_state);
@@ -2182,6 +2190,7 @@ static const TypeInfo qemu_console_info = {
 
 static const CharDriver vc_driver = {
     { "vc" }, CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init,
+    sizeof(VCDriverState),
     .chr_write = console_puts,
     .chr_set_echo = text_console_set_echo,
 };
diff --git a/ui/gtk.c b/ui/gtk.c
index 1566faa..03281da 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1653,7 +1653,7 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
 
 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    VirtualConsole *vc = chr->opaque;
+    VirtualConsole *vc = (VirtualConsole *)chr;
 
     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
     return len;
@@ -1661,7 +1661,7 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
 {
-    VirtualConsole *vc = chr->opaque;
+    VirtualConsole *vc = (VirtualConsole *)chr;
 
     vc->vte.echo = echo;
 }
@@ -1673,6 +1673,7 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
 {
     static const CharDriver gd_vc_driver = {
         { "vc" }, CHARDEV_BACKEND_KIND_VC,
+        .instance_size = sizeof(VirtualConsole),
         .chr_write = gd_vc_chr_write,
         .chr_set_echo = gd_vc_chr_set_echo,
     };
@@ -1685,9 +1686,6 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
         return NULL;
     }
 
-    /* Temporary, until gd_vc_vte_init runs.  */
-    chr->opaque = g_new0(VirtualConsole, 1);
-
     vcs[nb_vcs++] = chr;
 
     return chr;
@@ -1728,13 +1726,12 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
     GtkWidget *box;
     GtkWidget *scrollbar;
     GtkAdjustment *vadjustment;
-    VirtualConsole *tmp_vc = chr->opaque;
+    VirtualConsole *tmp_vc = (VirtualConsole *)chr;
 
     vc->s = s;
     vc->vte.echo = tmp_vc->vte.echo;
 
     vc->vte.chr = chr;
-    chr->opaque = vc;
     g_free(tmp_vc);
 
     snprintf(buffer, sizeof(buffer), "vc%d", idx);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 96b464a..a072edc 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -93,7 +93,6 @@ struct CharDriverState {
     const CharDriver *driver;
     QemuMutex chr_write_lock;
     CharBackend *be;
-    void *opaque;
     char *label;
     char *filename;
     int logfd;
@@ -482,6 +481,7 @@ struct CharDriver {
                                ChardevBackend *backend,
                                ChardevReturn *ret, bool *be_opened,
                                Error **errp);
+    size_t instance_size;
 
     int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
     int (*chr_sync_read)(struct CharDriverState *s,
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 42ca0fe..6fec03f 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -23,6 +23,8 @@
 #include "ui/egl-context.h"
 #endif
 
+#include "sysemu/char.h"
+
 /* Compatibility define to let us build on both Gtk2 and Gtk3 */
 #if GTK_CHECK_VERSION(3, 0, 0)
 static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
@@ -71,6 +73,7 @@ typedef enum VirtualConsoleType {
 } VirtualConsoleType;
 
 typedef struct VirtualConsole {
+    CharDriverState parent;
     GtkDisplayState *s;
     char *label;
     GtkWidget *window;
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 38/38] bt: use qemu_chr_alloc()
  2016-10-22 10:16 ` [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object Marc-André Lureau
@ 2016-10-22 10:16   ` Marc-André Lureau
  2016-10-23 12:28   ` [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object Paolo Bonzini
  1 sibling, 0 replies; 51+ messages in thread
From: Marc-André Lureau @ 2016-10-22 10:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, Marc-André Lureau

Use common allocator for CharDriverState.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/bt/hci-csr.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index bf2deb0..e2c78b8 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -26,9 +26,10 @@
 #include "hw/irq.h"
 #include "sysemu/bt.h"
 #include "hw/bt.h"
+#include "qapi/error.h"
 
 struct csrhci_s {
-    CharDriverState chr;
+    CharDriverState parent;
     int enable;
     qemu_irq *pins;
     int pin_state;
@@ -78,7 +79,8 @@ enum {
 
 static inline void csrhci_fifo_wake(struct csrhci_s *s)
 {
-    CharBackend *be = s->chr.be;
+    CharDriverState *chr = (CharDriverState *)s;
+    CharBackend *be = chr->be;
 
     if (!s->enable || !s->out_len)
         return;
@@ -468,10 +470,15 @@ CharDriverState *uart_hci_init(void)
         .chr_write = csrhci_write,
         .chr_ioctl = csrhci_ioctl,
     };
-    struct csrhci_s *s = (struct csrhci_s *)
-            g_malloc0(sizeof(struct csrhci_s));
+    Error *err = NULL;
+    ChardevCommon common = { 0, };
+    CharDriverState *chr = qemu_chr_alloc(&hci_driver, &common, &err);
+    struct csrhci_s *s = (struct csrhci_s *)chr;
 
-    s->chr.driver = &hci_driver;
+    if (err) {
+        error_report_err(err);
+        return NULL;
+    }
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
@@ -482,5 +489,5 @@ CharDriverState *uart_hci_init(void)
     s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
     csrhci_reset(s);
 
-    return &s->chr;
+    return chr;
 }
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 07/38] xilinx: fix buffer overflow on realize
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 07/38] xilinx: fix buffer overflow on realize Marc-André Lureau
@ 2016-10-23 12:01   ` Paolo Bonzini
  0 siblings, 0 replies; 51+ messages in thread
From: Paolo Bonzini @ 2016-10-23 12:01 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel



On 22/10/2016 11:52, Marc-André Lureau wrote:
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  hw/ssi/xilinx_spips.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
> index e2b77dc..ab7fa6f 100644
> --- a/hw/ssi/xilinx_spips.c
> +++ b/hw/ssi/xilinx_spips.c
> @@ -620,7 +620,7 @@ 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]);

Better add a for loop going up to s->num_busses.  I can do that on commit.

Paolo

>      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]);
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 09/38] char: introduce CharBackend
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 09/38] char: introduce CharBackend Marc-André Lureau
@ 2016-10-23 12:03   ` Paolo Bonzini
  0 siblings, 0 replies; 51+ messages in thread
From: Paolo Bonzini @ 2016-10-23 12:03 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel



On 22/10/2016 11:52, Marc-André Lureau wrote:
> +    assert(be);

I feel a GNOME perturbation in the force. :D

The assertion (unlike g_return_value_if_fail) is unnecessary, because
you'd crash anyway eventually.

Paolo

> +    return be->chr;
> +}
> +
> +bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
> +{
> +    int tag = 0;
> +
> +    assert(b);
> +    assert(s);
> +

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 32/38] char: use a static array for backends
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 32/38] char: use a static array for backends Marc-André Lureau
@ 2016-10-23 12:21     ` Paolo Bonzini
  0 siblings, 0 replies; 51+ messages in thread
From: Paolo Bonzini @ 2016-10-23 12:21 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel



On 22/10/2016 12:09, Marc-André Lureau wrote:
> Number and kinds of backends is known at compile-time, use a fixed-sized
> static array to simplify iterations & lookups.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  backends/baum.c       |   2 +-
>  backends/msmouse.c    |   2 +-
>  backends/testdev.c    |   2 +-
>  qemu-char.c           | 117 +++++++++++++++++++++++++++-----------------------
>  spice-qemu-char.c     |   4 +-
>  ui/console.c          |   2 +-
>  include/sysemu/char.h |   2 +-
>  7 files changed, 70 insertions(+), 61 deletions(-)
> 
> diff --git a/backends/baum.c b/backends/baum.c
> index 4fe11de..0a65c99 100644
> --- a/backends/baum.c
> +++ b/backends/baum.c
> @@ -644,7 +644,7 @@ fail_handle:
>  static void register_types(void)
>  {
>      static const CharDriver driver = {
> -        "braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init
> +        { "braille" }, CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init
>      };
>  
>      register_char_driver(&driver);
> diff --git a/backends/msmouse.c b/backends/msmouse.c
> index d6ab4ca..3367d67 100644
> --- a/backends/msmouse.c
> +++ b/backends/msmouse.c
> @@ -180,7 +180,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
>  static void register_types(void)
>  {
>      static const CharDriver driver = {
> -        "msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL, qemu_chr_open_msmouse
> +        { "msmouse" }, CHARDEV_BACKEND_KIND_MSMOUSE, NULL, qemu_chr_open_msmouse
>      };
>      register_char_driver(&driver);
>  }
> diff --git a/backends/testdev.c b/backends/testdev.c
> index 5936189..d41352a 100644
> --- a/backends/testdev.c
> +++ b/backends/testdev.c
> @@ -131,7 +131,7 @@ static CharDriverState *chr_testdev_init(const char *id,
>  static void register_types(void)
>  {
>      static const CharDriver driver = {
> -        "testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init
> +        { "testdev" }, CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init
>      };
>      register_char_driver(&driver);
>  }
> diff --git a/qemu-char.c b/qemu-char.c
> index 594e795..7348cb0 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -4042,20 +4042,20 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
>      }
>  }
>  
> -static GSList *backends;
> +static const CharDriver *backends[CHARDEV_BACKEND_KIND__MAX];
>  
>  void register_char_driver(const CharDriver *driver)
>  {
> -    backends = g_slist_append(backends, (void *)driver);
> +    backends[driver->kind] = driver;
>  }
>  
>  CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
>                                          Error **errp)
>  {
>      Error *local_err = NULL;
> -    CharDriver *cd;
> +    const CharDriver *cd;
>      CharDriverState *chr;
> -    GSList *i;
> +    int i;
>      ChardevReturn *ret = NULL;
>      ChardevBackend *backend;
>      const char *id = qemu_opts_id(opts);
> @@ -4069,9 +4069,14 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
>  
>      if (is_help_option(qemu_opt_get(opts, "backend"))) {
>          fprintf(stderr, "Available chardev backend types:\n");
> -        for (i = backends; i; i = i->next) {
> -            cd = i->data;
> -            fprintf(stderr, "%s\n", cd->name);
> +        for (i = 0; i < ARRAY_SIZE(backends); i++) {
> +            cd = backends[i];
> +            if (cd) {
> +                fprintf(stderr, "%s\n", cd->name[0]);
> +                if (cd->name[1]) {
> +                    fprintf(stderr, "%s\n", cd->name[1]);
> +                }
> +            }
>          }
>          exit(!is_help_option(qemu_opt_get(opts, "backend")));
>      }
> @@ -4081,14 +4086,17 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
>          goto err;
>      }
>  
> -    for (i = backends; i; i = i->next) {
> -        cd = i->data;
> +    cd = NULL;
> +    for (i = 0; i < ARRAY_SIZE(backends); i++) {
> +        const char *name = qemu_opt_get(opts, "backend");
> +        cd = backends[i];
>  
> -        if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
> +        if (cd && (g_str_equal(cd->name[0], name) ||
> +                   (cd->name[1] && g_str_equal(cd->name[1], name)))) {
>              break;
>          }
>      }
> -    if (i == NULL) {
> +    if (cd == NULL) {
>          error_setg(errp, "chardev: backend \"%s\" not found",
>                     qemu_opt_get(opts, "backend"));
>          goto err;
> @@ -4293,20 +4301,32 @@ ChardevInfoList *qmp_query_chardev(Error **errp)
>      return chr_list;
>  }
>  
> +static ChardevBackendInfoList *
> +qmp_prepend_backend(ChardevBackendInfoList *list, const CharDriver *c,
> +                    const char *name)
> +{
> +    ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
> +    info->value = g_malloc0(sizeof(*info->value));
> +    info->value->name = g_strdup(name);
> +    info->next = list;
> +    return info;
> +
> +}
> +
>  ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
>  {
>      ChardevBackendInfoList *backend_list = NULL;
> -    CharDriver *c = NULL;
> -    GSList *i = NULL;
> -
> -    for (i = backends; i; i = i->next) {
> -        ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
> -        c = i->data;
> -        info->value = g_malloc0(sizeof(*info->value));
> -        info->value->name = g_strdup(c->name);
> +    const CharDriver *c;
> +    int i;
>  
> -        info->next = backend_list;
> -        backend_list = info;
> +    for (i = 0; i < ARRAY_SIZE(backends); i++) {
> +        c = backends[i];
> +        if (c) {
> +            backend_list = qmp_prepend_backend(backend_list, c, c->name[0]);
> +            if (c->name[1]) {
> +                backend_list = qmp_prepend_backend(backend_list, c, c->name[1]);
> +            }
> +        }
>      }
>  
>      return backend_list;
> @@ -4746,9 +4766,8 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
>  {
>      ChardevReturn *ret = g_new0(ChardevReturn, 1);
>      CharDriverState *chr = NULL;
> +    const CharDriver *cd;
>      Error *local_err = NULL;
> -    GSList *i;
> -    CharDriver *cd;
>      bool be_opened = true;
>  
>      chr = qemu_chr_find(id);
> @@ -4757,22 +4776,16 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
>          goto out_error;
>      }
>  
> -    for (i = backends; i; i = i->next) {
> -        cd = i->data;
> -
> -        if (cd->kind == backend->type) {
> -            chr = cd->create(id, backend, ret, &be_opened, &local_err);
> -            if (local_err) {
> -                error_propagate(errp, local_err);
> -                goto out_error;
> -            }
> -            break;
> -        }
> +    cd = (int)backend->type >= 0 && backend->type < ARRAY_SIZE(backends) ?
> +        backends[backend->type] : NULL;
> +    if (cd == NULL) {
> +        error_setg(errp, "chardev backend not available");
> +        goto out_error;
>      }
>  
> -    if (chr == NULL) {
> -        assert(!i);
> -        error_setg(errp, "chardev backend not available");
> +    chr = cd->create(id, backend, ret, &be_opened, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
>          goto out_error;
>      }
>  
> @@ -4825,42 +4838,38 @@ static void register_types(void)
>  {
>      int i;
>      static const CharDriver drivers[] = {
> -        { "null", CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null },
> -        { "socket", CHARDEV_BACKEND_KIND_SOCKET,
> +        { { "null" }, CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null },
> +        { { "socket" }, CHARDEV_BACKEND_KIND_SOCKET,
>            qemu_chr_parse_socket, qmp_chardev_open_socket },
> -        { "udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
> +        { { "udp" }, CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
>            qmp_chardev_open_udp },
> -        { "ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
> +        { { "ringbuf" }, CHARDEV_BACKEND_KIND_RINGBUF,
>            qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
> -        { "file", CHARDEV_BACKEND_KIND_FILE,
> +        { { "file" }, CHARDEV_BACKEND_KIND_FILE,
>            qemu_chr_parse_file_out, qmp_chardev_open_file },
> -        { "stdio", CHARDEV_BACKEND_KIND_STDIO,
> +        { { "stdio" }, CHARDEV_BACKEND_KIND_STDIO,
>            qemu_chr_parse_stdio, qemu_chr_open_stdio },
>  #if defined HAVE_CHARDEV_SERIAL
> -        { "serial", CHARDEV_BACKEND_KIND_SERIAL,
> -          qemu_chr_parse_serial, qmp_chardev_open_serial },
> -        { "tty", CHARDEV_BACKEND_KIND_SERIAL,
> +        { { "serial", "tty" }, CHARDEV_BACKEND_KIND_SERIAL,
>            qemu_chr_parse_serial, qmp_chardev_open_serial },
>  #endif
>  #ifdef HAVE_CHARDEV_PARPORT
> -        { "parallel", CHARDEV_BACKEND_KIND_PARALLEL,
> -          qemu_chr_parse_parallel, qmp_chardev_open_parallel },
> -        { "parport", CHARDEV_BACKEND_KIND_PARALLEL,
> +        { { "parallel", "parport" }, CHARDEV_BACKEND_KIND_PARALLEL,
>            qemu_chr_parse_parallel, qmp_chardev_open_parallel },
>  #endif
>  #ifdef HAVE_CHARDEV_PTY
> -        { "pty", CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty },
> +        { { "pty" }, CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty },
>  #endif
>  #ifdef _WIN32
> -        { "console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
> +        { { "console" }, CHARDEV_BACKEND_KIND_CONSOLE, NULL,
>            qemu_chr_open_win_con },
>  #endif
> -        { "pipe", CHARDEV_BACKEND_KIND_PIPE,
> +        { { "pipe" }, CHARDEV_BACKEND_KIND_PIPE,
>            qemu_chr_parse_pipe, qemu_chr_open_pipe },
> -        { "mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
> +        { { "mux" }, CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
>            qemu_chr_open_mux },
>          /* Bug-compatibility: */
> -        { "memory", CHARDEV_BACKEND_KIND_MEMORY,
> +        { { "memory" }, CHARDEV_BACKEND_KIND_MEMORY,
>            qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
>      };
>  
> diff --git a/spice-qemu-char.c b/spice-qemu-char.c
> index 7ce8527..3172461 100644
> --- a/spice-qemu-char.c
> +++ b/spice-qemu-char.c
> @@ -390,11 +390,11 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
>  static void register_types(void)
>  {
>      static const CharDriver vmc_driver = {
> -        "spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
> +        { "spicevmc" }, CHARDEV_BACKEND_KIND_SPICEVMC,
>          qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc
>      };
>      static const CharDriver port_driver = {
> -        "spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
> +        { "spiceport" }, CHARDEV_BACKEND_KIND_SPICEPORT,
>          qemu_chr_parse_spice_port, qemu_chr_open_spice_port
>      };
>      register_char_driver(&vmc_driver);
> diff --git a/ui/console.c b/ui/console.c
> index a65223a..4e31cf2 100644
> --- a/ui/console.c
> +++ b/ui/console.c
> @@ -2183,7 +2183,7 @@ static const TypeInfo qemu_console_info = {
>  static void register_types(void)
>  {
>      static const CharDriver vc_driver = {
> -        "vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init
> +        { "vc" }, CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init
>      };
>  
>      type_register_static(&qemu_console_info);
> diff --git a/include/sysemu/char.h b/include/sysemu/char.h
> index 6a4b3ef..ee5618b 100644
> --- a/include/sysemu/char.h
> +++ b/include/sysemu/char.h
> @@ -474,7 +474,7 @@ void qemu_chr_set_feature(CharDriverState *chr,
>  QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
>  
>  typedef struct CharDriver {
> -    const char *name;
> +    const char *name[2]; /* name & opt alias */

With designated initializers it is nicer to write this as

    const char *name;
    const char *alias;

instead.

Paolo

>      ChardevBackendKind kind;
>      void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
>      CharDriverState *(*create)(const char *id,
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 31/38] char: use a const CharDriver
  2016-10-22 10:09   ` [Qemu-devel] [PATCH 31/38] char: use a const CharDriver Marc-André Lureau
@ 2016-10-23 12:24     ` Paolo Bonzini
  0 siblings, 0 replies; 51+ messages in thread
From: Paolo Bonzini @ 2016-10-23 12:24 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel



On 22/10/2016 12:09, Marc-André Lureau wrote:
> No need to allocate & copy fileds, let's use static const struct
> instead.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  backends/baum.c       |  7 ++--
>  backends/msmouse.c    |  6 ++--
>  backends/testdev.c    |  6 ++--
>  qemu-char.c           | 89 +++++++++++++++++++++++----------------------------
>  spice-qemu-char.c     | 14 +++++---
>  ui/console.c          |  8 +++--
>  include/sysemu/char.h | 20 ++++++------
>  7 files changed, 79 insertions(+), 71 deletions(-)
> 
> diff --git a/backends/baum.c b/backends/baum.c
> index 919844e..4fe11de 100644
> --- a/backends/baum.c
> +++ b/backends/baum.c
> @@ -643,8 +643,11 @@ fail_handle:
>  
>  static void register_types(void)
>  {
> -    register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL,
> -                         chr_baum_init);
> +    static const CharDriver driver = {
> +        "braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init

As mentioned in the review to patch 32, please use designated
initializers for this.

But actually const char *name is superfluous even, because you can use
ChardevBackendKind_lookup[cd->kind] instead.

Paolo

> +    };
> +
> +    register_char_driver(&driver);
>  }
>  
>  type_init(register_types);
> diff --git a/backends/msmouse.c b/backends/msmouse.c
> index 733ca80..d6ab4ca 100644
> --- a/backends/msmouse.c
> +++ b/backends/msmouse.c
> @@ -179,8 +179,10 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
>  
>  static void register_types(void)
>  {
> -    register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL,
> -                         qemu_chr_open_msmouse);
> +    static const CharDriver driver = {
> +        "msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL, qemu_chr_open_msmouse
> +    };
> +    register_char_driver(&driver);
>  }
>  
>  type_init(register_types);
> diff --git a/backends/testdev.c b/backends/testdev.c
> index 60156e3..5936189 100644
> --- a/backends/testdev.c
> +++ b/backends/testdev.c
> @@ -130,8 +130,10 @@ static CharDriverState *chr_testdev_init(const char *id,
>  
>  static void register_types(void)
>  {
> -    register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL,
> -                         chr_testdev_init);
> +    static const CharDriver driver = {
> +        "testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init
> +    };
> +    register_char_driver(&driver);
>  }
>  
>  type_init(register_types);
> diff --git a/qemu-char.c b/qemu-char.c
> index 8e05c8c..594e795 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -4042,27 +4042,11 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
>      }
>  }
>  
> -typedef struct CharDriver {
> -    const char *name;
> -    ChardevBackendKind kind;
> -    CharDriverParse *parse;
> -    CharDriverCreate *create;
> -} CharDriver;
> -
>  static GSList *backends;
>  
> -void register_char_driver(const char *name, ChardevBackendKind kind,
> -                          CharDriverParse *parse, CharDriverCreate *create)
> +void register_char_driver(const CharDriver *driver)
>  {
> -    CharDriver *s;
> -
> -    s = g_malloc0(sizeof(*s));
> -    s->name = g_strdup(name);
> -    s->kind = kind;
> -    s->parse = parse;
> -    s->create = create;
> -
> -    backends = g_slist_append(backends, s);
> +    backends = g_slist_append(backends, (void *)driver);
>  }
>  
>  CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
> @@ -4839,45 +4823,52 @@ void qemu_chr_cleanup(void)
>  
>  static void register_types(void)
>  {
> -    register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL,
> -                         qemu_chr_open_null);
> -    register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET,
> -                         qemu_chr_parse_socket, qmp_chardev_open_socket);
> -    register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
> -                         qmp_chardev_open_udp);
> -    register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
> -                         qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
> -    register_char_driver("file", CHARDEV_BACKEND_KIND_FILE,
> -                         qemu_chr_parse_file_out, qmp_chardev_open_file);
> -    register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO,
> -                         qemu_chr_parse_stdio, qemu_chr_open_stdio);
> +    int i;
> +    static const CharDriver drivers[] = {
> +        { "null", CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null },
> +        { "socket", CHARDEV_BACKEND_KIND_SOCKET,
> +          qemu_chr_parse_socket, qmp_chardev_open_socket },
> +        { "udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
> +          qmp_chardev_open_udp },
> +        { "ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
> +          qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
> +        { "file", CHARDEV_BACKEND_KIND_FILE,
> +          qemu_chr_parse_file_out, qmp_chardev_open_file },
> +        { "stdio", CHARDEV_BACKEND_KIND_STDIO,
> +          qemu_chr_parse_stdio, qemu_chr_open_stdio },
>  #if defined HAVE_CHARDEV_SERIAL
> -    register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL,
> -                         qemu_chr_parse_serial, qmp_chardev_open_serial);
> -    register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL,
> -                         qemu_chr_parse_serial, qmp_chardev_open_serial);
> +        { "serial", CHARDEV_BACKEND_KIND_SERIAL,
> +          qemu_chr_parse_serial, qmp_chardev_open_serial },
> +        { "tty", CHARDEV_BACKEND_KIND_SERIAL,
> +          qemu_chr_parse_serial, qmp_chardev_open_serial },
>  #endif
>  #ifdef HAVE_CHARDEV_PARPORT
> -    register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
> -                         qemu_chr_parse_parallel, qmp_chardev_open_parallel);
> -    register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL,
> -                         qemu_chr_parse_parallel, qmp_chardev_open_parallel);
> +        { "parallel", CHARDEV_BACKEND_KIND_PARALLEL,
> +          qemu_chr_parse_parallel, qmp_chardev_open_parallel },
> +        { "parport", CHARDEV_BACKEND_KIND_PARALLEL,
> +          qemu_chr_parse_parallel, qmp_chardev_open_parallel },
>  #endif
>  #ifdef HAVE_CHARDEV_PTY
> -    register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL,
> -                         qemu_chr_open_pty);
> +        { "pty", CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty },
>  #endif
>  #ifdef _WIN32
> -    register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
> -                         qemu_chr_open_win_con);
> +        { "console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
> +          qemu_chr_open_win_con },
>  #endif
> -    register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE,
> -                         qemu_chr_parse_pipe, qemu_chr_open_pipe);
> -    register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
> -                         qemu_chr_open_mux);
> -    /* Bug-compatibility: */
> -    register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
> -                         qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
> +        { "pipe", CHARDEV_BACKEND_KIND_PIPE,
> +          qemu_chr_parse_pipe, qemu_chr_open_pipe },
> +        { "mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
> +          qemu_chr_open_mux },
> +        /* Bug-compatibility: */
> +        { "memory", CHARDEV_BACKEND_KIND_MEMORY,
> +          qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf },
> +    };
> +
> +
> +    for (i = 0; i < ARRAY_SIZE(drivers); i++) {
> +        register_char_driver(&drivers[i]);
> +    }
> +
>      /* this must be done after machine init, since we register FEs with muxes
>       * as part of realize functions like serial_isa_realizefn when -nographic
>       * is specified
> diff --git a/spice-qemu-char.c b/spice-qemu-char.c
> index 276c4ae..7ce8527 100644
> --- a/spice-qemu-char.c
> +++ b/spice-qemu-char.c
> @@ -389,10 +389,16 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
>  
>  static void register_types(void)
>  {
> -    register_char_driver("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
> -                         qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc);
> -    register_char_driver("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
> -                         qemu_chr_parse_spice_port, qemu_chr_open_spice_port);
> +    static const CharDriver vmc_driver = {
> +        "spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
> +        qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc
> +    };
> +    static const CharDriver port_driver = {
> +        "spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
> +        qemu_chr_parse_spice_port, qemu_chr_open_spice_port
> +    };
> +    register_char_driver(&vmc_driver);
> +    register_char_driver(&port_driver);
>  }
>  
>  type_init(register_types);
> diff --git a/ui/console.c b/ui/console.c
> index ed888e5..a65223a 100644
> --- a/ui/console.c
> +++ b/ui/console.c
> @@ -2180,12 +2180,14 @@ static const TypeInfo qemu_console_info = {
>      .class_size = sizeof(QemuConsoleClass),
>  };
>  
> -
>  static void register_types(void)
>  {
> +    static const CharDriver vc_driver = {
> +        "vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init
> +    };
> +
>      type_register_static(&qemu_console_info);
> -    register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc,
> -                         vc_init);
> +    register_char_driver(&vc_driver);
>  }
>  
>  type_init(register_types);
> diff --git a/include/sysemu/char.h b/include/sysemu/char.h
> index 0a14942..6a4b3ef 100644
> --- a/include/sysemu/char.h
> +++ b/include/sysemu/char.h
> @@ -473,15 +473,17 @@ void qemu_chr_set_feature(CharDriverState *chr,
>                            CharDriverFeature feature);
>  QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
>  
> -typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend,
> -                             Error **errp);
> -typedef CharDriverState *CharDriverCreate(const char *id,
> -                                          ChardevBackend *backend,
> -                                          ChardevReturn *ret, bool *be_opened,
> -                                          Error **errp);
> -
> -void register_char_driver(const char *name, ChardevBackendKind kind,
> -                          CharDriverParse *parse, CharDriverCreate *create);
> +typedef struct CharDriver {
> +    const char *name;
> +    ChardevBackendKind kind;
> +    void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
> +    CharDriverState *(*create)(const char *id,
> +                               ChardevBackend *backend,
> +                               ChardevReturn *ret, bool *be_opened,
> +                               Error **errp);
> +} CharDriver;
> +
> +void register_char_driver(const CharDriver *driver);
>  
>  extern int term_escape_char;
>  
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object
  2016-10-22 10:16 ` [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object Marc-André Lureau
  2016-10-22 10:16   ` [Qemu-devel] [PATCH 38/38] bt: use qemu_chr_alloc() Marc-André Lureau
@ 2016-10-23 12:28   ` Paolo Bonzini
  1 sibling, 0 replies; 51+ messages in thread
From: Paolo Bonzini @ 2016-10-23 12:28 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel



On 22/10/2016 12:16, Marc-André Lureau wrote:
> Use a single allocation for CharDriverState, this avoids extra
> allocations & pointers, and is a step towards more object-oriented
> CharDriver.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  backends/baum.c       |  23 ++---
>  backends/msmouse.c    |  16 +--
>  backends/testdev.c    |  22 ++--
>  gdbstub.c             |   1 +
>  hw/bt/hci-csr.c       |  10 +-
>  qemu-char.c           | 275 ++++++++++++++++++++++++++------------------------
>  spice-qemu-char.c     |  39 +++----
>  ui/console.c          |  21 ++--
>  ui/gtk.c              |  11 +-
>  include/sysemu/char.h |   2 +-
>  include/ui/gtk.h      |   3 +
>  11 files changed, 215 insertions(+), 208 deletions(-)
> 
> diff --git a/backends/baum.c b/backends/baum.c
> index b2d4719..45a6fd4 100644
> --- a/backends/baum.c
> +++ b/backends/baum.c
> @@ -87,7 +87,7 @@
>  #define BUF_SIZE 256
>  
>  typedef struct {
> -    CharDriverState *chr;
> +    CharDriverState parent;
>  
>      brlapi_handle_t *brlapi;
>      int brlapi_fd;
> @@ -219,7 +219,7 @@ static const uint8_t nabcc_translation[256] = {
>  /* The serial port can receive more of our data */
>  static void baum_accept_input(struct CharDriverState *chr)
>  {
> -    BaumDriverState *baum = chr->opaque;
> +    BaumDriverState *baum = (BaumDriverState *)chr;
>      int room, first;
>  
>      if (!baum->out_buf_used)
> @@ -245,22 +245,23 @@ static void baum_accept_input(struct CharDriverState *chr)
>  /* We want to send a packet */
>  static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len)
>  {
> +    CharDriverState *chr = (CharDriverState *)baum;
>      uint8_t io_buf[1 + 2 * len], *cur = io_buf;
>      int room;
>      *cur++ = ESC;
>      while (len--)
>          if ((*cur++ = *buf++) == ESC)
>              *cur++ = ESC;
> -    room = qemu_chr_be_can_write(baum->chr);
> +    room = qemu_chr_be_can_write(chr);

Let's use &baum->chr instead, and likewise everywhere else.

Paolo

>      len = cur - io_buf;
>      if (len <= room) {
>          /* Fits */
> -        qemu_chr_be_write(baum->chr, io_buf, len);
> +        qemu_chr_be_write(chr, io_buf, len);
>      } else {
>          int first;
>          uint8_t out;
>          /* Can't fit all, send what can be, and store the rest. */
> -        qemu_chr_be_write(baum->chr, io_buf, room);
> +        qemu_chr_be_write(chr, io_buf, room);
>          len -= room;
>          cur = io_buf + room;
>          if (len > BUF_SIZE - baum->out_buf_used) {
> @@ -433,7 +434,7 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
>  /* The other end is writing some data.  Store it and try to interpret */
>  static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    BaumDriverState *baum = chr->opaque;
> +    BaumDriverState *baum = (BaumDriverState *)chr;
>      int tocopy, cur, eaten, orig_len = len;
>  
>      if (!len)
> @@ -553,14 +554,13 @@ static void baum_chr_read(void *opaque)
>  
>  static void baum_free(struct CharDriverState *chr)
>  {
> -    BaumDriverState *baum = chr->opaque;
> +    BaumDriverState *baum = (BaumDriverState *)chr;
>  
>      timer_free(baum->cellCount_timer);
>      if (baum->brlapi) {
>          brlapi__closeConnection(baum->brlapi);
>          g_free(baum->brlapi);
>      }
> -    g_free(baum);
>  }
>  
>  static CharDriverState *chr_baum_init(const CharDriver *driver,
> @@ -585,10 +585,7 @@ static CharDriverState *chr_baum_init(const CharDriver *driver,
>      if (!chr) {
>          return NULL;
>      }
> -    baum = g_malloc0(sizeof(BaumDriverState));
> -    baum->chr = chr;
> -
> -    chr->opaque = baum;
> +    baum = (BaumDriverState *)chr;
>  
>      handle = g_malloc0(brlapi_getHandleSize());
>      baum->brlapi = handle;
> @@ -635,7 +632,6 @@ fail:
>  fail_handle:
>      g_free(handle);
>      g_free(chr);
> -    g_free(baum);
>      return NULL;
>  }
>  
> @@ -643,6 +639,7 @@ static void register_types(void)
>  {
>      static const CharDriver driver = {
>          { "braille" }, CHARDEV_BACKEND_KIND_BRAILLE, NULL, chr_baum_init,
> +        sizeof(BaumDriverState),
>          .chr_write = baum_write,
>          .chr_accept_input = baum_accept_input,
>          .chr_free = baum_free,
> diff --git a/backends/msmouse.c b/backends/msmouse.c
> index f71aab0..37e7b82 100644
> --- a/backends/msmouse.c
> +++ b/backends/msmouse.c
> @@ -31,7 +31,8 @@
>  #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
>  
>  typedef struct {
> -    CharDriverState *chr;
> +    CharDriverState parent;
> +
>      QemuInputHandlerState *hs;
>      int axis[INPUT_AXIS__MAX];
>      bool btns[INPUT_BUTTON__MAX];
> @@ -42,7 +43,7 @@ typedef struct {
>  
>  static void msmouse_chr_accept_input(CharDriverState *chr)
>  {
> -    MouseState *mouse = chr->opaque;
> +    MouseState *mouse = (MouseState *)chr;
>      int len;
>  
>      len = qemu_chr_be_can_write(chr);
> @@ -122,9 +123,10 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
>  static void msmouse_input_sync(DeviceState *dev)
>  {
>      MouseState *mouse = (MouseState *)dev;
> +    CharDriverState *chr = (CharDriverState *)dev;
>  
>      msmouse_queue_event(mouse);
> -    msmouse_chr_accept_input(mouse->chr);
> +    msmouse_chr_accept_input(chr);
>  }
>  
>  static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
> @@ -135,10 +137,9 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int
>  
>  static void msmouse_chr_free(struct CharDriverState *chr)
>  {
> -    MouseState *mouse = chr->opaque;
> +    MouseState *mouse = (MouseState *)chr;
>  
>      qemu_input_handler_unregister(mouse->hs);
> -    g_free(mouse);
>  }
>  
>  static QemuInputHandler msmouse_handler = {
> @@ -165,12 +166,10 @@ static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
>      }
>      *be_opened = false;
>  
> -    mouse = g_new0(MouseState, 1);
> +    mouse = (MouseState *)chr;
>      mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
>                                              &msmouse_handler);
>  
> -    mouse->chr = chr;
> -    chr->opaque = mouse;
>  
>      return chr;
>  }
> @@ -180,6 +179,7 @@ static void register_types(void)
>      static const CharDriver driver = {
>          { "msmouse" }, CHARDEV_BACKEND_KIND_MSMOUSE,
>          NULL, qemu_chr_open_msmouse,
> +        sizeof(MouseState),
>          .chr_write = msmouse_chr_write,
>          .chr_accept_input = msmouse_chr_accept_input,
>          .chr_free = msmouse_chr_free,
> diff --git a/backends/testdev.c b/backends/testdev.c
> index 774cd47..4b1953b 100644
> --- a/backends/testdev.c
> +++ b/backends/testdev.c
> @@ -30,7 +30,8 @@
>  #define BUF_SIZE 32
>  
>  typedef struct {
> -    CharDriverState *chr;
> +    CharDriverState parent;
> +
>      uint8_t in_buf[32];
>      int in_buf_used;
>  } TestdevCharState;
> @@ -79,7 +80,7 @@ static int testdev_eat_packet(TestdevCharState *testdev)
>  /* The other end is writing some data.  Store it and try to interpret */
>  static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    TestdevCharState *testdev = chr->opaque;
> +    TestdevCharState *testdev = (TestdevCharState *)chr;
>      int tocopy, eaten, orig_len = len;
>  
>      while (len) {
> @@ -102,13 +103,6 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
>      return orig_len;
>  }
>  
> -static void testdev_free(struct CharDriverState *chr)
> -{
> -    TestdevCharState *testdev = chr->opaque;
> -
> -    g_free(testdev);
> -}
> -
>  static CharDriverState *chr_testdev_init(const CharDriver *driver,
>                                           const char *id,
>                                           ChardevBackend *backend,
> @@ -116,14 +110,10 @@ static CharDriverState *chr_testdev_init(const CharDriver *driver,
>                                           bool *be_opened,
>                                           Error **errp)
>  {
> -    TestdevCharState *testdev;
> -    CharDriverState *chr;
> -
> -    testdev = g_new0(TestdevCharState, 1);
> -    testdev->chr = chr = g_new0(CharDriverState, 1);
> +    TestdevCharState *testdev = g_new0(TestdevCharState, 1);;
> +    CharDriverState *chr = (CharDriverState *)testdev;
>  
>      chr->driver = driver;
> -    chr->opaque = testdev;
>  
>      return chr;
>  }
> @@ -132,8 +122,8 @@ static void register_types(void)
>  {
>      static const CharDriver driver = {
>          { "testdev" }, CHARDEV_BACKEND_KIND_TESTDEV, NULL, chr_testdev_init,
> +        sizeof(TestdevCharState),
>          .chr_write = testdev_write,
> -        .chr_free = testdev_free,
>      };
>      register_char_driver(&driver);
>  }
> diff --git a/gdbstub.c b/gdbstub.c
> index 8e43836..3a9f857 100644
> --- a/gdbstub.c
> +++ b/gdbstub.c
> @@ -1732,6 +1732,7 @@ int gdbserver_start(const char *device)
>      CharDriverState *mon_chr;
>      ChardevCommon common = { 0 };
>      static const CharDriver driver = {
> +        .instance_size = sizeof(CharDriverState),
>          .kind = -1,
>          .chr_write = gdb_monitor_write
>      };
> diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
> index 9c3fb3c..bf2deb0 100644
> --- a/hw/bt/hci-csr.c
> +++ b/hw/bt/hci-csr.c
> @@ -28,11 +28,11 @@
>  #include "hw/bt.h"
>  
>  struct csrhci_s {
> +    CharDriverState chr;
>      int enable;
>      qemu_irq *pins;
>      int pin_state;
>      int modem_state;
> -    CharDriverState chr;
>  #define FIFO_LEN	4096
>      int out_start;
>      int out_len;
> @@ -314,7 +314,7 @@ static void csrhci_ready_for_next_inpkt(struct csrhci_s *s)
>  static int csrhci_write(struct CharDriverState *chr,
>                  const uint8_t *buf, int len)
>  {
> -    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
> +    struct csrhci_s *s = (struct csrhci_s *)chr;
>      int total = 0;
>  
>      if (!s->enable)
> @@ -387,7 +387,7 @@ static void csrhci_out_hci_packet_acl(void *opaque,
>  static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
>  {
>      QEMUSerialSetParams *ssp;
> -    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
> +    struct csrhci_s *s = (struct csrhci_s *) chr;
>      int prev_state = s->modem_state;
>  
>      switch (cmd) {
> @@ -455,7 +455,7 @@ static void csrhci_pins(void *opaque, int line, int level)
>  
>  qemu_irq *csrhci_pins_get(CharDriverState *chr)
>  {
> -    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
> +    struct csrhci_s *s = (struct csrhci_s *) chr;
>  
>      return s->pins;
>  }
> @@ -463,6 +463,7 @@ qemu_irq *csrhci_pins_get(CharDriverState *chr)
>  CharDriverState *uart_hci_init(void)
>  {
>      static const CharDriver hci_driver = {
> +        .instance_size = sizeof(struct csrhci_s),
>          .kind = -1,
>          .chr_write = csrhci_write,
>          .chr_ioctl = csrhci_ioctl,
> @@ -470,7 +471,6 @@ CharDriverState *uart_hci_init(void)
>      struct csrhci_s *s = (struct csrhci_s *)
>              g_malloc0(sizeof(struct csrhci_s));
>  
> -    s->chr.opaque = s;
>      s->chr.driver = &hci_driver;
>  
>      s->hci = qemu_next_hci();
> diff --git a/qemu-char.c b/qemu-char.c
> index fe8f717..1da7244 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -165,12 +165,14 @@ static void qemu_chr_free_common(CharDriverState *chr);
>  CharDriverState *qemu_chr_alloc(const CharDriver *driver,
>                                  ChardevCommon *backend, Error **errp)
>  {
> -    CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
> -    qemu_mutex_init(&chr->chr_write_lock);
> +    CharDriverState *chr;
>  
>      assert(driver);
>      assert(driver->chr_write);
> +    assert(driver->instance_size >= sizeof(CharDriverState));
>  
> +    chr = g_malloc0(driver->instance_size);
> +    qemu_mutex_init(&chr->chr_write_lock);
>      if (backend->has_logfile) {
>          int flags = O_WRONLY | O_CREAT;
>          if (backend->has_logappend &&
> @@ -537,6 +539,7 @@ static CharDriverState *qemu_chr_open_null(const CharDriver *driver,
>  
>  static const CharDriver null_driver = {
>      { "null" }, CHARDEV_BACKEND_KIND_NULL, NULL, qemu_chr_open_null,
> +    sizeof(CharDriverState),
>      .chr_write = null_chr_write
>  };
>  
> @@ -545,6 +548,7 @@ static const CharDriver null_driver = {
>  #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
>  #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
>  struct MuxDriver {
> +    CharDriverState parent;
>      CharBackend *backends[MAX_MUX];
>      CharBackend chr;
>      int focus;
> @@ -567,7 +571,7 @@ struct MuxDriver {
>  /* Called with chr_write_lock held.  */
>  static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    MuxDriver *d = chr->opaque;
> +    MuxDriver *d = (MuxDriver *)chr;
>      int ret;
>      if (!d->timestamps) {
>          ret = qemu_chr_fe_write(&d->chr, buf, len);
> @@ -701,7 +705,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
>  
>  static void mux_chr_accept_input(CharDriverState *chr)
>  {
> -    MuxDriver *d = chr->opaque;
> +    MuxDriver *d = (MuxDriver *)chr;
>      int m = d->focus;
>      CharBackend *be = d->backends[m];
>  
> @@ -714,8 +718,7 @@ static void mux_chr_accept_input(CharDriverState *chr)
>  
>  static int mux_chr_can_read(void *opaque)
>  {
> -    CharDriverState *chr = opaque;
> -    MuxDriver *d = chr->opaque;
> +    MuxDriver *d = opaque;
>      int m = d->focus;
>      CharBackend *be = d->backends[m];
>  
> @@ -733,7 +736,7 @@ static int mux_chr_can_read(void *opaque)
>  static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
>  {
>      CharDriverState *chr = opaque;
> -    MuxDriver *d = chr->opaque;
> +    MuxDriver *d = opaque;
>      int m = d->focus;
>      CharBackend *be = d->backends[m];
>      int i;
> @@ -753,8 +756,7 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
>  
>  static void mux_chr_event(void *opaque, int event)
>  {
> -    CharDriverState *chr = opaque;
> -    MuxDriver *d = chr->opaque;
> +    MuxDriver *d = opaque;
>      int i;
>  
>      /* Send the event to all registered listeners */
> @@ -782,7 +784,7 @@ static void muxes_realize_done(Notifier *notifier, void *unused)
>  
>      QTAILQ_FOREACH(chr, &chardevs, next) {
>          if (qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MUX) {
> -            MuxDriver *d = chr->opaque;
> +            MuxDriver *d = (MuxDriver *)chr;
>              int i;
>  
>              /* send OPENED to all already-attached FEs */
> @@ -804,7 +806,7 @@ static Notifier muxes_realize_notify = {
>  
>  static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
>  {
> -    MuxDriver *d = s->opaque;
> +    MuxDriver *d = (MuxDriver *)s;
>      CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
>  
>      if (!chr->driver->chr_add_watch) {
> @@ -816,7 +818,7 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
>  
>  static void mux_chr_free(struct CharDriverState *chr)
>  {
> -    MuxDriver *d = chr->opaque;
> +    MuxDriver *d = (MuxDriver *)chr;
>      int i;
>  
>      for (i = 0; i < d->mux_cnt; i++) {
> @@ -826,12 +828,11 @@ static void mux_chr_free(struct CharDriverState *chr)
>          }
>      }
>      qemu_chr_fe_deinit(&d->chr);
> -    g_free(d);
>  }
>  
>  static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
>  {
> -    MuxDriver *d = chr->opaque;
> +    MuxDriver *d = (MuxDriver *)chr;
>  
>      /* Fix up the real driver with mux routines */
>      qemu_chr_fe_set_handlers(&d->chr,
> @@ -877,9 +878,7 @@ static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
>      if (!chr) {
>          return NULL;
>      }
> -    d = g_new0(MuxDriver, 1);
> -
> -    chr->opaque = d;
> +    d = (MuxDriver *)chr;
>      d->focus = -1;
>      /* only default to opened state if we've realized the initial
>       * set of muxes
> @@ -908,7 +907,7 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
>      assert(s);
>  
>      if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
> -        MuxDriver *d = s->opaque;
> +        MuxDriver *d = (MuxDriver *)s;
>  
>          if (d->mux_cnt >= MAX_MUX) {
>              goto unavailable;
> @@ -940,7 +939,7 @@ void qemu_chr_fe_deinit(CharBackend *b)
>          qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
>          b->chr->be = NULL;
>          if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
> -            MuxDriver *d = b->chr->opaque;
> +            MuxDriver *d = (MuxDriver *)b->chr;
>              d->backends[b->tag] = NULL;
>          }
>          b->chr = NULL;
> @@ -1006,7 +1005,7 @@ void qemu_chr_fe_take_focus(CharBackend *b)
>      }
>  
>      if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
> -        mux_set_focus(b->chr->opaque, b->tag);
> +        mux_set_focus((MuxDriver *)b->chr, b->tag);
>      }
>  }
>  
> @@ -1178,6 +1177,7 @@ static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
>  
>  
>  typedef struct FDCharDriver {
> +    CharDriverState parent;
>      CharDriverState *chr;
>      QIOChannel *ioc_in, *ioc_out;
>      int max_size;
> @@ -1186,15 +1186,15 @@ typedef struct FDCharDriver {
>  /* Called with chr_write_lock held.  */
>  static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    FDCharDriver *s = chr->opaque;
> -    
> +    FDCharDriver *s = (FDCharDriver *)chr;
> +
>      return io_channel_send(s->ioc_out, buf, len);
>  }
>  
>  static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    FDCharDriver *s = chr->opaque;
> +    FDCharDriver *s = opaque;
>      int len;
>      uint8_t buf[READ_BUF_LEN];
>      ssize_t ret;
> @@ -1224,7 +1224,7 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
>  static int fd_chr_read_poll(void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    FDCharDriver *s = chr->opaque;
> +    FDCharDriver *s = opaque;
>  
>      s->max_size = qemu_chr_be_can_write(chr);
>      return s->max_size;
> @@ -1232,14 +1232,14 @@ static int fd_chr_read_poll(void *opaque)
>  
>  static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
>  {
> -    FDCharDriver *s = chr->opaque;
> +    FDCharDriver *s = (FDCharDriver *)chr;
>      return qio_channel_create_watch(s->ioc_out, cond);
>  }
>  
>  static void fd_chr_update_read_handler(CharDriverState *chr,
>                                         GMainContext *context)
>  {
> -    FDCharDriver *s = chr->opaque;
> +    FDCharDriver *s = (FDCharDriver *)chr;
>  
>      remove_fd_in_watch(chr);
>      if (s->ioc_in) {
> @@ -1252,7 +1252,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr,
>  
>  static void fd_chr_free(struct CharDriverState *chr)
>  {
> -    FDCharDriver *s = chr->opaque;
> +    FDCharDriver *s = (FDCharDriver *)chr;
>  
>      remove_fd_in_watch(chr);
>      if (s->ioc_in) {
> @@ -1262,7 +1262,6 @@ static void fd_chr_free(struct CharDriverState *chr)
>          object_unref(OBJECT(s->ioc_out));
>      }
>  
> -    g_free(s);
>      qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
>  }
>  
> @@ -1278,12 +1277,11 @@ static CharDriverState *qemu_chr_open_fd(const CharDriver *driver,
>      if (!chr) {
>          return NULL;
>      }
> -    s = g_new0(FDCharDriver, 1);
> +    s = (FDCharDriver *)chr;
>      s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
>      s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
>      qemu_set_nonblock(fd_out);
>      s->chr = chr;
> -    chr->opaque = s;
>  
>      return chr;
>  }
> @@ -1424,6 +1422,7 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
>  #define HAVE_CHARDEV_PTY 1
>  
>  typedef struct {
> +    CharDriverState parent;
>      QIOChannel *ioc;
>      int read_bytes;
>  
> @@ -1439,7 +1438,7 @@ static void pty_chr_state(CharDriverState *chr, int connected);
>  static gboolean pty_chr_timer(gpointer opaque)
>  {
>      struct CharDriverState *chr = opaque;
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = opaque;
>  
>      qemu_mutex_lock(&chr->chr_write_lock);
>      s->timer_tag = 0;
> @@ -1455,7 +1454,7 @@ static gboolean pty_chr_timer(gpointer opaque)
>  /* Called with chr_write_lock held.  */
>  static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
>  {
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = (PtyCharDriver *)chr;
>  
>      if (s->timer_tag) {
>          g_source_remove(s->timer_tag);
> @@ -1472,7 +1471,7 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
>  /* Called with chr_write_lock held.  */
>  static void pty_chr_update_read_handler_locked(CharDriverState *chr)
>  {
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = (PtyCharDriver *)chr;
>      GPollFD pfd;
>      int rc;
>      QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
> @@ -1503,7 +1502,7 @@ static void pty_chr_update_read_handler(CharDriverState *chr,
>  /* Called with chr_write_lock held.  */
>  static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = (PtyCharDriver *)chr;
>  
>      if (!s->connected) {
>          /* guest sends data, check for (re-)connect */
> @@ -1517,7 +1516,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  
>  static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
>  {
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = (PtyCharDriver *)chr;
>      if (!s->connected) {
>          return NULL;
>      }
> @@ -1527,7 +1526,7 @@ static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
>  static int pty_chr_read_poll(void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = opaque;
>  
>      s->read_bytes = qemu_chr_be_can_write(chr);
>      return s->read_bytes;
> @@ -1536,7 +1535,7 @@ static int pty_chr_read_poll(void *opaque)
>  static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = opaque;
>      gsize len;
>      uint8_t buf[READ_BUF_LEN];
>      ssize_t ret;
> @@ -1561,7 +1560,7 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
>  static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
>  {
>      CharDriverState *chr = opaque;
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = opaque;
>  
>      s->open_tag = 0;
>      qemu_chr_be_generic_open(chr);
> @@ -1571,7 +1570,7 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
>  /* Called with chr_write_lock held.  */
>  static void pty_chr_state(CharDriverState *chr, int connected)
>  {
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = (PtyCharDriver *)chr;
>  
>      if (!connected) {
>          if (s->open_tag) {
> @@ -1605,7 +1604,7 @@ static void pty_chr_state(CharDriverState *chr, int connected)
>  
>  static void pty_chr_free(struct CharDriverState *chr)
>  {
> -    PtyCharDriver *s = chr->opaque;
> +    PtyCharDriver *s = (PtyCharDriver *)chr;
>  
>      qemu_mutex_lock(&chr->chr_write_lock);
>      pty_chr_state(chr, 0);
> @@ -1615,7 +1614,6 @@ static void pty_chr_free(struct CharDriverState *chr)
>          s->timer_tag = 0;
>      }
>      qemu_mutex_unlock(&chr->chr_write_lock);
> -    g_free(s);
>      qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
>  }
>  
> @@ -1654,18 +1652,17 @@ static CharDriverState *qemu_chr_open_pty(const CharDriver *driver,
>      fprintf(stderr, "char device redirected to %s (label %s)\n",
>              pty_name, id);
>  
> -    s = g_new0(PtyCharDriver, 1);
> -    chr->opaque = s;
> -    *be_opened = false;
> -
> +    s = (PtyCharDriver *)chr;
>      s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
>      s->timer_tag = 0;
> +    *be_opened = false;
>  
>      return chr;
>  }
>  
>  static const CharDriver pty_driver = {
>      { "pty" }, CHARDEV_BACKEND_KIND_PTY, NULL, qemu_chr_open_pty,
> +    sizeof(PtyCharDriver),
>      .chr_write = pty_chr_write,
>      .chr_update_read_handler = pty_chr_update_read_handler,
>      .chr_add_watch = pty_chr_add_watch,
> @@ -1789,7 +1786,7 @@ static void tty_serial_init(int fd, int speed,
>  
>  static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
>  {
> -    FDCharDriver *s = chr->opaque;
> +    FDCharDriver *s = (FDCharDriver *)chr;
>      QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
>  
>      switch(cmd) {
> @@ -1868,6 +1865,7 @@ static void qemu_chr_free_tty(CharDriverState *chr)
>  #define HAVE_CHARDEV_PARPORT 1
>  
>  typedef struct {
> +    CharDriverState parent;
>      int fd;
>      int mode;
>  } ParallelCharDriver;
> @@ -1885,7 +1883,7 @@ static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
>  
>  static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
>  {
> -    ParallelCharDriver *drv = chr->opaque;
> +    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
>      int fd = drv->fd;
>      uint8_t b;
>  
> @@ -1966,13 +1964,12 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
>  
>  static void pp_free(CharDriverState *chr)
>  {
> -    ParallelCharDriver *drv = chr->opaque;
> +    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
>      int fd = drv->fd;
>  
>      pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
>      ioctl(fd, PPRELEASE);
>      close(fd);
> -    g_free(drv);
>      qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
>  }
>  
> @@ -1996,9 +1993,7 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
>          return NULL;
>      }
>  
> -    drv = g_new0(ParallelCharDriver, 1);
> -    chr->opaque = drv;
> -
> +    drv = (ParallelCharDriver *)chr;
>      drv->fd = fd;
>      drv->mode = IEEE1284_MODE_COMPAT;
>  
> @@ -2010,35 +2005,45 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
>  
>  #define HAVE_CHARDEV_PARPORT 1
>  
> +typedef struct {
> +    CharDriverState parent;
> +    int fd;
> +} ParallelCharDriver;
> +
>  static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
>  {
> -    int fd = (int)(intptr_t)chr->opaque;
> +    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
>      uint8_t b;
>  
> -    switch(cmd) {
> +    switch (cmd) {
>      case CHR_IOCTL_PP_READ_DATA:
> -        if (ioctl(fd, PPIGDATA, &b) < 0)
> +        if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
>              return -ENOTSUP;
> +        }
>          *(uint8_t *)arg = b;
>          break;
>      case CHR_IOCTL_PP_WRITE_DATA:
>          b = *(uint8_t *)arg;
> -        if (ioctl(fd, PPISDATA, &b) < 0)
> +        if (ioctl(drv->fd, PPISDATA, &b) < 0) {
>              return -ENOTSUP;
> +        }
>          break;
>      case CHR_IOCTL_PP_READ_CONTROL:
> -        if (ioctl(fd, PPIGCTRL, &b) < 0)
> +        if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
>              return -ENOTSUP;
> +        }
>          *(uint8_t *)arg = b;
>          break;
>      case CHR_IOCTL_PP_WRITE_CONTROL:
>          b = *(uint8_t *)arg;
> -        if (ioctl(fd, PPISCTRL, &b) < 0)
> +        if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
>              return -ENOTSUP;
> +        }
>          break;
>      case CHR_IOCTL_PP_READ_STATUS:
> -        if (ioctl(fd, PPIGSTATUS, &b) < 0)
> +        if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
>              return -ENOTSUP;
> +        }
>          *(uint8_t *)arg = b;
>          break;
>      default:
> @@ -2054,12 +2059,14 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
>                                              Error **errp)
>  {
>      CharDriverState *chr;
> +    ParallelCharDriver *drv;
>  
>      chr = qemu_chr_alloc(driver, backend, errp);
>      if (!chr) {
>          return NULL;
>      }
> -    chr->opaque = (void *)(intptr_t)fd;
> +    drv = (ParallelCharDriver *)chr;
> +    drv->fd = fd;
>      *be_opened = false;
>      return chr;
>  }
> @@ -2070,6 +2077,7 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
>  #define HAVE_CHARDEV_SERIAL 1
>  
>  typedef struct {
> +    CharDriverState parent;
>      int max_size;
>      HANDLE hcom, hrecv, hsend;
>      OVERLAPPED orecv;
> @@ -2081,6 +2089,7 @@ typedef struct {
>  } WinCharState;
>  
>  typedef struct {
> +    CharDriverState parent;
>      HANDLE  hStdIn;
>      HANDLE  hInputReadyEvent;
>      HANDLE  hInputDoneEvent;
> @@ -2098,7 +2107,7 @@ static int win_chr_pipe_poll(void *opaque);
>  
>  static void win_chr_free(CharDriverState *chr)
>  {
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = (WinCharState *)chr;
>  
>      if (s->hsend) {
>          CloseHandle(s->hsend);
> @@ -2122,7 +2131,7 @@ static void win_chr_free(CharDriverState *chr)
>  
>  static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp)
>  {
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = (WinCharState *)chr;
>      COMMCONFIG comcfg;
>      COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
>      COMSTAT comstat;
> @@ -2190,7 +2199,7 @@ static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp
>  /* Called with chr_write_lock held.  */
>  static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
>  {
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = (WinCharState *)chr;
>      DWORD len, ret, size, err;
>  
>      len = len1;
> @@ -2224,7 +2233,7 @@ static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
>  
>  static int win_chr_read_poll(CharDriverState *chr)
>  {
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = (WinCharState *)chr;
>  
>      s->max_size = qemu_chr_be_can_write(chr);
>      return s->max_size;
> @@ -2232,7 +2241,7 @@ static int win_chr_read_poll(CharDriverState *chr)
>  
>  static void win_chr_readfile(CharDriverState *chr)
>  {
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = (WinCharState *)chr;
>      int ret, err;
>      uint8_t buf[READ_BUF_LEN];
>      DWORD size;
> @@ -2254,7 +2263,7 @@ static void win_chr_readfile(CharDriverState *chr)
>  
>  static void win_chr_read(CharDriverState *chr)
>  {
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = (WinCharState *)chr;
>  
>      if (s->len > s->max_size)
>          s->len = s->max_size;
> @@ -2267,7 +2276,7 @@ static void win_chr_read(CharDriverState *chr)
>  static int win_chr_poll(void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = opaque;
>      COMSTAT status;
>      DWORD comerr;
>  
> @@ -2284,7 +2293,7 @@ static int win_chr_poll(void *opaque)
>  static int win_chr_pipe_poll(void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = opaque;
>      DWORD size;
>  
>      PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
> @@ -2300,7 +2309,7 @@ static int win_chr_pipe_poll(void *opaque)
>  static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
>                               Error **errp)
>  {
> -    WinCharState *s = chr->opaque;
> +    WinCharState *s = (WinCharState *)chr;
>      OVERLAPPED ov;
>      int ret;
>      DWORD size;
> @@ -2372,18 +2381,14 @@ static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
>      ChardevHostdev *opts = backend->u.pipe.data;
>      const char *filename = opts->device;
>      CharDriverState *chr;
> -    WinCharState *s;
>      ChardevCommon *common = qapi_ChardevHostdev_base(opts);
>  
>      chr = qemu_chr_alloc(driver, common, errp);
>      if (!chr) {
>          return NULL;
>      }
> -    s = g_new0(WinCharState, 1);
> -    chr->opaque = s;
>  
>      if (win_chr_pipe_init(chr, filename, errp) < 0) {
> -        g_free(s);
>          qemu_chr_free_common(chr);
>          return NULL;
>      }
> @@ -2402,9 +2407,8 @@ static CharDriverState *qemu_chr_open_win_file(const CharDriver *driver,
>      if (!chr) {
>          return NULL;
>      }
> -    s = g_new0(WinCharState, 1);
> +    s = (WinCharState *)chr;
>      s->hcom = fd_out;
> -    chr->opaque = s;
>      return chr;
>  }
>  
> @@ -2423,6 +2427,7 @@ static CharDriverState *qemu_chr_open_win_con(const CharDriver *driver,
>  
>  static const CharDriver console_driver = {
>      { "console" }, CHARDEV_BACKEND_KIND_CONSOLE, NULL, qemu_chr_open_win_con,
> +    sizeof(WinCharState),
>      .chr_write = win_chr_write,
>  };
>  
> @@ -2448,7 +2453,7 @@ static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
>  static void win_stdio_wait_func(void *opaque)
>  {
>      CharDriverState   *chr   = opaque;
> -    WinStdioCharState *stdio = chr->opaque;
> +    WinStdioCharState *stdio = opaque;
>      INPUT_RECORD       buf[4];
>      int                ret;
>      DWORD              dwSize;
> @@ -2481,8 +2486,7 @@ static void win_stdio_wait_func(void *opaque)
>  
>  static DWORD WINAPI win_stdio_thread(LPVOID param)
>  {
> -    CharDriverState   *chr   = param;
> -    WinStdioCharState *stdio = chr->opaque;
> +    WinStdioCharState *stdio = param;
>      int                ret;
>      DWORD              dwSize;
>  
> @@ -2521,7 +2525,7 @@ static DWORD WINAPI win_stdio_thread(LPVOID param)
>  static void win_stdio_thread_wait_func(void *opaque)
>  {
>      CharDriverState   *chr   = opaque;
> -    WinStdioCharState *stdio = chr->opaque;
> +    WinStdioCharState *stdio = opaque;
>  
>      if (qemu_chr_be_can_write(chr)) {
>          qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
> @@ -2532,7 +2536,7 @@ static void win_stdio_thread_wait_func(void *opaque)
>  
>  static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
>  {
> -    WinStdioCharState *stdio  = chr->opaque;
> +    WinStdioCharState *stdio  = (WinStdioCharState *)chr;
>      DWORD              dwMode = 0;
>  
>      GetConsoleMode(stdio->hStdIn, &dwMode);
> @@ -2546,7 +2550,7 @@ static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
>  
>  static void win_stdio_free(CharDriverState *chr)
>  {
> -    WinStdioCharState *stdio = chr->opaque;
> +    WinStdioCharState *stdio = (WinStdioCharState *)chr;
>  
>      if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
>          CloseHandle(stdio->hInputReadyEvent);
> @@ -2557,8 +2561,6 @@ static void win_stdio_free(CharDriverState *chr)
>      if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
>          TerminateThread(stdio->hInputThread, 0);
>      }
> -
> -    g_free(chr->opaque);
>  }
>  
>  static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
> @@ -2578,7 +2580,7 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
>      if (!chr) {
>          return NULL;
>      }
> -    stdio = g_new0(WinStdioCharState, 1);
> +    stdio = (WinStdioCharState *)chr;
>  
>      stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
>      if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
> @@ -2588,8 +2590,6 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
>  
>      is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
>  
> -    chr->opaque    = stdio;
> -
>      if (is_console) {
>          if (qemu_add_wait_object(stdio->hStdIn,
>                                   win_stdio_wait_func, chr)) {
> @@ -2649,6 +2649,7 @@ err1:
>  /* UDP Net console */
>  
>  typedef struct {
> +    CharDriverState parent;
>      QIOChannel *ioc;
>      uint8_t buf[READ_BUF_LEN];
>      int bufcnt;
> @@ -2659,7 +2660,7 @@ typedef struct {
>  /* Called with chr_write_lock held.  */
>  static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    NetCharDriver *s = chr->opaque;
> +    NetCharDriver *s = (NetCharDriver *)chr;
>  
>      return qio_channel_write(
>          s->ioc, (const char *)buf, len, NULL);
> @@ -2668,7 +2669,7 @@ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  static int udp_chr_read_poll(void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    NetCharDriver *s = chr->opaque;
> +    NetCharDriver *s = opaque;
>  
>      s->max_size = qemu_chr_be_can_write(chr);
>  
> @@ -2686,7 +2687,7 @@ static int udp_chr_read_poll(void *opaque)
>  static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    NetCharDriver *s = chr->opaque;
> +    NetCharDriver *s = opaque;
>      ssize_t ret;
>  
>      if (s->max_size == 0) {
> @@ -2713,7 +2714,7 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
>  static void udp_chr_update_read_handler(CharDriverState *chr,
>                                          GMainContext *context)
>  {
> -    NetCharDriver *s = chr->opaque;
> +    NetCharDriver *s = (NetCharDriver *)chr;
>  
>      remove_fd_in_watch(chr);
>      if (s->ioc) {
> @@ -2726,13 +2727,12 @@ static void udp_chr_update_read_handler(CharDriverState *chr,
>  
>  static void udp_chr_free(CharDriverState *chr)
>  {
> -    NetCharDriver *s = chr->opaque;
> +    NetCharDriver *s = (NetCharDriver *)chr;
>  
>      remove_fd_in_watch(chr);
>      if (s->ioc) {
>          object_unref(OBJECT(s->ioc));
>      }
> -    g_free(s);
>      qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
>  }
>  
> @@ -2740,6 +2740,7 @@ static void udp_chr_free(CharDriverState *chr)
>  /* TCP Net console */
>  
>  typedef struct {
> +    CharDriverState parent;
>      QIOChannel *ioc; /* Client I/O channel */
>      QIOChannelSocket *sioc; /* Client master channel */
>      QIOChannelSocket *listen_ioc;
> @@ -2768,7 +2769,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque);
>  
>  static void qemu_chr_socket_restart_timer(CharDriverState *chr)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>      assert(s->connected == 0);
>      s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
>                                                 socket_reconnect_timeout, chr);
> @@ -2777,7 +2778,7 @@ static void qemu_chr_socket_restart_timer(CharDriverState *chr)
>  static void check_report_connect_error(CharDriverState *chr,
>                                         Error *err)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>  
>      if (!s->connect_err_reported) {
>          error_report("Unable to connect character device %s: %s",
> @@ -2794,7 +2795,8 @@ static gboolean tcp_chr_accept(QIOChannel *chan,
>  /* Called with chr_write_lock held.  */
>  static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
> +
>      if (s->connected) {
>          int ret =  io_channel_send_full(s->ioc, buf, len,
>                                          s->write_msgfds,
> @@ -2817,7 +2819,7 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  static int tcp_chr_read_poll(void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = opaque;
>      if (!s->connected)
>          return 0;
>      s->max_size = qemu_chr_be_can_write(chr);
> @@ -2876,7 +2878,8 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
>  
>  static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
> +
>      int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
>  
>      assert(num <= TCP_MAX_FDS);
> @@ -2901,7 +2904,7 @@ static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
>  
>  static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>  
>      /* clear old pending fd array */
>      g_free(s->write_msgfds);
> @@ -2926,7 +2929,7 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
>  
>  static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>      struct iovec iov = { .iov_base = buf, .iov_len = len };
>      int ret;
>      size_t i;
> @@ -2983,13 +2986,13 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
>  
>  static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>      return qio_channel_create_watch(s->ioc, cond);
>  }
>  
>  static void tcp_chr_free_connection(CharDriverState *chr)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>      int i;
>  
>      if (!s->connected) {
> @@ -3018,7 +3021,7 @@ static void tcp_chr_free_connection(CharDriverState *chr)
>  
>  static void tcp_chr_disconnect(CharDriverState *chr)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>  
>      if (!s->connected) {
>          return;
> @@ -3041,7 +3044,7 @@ static void tcp_chr_disconnect(CharDriverState *chr)
>  static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = opaque;
>      uint8_t buf[READ_BUF_LEN];
>      int len, size;
>  
> @@ -3067,7 +3070,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
>  
>  static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>      int size;
>  
>      if (!s->connected) {
> @@ -3086,7 +3089,7 @@ static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
>  static void tcp_chr_connect(void *opaque)
>  {
>      CharDriverState *chr = opaque;
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = opaque;
>  
>      g_free(chr->filename);
>      chr->filename = sockaddr_to_str(
> @@ -3107,7 +3110,7 @@ static void tcp_chr_connect(void *opaque)
>  static void tcp_chr_update_read_handler(CharDriverState *chr,
>                                          GMainContext *context)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>  
>      if (!s->connected) {
>          return;
> @@ -3158,7 +3161,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
>  
>  static void tcp_chr_telnet_init(CharDriverState *chr)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>      TCPCharDriverTelnetInit *init =
>          g_new0(TCPCharDriverTelnetInit, 1);
>      size_t n = 0;
> @@ -3194,7 +3197,7 @@ static void tcp_chr_tls_handshake(Object *source,
>                                    gpointer user_data)
>  {
>      CharDriverState *chr = user_data;
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = user_data;
>  
>      if (err) {
>          tcp_chr_disconnect(chr);
> @@ -3210,7 +3213,7 @@ static void tcp_chr_tls_handshake(Object *source,
>  
>  static void tcp_chr_tls_init(CharDriverState *chr)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>      QIOChannelTLS *tioc;
>      Error *err = NULL;
>  
> @@ -3242,7 +3245,8 @@ static void tcp_chr_tls_init(CharDriverState *chr)
>  
>  static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
> +
>      if (s->ioc != NULL) {
>  	return -1;
>      }
> @@ -3312,7 +3316,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
>  
>  static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>      QIOChannelSocket *sioc;
>  
>      /* It can't wait on s->connected, since it is set asynchronously
> @@ -3359,7 +3363,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
>  
>  static void tcp_chr_free(CharDriverState *chr)
>  {
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>  
>      tcp_chr_free_connection(chr);
>  
> @@ -3378,7 +3382,7 @@ static void tcp_chr_free(CharDriverState *chr)
>      if (s->tls_creds) {
>          object_unref(OBJECT(s->tls_creds));
>      }
> -    g_free(s);
> +
>      qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
>  }
>  
> @@ -3387,7 +3391,7 @@ static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque)
>  {
>      QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(src);
>      CharDriverState *chr = opaque;
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = (TCPCharDriver *)chr;
>  
>      if (err) {
>          check_report_connect_error(chr, err);
> @@ -3405,6 +3409,7 @@ static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque)
>  /* Ring buffer chardev */
>  
>  typedef struct {
> +    CharDriverState parent;
>      size_t size;
>      size_t prod;
>      size_t cons;
> @@ -3413,7 +3418,7 @@ typedef struct {
>  
>  static size_t ringbuf_count(const CharDriverState *chr)
>  {
> -    const RingBufCharDriver *d = chr->opaque;
> +    const RingBufCharDriver *d = (RingBufCharDriver *)chr;
>  
>      return d->prod - d->cons;
>  }
> @@ -3421,7 +3426,7 @@ static size_t ringbuf_count(const CharDriverState *chr)
>  /* Called with chr_write_lock held.  */
>  static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    RingBufCharDriver *d = chr->opaque;
> +    RingBufCharDriver *d = (RingBufCharDriver *)chr;
>      int i;
>  
>      if (!buf || (len < 0)) {
> @@ -3440,7 +3445,7 @@ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  
>  static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
>  {
> -    RingBufCharDriver *d = chr->opaque;
> +    RingBufCharDriver *d = (RingBufCharDriver *)chr;
>      int i;
>  
>      qemu_mutex_lock(&chr->chr_write_lock);
> @@ -3454,11 +3459,9 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
>  
>  static void ringbuf_chr_free(struct CharDriverState *chr)
>  {
> -    RingBufCharDriver *d = chr->opaque;
> +    RingBufCharDriver *d = (RingBufCharDriver *)chr;
>  
>      g_free(d->cbuf);
> -    g_free(d);
> -    chr->opaque = NULL;
>  }
>  
>  static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
> @@ -3477,7 +3480,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
>      if (!chr) {
>          return NULL;
>      }
> -    d = g_malloc(sizeof(*d));
> +    d = (RingBufCharDriver *)chr;
>  
>      d->size = opts->has_size ? opts->size : 65536;
>  
> @@ -3491,12 +3494,9 @@ static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
>      d->cons = 0;
>      d->cbuf = g_malloc0(d->size);
>  
> -    chr->opaque = d;
> -
>      return chr;
>  
>  fail:
> -    g_free(d);
>      qemu_chr_free_common(chr);
>      return NULL;
>  }
> @@ -3786,10 +3786,12 @@ static const CharDriver stdio_driver = {
>      { "stdio" }, CHARDEV_BACKEND_KIND_STDIO,
>      qemu_chr_parse_stdio, qemu_chr_open_stdio,
>  #ifdef _WIN32
> +    sizeof(WinStdioCharState),
>      .chr_write = win_stdio_write,
>      .chr_set_echo = qemu_chr_set_echo_win_stdio,
>      .chr_free = win_stdio_free,
>  #else
> +    sizeof(FDCharDriver),
>      .chr_add_watch = fd_chr_add_watch,
>      .chr_write = fd_chr_write,
>      .chr_update_read_handler = fd_chr_update_read_handler,
> @@ -3851,9 +3853,11 @@ static const CharDriver pipe_driver = {
>      { "pipe" }, CHARDEV_BACKEND_KIND_PIPE,
>      qemu_chr_parse_pipe, qemu_chr_open_pipe,
>  #ifdef _WIN32
> +    sizeof(WinCharState),
>      .chr_write = win_chr_write,
>      .chr_free = win_chr_free,
>  #else
> +    sizeof(FDCharDriver),
>      .chr_add_watch = fd_chr_add_watch,
>      .chr_write = fd_chr_write,
>      .chr_update_read_handler = fd_chr_update_read_handler,
> @@ -3880,6 +3884,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
>  static const CharDriver ringbuf_driver = {
>      { "ringbuf" }, CHARDEV_BACKEND_KIND_RINGBUF,
>      qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf,
> +    sizeof(RingBufCharDriver),
>      .chr_write = ringbuf_chr_write,
>      .chr_free = ringbuf_chr_free,
>  };
> @@ -3888,6 +3893,7 @@ static const CharDriver ringbuf_driver = {
>  static const CharDriver memory_driver = {
>      { "memory" }, CHARDEV_BACKEND_KIND_MEMORY,
>      qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf,
> +    sizeof(RingBufCharDriver),
>      .chr_write = ringbuf_chr_write,
>      .chr_free = ringbuf_chr_free,
>  };
> @@ -3909,6 +3915,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
>  
>  static const CharDriver mux_driver = {
>      { "mux" }, CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux, qemu_chr_open_mux,
> +    sizeof(MuxDriver),
>      .chr_free = mux_chr_free,
>      .chr_write = mux_chr_write,
>      .chr_accept_input = mux_chr_accept_input,
> @@ -4489,17 +4496,13 @@ static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
>      ChardevHostdev *serial = backend->u.serial.data;
>      ChardevCommon *common = qapi_ChardevHostdev_base(serial);
>      CharDriverState *chr;
> -    WinCharState *s;
>  
>      chr = qemu_chr_alloc(driver, common, errp);
>      if (!chr) {
>          return NULL;
>      }
>  
> -    s = g_new0(WinCharState, 1);
> -    chr->opaque = s;
>      if (win_chr_init(chr, serial->device, errp) < 0) {
> -        g_free(s);
>          qemu_chr_free_common(chr);
>          return NULL;
>      }
> @@ -4601,6 +4604,7 @@ static CharDriverState *qmp_chardev_open_parallel(const CharDriver *driver,
>  static const CharDriver parallel_driver = {
>      { "parallel", "parport" }, CHARDEV_BACKEND_KIND_PARALLEL,
>      qemu_chr_parse_parallel, qmp_chardev_open_parallel,
> +    sizeof(ParallelCharDriver),
>  #if defined(__linux__)
>      .chr_write = null_chr_write,
>      .chr_ioctl = pp_ioctl,
> @@ -4618,8 +4622,10 @@ static const CharDriver file_driver = {
>      { "file" }, CHARDEV_BACKEND_KIND_FILE,
>      qemu_chr_parse_file_out, qmp_chardev_open_file,
>  #ifdef _WIN32
> +    sizeof(WinCharState),
>      .chr_write = win_chr_write,
>  #else
> +    sizeof(FDCharDriver),
>      .chr_add_watch = fd_chr_add_watch,
>      .chr_write = fd_chr_write,
>      .chr_update_read_handler = fd_chr_update_read_handler,
> @@ -4632,9 +4638,11 @@ static const CharDriver serial_driver = {
>      { "serial", "tty" }, CHARDEV_BACKEND_KIND_SERIAL,
>      qemu_chr_parse_serial, qmp_chardev_open_serial,
>  #ifdef _WIN32
> +    sizeof(WinCharState),
>      .chr_write = win_chr_write,
>      .chr_free = win_chr_free,
>  #else
> +    sizeof(FDCharDriver),
>      .chr_add_watch = fd_chr_add_watch,
>      .chr_write = fd_chr_write,
>      .chr_update_read_handler = fd_chr_update_read_handler,
> @@ -4647,7 +4655,7 @@ static const CharDriver serial_driver = {
>  static gboolean socket_reconnect_timeout(gpointer opaque)
>  {
>      CharDriverState *chr = opaque;
> -    TCPCharDriver *s = chr->opaque;
> +    TCPCharDriver *s = opaque;
>      QIOChannelSocket *sioc;
>  
>      s->reconnect_timer = 0;
> @@ -4687,7 +4695,7 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
>      if (!chr) {
>          return NULL;
>      }
> -    s = g_new0(TCPCharDriver, 1);
> +    s = (TCPCharDriver *)chr;
>  
>      s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
>      s->is_listen = is_listen;
> @@ -4733,7 +4741,6 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
>          qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
>      }
>  
> -    chr->opaque = s;
>      /* be isn't opened until we get a connection */
>      *be_opened = false;
>  
> @@ -4783,7 +4790,6 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
>      if (s->tls_creds) {
>          object_unref(OBJECT(s->tls_creds));
>      }
> -    g_free(s);
>      qemu_chr_free_common(chr);
>      return NULL;
>  }
> @@ -4791,6 +4797,7 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
>  static const CharDriver socket_driver = {
>      { "socket" }, CHARDEV_BACKEND_KIND_SOCKET,
>      qemu_chr_parse_socket, qmp_chardev_open_socket,
> +    sizeof(TCPCharDriver),
>      .chr_wait_connected = tcp_chr_wait_connected,
>      .chr_write = tcp_chr_write,
>      .chr_sync_read = tcp_chr_sync_read,
> @@ -4828,11 +4835,10 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
>          return NULL;
>      }
>  
> -    s = g_new0(NetCharDriver, 1);
> +    s = (NetCharDriver *)chr;
>      s->ioc = QIO_CHANNEL(sioc);
>      s->bufcnt = 0;
>      s->bufptr = 0;
> -    chr->opaque = s;
>      /* be isn't opened until we get a connection */
>      *be_opened = false;
>  
> @@ -4842,6 +4848,7 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
>  static const CharDriver udp_driver = {
>      { "udp" }, CHARDEV_BACKEND_KIND_UDP,
>      qemu_chr_parse_udp, qmp_chardev_open_udp,
> +    sizeof(NetCharDriver),
>      .chr_write = udp_chr_write,
>      .chr_update_read_handler = udp_chr_update_read_handler,
>      .chr_free = udp_chr_free,
> diff --git a/spice-qemu-char.c b/spice-qemu-char.c
> index 14afd4a..bd8b898 100644
> --- a/spice-qemu-char.c
> +++ b/spice-qemu-char.c
> @@ -7,7 +7,8 @@
>  
>  
>  typedef struct SpiceCharDriver {
> -    CharDriverState*      chr;
> +    CharDriverState       parent;
> +
>      SpiceCharDeviceInstance     sin;
>      bool                  active;
>      bool                  blocked;
> @@ -27,17 +28,18 @@ static QLIST_HEAD(, SpiceCharDriver) spice_chars =
>  static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
>  {
>      SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
> +    CharDriverState *chr = (CharDriverState *)scd;
>      ssize_t out = 0;
>      ssize_t last_out;
>      uint8_t* p = (uint8_t*)buf;
>  
>      while (len > 0) {
> -        int can_write = qemu_chr_be_can_write(scd->chr);
> +        int can_write = qemu_chr_be_can_write(chr);
>          last_out = MIN(len, can_write);
>          if (last_out <= 0) {
>              break;
>          }
> -        qemu_chr_be_write(scd->chr, p, last_out);
> +        qemu_chr_be_write(chr, p, last_out);
>          out += last_out;
>          len -= last_out;
>          p += last_out;
> @@ -70,6 +72,7 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
>  static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
>  {
>      SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
> +    CharDriverState *chr = (CharDriverState *)scd;
>      int chr_event;
>  
>      switch (event) {
> @@ -81,20 +84,21 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
>      }
>  
>      trace_spice_vmc_event(chr_event);
> -    qemu_chr_be_event(scd->chr, chr_event);
> +    qemu_chr_be_event(chr, chr_event);
>  }
>  #endif
>  
>  static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
>  {
>      SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
> +    CharDriverState *chr = (CharDriverState *)scd;
>  
> -    if ((scd->chr->be_open && connected) ||
> -        (!scd->chr->be_open && !connected)) {
> +    if ((chr->be_open && connected) ||
> +        (!chr->be_open && !connected)) {
>          return;
>      }
>  
> -    qemu_chr_be_event(scd->chr,
> +    qemu_chr_be_event(chr,
>                        connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
>  }
>  
> @@ -168,7 +172,7 @@ static GSourceFuncs SpiceCharSourceFuncs = {
>  
>  static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
>  {
> -    SpiceCharDriver *scd = chr->opaque;
> +    SpiceCharDriver *scd = (SpiceCharDriver *)chr;
>      SpiceCharSource *src;
>  
>      assert(cond & G_IO_OUT);
> @@ -182,7 +186,7 @@ static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
>  
>  static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    SpiceCharDriver *s = chr->opaque;
> +    SpiceCharDriver *s = (SpiceCharDriver *)chr;
>      int read_bytes;
>  
>      assert(s->datalen == 0);
> @@ -201,7 +205,7 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  
>  static void spice_chr_free(struct CharDriverState *chr)
>  {
> -    SpiceCharDriver *s = chr->opaque;
> +    SpiceCharDriver *s = (SpiceCharDriver *)chr;
>  
>      vmc_unregister_interface(s);
>      QLIST_REMOVE(s, next);
> @@ -210,12 +214,11 @@ static void spice_chr_free(struct CharDriverState *chr)
>  #if SPICE_SERVER_VERSION >= 0x000c02
>      g_free((char *)s->sin.portname);
>  #endif
> -    g_free(s);
>  }
>  
>  static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
>  {
> -    SpiceCharDriver *s = chr->opaque;
> +    SpiceCharDriver *s = (SpiceCharDriver *)chr;
>      if (fe_open) {
>          vmc_register_interface(s);
>      } else {
> @@ -226,7 +229,7 @@ static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
>  static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
>  {
>  #if SPICE_SERVER_VERSION >= 0x000c02
> -    SpiceCharDriver *s = chr->opaque;
> +    SpiceCharDriver *s = (SpiceCharDriver *)chr;
>  
>      if (fe_open) {
>          spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
> @@ -255,7 +258,7 @@ static void print_allowed_subtypes(void)
>  
>  static void spice_chr_accept_input(struct CharDriverState *chr)
>  {
> -    SpiceCharDriver *s = chr->opaque;
> +    SpiceCharDriver *s = (SpiceCharDriver *)chr;
>  
>      spice_server_char_device_wakeup(&s->sin);
>  }
> @@ -272,11 +275,9 @@ static CharDriverState *chr_open(const CharDriver *driver,
>      if (!chr) {
>          return NULL;
>      }
> -    s = g_malloc0(sizeof(SpiceCharDriver));
> -    s->chr = chr;
> +    s = (SpiceCharDriver *)chr;
>      s->active = false;
>      s->sin.subtype = g_strdup(subtype);
> -    chr->opaque = s;
>  
>      QLIST_INSERT_HEAD(&spice_chars, s, next);
>  
> @@ -334,7 +335,7 @@ static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
>          return NULL;
>      }
>      *be_opened = false;
> -    s = chr->opaque;
> +    s = (SpiceCharDriver *)chr;
>      s->sin.portname = g_strdup(name);
>  
>      return chr;
> @@ -388,6 +389,7 @@ static void register_types(void)
>      static const CharDriver vmc_driver = {
>          { "spicevmc" }, CHARDEV_BACKEND_KIND_SPICEVMC,
>          qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc,
> +        sizeof(SpiceCharDriver),
>          .chr_write = spice_chr_write,
>          .chr_add_watch = spice_chr_add_watch,
>          .chr_set_fe_open = spice_vmc_set_fe_open,
> @@ -397,6 +399,7 @@ static void register_types(void)
>      static const CharDriver port_driver = {
>          { "spiceport" }, CHARDEV_BACKEND_KIND_SPICEPORT,
>          qemu_chr_parse_spice_port, qemu_chr_open_spice_port,
> +        sizeof(SpiceCharDriver),
>          .chr_write = spice_chr_write,
>          .chr_add_watch = spice_chr_add_watch,
>          .chr_set_fe_open = spice_port_set_fe_open,
> diff --git a/ui/console.c b/ui/console.c
> index 2944d22..b8c6328 100644
> --- a/ui/console.c
> +++ b/ui/console.c
> @@ -1035,9 +1035,15 @@ void console_select(unsigned int index)
>      }
>  }
>  
> +typedef struct VCDriverState {
> +    CharDriverState parent;
> +    QemuConsole *console;
> +} VCDriverState;
> +
>  static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    QemuConsole *s = chr->opaque;
> +    VCDriverState *drv = (VCDriverState *)chr;
> +    QemuConsole *s = drv->console;
>      int i;
>  
>      s->update_x0 = s->width * FONT_WIDTH;
> @@ -1943,7 +1949,8 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
>  
>  static void text_console_set_echo(CharDriverState *chr, bool echo)
>  {
> -    QemuConsole *s = chr->opaque;
> +    VCDriverState *drv = (VCDriverState *)chr;
> +    QemuConsole *s = drv->console;
>  
>      s->echo = echo;
>  }
> @@ -1983,12 +1990,11 @@ static const GraphicHwOps text_console_ops = {
>  
>  static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
>  {
> -    QemuConsole *s;
> +    VCDriverState *drv = (VCDriverState *)chr;
> +    QemuConsole *s = drv->console;
>      int g_width = 80 * FONT_WIDTH;
>      int g_height = 24 * FONT_HEIGHT;
>  
> -    s = chr->opaque;
> -
>      s->out_fifo.buf = s->out_fifo_buf;
>      s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
>      s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
> @@ -2041,6 +2047,7 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
>  {
>      ChardevCommon *common = qapi_ChardevVC_base(vc);
>      CharDriverState *chr;
> +    VCDriverState *drv;
>      QemuConsole *s;
>      unsigned width = 0;
>      unsigned height = 0;
> @@ -2077,7 +2084,8 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
>      }
>  
>      s->chr = chr;
> -    chr->opaque = s;
> +    drv = (VCDriverState *)chr;
> +    drv->console = s;
>  
>      if (display_state) {
>          text_console_do_init(chr, display_state);
> @@ -2182,6 +2190,7 @@ static const TypeInfo qemu_console_info = {
>  
>  static const CharDriver vc_driver = {
>      { "vc" }, CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, vc_init,
> +    sizeof(VCDriverState),
>      .chr_write = console_puts,
>      .chr_set_echo = text_console_set_echo,
>  };
> diff --git a/ui/gtk.c b/ui/gtk.c
> index 1566faa..03281da 100644
> --- a/ui/gtk.c
> +++ b/ui/gtk.c
> @@ -1653,7 +1653,7 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
>  
>  static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> -    VirtualConsole *vc = chr->opaque;
> +    VirtualConsole *vc = (VirtualConsole *)chr;
>  
>      vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
>      return len;
> @@ -1661,7 +1661,7 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  
>  static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
>  {
> -    VirtualConsole *vc = chr->opaque;
> +    VirtualConsole *vc = (VirtualConsole *)chr;
>  
>      vc->vte.echo = echo;
>  }
> @@ -1673,6 +1673,7 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
>  {
>      static const CharDriver gd_vc_driver = {
>          { "vc" }, CHARDEV_BACKEND_KIND_VC,
> +        .instance_size = sizeof(VirtualConsole),
>          .chr_write = gd_vc_chr_write,
>          .chr_set_echo = gd_vc_chr_set_echo,
>      };
> @@ -1685,9 +1686,6 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
>          return NULL;
>      }
>  
> -    /* Temporary, until gd_vc_vte_init runs.  */
> -    chr->opaque = g_new0(VirtualConsole, 1);
> -
>      vcs[nb_vcs++] = chr;
>  
>      return chr;
> @@ -1728,13 +1726,12 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
>      GtkWidget *box;
>      GtkWidget *scrollbar;
>      GtkAdjustment *vadjustment;
> -    VirtualConsole *tmp_vc = chr->opaque;
> +    VirtualConsole *tmp_vc = (VirtualConsole *)chr;
>  
>      vc->s = s;
>      vc->vte.echo = tmp_vc->vte.echo;
>  
>      vc->vte.chr = chr;
> -    chr->opaque = vc;
>      g_free(tmp_vc);
>  
>      snprintf(buffer, sizeof(buffer), "vc%d", idx);
> diff --git a/include/sysemu/char.h b/include/sysemu/char.h
> index 96b464a..a072edc 100644
> --- a/include/sysemu/char.h
> +++ b/include/sysemu/char.h
> @@ -93,7 +93,6 @@ struct CharDriverState {
>      const CharDriver *driver;
>      QemuMutex chr_write_lock;
>      CharBackend *be;
> -    void *opaque;
>      char *label;
>      char *filename;
>      int logfd;
> @@ -482,6 +481,7 @@ struct CharDriver {
>                                 ChardevBackend *backend,
>                                 ChardevReturn *ret, bool *be_opened,
>                                 Error **errp);
> +    size_t instance_size;
>  
>      int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
>      int (*chr_sync_read)(struct CharDriverState *s,
> diff --git a/include/ui/gtk.h b/include/ui/gtk.h
> index 42ca0fe..6fec03f 100644
> --- a/include/ui/gtk.h
> +++ b/include/ui/gtk.h
> @@ -23,6 +23,8 @@
>  #include "ui/egl-context.h"
>  #endif
>  
> +#include "sysemu/char.h"
> +
>  /* Compatibility define to let us build on both Gtk2 and Gtk3 */
>  #if GTK_CHECK_VERSION(3, 0, 0)
>  static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
> @@ -71,6 +73,7 @@ typedef enum VirtualConsoleType {
>  } VirtualConsoleType;
>  
>  typedef struct VirtualConsole {
> +    CharDriverState parent;
>      GtkDisplayState *s;
>      char *label;
>      GtkWidget *window;
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression")
  2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
                   ` (24 preceding siblings ...)
  2016-10-22 10:16 ` [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object Marc-André Lureau
@ 2016-10-23 18:15 ` Paolo Bonzini
  25 siblings, 0 replies; 51+ messages in thread
From: Paolo Bonzini @ 2016-10-23 18:15 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel



On 22/10/2016 11:52, Marc-André Lureau wrote:
> Hi,
> 
> This is a followup of the series "[PATCH 0/9] Fix mux regression
> (commit 949055a2)". Paolo suggested a new API for qemu_chr_fe_* taking
> a new CharBackend* structure as argument, and modifying properties to
> hold such structure. I followed his advise in the series, and it turns
> out to bring some nice clean-up benefits. I continue a bit the
> cleaning, and there is 3 structures after this series: CharBackend (to
> hold backend users), CharDriverState (instance of char), CharDriver
> (the char "class"), it would be easy to come up with better naming,
> and I would rather name CharBackend->CharFrontend, but Paolo prefered
> the former. I started investigating switching to QOM object, that will
> be for a later series as this one is large enough.

Uhm, wow. :)  I definitely want to merge patches 1 to 30 in QEMU 2.8.
I'll include them in my next pull request.

Paolo

> While doing this work, I found a few issues that are at the beginning
> of this series.
> 
> Marc-André Lureau (38):
>   rng: remove unused included header
>   char: remove use-after-free on win-stdio
>   ringbuf: fix chr_write return value
>   sun4uv: fix serial initialization regression
>   malta: replace chr init by CHR_EVENT_OPENED handler
>   char: remove init callback
>   xilinx: fix buffer overflow on realize
>   mux: split mux_chr_update_read_handler()
>   char: introduce CharBackend
>   char: start converting mux driver to use CharBackend
>   char: replace PROP_CHR with CharBackend
>   char: remaining switch to CharBackend in frontend
>   char: rename some frontend functions
>   colo: claim in find_and_check_chardev
>   char: use qemu_chr_fe* functions with CharBackend argument
>   char: fold qemu_chr_set_handlers in qemu_chr_fe_set_handlers
>   vhost-user: only initialize queue 0 CharBackend
>   char: replace qemu_chr_claim/release with qemu_chr_fe_init/deinit
>   char: make some qemu_chr_fe skip if no driver
>   tests: start chardev unit tests
>   char: move front end handlers in CharBackend
>   char: rename chr_close/chr_free
>   char: remove explicit_fe_open, use a set_handlers argument
>   char: move fe_open in CharBackend
>   char: remove unused CHR_EVENT_FOCUS
>   char: use an enum for CHR_EVENT
>   char: remove unused qemu_chr_fe_event
>   char: replace avail_connections
>   char: use common error path in qmp_chardev_add
>   char: remove explicit_be_open from CharDriverState
>   char: use a const CharDriver
>   char: use a static array for backends
>   char: move callbacks in CharDriver
>   char: fold single-user functions in caller
>   char: introduce generic qemu_chr_get_kind()
>   char: use a feature bit for replay
>   char: allocate CharDriverState as a single object
>   bt: use qemu_chr_alloc()
> 
>  backends/baum.c                   |   44 +-
>  backends/msmouse.c                |   39 +-
>  backends/rng-egd.c                |   29 +-
>  backends/testdev.c                |   35 +-
>  gdbstub.c                         |   39 +-
>  hmp.c                             |    2 +-
>  hw/alpha/dp264.c                  |    2 +-
>  hw/arm/fsl-imx25.c                |    2 +-
>  hw/arm/fsl-imx31.c                |    2 +-
>  hw/arm/fsl-imx6.c                 |    2 +-
>  hw/arm/omap2.c                    |   16 +-
>  hw/arm/pxa2xx.c                   |   19 +-
>  hw/arm/strongarm.c                |   22 +-
>  hw/bt/hci-csr.c                   |   46 +-
>  hw/char/bcm2835_aux.c             |   18 +-
>  hw/char/cadence_uart.c            |   30 +-
>  hw/char/debugcon.c                |    8 +-
>  hw/char/digic-uart.c              |   13 +-
>  hw/char/escc.c                    |   22 +-
>  hw/char/etraxfs_ser.c             |   12 +-
>  hw/char/exynos4210_uart.c         |   15 +-
>  hw/char/grlib_apbuart.c           |   17 +-
>  hw/char/imx_serial.c              |   29 +-
>  hw/char/ipoctal232.c              |   23 +-
>  hw/char/lm32_juart.c              |   15 +-
>  hw/char/lm32_uart.c               |   17 +-
>  hw/char/mcf_uart.c                |   20 +-
>  hw/char/milkymist-uart.c          |   13 +-
>  hw/char/omap_uart.c               |    4 +-
>  hw/char/parallel.c                |   46 +-
>  hw/char/pl011.c                   |   19 +-
>  hw/char/sclpconsole-lm.c          |   13 +-
>  hw/char/sclpconsole.c             |   12 +-
>  hw/char/serial-isa.c              |    7 +-
>  hw/char/serial.c                  |   36 +-
>  hw/char/sh_serial.c               |   16 +-
>  hw/char/spapr_vty.c               |   12 +-
>  hw/char/stm32f2xx_usart.c         |   22 +-
>  hw/char/virtio-console.c          |   29 +-
>  hw/char/xen_console.c             |   43 +-
>  hw/char/xilinx_uartlite.c         |   16 +-
>  hw/core/qdev-properties-system.c  |   80 +-
>  hw/i386/pc.c                      |    2 +-
>  hw/ipmi/ipmi_bmc_extern.c         |    9 +-
>  hw/isa/pc87312.c                  |    4 +-
>  hw/mips/mips_fulong2e.c           |    2 +-
>  hw/mips/mips_malta.c              |   44 +-
>  hw/mips/mips_r4k.c                |    2 +-
>  hw/misc/ivshmem.c                 |   23 +-
>  hw/sparc64/sun4u.c                |    2 +-
>  hw/ssi/xilinx_spips.c             |    2 +-
>  hw/usb/ccid-card-passthru.c       |   21 +-
>  hw/usb/dev-serial.c               |   31 +-
>  hw/usb/redirect.c                 |   22 +-
>  hw/virtio/vhost-user.c            |    4 +-
>  hw/xtensa/xtfpga.c                |    2 +-
>  monitor.c                         |   31 +-
>  net/colo-compare.c                |   59 +-
>  net/filter-mirror.c               |   64 +-
>  net/slirp.c                       |   25 +-
>  net/vhost-user.c                  |   49 +-
>  qemu-char.c                       | 1445 +++++++++++++++++++++----------------
>  qtest.c                           |   29 +-
>  spice-qemu-char.c                 |  102 +--
>  tests/test-char.c                 |  253 +++++++
>  tests/vhost-user-test.c           |   27 +-
>  ui/console.c                      |   56 +-
>  ui/gtk.c                          |   28 +-
>  vl.c                              |   13 +-
>  tests/Makefile.include            |    4 +
>  include/hw/char/bcm2835_aux.h     |    2 +-
>  include/hw/char/cadence_uart.h    |    2 +-
>  include/hw/char/digic-uart.h      |    3 +-
>  include/hw/char/imx_serial.h      |    3 +-
>  include/hw/char/serial.h          |    6 +-
>  include/hw/char/stm32f2xx_usart.h |    2 +-
>  include/hw/qdev-properties.h      |    2 +-
>  include/sysemu/char.h             |  290 ++++----
>  include/ui/gtk.h                  |    3 +
>  79 files changed, 2064 insertions(+), 1510 deletions(-)
>  create mode 100644 tests/test-char.c
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 21/38] char: move front end handlers in CharBackend
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 21/38] char: move front end handlers in CharBackend Marc-André Lureau
@ 2016-10-24 13:40   ` Paolo Bonzini
  0 siblings, 0 replies; 51+ messages in thread
From: Paolo Bonzini @ 2016-10-24 13:40 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel



On 22/10/2016 11:53, Marc-André Lureau wrote:
> -    if (chr->chr_can_read || chr->chr_read ||
> -        chr->chr_event || chr->handler_opaque) {
> +    if (chr->be) {
>          error_setg(errp, "Chardev '%s' is busy", id);

This doesn't work with the mux chardev.  To some extent a preexisting
bug, but made worse by this patch.  But it can be fixed easily:

diff --git a/qemu-char.c b/qemu-char.c
index b9330f3..6dd779f 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -915,6 +915,16 @@ unavailable:
     return false;
 }
 
+static bool qemu_chr_is_busy(CharDriverState *s)
+{
+    if (s->is_mux) {
+        MuxDriver *d = s->opaque;
+        return d->mux_cnt >= 0;
+    } else {
+        return s->be != NULL;
+    }
+}
+
 void qemu_chr_fe_deinit(CharBackend *b)
 {
     assert(b);
@@ -4807,7 +4817,7 @@ void qmp_chardev_remove(const char *id, Error **errp)
         error_setg(errp, "Chardev '%s' not found", id);
         return;
     }
-    if (chr->be) {
+    if (qemu_chr_is_busy(chr)) {
         error_setg(errp, "Chardev '%s' is busy", id);
         return;
     }

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 08/38] mux: split mux_chr_update_read_handler()
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 08/38] mux: split mux_chr_update_read_handler() Marc-André Lureau
@ 2016-10-24 19:51   ` Eric Blake
  0 siblings, 0 replies; 51+ messages in thread
From: Eric Blake @ 2016-10-24 19:51 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel; +Cc: pbonzini

[-- Attachment #1: Type: text/plain, Size: 1007 bytes --]

On 10/22/2016 04:52 AM, Marc-André Lureau wrote:
> Make qemu_chr_add_handlers_full() aware of mux handling. This allows
> introduction of a tag associated with the fe handlers and a
> qemu_chr_set_handlers() function to set the handler for a particular
> tag. That will allow to get rid of qemu_chr_add_handlers*() in later
> changes, in favor of qemu_chr_fe_set_handler().
> 
> To this end, chr_update_read_handler callback is enhanced with a tag
> argument, and mux_chr_update_read_handler() is splitted in new

s/splitted in/split into/

> functions: mux_chr_new_handler_tag(), mux_chr_set_handlers(),
> mux_set_focus().
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qemu-char.c           | 139 +++++++++++++++++++++++++++++++++++---------------
>  include/sysemu/char.h |   2 +-
>  2 files changed, 99 insertions(+), 42 deletions(-)
> 


-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 20/38] tests: start chardev unit tests
  2016-10-22  9:53 ` [Qemu-devel] [PATCH 20/38] tests: start chardev unit tests Marc-André Lureau
@ 2016-10-27 18:27   ` Eric Blake
  0 siblings, 0 replies; 51+ messages in thread
From: Eric Blake @ 2016-10-27 18:27 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel; +Cc: pbonzini

[-- Attachment #1: Type: text/plain, Size: 828 bytes --]

On 10/22/2016 04:53 AM, Marc-André Lureau wrote:
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  tests/test-char.c      | 253 +++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/Makefile.include |   4 +
>  2 files changed, 257 insertions(+)
>  create mode 100644 tests/test-char.c

This failed to add test-char to tests/.gitignore.  I guess that's
because Marc-Andre is still waiting for a review on his v2 patch to
generate the file?
https://lists.gnu.org/archive/html/qemu-devel/2016-09/msg05965.html

Depending on whether that gets reviewed and goes in soon, maybe we want
to do the trivial followup to manually ignore the newly built executable
in the meantime.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 19/38] char: make some qemu_chr_fe skip if no driver
  2016-10-22  9:52 ` [Qemu-devel] [PATCH 19/38] char: make some qemu_chr_fe skip if no driver Marc-André Lureau
@ 2023-02-15 22:13   ` Philippe Mathieu-Daudé
  2023-02-16 14:23     ` Marc-André Lureau
  0 siblings, 1 reply; 51+ messages in thread
From: Philippe Mathieu-Daudé @ 2023-02-15 22:13 UTC (permalink / raw)
  To: Marc-André Lureau, Bin Meng
  Cc: pbonzini, Edgar E. Iglesias, qemu-devel, Alistair Francis

Hi Marc-André,

[very old patch...]

On 22/10/16 11:52, Marc-André Lureau wrote:
> In most cases, front ends do not care about the side effect of
> CharBackend, so we can simply skip the checks and call the qemu_chr_fe
> functions even without associated CharDriver.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>   hw/arm/pxa2xx.c           |  8 +++-----
>   hw/arm/strongarm.c        | 16 ++++++---------
>   hw/char/bcm2835_aux.c     | 18 ++++++-----------
>   hw/char/cadence_uart.c    | 24 +++++++---------------

>   qemu-char.c               | 51 ++++++++++++++++++++++++++++++++++++++---------
>   include/sysemu/char.h     | 40 +++++++++++++++++++++++++------------
>   22 files changed, 156 insertions(+), 191 deletions(-)


> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> index 4459b2d..291818e 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.chr) {
> -        qemu_chr_fe_accept_input(&s->chr);
> -    }
> +    qemu_chr_fe_accept_input(&s->chr);

I'm trying to understand this change. This code comes from:

commit 9121d02cb33c96b444a3973579f5edc119597e81

     char/cadence_uart: Fix reset for unattached instances

     commit 1db8b5efe0c2b5000e50691eea61264a615f43de introduced an issue
     where QEMU would segfault if you have an unattached Cadence UART.

     Fix by guarding the flush-on-reset logic on there being a qemu_chr
     attachment.

diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index 131370a74b..4d457f8c65 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -157,7 +157,9 @@ static void uart_rx_reset(UartState *s)
  {
      s->rx_wpos = 0;
      s->rx_count = 0;
-    qemu_chr_accept_input(s->chr);
+    if (s->chr) {
+        qemu_chr_accept_input(s->chr);
+    }

When resetting the xlnx-zcu102 machine, I hit:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = 
EXC_BAD_ACCESS (code=1, address=0x50)
   * frame #0: 0x10020a740 gd_vc_send_chars(vc=0x000000000) at 
gtk.c:1759:41 [opt]
     frame #1: 0x100636264 qemu_chr_fe_accept_input(be=<unavailable>) at 
char-fe.c:159:9 [opt]
     frame #2: 0x1000608e0 cadence_uart_reset_hold [inlined] 
uart_rx_reset(s=0x10810a960) at cadence_uart.c:158:5 [opt]
     frame #3: 0x1000608d4 cadence_uart_reset_hold(obj=0x10810a960) at 
cadence_uart.c:530:5 [opt]
     frame #4: 0x100580ab4 resettable_phase_hold(obj=0x10810a960, 
opaque=0x000000000, type=<unavailable>) at resettable.c:0 [opt]
     frame #5: 0x10057d1b0 bus_reset_child_foreach(obj=<unavailable>, 
cb=(resettable_phase_hold at resettable.c:162), opaque=0x000000000, 
type=RESET_TYPE_COLD) at bus.c:97:13 [opt]
     frame #6: 0x1005809f8 resettable_phase_hold [inlined] 
resettable_child_foreach(rc=0x000060000332d2c0, obj=0x0000600002c1c180, 
cb=<unavailable>, opaque=0x000000000, type=RESET_TYPE_COLD) at 
resettable.c:96:9 [opt]
     frame #7: 0x1005809d8 resettable_phase_hold(obj=0x0000600002c1c180, 
opaque=0x000000000, type=RESET_TYPE_COLD) at resettable.c:173:5 [opt]
     frame #8: 0x1005803a0 
resettable_assert_reset(obj=0x0000600002c1c180, type=<unavailable>) at 
resettable.c:60:5 [opt]
     frame #9: 0x10058027c resettable_reset(obj=0x0000600002c1c180, 
type=RESET_TYPE_COLD) at resettable.c:45:5 [opt]

Doing similar to commit 9121d02cb3...:

-- >8 --
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index c069a30842..deadee1788 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -155,7 +155,9 @@ static void uart_rx_reset(CadenceUARTState *s)
  {
      s->rx_wpos = 0;
      s->rx_count = 0;
-    qemu_chr_fe_accept_input(&s->chr);
+    if (qemu_chr_fe_backend_open(&s->chr)) {
+        qemu_chr_fe_accept_input(&s->chr);
+    }
  }
---

... fixes the issue but I'm not sure 1/ this is a correct use of the
chardev API and 2/ this is how the HW work at reset.

Can you help me with 1/ before I ask Xilinx folks for 2/ ? :)

Thanks,

Phil.


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 19/38] char: make some qemu_chr_fe skip if no driver
  2023-02-15 22:13   ` Philippe Mathieu-Daudé
@ 2023-02-16 14:23     ` Marc-André Lureau
  2023-02-16 15:29       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 51+ messages in thread
From: Marc-André Lureau @ 2023-02-16 14:23 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Bin Meng, pbonzini, Edgar E. Iglesias, qemu-devel,
	Alistair Francis

Hi Philippe

On Thu, Feb 16, 2023 at 2:14 AM Philippe Mathieu-Daudé
<philmd@linaro.org> wrote:
>
> Hi Marc-André,
>
> [very old patch...]
>
> On 22/10/16 11:52, Marc-André Lureau wrote:
> > In most cases, front ends do not care about the side effect of
> > CharBackend, so we can simply skip the checks and call the qemu_chr_fe
> > functions even without associated CharDriver.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >   hw/arm/pxa2xx.c           |  8 +++-----
> >   hw/arm/strongarm.c        | 16 ++++++---------
> >   hw/char/bcm2835_aux.c     | 18 ++++++-----------
> >   hw/char/cadence_uart.c    | 24 +++++++---------------
>
> >   qemu-char.c               | 51 ++++++++++++++++++++++++++++++++++++++---------
> >   include/sysemu/char.h     | 40 +++++++++++++++++++++++++------------
> >   22 files changed, 156 insertions(+), 191 deletions(-)
>
>
> > diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> > index 4459b2d..291818e 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.chr) {
> > -        qemu_chr_fe_accept_input(&s->chr);
> > -    }
> > +    qemu_chr_fe_accept_input(&s->chr);
>
> I'm trying to understand this change. This code comes from:
>
> commit 9121d02cb33c96b444a3973579f5edc119597e81
>
>      char/cadence_uart: Fix reset for unattached instances
>
>      commit 1db8b5efe0c2b5000e50691eea61264a615f43de introduced an issue
>      where QEMU would segfault if you have an unattached Cadence UART.
>
>      Fix by guarding the flush-on-reset logic on there being a qemu_chr
>      attachment.
>
> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> index 131370a74b..4d457f8c65 100644
> --- a/hw/char/cadence_uart.c
> +++ b/hw/char/cadence_uart.c
> @@ -157,7 +157,9 @@ static void uart_rx_reset(UartState *s)
>   {
>       s->rx_wpos = 0;
>       s->rx_count = 0;
> -    qemu_chr_accept_input(s->chr);
> +    if (s->chr) {
> +        qemu_chr_accept_input(s->chr);
> +    }
>
> When resetting the xlnx-zcu102 machine, I hit:
>
> (lldb) bt
> * thread #1, queue = 'com.apple.main-thread', stop reason =
> EXC_BAD_ACCESS (code=1, address=0x50)
>    * frame #0: 0x10020a740 gd_vc_send_chars(vc=0x000000000) at
> gtk.c:1759:41 [opt]
>      frame #1: 0x100636264 qemu_chr_fe_accept_input(be=<unavailable>) at
> char-fe.c:159:9 [opt]
>      frame #2: 0x1000608e0 cadence_uart_reset_hold [inlined]
> uart_rx_reset(s=0x10810a960) at cadence_uart.c:158:5 [opt]
>      frame #3: 0x1000608d4 cadence_uart_reset_hold(obj=0x10810a960) at
> cadence_uart.c:530:5 [opt]
>      frame #4: 0x100580ab4 resettable_phase_hold(obj=0x10810a960,
> opaque=0x000000000, type=<unavailable>) at resettable.c:0 [opt]
>      frame #5: 0x10057d1b0 bus_reset_child_foreach(obj=<unavailable>,
> cb=(resettable_phase_hold at resettable.c:162), opaque=0x000000000,
> type=RESET_TYPE_COLD) at bus.c:97:13 [opt]
>      frame #6: 0x1005809f8 resettable_phase_hold [inlined]
> resettable_child_foreach(rc=0x000060000332d2c0, obj=0x0000600002c1c180,
> cb=<unavailable>, opaque=0x000000000, type=RESET_TYPE_COLD) at
> resettable.c:96:9 [opt]
>      frame #7: 0x1005809d8 resettable_phase_hold(obj=0x0000600002c1c180,
> opaque=0x000000000, type=RESET_TYPE_COLD) at resettable.c:173:5 [opt]
>      frame #8: 0x1005803a0
> resettable_assert_reset(obj=0x0000600002c1c180, type=<unavailable>) at
> resettable.c:60:5 [opt]
>      frame #9: 0x10058027c resettable_reset(obj=0x0000600002c1c180,
> type=RESET_TYPE_COLD) at resettable.c:45:5 [opt]
>
> Doing similar to commit 9121d02cb3...:
>
> -- >8 --
> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> index c069a30842..deadee1788 100644
> --- a/hw/char/cadence_uart.c
> +++ b/hw/char/cadence_uart.c
> @@ -155,7 +155,9 @@ static void uart_rx_reset(CadenceUARTState *s)
>   {
>       s->rx_wpos = 0;
>       s->rx_count = 0;
> -    qemu_chr_fe_accept_input(&s->chr);
> +    if (qemu_chr_fe_backend_open(&s->chr)) {
> +        qemu_chr_fe_accept_input(&s->chr);
> +    }
>   }
> ---
>
> ... fixes the issue but I'm not sure 1/ this is a correct use of the
> chardev API and 2/ this is how the HW work at reset.

The trouble is that GTK/VTE console/chardev creation is done later.

I think we should rather fix ui/gtk.c, as this could happen with other
char frontends:

diff --git a/ui/gtk.c b/ui/gtk.c
index 4817623c8f..dfaf6d33c3 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1783,7 +1783,9 @@ static void gd_vc_chr_accept_input(Chardev *chr)
     VCChardev *vcd = VC_CHARDEV(chr);
     VirtualConsole *vc = vcd->console;

-    gd_vc_send_chars(vc);
+    if (vc) {
+        gd_vc_send_chars(vc);
+    }
 }


>
> Can you help me with 1/ before I ask Xilinx folks for 2/ ? :)
>
> Thanks,
>
> Phil.
>


-- 
Marc-André Lureau


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 19/38] char: make some qemu_chr_fe skip if no driver
  2023-02-16 14:23     ` Marc-André Lureau
@ 2023-02-16 15:29       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 51+ messages in thread
From: Philippe Mathieu-Daudé @ 2023-02-16 15:29 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: Bin Meng, pbonzini, Edgar E. Iglesias, qemu-devel,
	Alistair Francis

On 16/2/23 15:23, Marc-André Lureau wrote:
> Hi Philippe
> 
> On Thu, Feb 16, 2023 at 2:14 AM Philippe Mathieu-Daudé
> <philmd@linaro.org> wrote:
>>
>> Hi Marc-André,
>>
>> [very old patch...]
>>
>> On 22/10/16 11:52, Marc-André Lureau wrote:
>>> In most cases, front ends do not care about the side effect of
>>> CharBackend, so we can simply skip the checks and call the qemu_chr_fe
>>> functions even without associated CharDriver.
>>>
>>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>> ---
>>>    hw/arm/pxa2xx.c           |  8 +++-----
>>>    hw/arm/strongarm.c        | 16 ++++++---------
>>>    hw/char/bcm2835_aux.c     | 18 ++++++-----------
>>>    hw/char/cadence_uart.c    | 24 +++++++---------------
>>
>>>    qemu-char.c               | 51 ++++++++++++++++++++++++++++++++++++++---------
>>>    include/sysemu/char.h     | 40 +++++++++++++++++++++++++------------
>>>    22 files changed, 156 insertions(+), 191 deletions(-)
>>
>>
>>> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
>>> index 4459b2d..291818e 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.chr) {
>>> -        qemu_chr_fe_accept_input(&s->chr);
>>> -    }
>>> +    qemu_chr_fe_accept_input(&s->chr);
>>
>> I'm trying to understand this change. This code comes from:
>>
>> commit 9121d02cb33c96b444a3973579f5edc119597e81
>>
>>       char/cadence_uart: Fix reset for unattached instances
>>
>>       commit 1db8b5efe0c2b5000e50691eea61264a615f43de introduced an issue
>>       where QEMU would segfault if you have an unattached Cadence UART.
>>
>>       Fix by guarding the flush-on-reset logic on there being a qemu_chr
>>       attachment.
>>
>> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
>> index 131370a74b..4d457f8c65 100644
>> --- a/hw/char/cadence_uart.c
>> +++ b/hw/char/cadence_uart.c
>> @@ -157,7 +157,9 @@ static void uart_rx_reset(UartState *s)
>>    {
>>        s->rx_wpos = 0;
>>        s->rx_count = 0;
>> -    qemu_chr_accept_input(s->chr);
>> +    if (s->chr) {
>> +        qemu_chr_accept_input(s->chr);
>> +    }
>>
>> When resetting the xlnx-zcu102 machine, I hit:
>>
>> (lldb) bt
>> * thread #1, queue = 'com.apple.main-thread', stop reason =
>> EXC_BAD_ACCESS (code=1, address=0x50)
>>     * frame #0: 0x10020a740 gd_vc_send_chars(vc=0x000000000) at
>> gtk.c:1759:41 [opt]
>>       frame #1: 0x100636264 qemu_chr_fe_accept_input(be=<unavailable>) at
>> char-fe.c:159:9 [opt]
>>       frame #2: 0x1000608e0 cadence_uart_reset_hold [inlined]
>> uart_rx_reset(s=0x10810a960) at cadence_uart.c:158:5 [opt]
>>       frame #3: 0x1000608d4 cadence_uart_reset_hold(obj=0x10810a960) at
>> cadence_uart.c:530:5 [opt]
>>       frame #4: 0x100580ab4 resettable_phase_hold(obj=0x10810a960,
>> opaque=0x000000000, type=<unavailable>) at resettable.c:0 [opt]
>>       frame #5: 0x10057d1b0 bus_reset_child_foreach(obj=<unavailable>,
>> cb=(resettable_phase_hold at resettable.c:162), opaque=0x000000000,
>> type=RESET_TYPE_COLD) at bus.c:97:13 [opt]
>>       frame #6: 0x1005809f8 resettable_phase_hold [inlined]
>> resettable_child_foreach(rc=0x000060000332d2c0, obj=0x0000600002c1c180,
>> cb=<unavailable>, opaque=0x000000000, type=RESET_TYPE_COLD) at
>> resettable.c:96:9 [opt]
>>       frame #7: 0x1005809d8 resettable_phase_hold(obj=0x0000600002c1c180,
>> opaque=0x000000000, type=RESET_TYPE_COLD) at resettable.c:173:5 [opt]
>>       frame #8: 0x1005803a0
>> resettable_assert_reset(obj=0x0000600002c1c180, type=<unavailable>) at
>> resettable.c:60:5 [opt]
>>       frame #9: 0x10058027c resettable_reset(obj=0x0000600002c1c180,
>> type=RESET_TYPE_COLD) at resettable.c:45:5 [opt]
>>
>> Doing similar to commit 9121d02cb3...:
>>
>> -- >8 --
>> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
>> index c069a30842..deadee1788 100644
>> --- a/hw/char/cadence_uart.c
>> +++ b/hw/char/cadence_uart.c
>> @@ -155,7 +155,9 @@ static void uart_rx_reset(CadenceUARTState *s)
>>    {
>>        s->rx_wpos = 0;
>>        s->rx_count = 0;
>> -    qemu_chr_fe_accept_input(&s->chr);
>> +    if (qemu_chr_fe_backend_open(&s->chr)) {
>> +        qemu_chr_fe_accept_input(&s->chr);
>> +    }
>>    }
>> ---
>>
>> ... fixes the issue but I'm not sure 1/ this is a correct use of the
>> chardev API and 2/ this is how the HW work at reset.
> 
> The trouble is that GTK/VTE console/chardev creation is done later.
> 
> I think we should rather fix ui/gtk.c, as this could happen with other
> char frontends:
> 
> diff --git a/ui/gtk.c b/ui/gtk.c
> index 4817623c8f..dfaf6d33c3 100644
> --- a/ui/gtk.c
> +++ b/ui/gtk.c
> @@ -1783,7 +1783,9 @@ static void gd_vc_chr_accept_input(Chardev *chr)
>       VCChardev *vcd = VC_CHARDEV(chr);
>       VirtualConsole *vc = vcd->console;
> 
> -    gd_vc_send_chars(vc);
> +    if (vc) {
> +        gd_vc_send_chars(vc);
> +    }

Easy :) If you send a proper patch, feel free to include:

Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>

Thanks!



^ permalink raw reply	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2023-02-16 15:30 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-10-22  9:52 [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 01/38] rng: remove unused included header Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 02/38] char: remove use-after-free on win-stdio Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 03/38] ringbuf: fix chr_write return value Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 04/38] sun4uv: fix serial initialization regression Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 05/38] malta: replace chr init by CHR_EVENT_OPENED handler Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 06/38] char: remove init callback Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 07/38] xilinx: fix buffer overflow on realize Marc-André Lureau
2016-10-23 12:01   ` Paolo Bonzini
2016-10-22  9:52 ` [Qemu-devel] [PATCH 08/38] mux: split mux_chr_update_read_handler() Marc-André Lureau
2016-10-24 19:51   ` Eric Blake
2016-10-22  9:52 ` [Qemu-devel] [PATCH 09/38] char: introduce CharBackend Marc-André Lureau
2016-10-23 12:03   ` Paolo Bonzini
2016-10-22  9:52 ` [Qemu-devel] [PATCH 10/38] char: start converting mux driver to use CharBackend Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 11/38] char: replace PROP_CHR with CharBackend Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 12/38] char: remaining switch to CharBackend in frontend Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 13/38] char: rename some frontend functions Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 14/38] colo: claim in find_and_check_chardev Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 15/38] char: use qemu_chr_fe* functions with CharBackend argument Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 16/38] char: fold qemu_chr_set_handlers in qemu_chr_fe_set_handlers Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 17/38] vhost-user: only initialize queue 0 CharBackend Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 18/38] char: replace qemu_chr_claim/release with qemu_chr_fe_init/deinit Marc-André Lureau
2016-10-22  9:52 ` [Qemu-devel] [PATCH 19/38] char: make some qemu_chr_fe skip if no driver Marc-André Lureau
2023-02-15 22:13   ` Philippe Mathieu-Daudé
2023-02-16 14:23     ` Marc-André Lureau
2023-02-16 15:29       ` Philippe Mathieu-Daudé
2016-10-22  9:53 ` [Qemu-devel] [PATCH 20/38] tests: start chardev unit tests Marc-André Lureau
2016-10-27 18:27   ` Eric Blake
2016-10-22  9:53 ` [Qemu-devel] [PATCH 21/38] char: move front end handlers in CharBackend Marc-André Lureau
2016-10-24 13:40   ` Paolo Bonzini
2016-10-22  9:53 ` [Qemu-devel] [PATCH 22/38] char: rename chr_close/chr_free Marc-André Lureau
2016-10-22  9:53 ` [Qemu-devel] [PATCH 23/38] char: remove explicit_fe_open, use a set_handlers argument Marc-André Lureau
2016-10-22 10:09 ` [Qemu-devel] [PATCH 24/38] char: move fe_open in CharBackend Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 25/38] char: remove unused CHR_EVENT_FOCUS Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 26/38] char: use an enum for CHR_EVENT Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 27/38] char: remove unused qemu_chr_fe_event Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 28/38] char: replace avail_connections Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 29/38] char: use common error path in qmp_chardev_add Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 30/38] char: remove explicit_be_open from CharDriverState Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 31/38] char: use a const CharDriver Marc-André Lureau
2016-10-23 12:24     ` Paolo Bonzini
2016-10-22 10:09   ` [Qemu-devel] [PATCH 32/38] char: use a static array for backends Marc-André Lureau
2016-10-23 12:21     ` Paolo Bonzini
2016-10-22 10:09   ` [Qemu-devel] [PATCH 33/38] char: move callbacks in CharDriver Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 34/38] char: fold single-user functions in caller Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 35/38] char: introduce generic qemu_chr_get_kind() Marc-André Lureau
2016-10-22 10:09   ` [Qemu-devel] [PATCH 36/38] char: use a feature bit for replay Marc-André Lureau
2016-10-22 10:16 ` [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object Marc-André Lureau
2016-10-22 10:16   ` [Qemu-devel] [PATCH 38/38] bt: use qemu_chr_alloc() Marc-André Lureau
2016-10-23 12:28   ` [Qemu-devel] [PATCH 37/38] char: allocate CharDriverState as a single object Paolo Bonzini
2016-10-23 18:15 ` [Qemu-devel] [PATCH 00/38] char: fixes and improvements (was "[PATCH 0/9] Fix mux regression") Paolo Bonzini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).