All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 0/8] -audiodev option
@ 2015-06-18 16:43 Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 1/8] qapi: support implicit structs in OptsVisitor Kővágó, Zoltán
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Eduardo Habkost

This series of patches adds a new -audiodev command line option to
specify audio subsytem parameters instead of environment variables.
This  will later allow us to specify multiple audio backends.  The
syntax is something like this:
 -audiodev driver_name,id=id,property=value,...
like:
 -audiodev alsa,id=foo,frequency=8000,channels=1

This series also convert numa and netdev qapi to flat unions, to avoid
extra complexity in OptsVisitor.  Please refer to the thread at v2
patch.

You are encouraged check whether I break anything during the
refactorings (especially at netdev and various audio backends).  For
easier testing, pull https://github.com/DirtYiCE/qemu.git tag 
audio-cmdline-v3.

Please review (especially the qapi refactoring I did...)

Changes from v2:
* consistency, style issues in audio qapi
* converted NumaOptions and Netdev and friends to flat union
* OptsVisitor: only full paths accepted
* qemu_opts_print: hopefully produces valid command line in all cases
* other small fixes...

Changes from v1:
* updated to master, removed commits already merged
* now every time-related option takes usecs
* some small fixes

Kővágó, Zoltán (8):
  qapi: support implicit structs in OptsVisitor
  qapi: convert NumaOptions into a flat union
  qapi: change Netdev and NetLegacy into a flat union
  qapi: qapi for audio backends
  qapi: support nested structs in OptsVisitor
  opts: produce valid command line in qemu_opts_print
  audio: use qapi AudioFormat instead of audfmt_e
  audio: -audiodev command line option

 Makefile                                |   4 +-
 audio/Makefile.objs                     |   2 +-
 audio/alsaaudio.c                       | 346 +++++---------
 audio/audio.c                           | 792 +++++++++-----------------------
 audio/audio.h                           |  34 +-
 audio/audio_int.h                       |   7 +-
 audio/audio_legacy.c                    | 328 +++++++++++++
 audio/audio_template.h                  |  13 +-
 audio/audio_win_int.c                   |  18 +-
 audio/coreaudio.c                       |  49 +-
 audio/dsound_template.h                 |   6 +-
 audio/dsoundaudio.c                     |  60 +--
 audio/noaudio.c                         |   3 +-
 audio/ossaudio.c                        | 182 +++-----
 audio/paaudio.c                         | 108 ++---
 audio/sdlaudio.c                        |  50 +-
 audio/spiceaudio.c                      |  11 +-
 audio/wavaudio.c                        |  75 +--
 audio/wavcapture.c                      |   2 +-
 block.c                                 |   2 +-
 hw/arm/musicpal.c                       |   2 +-
 hw/arm/omap2.c                          |   2 +-
 hw/audio/ac97.c                         |   2 +-
 hw/audio/adlib.c                        |   2 +-
 hw/audio/cs4231a.c                      |   6 +-
 hw/audio/es1370.c                       |   4 +-
 hw/audio/gus.c                          |   2 +-
 hw/audio/hda-codec.c                    |  18 +-
 hw/audio/lm4549.c                       |   6 +-
 hw/audio/milkymist-ac97.c               |   2 +-
 hw/audio/pcspk.c                        |   2 +-
 hw/audio/sb16.c                         |  14 +-
 hw/audio/wm8750.c                       |   4 +-
 hw/core/qdev-properties-system.c        |   2 +-
 hw/input/tsc210x.c                      |   2 +-
 hw/net/allwinner_emac.c                 |   2 +-
 hw/net/cadence_gem.c                    |   2 +-
 hw/net/dp8393x.c                        |   2 +-
 hw/net/e1000.c                          |   2 +-
 hw/net/eepro100.c                       |   2 +-
 hw/net/etraxfs_eth.c                    |   2 +-
 hw/net/fsl_etsec/etsec.c                |   2 +-
 hw/net/lan9118.c                        |   2 +-
 hw/net/lance.c                          |   2 +-
 hw/net/mcf_fec.c                        |   2 +-
 hw/net/milkymist-minimac2.c             |   2 +-
 hw/net/mipsnet.c                        |   2 +-
 hw/net/ne2000-isa.c                     |   2 +-
 hw/net/ne2000.c                         |   2 +-
 hw/net/opencores_eth.c                  |   2 +-
 hw/net/pcnet-pci.c                      |   2 +-
 hw/net/rocker/rocker_fp.c               |   2 +-
 hw/net/rtl8139.c                        |   2 +-
 hw/net/smc91c111.c                      |   2 +-
 hw/net/spapr_llan.c                     |   2 +-
 hw/net/stellaris_enet.c                 |   2 +-
 hw/net/vhost_net.c                      |  18 +-
 hw/net/virtio-net.c                     |   6 +-
 hw/net/vmxnet3.c                        |   2 +-
 hw/net/xen_nic.c                        |   2 +-
 hw/net/xgmac.c                          |   2 +-
 hw/net/xilinx_axienet.c                 |   2 +-
 hw/net/xilinx_ethlite.c                 |   2 +-
 hw/usb/dev-audio.c                      |   2 +-
 hw/usb/dev-network.c                    |   2 +-
 include/net/net.h                       |   4 +-
 monitor.c                               |  14 +-
 net/clients.h                           |  20 +-
 net/dump.c                              |   9 +-
 net/hub.c                               |  24 +-
 net/l2tpv3.c                            |   9 +-
 net/net.c                               | 110 ++---
 net/netmap.c                            |   6 +-
 net/slirp.c                             |   9 +-
 net/socket.c                            |  11 +-
 net/tap-win32.c                         |   9 +-
 net/tap.c                               |  29 +-
 net/vde.c                               |   9 +-
 net/vhost-user.c                        |  15 +-
 numa.c                                  |   2 +-
 qapi-schema.json                        | 153 ++++--
 qapi/audio.json                         | 250 ++++++++++
 qapi/opts-visitor.c                     | 129 +++++-
 qemu-options.hx                         | 230 +++++++++-
 tests/qapi-schema/qapi-schema-test.json |   9 +-
 tests/test-opts-visitor.c               |  34 ++
 ui/vnc.c                                |  14 +-
 util/qemu-option.c                      |  47 +-
 vl.c                                    |   9 +-
 89 files changed, 1876 insertions(+), 1521 deletions(-)
 create mode 100644 audio/audio_legacy.c
 create mode 100644 qapi/audio.json

-- 
2.4.3

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

* [Qemu-devel] [PATCH v3 1/8] qapi: support implicit structs in OptsVisitor
  2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
@ 2015-06-18 16:43 ` Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 2/8] qapi: convert NumaOptions into a flat union Kővágó, Zoltán
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Roth, László Ersek, Gerd Hoffmann, Markus Armbruster

They are required for flat unions (you still have to allocate the
structs).

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 qapi/opts-visitor.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index f2ad6d7..f02059d 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -149,6 +149,12 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
     }
 }
 
+static void
+opts_start_implicit_struct(Visitor *v, void **obj, size_t size, Error **errp)
+{
+    opts_start_struct(v, obj, NULL, NULL, size, errp);
+}
+
 
 static gboolean
 ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
@@ -185,6 +191,12 @@ opts_end_struct(Visitor *v, Error **errp)
     ov->fake_id_opt = NULL;
 }
 
+static void
+opts_end_implicit_struct(Visitor *v, Error **errp)
+{
+    opts_end_struct(v, errp);
+}
+
 
 static GQueue *
 lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
@@ -508,6 +520,9 @@ opts_visitor_new(const QemuOpts *opts)
     ov->visitor.start_struct = &opts_start_struct;
     ov->visitor.end_struct   = &opts_end_struct;
 
+    ov->visitor.start_implicit_struct = &opts_start_implicit_struct;
+    ov->visitor.end_implicit_struct = &opts_end_implicit_struct;
+
     ov->visitor.start_list = &opts_start_list;
     ov->visitor.next_list  = &opts_next_list;
     ov->visitor.end_list   = &opts_end_list;
-- 
2.4.3

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

* [Qemu-devel] [PATCH v3 2/8] qapi: convert NumaOptions into a flat union
  2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 1/8] qapi: support implicit structs in OptsVisitor Kővágó, Zoltán
@ 2015-06-18 16:43 ` Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 3/8] qapi: change Netdev and NetLegacy " Kővágó, Zoltán
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster, Gerd Hoffmann, Eduardo Habkost

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 numa.c           |  2 +-
 qapi-schema.json | 47 ++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/numa.c b/numa.c
index d227ccc..822a22e 100644
--- a/numa.c
+++ b/numa.c
@@ -141,7 +141,7 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
     }
 
     switch (object->kind) {
-    case NUMA_OPTIONS_KIND_NODE:
+    case NUMA_DRIVER_NODE:
         numa_node_parse(object->node, opts, &err);
         if (err) {
             goto error;
diff --git a/qapi-schema.json b/qapi-schema.json
index 106008c..7ebf99e 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3537,17 +3537,6 @@
   'data': { '*console':'int', 'events': [ 'InputEvent' ] } }
 
 ##
-# @NumaOptions
-#
-# A discriminated record of NUMA options. (for OptsVisitor)
-#
-# Since 2.1
-##
-{ 'union': 'NumaOptions',
-  'data': {
-    'node': 'NumaNodeOptions' }}
-
-##
 # @NumaNodeOptions
 #
 # Create a guest NUMA node. (for OptsVisitor)
@@ -3574,6 +3563,42 @@
    '*memdev': 'str' }}
 
 ##
+# @NumaDriver
+#
+# List of possible numa drivers.
+#
+# Since: 2.4
+##
+{ 'enum': 'NumaDriver',
+  'data': [ 'node' ] }
+
+##
+# @NumaCommonOptions
+#
+# Common set of numa options.
+#
+# @type: the numa driver to use
+#
+# Since: 2.4
+##
+{ 'struct': 'NumaCommonOptions',
+  'data': {
+    'type': 'NumaDriver' } }
+
+##
+# @NumaOptions
+#
+# A discriminated record of NUMA options. (for OptsVisitor)
+#
+# Since 2.1
+##
+{ 'union': 'NumaOptions',
+  'base': 'NumaCommonOptions',
+  'discriminator': 'type',
+  'data': {
+    'node': 'NumaNodeOptions' }}
+
+##
 # @HostMemPolicy
 #
 # Host memory policy types
-- 
2.4.3

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

* [Qemu-devel] [PATCH v3 3/8] qapi: change Netdev and NetLegacy into a flat union
  2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 1/8] qapi: support implicit structs in OptsVisitor Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 2/8] qapi: convert NumaOptions into a flat union Kővágó, Zoltán
@ 2015-06-18 16:43 ` Kővágó, Zoltán
  2015-06-19 14:06   ` Stefan Hajnoczi
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 4/8] qapi: qapi for audio backends Kővágó, Zoltán
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang, Vincenzo Maffione,
	Markus Armbruster, Max Filippov, Gerd Hoffmann, Dmitry Fleytman,
	Edgar E. Iglesias, Rob Herring, Alexander Graf, Scott Feldman,
	Jiri Pirko, Jan Kiszka, Stefan Hajnoczi, Giuseppe Lettieri,
	Luiz Capitulino, Luigi Rizzo, David Gibson, Peter Crosthwaite,
	Michael Walle, open list:sPAPR pseries

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 hw/arm/musicpal.c                |   2 +-
 hw/core/qdev-properties-system.c |   2 +-
 hw/net/allwinner_emac.c          |   2 +-
 hw/net/cadence_gem.c             |   2 +-
 hw/net/dp8393x.c                 |   2 +-
 hw/net/e1000.c                   |   2 +-
 hw/net/eepro100.c                |   2 +-
 hw/net/etraxfs_eth.c             |   2 +-
 hw/net/fsl_etsec/etsec.c         |   2 +-
 hw/net/lan9118.c                 |   2 +-
 hw/net/lance.c                   |   2 +-
 hw/net/mcf_fec.c                 |   2 +-
 hw/net/milkymist-minimac2.c      |   2 +-
 hw/net/mipsnet.c                 |   2 +-
 hw/net/ne2000-isa.c              |   2 +-
 hw/net/ne2000.c                  |   2 +-
 hw/net/opencores_eth.c           |   2 +-
 hw/net/pcnet-pci.c               |   2 +-
 hw/net/rocker/rocker_fp.c        |   2 +-
 hw/net/rtl8139.c                 |   2 +-
 hw/net/smc91c111.c               |   2 +-
 hw/net/spapr_llan.c              |   2 +-
 hw/net/stellaris_enet.c          |   2 +-
 hw/net/vhost_net.c               |  18 +++----
 hw/net/virtio-net.c              |   6 +--
 hw/net/vmxnet3.c                 |   2 +-
 hw/net/xen_nic.c                 |   2 +-
 hw/net/xgmac.c                   |   2 +-
 hw/net/xilinx_axienet.c          |   2 +-
 hw/net/xilinx_ethlite.c          |   2 +-
 hw/usb/dev-network.c             |   2 +-
 include/net/net.h                |   4 +-
 monitor.c                        |  14 ++---
 net/clients.h                    |  20 +++----
 net/dump.c                       |   9 ++--
 net/hub.c                        |  24 ++++-----
 net/l2tpv3.c                     |   9 ++--
 net/net.c                        | 110 +++++++++++++++++++--------------------
 net/netmap.c                     |   6 +--
 net/slirp.c                      |   9 ++--
 net/socket.c                     |  11 ++--
 net/tap-win32.c                  |   9 ++--
 net/tap.c                        |  29 +++++------
 net/vde.c                        |   9 ++--
 net/vhost-user.c                 |  15 +++---
 qapi-schema.json                 | 103 +++++++++++++++++++++++++-----------
 46 files changed, 239 insertions(+), 224 deletions(-)

diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index a3b1314..72e2f8f 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -379,7 +379,7 @@ static void eth_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_mv88w8618_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = eth_can_receive,
     .receive = eth_receive,
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 0309fe5..f094548 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -213,7 +213,7 @@ static void set_netdev(Object *obj, Visitor *v, void *opaque,
     }
 
     queues = qemu_find_net_clients_except(str, peers,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          NET_CLIENT_DRIVER_NIC,
                                           MAX_QUEUE_NUM);
     if (queues == 0) {
         err = -ENOENT;
diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
index 0407dee..4fdf824 100644
--- a/hw/net/allwinner_emac.c
+++ b/hw/net/allwinner_emac.c
@@ -422,7 +422,7 @@ static const MemoryRegionOps aw_emac_mem_ops = {
 };
 
 static NetClientInfo net_aw_emac_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = aw_emac_can_receive,
     .receive = aw_emac_receive,
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 494a346..d74136a 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1175,7 +1175,7 @@ static void gem_set_link(NetClientState *nc)
 }
 
 static NetClientInfo net_gem_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = gem_can_receive,
     .receive = gem_receive,
diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c
index cd889bc..504a4a1 100644
--- a/hw/net/dp8393x.c
+++ b/hw/net/dp8393x.c
@@ -807,7 +807,7 @@ static void dp8393x_reset(DeviceState *dev)
 }
 
 static NetClientInfo net_dp83932_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = dp8393x_can_receive,
     .receive = dp8393x_receive,
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index bab8e2a..fc8bf0d 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -1512,7 +1512,7 @@ pci_e1000_uninit(PCIDevice *dev)
 }
 
 static NetClientInfo net_e1000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = e1000_can_receive,
     .receive = e1000_receive,
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index c374c1a..cc4e0ae 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -1842,7 +1842,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev)
 }
 
 static NetClientInfo net_eepro100_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = nic_can_receive,
     .receive = nic_receive,
diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c
index 4773dea..5689fd8 100644
--- a/hw/net/etraxfs_eth.c
+++ b/hw/net/etraxfs_eth.c
@@ -582,7 +582,7 @@ static const MemoryRegionOps eth_ops = {
 };
 
 static NetClientInfo net_etraxfs_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = eth_can_receive,
     .receive = eth_receive,
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index c57365f..0bdc6f4 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -368,7 +368,7 @@ static void etsec_set_link_status(NetClientState *nc)
 }
 
 static NetClientInfo net_etsec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = etsec_can_receive,
     .receive = etsec_receive,
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index f169c38..08eecf0 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -1310,7 +1310,7 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = {
 };
 
 static NetClientInfo net_lan9118_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = lan9118_can_receive,
     .receive = lan9118_receive,
diff --git a/hw/net/lance.c b/hw/net/lance.c
index 4baa016..08b36ff 100644
--- a/hw/net/lance.c
+++ b/hw/net/lance.c
@@ -92,7 +92,7 @@ static const MemoryRegionOps lance_mem_ops = {
 };
 
 static NetClientInfo net_lance_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = pcnet_can_receive,
     .receive = pcnet_receive,
diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c
index 0255612..d13431e 100644
--- a/hw/net/mcf_fec.c
+++ b/hw/net/mcf_fec.c
@@ -440,7 +440,7 @@ static const MemoryRegionOps mcf_fec_ops = {
 };
 
 static NetClientInfo net_mcf_fec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = mcf_fec_can_receive,
     .receive = mcf_fec_receive,
diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c
index f06afaa..d3f2feb 100644
--- a/hw/net/milkymist-minimac2.c
+++ b/hw/net/milkymist-minimac2.c
@@ -443,7 +443,7 @@ static void milkymist_minimac2_reset(DeviceState *d)
 }
 
 static NetClientInfo net_milkymist_minimac2_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = minimac2_can_rx,
     .receive = minimac2_rx,
diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c
index c813e0c..89d11ec 100644
--- a/hw/net/mipsnet.c
+++ b/hw/net/mipsnet.c
@@ -212,7 +212,7 @@ static const VMStateDescription vmstate_mipsnet = {
 };
 
 static NetClientInfo net_mipsnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = mipsnet_can_receive,
     .receive = mipsnet_receive,
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
index 17e7199..7da26c9 100644
--- a/hw/net/ne2000-isa.c
+++ b/hw/net/ne2000-isa.c
@@ -42,7 +42,7 @@ typedef struct ISANE2000State {
 } ISANE2000State;
 
 static NetClientInfo net_ne2000_isa_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = ne2000_can_receive,
     .receive = ne2000_receive,
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
index 3492db3..09fa0e3 100644
--- a/hw/net/ne2000.c
+++ b/hw/net/ne2000.c
@@ -703,7 +703,7 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size)
 }
 
 static NetClientInfo net_ne2000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = ne2000_can_receive,
     .receive = ne2000_receive,
diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c
index 3642046..4b6407b 100644
--- a/hw/net/opencores_eth.c
+++ b/hw/net/opencores_eth.c
@@ -473,7 +473,7 @@ static ssize_t open_eth_receive(NetClientState *nc,
 }
 
 static NetClientInfo net_open_eth_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = open_eth_can_receive,
     .receive = open_eth_receive,
diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c
index 8305d1b..cb81de9 100644
--- a/hw/net/pcnet-pci.c
+++ b/hw/net/pcnet-pci.c
@@ -271,7 +271,7 @@ static void pci_pcnet_uninit(PCIDevice *dev)
 }
 
 static NetClientInfo net_pci_pcnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = pcnet_can_receive,
     .receive = pcnet_receive,
diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c
index d8d934c..36d8e6c 100644
--- a/hw/net/rocker/rocker_fp.c
+++ b/hw/net/rocker/rocker_fp.c
@@ -163,7 +163,7 @@ static void fp_port_set_link_status(NetClientState *nc)
 }
 
 static NetClientInfo fp_port_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = fp_port_can_receive,
     .receive = fp_port_receive,
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index e0db472..391d1fb 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -3445,7 +3445,7 @@ static void rtl8139_set_link_status(NetClientState *nc)
 }
 
 static NetClientInfo net_rtl8139_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = rtl8139_can_receive,
     .receive = rtl8139_receive,
diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c
index 74e06e6..4c85eb5 100644
--- a/hw/net/smc91c111.c
+++ b/hw/net/smc91c111.c
@@ -737,7 +737,7 @@ static const MemoryRegionOps smc91c111_mem_ops = {
 };
 
 static NetClientInfo net_smc91c111_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = smc91c111_can_receive,
     .receive = smc91c111_receive,
diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c
index 2dd5ec1..45c61f5 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -188,7 +188,7 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
 }
 
 static NetClientInfo net_spapr_vlan_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = spapr_vlan_can_receive,
     .receive = spapr_vlan_receive,
diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
index 278a654..9baf1c1 100644
--- a/hw/net/stellaris_enet.c
+++ b/hw/net/stellaris_enet.c
@@ -452,7 +452,7 @@ static void stellaris_enet_reset(stellaris_enet_state *s)
 }
 
 static NetClientInfo net_stellaris_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = stellaris_enet_can_receive,
     .receive = stellaris_enet_receive,
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index 1c55517..3e5ba69 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -94,10 +94,10 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net)
     const int *feature_bits = 0;
 
     switch (net->nc->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         feature_bits = kernel_feature_bits;
         break;
-    case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+    case NET_CLIENT_DRIVER_VHOST_USER:
         feature_bits = user_feature_bits;
         break;
     default:
@@ -124,7 +124,7 @@ void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
 static int vhost_net_get_fd(NetClientState *backend)
 {
     switch (backend->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         return tap_get_fd(backend);
     default:
         fprintf(stderr, "vhost-net requires tap backend\n");
@@ -220,7 +220,7 @@ static int vhost_net_start_one(struct vhost_net *net,
         net->nc->info->poll(net->nc, false);
     }
 
-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         qemu_set_fd_handler(net->backend, NULL, NULL, NULL);
         file.fd = net->backend;
         for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
@@ -236,7 +236,7 @@ static int vhost_net_start_one(struct vhost_net *net,
     return 0;
 fail:
     file.fd = -1;
-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         while (file.index-- > 0) {
             const VhostOps *vhost_ops = net->dev.vhost_ops;
             int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
@@ -259,14 +259,14 @@ static void vhost_net_stop_one(struct vhost_net *net,
 {
     struct vhost_vring_file file = { .fd = -1 };
 
-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
             const VhostOps *vhost_ops = net->dev.vhost_ops;
             int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
                                           &file);
             assert(r >= 0);
         }
-    } else if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+    } else if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
         for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
             const VhostOps *vhost_ops = net->dev.vhost_ops;
             int r = vhost_ops->vhost_call(&net->dev, VHOST_RESET_OWNER,
@@ -393,10 +393,10 @@ VHostNetState *get_vhost_net(NetClientState *nc)
     }
 
     switch (nc->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         vhost_net = tap_get_vhost_net(nc);
         break;
-    case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+    case NET_CLIENT_DRIVER_VHOST_USER:
         vhost_net = vhost_user_get_vhost_net(nc);
         break;
     default:
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 9281aa1..7762b92 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -402,7 +402,7 @@ static int peer_attach(VirtIONet *n, int index)
         return 0;
     }
 
-    if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
         return 0;
     }
 
@@ -417,7 +417,7 @@ static int peer_detach(VirtIONet *n, int index)
         return 0;
     }
 
-    if (nc->peer->info->type !=  NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
         return 0;
     }
 
@@ -1505,7 +1505,7 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
 }
 
 static NetClientInfo net_virtio_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = virtio_net_can_receive,
     .receive = virtio_net_receive,
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 8bcdf3e..e4fa8ca 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -1927,7 +1927,7 @@ static void vmxnet3_set_link_status(NetClientState *nc)
 }
 
 static NetClientInfo net_vmxnet3_info = {
-        .type = NET_CLIENT_OPTIONS_KIND_NIC,
+        .type = NET_CLIENT_DRIVER_NIC,
         .size = sizeof(NICState),
         .can_receive = vmxnet3_can_receive,
         .receive = vmxnet3_receive,
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index 19ecfc4..b6fc62b 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -302,7 +302,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
 /* ------------------------------------------------------------- */
 
 static NetClientInfo net_xen_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = net_rx_ok,
     .receive = net_rx_packet,
diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c
index b068f3a..316f4a8 100644
--- a/hw/net/xgmac.c
+++ b/hw/net/xgmac.c
@@ -369,7 +369,7 @@ out:
 }
 
 static NetClientInfo net_xgmac_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = eth_can_rx,
     .receive = eth_rx,
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 21efedf..64f7d63 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -924,7 +924,7 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size)
 }
 
 static NetClientInfo net_xilinx_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = eth_can_rx,
     .receive = eth_rx,
diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
index ad6b553..ab555f6 100644
--- a/hw/net/xilinx_ethlite.c
+++ b/hw/net/xilinx_ethlite.c
@@ -214,7 +214,7 @@ static void xilinx_ethlite_reset(DeviceState *dev)
 }
 
 static NetClientInfo net_xilinx_ethlite_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = eth_can_rx,
     .receive = eth_rx,
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 743c231..0e8e673 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1341,7 +1341,7 @@ static void usb_net_handle_destroy(USBDevice *dev)
 }
 
 static NetClientInfo net_usbnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = usbnet_can_receive,
     .receive = usbnet_receive,
diff --git a/include/net/net.h b/include/net/net.h
index e66ca03..c1a3579 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -57,7 +57,7 @@ typedef void (SetOffload)(NetClientState *, int, int, int, int, int);
 typedef void (SetVnetHdrLen)(NetClientState *, int);
 
 typedef struct NetClientInfo {
-    NetClientOptionsKind type;
+    NetClientDriver type;
     size_t size;
     NetReceive *receive;
     NetReceive *receive_raw;
@@ -100,7 +100,7 @@ typedef struct NICState {
 char *qemu_mac_strdup_printf(const uint8_t *macaddr);
 NetClientState *qemu_find_netdev(const char *id);
 int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
-                                 NetClientOptionsKind type, int max);
+                                 NetClientDriver type, int max);
 NetClientState *qemu_new_net_client(NetClientInfo *info,
                                     NetClientState *peer,
                                     const char *model,
diff --git a/monitor.c b/monitor.c
index 8e1a2e8..f8275eb 100644
--- a/monitor.c
+++ b/monitor.c
@@ -4199,8 +4199,8 @@ void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
     }
     len = strlen(str);
     readline_set_completion_index(rs, len);
-    for (i = 0; NetClientOptionsKind_lookup[i]; i++) {
-        add_completion_option(rs, str, NetClientOptionsKind_lookup[i]);
+    for (i = 0; NetClientDriver_lookup[i]; i++) {
+        add_completion_option(rs, str, NetClientDriver_lookup[i]);
     }
 }
 
@@ -4400,7 +4400,7 @@ void set_link_completion(ReadLineState *rs, int nb_args, const char *str)
         NetClientState *ncs[MAX_QUEUE_NUM];
         int count, i;
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NONE,
+                                             NET_CLIENT_DRIVER_NONE,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             const char *name = ncs[i]->name;
@@ -4425,7 +4425,7 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)
 
     len = strlen(str);
     readline_set_completion_index(rs, len);
-    count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC,
+    count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC,
                                          MAX_QUEUE_NUM);
     for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
         QemuOpts *opts;
@@ -4517,7 +4517,7 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str)
     readline_set_completion_index(rs, len);
     if (nb_args == 2) {
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NONE,
+                                             NET_CLIENT_DRIVER_NONE,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             int id;
@@ -4534,13 +4534,13 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str)
         return;
     } else if (nb_args == 3) {
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NIC,
+                                             NET_CLIENT_DRIVER_NIC,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             int id;
             const char *name;
 
-            if (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT ||
+            if (ncs[i]->info->type == NET_CLIENT_DRIVER_HUBPORT ||
                 net_hub_id_for_client(ncs[i], &id)) {
                 continue;
             }
diff --git a/net/clients.h b/net/clients.h
index d47530e..2aac0ee 100644
--- a/net/clients.h
+++ b/net/clients.h
@@ -27,39 +27,39 @@
 #include "net/net.h"
 #include "qapi-types.h"
 
-int net_init_dump(const NetClientOptions *opts, const char *name,
+int net_init_dump(const void *opts, const char *name,
                   NetClientState *peer, Error **errp);
 
 #ifdef CONFIG_SLIRP
-int net_init_slirp(const NetClientOptions *opts, const char *name,
+int net_init_slirp(const void *opts, const char *name,
                    NetClientState *peer, Error **errp);
 #endif
 
-int net_init_hubport(const NetClientOptions *opts, const char *name,
+int net_init_hubport(const void *opts, const char *name,
                      NetClientState *peer, Error **errp);
 
-int net_init_socket(const NetClientOptions *opts, const char *name,
+int net_init_socket(const void *opts, const char *name,
                     NetClientState *peer, Error **errp);
 
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const void *opts, const char *name,
                  NetClientState *peer, Error **errp);
 
-int net_init_bridge(const NetClientOptions *opts, const char *name,
+int net_init_bridge(const void *opts, const char *name,
                     NetClientState *peer, Error **errp);
 
-int net_init_l2tpv3(const NetClientOptions *opts, const char *name,
+int net_init_l2tpv3(const void *opts, const char *name,
                     NetClientState *peer, Error **errp);
 #ifdef CONFIG_VDE
-int net_init_vde(const NetClientOptions *opts, const char *name,
+int net_init_vde(const void *opts, const char *name,
                  NetClientState *peer, Error **errp);
 #endif
 
 #ifdef CONFIG_NETMAP
-int net_init_netmap(const NetClientOptions *opts, const char *name,
+int net_init_netmap(const void *opts, const char *name,
                     NetClientState *peer, Error **errp);
 #endif
 
-int net_init_vhost_user(const NetClientOptions *opts, const char *name,
+int net_init_vhost_user(const void *opts, const char *name,
                         NetClientState *peer, Error **errp);
 
 #endif /* QEMU_NET_CLIENTS_H */
diff --git a/net/dump.c b/net/dump.c
index 02c8064..6aca19d 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -94,7 +94,7 @@ static void dump_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_dump_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_DUMP,
+    .type = NET_CLIENT_DRIVER_DUMP,
     .size = sizeof(DumpState),
     .receive = dump_receive,
     .cleanup = dump_cleanup,
@@ -146,16 +146,13 @@ static int net_dump_init(NetClientState *peer, const char *device,
     return 0;
 }
 
-int net_init_dump(const NetClientOptions *opts, const char *name,
+int net_init_dump(const void *opts, const char *name,
                   NetClientState *peer, Error **errp)
 {
     int len;
     const char *file;
     char def_file[128];
-    const NetdevDumpOptions *dump;
-
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
-    dump = opts->dump;
+    const NetdevDumpOptions *dump = opts;
 
     assert(peer);
 
diff --git a/net/hub.c b/net/hub.c
index 3047f12..b47ee5d 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -130,7 +130,7 @@ static void net_hub_port_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_hub_port_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
+    .type = NET_CLIENT_DRIVER_HUBPORT,
     .size = sizeof(NetHubPort),
     .can_receive = net_hub_port_can_receive,
     .receive = net_hub_port_receive,
@@ -265,10 +265,10 @@ int net_hub_id_for_client(NetClientState *nc, int *id)
 {
     NetHubPort *port;
 
-    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+    if (nc->info->type == NET_CLIENT_DRIVER_HUBPORT) {
         port = DO_UPCAST(NetHubPort, nc, nc);
     } else if (nc->peer != NULL && nc->peer->info->type ==
-            NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+            NET_CLIENT_DRIVER_HUBPORT) {
         port = DO_UPCAST(NetHubPort, nc, nc->peer);
     } else {
         return -ENOENT;
@@ -280,14 +280,12 @@ int net_hub_id_for_client(NetClientState *nc, int *id)
     return 0;
 }
 
-int net_init_hubport(const NetClientOptions *opts, const char *name,
+int net_init_hubport(const void *opts, const char *name,
                      NetClientState *peer, Error **errp)
 {
-    const NetdevHubPortOptions *hubport;
+    const NetdevHubPortOptions *hubport = opts;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
     assert(!peer);
-    hubport = opts->hubport;
 
     net_hub_add_port(hubport->hubid, name);
     return 0;
@@ -314,14 +312,14 @@ void net_hub_check_clients(void)
             }
 
             switch (peer->info->type) {
-            case NET_CLIENT_OPTIONS_KIND_NIC:
+            case NET_CLIENT_DRIVER_NIC:
                 has_nic = 1;
                 break;
-            case NET_CLIENT_OPTIONS_KIND_USER:
-            case NET_CLIENT_OPTIONS_KIND_TAP:
-            case NET_CLIENT_OPTIONS_KIND_SOCKET:
-            case NET_CLIENT_OPTIONS_KIND_VDE:
-            case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+            case NET_CLIENT_DRIVER_USER:
+            case NET_CLIENT_DRIVER_TAP:
+            case NET_CLIENT_DRIVER_SOCKET:
+            case NET_CLIENT_DRIVER_VDE:
+            case NET_CLIENT_DRIVER_VHOST_USER:
                 has_host_dev = 1;
                 break;
             default:
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index 356dae2..f50956a 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -517,7 +517,7 @@ static void net_l2tpv3_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_l2tpv3_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_L2TPV3,
+    .type = NET_CLIENT_DRIVER_L2TPV3,
     .size = sizeof(NetL2TPV3State),
     .receive = net_l2tpv3_receive_dgram,
     .receive_iov = net_l2tpv3_receive_dgram_iov,
@@ -525,12 +525,12 @@ static NetClientInfo net_l2tpv3_info = {
     .cleanup = net_l2tpv3_cleanup,
 };
 
-int net_init_l2tpv3(const NetClientOptions *opts,
+int net_init_l2tpv3(const void *opts,
                     const char *name,
                     NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
-    const NetdevL2TPv3Options *l2tpv3;
+    const NetdevL2TPv3Options *l2tpv3 = opts;
     NetL2TPV3State *s;
     NetClientState *nc;
     int fd = -1, gairet;
@@ -546,9 +546,6 @@ int net_init_l2tpv3(const NetClientOptions *opts,
     s->queue_tail = 0;
     s->header_mismatch = false;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_L2TPV3);
-    l2tpv3 = opts->l2tpv3;
-
     if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
         s->ipv6 = l2tpv3->ipv6;
     } else {
diff --git a/net/net.c b/net/net.c
index 25c2ef3..64da0a6 100644
--- a/net/net.c
+++ b/net/net.c
@@ -310,7 +310,7 @@ NICState *qemu_new_nic(NetClientInfo *info,
     NICState *nic;
     int i, queues = MAX(1, conf->peers.queues);
 
-    assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
+    assert(info->type == NET_CLIENT_DRIVER_NIC);
     assert(info->size >= sizeof(NICState));
 
     nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
@@ -380,18 +380,18 @@ void qemu_del_net_client(NetClientState *nc)
     NetClientState *ncs[MAX_QUEUE_NUM];
     int queues, i;
 
-    assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
+    assert(nc->info->type != NET_CLIENT_DRIVER_NIC);
 
     /* If the NetClientState belongs to a multiqueue backend, we will change all
      * other NetClientStates also.
      */
     queues = qemu_find_net_clients_except(nc->name, ncs,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          NET_CLIENT_DRIVER_NIC,
                                           MAX_QUEUE_NUM);
     assert(queues != 0);
 
     /* If there is a peer NIC, delete and cleanup client, but do not free. */
-    if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+    if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
         NICState *nic = qemu_get_nic(nc->peer);
         if (nic->peer_deleted) {
             return;
@@ -447,7 +447,7 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
     NetClientState *nc;
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
             if (nc->queue_index == 0) {
                 func(qemu_get_nic(nc), opaque);
             }
@@ -575,7 +575,7 @@ void qemu_flush_or_purge_queued_packets(NetClientState *nc, bool purge)
 {
     nc->receive_disabled = 0;
 
-    if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+    if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_HUBPORT) {
         if (net_hub_flush(nc->peer)) {
             qemu_notify_event();
         }
@@ -705,7 +705,7 @@ NetClientState *qemu_find_netdev(const char *id)
     NetClientState *nc;
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC)
             continue;
         if (!strcmp(nc->name, id)) {
             return nc;
@@ -716,7 +716,7 @@ NetClientState *qemu_find_netdev(const char *id)
 }
 
 int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
-                                 NetClientOptionsKind type, int max)
+                                 NetClientDriver type, int max)
 {
     NetClientState *nc;
     int ret = 0;
@@ -790,15 +790,12 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
     return -1;
 }
 
-static int net_init_nic(const NetClientOptions *opts, const char *name,
+static int net_init_nic(const void *opts, const char *name,
                         NetClientState *peer, Error **errp)
 {
     int idx;
     NICInfo *nd;
-    const NetLegacyNicOptions *nic;
-
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_NIC);
-    nic = opts->nic;
+    const NetLegacyNicOptions *nic = opts;
 
     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -858,32 +855,32 @@ static int net_init_nic(const NetClientOptions *opts, const char *name,
 }
 
 
-static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
-    const NetClientOptions *opts,
+static int (* const net_client_init_fun[NET_CLIENT_DRIVER_MAX])(
+    const void *opts,
     const char *name,
     NetClientState *peer, Error **errp) = {
-        [NET_CLIENT_OPTIONS_KIND_NIC]       = net_init_nic,
+        [NET_CLIENT_DRIVER_NIC]       = net_init_nic,
 #ifdef CONFIG_SLIRP
-        [NET_CLIENT_OPTIONS_KIND_USER]      = net_init_slirp,
+        [NET_CLIENT_DRIVER_USER]      = net_init_slirp,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_TAP]       = net_init_tap,
-        [NET_CLIENT_OPTIONS_KIND_SOCKET]    = net_init_socket,
+        [NET_CLIENT_DRIVER_TAP]       = net_init_tap,
+        [NET_CLIENT_DRIVER_SOCKET]    = net_init_socket,
 #ifdef CONFIG_VDE
-        [NET_CLIENT_OPTIONS_KIND_VDE]       = net_init_vde,
+        [NET_CLIENT_DRIVER_VDE]       = net_init_vde,
 #endif
 #ifdef CONFIG_NETMAP
-        [NET_CLIENT_OPTIONS_KIND_NETMAP]    = net_init_netmap,
+        [NET_CLIENT_DRIVER_NETMAP]    = net_init_netmap,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_DUMP]      = net_init_dump,
+        [NET_CLIENT_DRIVER_DUMP]      = net_init_dump,
 #ifdef CONFIG_NET_BRIDGE
-        [NET_CLIENT_OPTIONS_KIND_BRIDGE]    = net_init_bridge,
+        [NET_CLIENT_DRIVER_BRIDGE]    = net_init_bridge,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_HUBPORT]   = net_init_hubport,
+        [NET_CLIENT_DRIVER_HUBPORT]   = net_init_hubport,
 #ifdef CONFIG_VHOST_NET_USED
-        [NET_CLIENT_OPTIONS_KIND_VHOST_USER] = net_init_vhost_user,
+        [NET_CLIENT_DRIVER_VHOST_USER] = net_init_vhost_user,
 #endif
 #ifdef CONFIG_L2TPV3
-        [NET_CLIENT_OPTIONS_KIND_L2TPV3]    = net_init_l2tpv3,
+        [NET_CLIENT_DRIVER_L2TPV3]    = net_init_l2tpv3,
 #endif
 };
 
@@ -894,35 +891,37 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         const Netdev    *netdev;
         const NetLegacy *net;
     } u;
-    const NetClientOptions *opts;
+    NetClientDriver kind;
+    const void *opts;
     const char *name;
 
     if (is_netdev) {
         u.netdev = object;
-        opts = u.netdev->opts;
+        kind = u.netdev->kind;
+        opts = u.netdev->data;
         name = u.netdev->id;
 
-        switch (opts->kind) {
+        switch (kind) {
 #ifdef CONFIG_SLIRP
-        case NET_CLIENT_OPTIONS_KIND_USER:
+        case NET_CLIENT_DRIVER_USER:
 #endif
-        case NET_CLIENT_OPTIONS_KIND_TAP:
-        case NET_CLIENT_OPTIONS_KIND_SOCKET:
+        case NET_CLIENT_DRIVER_TAP:
+        case NET_CLIENT_DRIVER_SOCKET:
 #ifdef CONFIG_VDE
-        case NET_CLIENT_OPTIONS_KIND_VDE:
+        case NET_CLIENT_DRIVER_VDE:
 #endif
 #ifdef CONFIG_NETMAP
-        case NET_CLIENT_OPTIONS_KIND_NETMAP:
+        case NET_CLIENT_DRIVER_NETMAP:
 #endif
 #ifdef CONFIG_NET_BRIDGE
-        case NET_CLIENT_OPTIONS_KIND_BRIDGE:
+        case NET_CLIENT_DRIVER_BRIDGE:
 #endif
-        case NET_CLIENT_OPTIONS_KIND_HUBPORT:
+        case NET_CLIENT_DRIVER_HUBPORT:
 #ifdef CONFIG_VHOST_NET_USED
-        case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+        case NET_CLIENT_DRIVER_VHOST_USER:
 #endif
 #ifdef CONFIG_L2TPV3
-        case NET_CLIENT_OPTIONS_KIND_L2TPV3:
+        case NET_CLIENT_DRIVER_L2TPV3:
 #endif
             break;
 
@@ -933,8 +932,9 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         }
     } else {
         u.net = object;
-        opts = u.net->opts;
-        if (opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+        kind = u.net->kind;
+        opts = u.net->data;
+        if (kind == NET_CLIENT_DRIVER_HUBPORT) {
             error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                       "a net type");
             return -1;
@@ -943,22 +943,22 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         name = u.net->has_id ? u.net->id : u.net->name;
     }
 
-    if (net_client_init_fun[opts->kind]) {
+    if (net_client_init_fun[kind]) {
         NetClientState *peer = NULL;
 
         /* Do not add to a vlan if it's a -netdev or a nic with a netdev=
          * parameter. */
         if (!is_netdev &&
-            (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
-             !opts->nic->has_netdev)) {
+            (kind != NET_CLIENT_DRIVER_NIC ||
+             !((NetLegacyNicOptions *) opts)->has_netdev)) {
             peer = net_hub_add_port(u.net->has_vlan ? u.net->vlan : 0, NULL);
         }
 
-        if (net_client_init_fun[opts->kind](opts, name, peer, errp) < 0) {
+        if (net_client_init_fun[kind](opts, name, peer, errp) < 0) {
             /* FIXME drop when all init functions store an Error */
             if (errp && !*errp) {
                 error_set(errp, QERR_DEVICE_INIT_FAILED,
-                          NetClientOptionsKind_lookup[opts->kind]);
+                          NetClientDriver_lookup[kind]);
             }
             return -1;
         }
@@ -1057,7 +1057,7 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict)
                      device, vlan_id);
         return;
     }
-    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+    if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
         error_report("invalid host network device '%s'", device);
         return;
     }
@@ -1126,7 +1126,7 @@ void print_net_client(Monitor *mon, NetClientState *nc)
 {
     monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
                    nc->queue_index,
-                   NetClientOptionsKind_lookup[nc->info->type],
+                   NetClientDriver_lookup[nc->info->type],
                    nc->info_str);
 }
 
@@ -1145,7 +1145,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
         }
 
         /* only query rx-filter information of NIC */
-        if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type != NET_CLIENT_DRIVER_NIC) {
             if (has_name) {
                 error_setg(errp, "net client(%s) isn't a NIC", name);
                 return NULL;
@@ -1185,7 +1185,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
 void hmp_info_network(Monitor *mon, const QDict *qdict)
 {
     NetClientState *nc, *peer;
-    NetClientOptionsKind type;
+    NetClientDriver type;
 
     net_hub_info(mon);
 
@@ -1198,10 +1198,10 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
             continue;
         }
 
-        if (!peer || type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (!peer || type == NET_CLIENT_DRIVER_NIC) {
             print_net_client(mon, nc);
         } /* else it's a netdev connected to a NIC, printed with the NIC */
-        if (peer && type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (peer && type == NET_CLIENT_DRIVER_NIC) {
             monitor_printf(mon, " \\ ");
             print_net_client(mon, peer);
         }
@@ -1215,7 +1215,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
     int queues, i;
 
     queues = qemu_find_net_clients_except(name, ncs,
-                                          NET_CLIENT_OPTIONS_KIND_MAX,
+                                          NET_CLIENT_DRIVER_MAX,
                                           MAX_QUEUE_NUM);
 
     if (queues == 0) {
@@ -1241,7 +1241,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
          * multiple clients that can still communicate with each other in
          * disconnected mode. For now maintain this compatibility.
          */
-        if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
             for (i = 0; i < queues; i++) {
                 ncs[i]->peer->link_down = !up;
             }
@@ -1277,7 +1277,7 @@ void net_cleanup(void)
      */
     while (!QTAILQ_EMPTY(&net_clients)) {
         nc = QTAILQ_FIRST(&net_clients);
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
             qemu_del_nic(qemu_get_nic(nc));
         } else {
             qemu_del_net_client(nc);
@@ -1309,7 +1309,7 @@ void net_check_clients(void)
     QTAILQ_FOREACH(nc, &net_clients, next) {
         if (!nc->peer) {
             fprintf(stderr, "Warning: %s %s has no peer\n",
-                    nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC ?
+                    nc->info->type == NET_CLIENT_DRIVER_NIC ?
                     "nic" : "netdev", nc->name);
         }
     }
diff --git a/net/netmap.c b/net/netmap.c
index 508b829..537de09 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -417,7 +417,7 @@ static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
 
 /* NetClientInfo methods */
 static NetClientInfo net_netmap_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NETMAP,
+    .type = NET_CLIENT_DRIVER_NETMAP,
     .size = sizeof(NetmapState),
     .receive = netmap_receive,
     .receive_iov = netmap_receive_iov,
@@ -435,11 +435,11 @@ static NetClientInfo net_netmap_info = {
  *
  * ... -net netmap,ifname="..."
  */
-int net_init_netmap(const NetClientOptions *opts,
+int net_init_netmap(const void *opts,
                     const char *name, NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
-    const NetdevNetmapOptions *netmap_opts = opts->netmap;
+    const NetdevNetmapOptions *netmap_opts = opts;
     NetClientState *nc;
     NetmapPriv me;
     NetmapState *s;
diff --git a/net/slirp.c b/net/slirp.c
index 3533837..39b4c69 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -125,7 +125,7 @@ static void net_slirp_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_slirp_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_USER,
+    .type = NET_CLIENT_DRIVER_USER,
     .size = sizeof(SlirpState),
     .receive = net_slirp_receive,
     .cleanup = net_slirp_cleanup,
@@ -735,19 +735,16 @@ static const char **slirp_dnssearch(const StringList *dnsname)
     return ret;
 }
 
-int net_init_slirp(const NetClientOptions *opts, const char *name,
+int net_init_slirp(const void *opts, const char *name,
                    NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
     struct slirp_config_str *config;
     char *vnet;
     int ret;
-    const NetdevUserOptions *user;
+    const NetdevUserOptions *user = opts;
     const char **dnssearch;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
-    user = opts->user;
-
     vnet = user->has_net ? g_strdup(user->net) :
            user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
            NULL;
diff --git a/net/socket.c b/net/socket.c
index c752696..c5b4f8d 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -346,7 +346,7 @@ static void net_socket_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_dgram_socket_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+    .type = NET_CLIENT_DRIVER_SOCKET,
     .size = sizeof(NetSocketState),
     .receive = net_socket_receive_dgram,
     .cleanup = net_socket_cleanup,
@@ -429,7 +429,7 @@ static void net_socket_connect(void *opaque)
 }
 
 static NetClientInfo net_socket_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+    .type = NET_CLIENT_DRIVER_SOCKET,
     .size = sizeof(NetSocketState),
     .receive = net_socket_receive,
     .cleanup = net_socket_cleanup,
@@ -699,15 +699,12 @@ static int net_socket_udp_init(NetClientState *peer,
     return 0;
 }
 
-int net_init_socket(const NetClientOptions *opts, const char *name,
+int net_init_socket(const void *opts, const char *name,
                     NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
     Error *err = NULL;
-    const NetdevSocketOptions *sock;
-
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
-    sock = opts->socket;
+    const NetdevSocketOptions *sock = opts;
 
     if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
         sock->has_udp != 1) {
diff --git a/net/tap-win32.c b/net/tap-win32.c
index f6fc961..33c860f 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -713,7 +713,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 }
 
 static NetClientInfo net_tap_win32_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_TAP,
+    .type = NET_CLIENT_DRIVER_TAP,
     .size = sizeof(TAPState),
     .receive = tap_receive,
     .cleanup = tap_cleanup,
@@ -751,14 +751,11 @@ static int tap_win32_init(NetClientState *peer, const char *model,
     return 0;
 }
 
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const void *opts, const char *name,
                  NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
-    const NetdevTapOptions *tap;
-
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->tap;
+    const NetdevTapOptions *tap = opts;
 
     if (!tap->has_ifname) {
         error_report("tap: no interface name");
diff --git a/net/tap.c b/net/tap.c
index aa8b3f5..d183d3b 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -221,7 +221,7 @@ static bool tap_has_ufo(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
 
     return s->has_ufo;
 }
@@ -230,7 +230,7 @@ static bool tap_has_vnet_hdr(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
 
     return !!s->host_vnet_hdr_len;
 }
@@ -239,7 +239,7 @@ static bool tap_has_vnet_hdr_len(NetClientState *nc, int len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
 
     return !!tap_probe_vnet_hdr_len(s->fd, len);
 }
@@ -248,7 +248,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
            len == sizeof(struct virtio_net_hdr));
 
@@ -260,7 +260,7 @@ static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
 
     s->using_vnet_hdr = using_vnet_hdr;
@@ -312,14 +312,14 @@ static void tap_poll(NetClientState *nc, bool enable)
 int tap_get_fd(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     return s->fd;
 }
 
 /* fd support */
 
 static NetClientInfo net_tap_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_TAP,
+    .type = NET_CLIENT_DRIVER_TAP,
     .size = sizeof(TAPState),
     .receive = tap_receive,
     .receive_raw = tap_receive_raw,
@@ -541,17 +541,14 @@ static int net_bridge_run_helper(const char *helper, const char *bridge,
     }
 }
 
-int net_init_bridge(const NetClientOptions *opts, const char *name,
+int net_init_bridge(const void *opts, const char *name,
                     NetClientState *peer, Error **errp)
 {
-    const NetdevBridgeOptions *bridge;
+    const NetdevBridgeOptions *bridge = opts;
     const char *helper, *br;
     TAPState *s;
     int fd, vnet_hdr;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
-    bridge = opts->bridge;
-
     helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
     br     = bridge->has_br     ? bridge->br     : DEFAULT_BRIDGE_INTERFACE;
 
@@ -701,10 +698,10 @@ static int get_fds(char *str, char *fds[], int max)
     return i;
 }
 
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const void *opts, const char *name,
                  NetClientState *peer, Error **errp)
 {
-    const NetdevTapOptions *tap;
+    const NetdevTapOptions *tap = opts;
     int fd, vnet_hdr = 0, i = 0, queues;
     /* for the no-fd, no-helper case */
     const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */
@@ -713,8 +710,6 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
     const char *vhostfdname;
     char ifname[128];
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->tap;
     queues = tap->has_queues ? tap->queues : 1;
     vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;
 
@@ -875,7 +870,7 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
 VHostNetState *tap_get_vhost_net(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     return s->vhost_net;
 }
 
diff --git a/net/vde.c b/net/vde.c
index dacaa64..2aaf6d5 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -68,7 +68,7 @@ static void vde_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_vde_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_VDE,
+    .type = NET_CLIENT_DRIVER_VDE,
     .size = sizeof(VDEState),
     .receive = vde_receive,
     .cleanup = vde_cleanup,
@@ -109,14 +109,11 @@ static int net_vde_init(NetClientState *peer, const char *model,
     return 0;
 }
 
-int net_init_vde(const NetClientOptions *opts, const char *name,
+int net_init_vde(const void *opts, const char *name,
                  NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
-    const NetdevVdeOptions *vde;
-
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
-    vde = opts->vde;
+    const NetdevVdeOptions *vde = opts;
 
     /* missing optional values have been initialized to "all bits zero" */
     if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 3930741..204b8c3 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -30,7 +30,7 @@ typedef struct VhostUserChardevProps {
 VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
 {
     VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
     return s->vhost_net;
 }
 
@@ -76,20 +76,20 @@ static void vhost_user_cleanup(NetClientState *nc)
 
 static bool vhost_user_has_vnet_hdr(NetClientState *nc)
 {
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
 
     return true;
 }
 
 static bool vhost_user_has_ufo(NetClientState *nc)
 {
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
 
     return true;
 }
 
 static NetClientInfo net_vhost_user_info = {
-        .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
+        .type = NET_CLIENT_DRIVER_VHOST_USER,
         .size = sizeof(VhostUserState),
         .cleanup = vhost_user_cleanup,
         .has_vnet_hdr = vhost_user_has_vnet_hdr,
@@ -228,16 +228,13 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
     return 0;
 }
 
-int net_init_vhost_user(const NetClientOptions *opts, const char *name,
+int net_init_vhost_user(const void *opts, const char *name,
                         NetClientState *peer, Error **errp)
 {
     uint32_t queues;
-    const NetdevVhostUserOptions *vhost_user_opts;
+    const NetdevVhostUserOptions *vhost_user_opts = opts;
     CharDriverState *chr;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
-    vhost_user_opts = opts->vhost_user;
-
     chr = net_vhost_parse_chardev(vhost_user_opts, errp);
     if (!chr) {
         return -1;
diff --git a/qapi-schema.json b/qapi-schema.json
index 7ebf99e..2a3cfe3 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2475,34 +2475,20 @@
     '*queues':        'uint32' } }
 
 ##
-# @NetClientOptions
+# @NetClientDriver
 #
-# A discriminated record of network device traits.
-#
-# Since 1.2
-#
-# 'l2tpv3' - since 2.1
+# Possible netdev drivers.
 #
+# Since 2.4
 ##
-{ 'union': 'NetClientOptions',
-  'data': {
-    'none':     'NetdevNoneOptions',
-    'nic':      'NetLegacyNicOptions',
-    'user':     'NetdevUserOptions',
-    'tap':      'NetdevTapOptions',
-    'l2tpv3':   'NetdevL2TPv3Options',
-    'socket':   'NetdevSocketOptions',
-    'vde':      'NetdevVdeOptions',
-    'dump':     'NetdevDumpOptions',
-    'bridge':   'NetdevBridgeOptions',
-    'hubport':  'NetdevHubPortOptions',
-    'netmap':   'NetdevNetmapOptions',
-    'vhost-user': 'NetdevVhostUserOptions' } }
+{ 'enum': 'NetClientDriver',
+  'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', 'dump',
+            'bridge', 'hubport', 'netmap', 'vhost-user' ] }
 
 ##
-# @NetLegacy
+# @NetLegacyBase
 #
-# Captures the configuration of a network device; legacy.
+# Captures the common configuration of a network device; legacy.
 #
 # @vlan: #optional vlan number
 #
@@ -2512,30 +2498,87 @@
 #
 # @opts: device type specific properties (legacy)
 #
-# Since 1.2
+# @type: the netdev driver to use
+#
+# Since 2.4
 ##
-{ 'struct': 'NetLegacy',
+{ 'struct': 'NetLegacyBase',
   'data': {
     '*vlan': 'int32',
     '*id':   'str',
     '*name': 'str',
-    'opts':  'NetClientOptions' } }
+    'type': 'NetClientDriver' } }
 
 ##
-# @Netdev
+# @NetLegacy
 #
-# Captures the configuration of a network device.
+# Captures the configuration of a network device; legacy.
+#
+# Since 1.2
+#
+# 'l2tpv3' - since 2.1
+##
+{ 'union': 'NetLegacy',
+  'base': 'NetLegacyBase',
+  'discriminator': 'type',
+  'data': {
+    'none':     'NetdevNoneOptions',
+    'nic':      'NetLegacyNicOptions',
+    'user':     'NetdevUserOptions',
+    'tap':      'NetdevTapOptions',
+    'l2tpv3':   'NetdevL2TPv3Options',
+    'socket':   'NetdevSocketOptions',
+    'vde':      'NetdevVdeOptions',
+    'dump':     'NetdevDumpOptions',
+    'bridge':   'NetdevBridgeOptions',
+    'hubport':  'NetdevHubPortOptions',
+    'netmap':   'NetdevNetmapOptions',
+    'vhost-user': 'NetdevVhostUserOptions' } }
+
+
+##
+# @NetdevBase
+#
+# Captures the common configuration of a network device.
 #
 # @id: identifier for monitor commands.
 #
 # @opts: device type specific properties
 #
-# Since 1.2
+# @type: the netdev driver to use
+#
+# Since 2.4
 ##
-{ 'struct': 'Netdev',
+{ 'struct': 'NetdevBase',
   'data': {
     'id':   'str',
-    'opts': 'NetClientOptions' } }
+    'type': 'NetClientDriver' } }
+
+##
+# @Netdev
+#
+# Captures the configuration of a network device.
+#
+# Since 1.2
+#
+# 'l2tpv3' - since 2.1
+##
+{ 'union': 'Netdev',
+  'base': 'NetdevBase',
+  'discriminator': 'type',
+  'data': {
+    'none':     'NetdevNoneOptions',
+    'nic':      'NetLegacyNicOptions',
+    'user':     'NetdevUserOptions',
+    'tap':      'NetdevTapOptions',
+    'l2tpv3':   'NetdevL2TPv3Options',
+    'socket':   'NetdevSocketOptions',
+    'vde':      'NetdevVdeOptions',
+    'dump':     'NetdevDumpOptions',
+    'bridge':   'NetdevBridgeOptions',
+    'hubport':  'NetdevHubPortOptions',
+    'netmap':   'NetdevNetmapOptions',
+    'vhost-user': 'NetdevVhostUserOptions' } }
 
 ##
 # @InetSocketAddress
-- 
2.4.3

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

* [Qemu-devel] [PATCH v3 4/8] qapi: qapi for audio backends
  2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
                   ` (2 preceding siblings ...)
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 3/8] qapi: change Netdev and NetLegacy " Kővágó, Zoltán
@ 2015-06-18 16:43 ` Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 5/8] qapi: support nested structs in OptsVisitor Kővágó, Zoltán
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Markus Armbruster

This patch adds structures into qapi to replace the existing
configuration structures used by audio backends currently. This qapi
will be the base of the -audiodev command line parameter (that replaces
the old environment variables based config).

This is not a 1:1 translation of the old options, I've tried to make
them much more consistent (e.g. almost every backend had an option to
specify buffer size, but the name was different for every backend, and
some backends required usecs, while some other required frames, samples
or bytes). Also tried to reduce the number of abbreviations used by the
config keys.

Some of the more important changes:
* use `in` and `out` instead of `ADC` and `DAC`, as the former is more
  user friendly imho
* moved buffer settings into the global setting area (so it's the same
  for all backends that support it. Backends that can't change buffer
  size will simply ignore them). Also using usecs, as it's probably more
  user friendly than samples or bytes.
* try-poll is now an alsa and oss backend specific option (as all other
  backends currently ignore it)

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>

---

Changes from v2 patch:
* documentation fixes
* rename mmap -> try-mmap, buffer -> buffer-len
* id is now required
* converted into flat unions
* renamed alsa's and oss's in an out, because it was incompatible with
  with flat union

Changes from v1 patch:
* every time-related field now take usecs (and removed -usecs, -millis suffixes)
* fixed inconsisten optional marking, language issues

Changes from v2 RFC patch:
* in, out are no longer optional
* try-poll: moved to alsa and oss (as no other backend used them)
* voices: added (env variables had this option)
* dsound: removed primary buffer related fields

Changes from v1 RFC patch:
* fixed style issues
* moved definitions into a separate file
* documented undocumented options (hopefully)
* removed plive option. It was useless even years ago so it can probably safely
  go away: https://lists.nongnu.org/archive/html/qemu-devel/2012-03/msg02427.html
* removed verbose, debug options. Backends should use trace events instead.
* removed *_retries options from dsound. It's a kludge.
* moved buffer_usecs and buffer_count to the global config options. Some driver
  might ignore it (as they do not expose API to change them).
* wav backend: removed frequecy, format, channels as AudiodevPerDirectionOptions
  already have them.

 Makefile         |   4 +-
 qapi-schema.json |   3 +
 qapi/audio.json  | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 255 insertions(+), 2 deletions(-)
 create mode 100644 qapi/audio.json

diff --git a/Makefile b/Makefile
index 3f97904..ac566fa 100644
--- a/Makefile
+++ b/Makefile
@@ -257,8 +257,8 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 		"  GEN   $@")
 
 qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
-               $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
-               $(SRC_PATH)/qapi/event.json
+               $(SRC_PATH)/qapi/audio.json  $(SRC_PATH)/qapi/block.json \
+               $(SRC_PATH)/qapi/block-core.json $(SRC_PATH)/qapi/event.json
 
 qapi-types.c qapi-types.h :\
 $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
diff --git a/qapi-schema.json b/qapi-schema.json
index 2a3cfe3..f1896f4 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -5,6 +5,9 @@
 # QAPI common definitions
 { 'include': 'qapi/common.json' }
 
+# QAPI audio definitions
+{ 'include': 'qapi/audio.json' }
+
 # QAPI block definitions
 { 'include': 'qapi/block.json' }
 
diff --git a/qapi/audio.json b/qapi/audio.json
new file mode 100644
index 0000000..e00f082
--- /dev/null
+++ b/qapi/audio.json
@@ -0,0 +1,250 @@
+# -*- mode: python -*-
+#
+# Copyright (C) 2015 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+##
+# @AudiodevNoOptions
+#
+# The none, coreaudio, sdl and spice audio backend have no options.
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevNoOptions',
+  'data': { } }
+
+##
+# @AudiodevAlsaPerDirectionOptions
+#
+# Options of the alsa backend that are used for both playback and recording.
+#
+# @dev: #optional the name of the alsa device to use (default 'default')
+#
+# @try-poll: #optional attempt to use poll mode, falling back to non polling
+#            access on failure (default on)
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevAlsaPerDirectionOptions',
+  'data': {
+    '*dev':      'str',
+    '*try-poll': 'bool' } }
+
+##
+# @AudiodevAlsaOptions
+#
+# Options of the alsa audio backend.
+#
+# @alsa-in: options of the capture stream
+#
+# @alsa-out: options of the playback stream
+#
+# @threshold: #optional set the threshold (in microsecods) when playback starts
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevAlsaOptions',
+  'data': {
+    'alsa-in':    'AudiodevAlsaPerDirectionOptions',
+    'alsa-out':   'AudiodevAlsaPerDirectionOptions',
+    '*threshold': 'int' } }
+
+##
+# @AudiodevDsoundOptions
+#
+# Options of the dsound audio backend.
+#
+# @latency: #optional add extra latency to playback in microseconds (default
+#           10000)
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevDsoundOptions',
+  'data': {
+    '*latency': 'int' } }
+
+##
+# @AudiodevOssPerDirectionOptions
+#
+# Options of the oss backend that are used for both playback and recording.
+#
+# @dev: #optional file name of the oss device (default '/dev/dsp')
+#
+# @try-poll: #optional attempt to use poll mode, falling back to non polling
+#            access on failure (default on)
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevOssPerDirectionOptions',
+  'data': {
+    '*dev':      'str',
+    '*try-poll': 'bool' } }
+
+##
+# @AudiodevOssOptions
+#
+# Options of the oss audio backend.
+#
+# @oss-in: options of the capture stream
+#
+# @oss-out: options of the playback stream
+#
+# @try-mmap: #optional try using memory mapped access, falling back to non
+#            memory mapped access on failure (default off)
+#
+# @exclusive: #optional open device in exclusive mode (vmix won't work)
+#             (default off)
+#
+# @dsp-policy: #optional set the timing policy of the device (between 0 and 10,
+#              where smaller number means smaller latency but higher CPU usage)
+#              or -1 to use fragment mode (option ignored on some platforms)
+#              (default 5)
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevOssOptions',
+  'data': {
+    'oss-in':      'AudiodevOssPerDirectionOptions',
+    'oss-out':     'AudiodevOssPerDirectionOptions',
+    '*try-mmap':   'bool',
+    '*exclusive':  'bool',
+    '*dsp-policy': 'int' } }
+
+##
+# @AudiodevPaOptions
+#
+# Options of the pa (PulseAudio) audio backend.
+#
+# @server: #optional PulseAudio server address (default: let PulseAudio choose)
+#
+# @sink: #optional sink device name (default: let PulseAudio choose)
+#
+# @source: #optional source device name (default: let PulseAudio choose)
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevPaOptions',
+  'data': {
+    '*server': 'str',
+    '*sink':   'str',
+    '*source': 'str' } }
+
+##
+# @AudiodevWavOptions
+#
+# Options of the wav audio backend.
+#
+# @path: #optional name of the wav file to record (default 'qemu.wav')
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevWavOptions',
+  'data': {
+    '*path': 'str' } }
+
+
+##
+# @AudioFormat
+#
+# An enumeration of possible audio formats.
+#
+# Since: 2.4
+##
+{ 'enum': 'AudioFormat',
+  'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] }
+
+##
+# @AudiodevDriver
+#
+# An enumeration of possible audio backend drivers.
+#
+# Since: 2.4
+##
+{ 'enum': 'AudiodevDriver',
+  'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl', 'spice',
+            'wav' ] }
+
+##
+# @AudiodevPerDirectionOptions
+#
+# General audio backend options that are used for both playback and recording.
+#
+# @fixed-settings: #optional use fixed settings for host input/output.  When
+#                  off, frequency, channels and format must not be specified
+#                  (default on)
+#
+# @frequency: #optional frequency to use when using fixed settings
+#             (default 44100)
+#
+# @channels: #optional number of channels when using fixed settings
+#            (default 2)
+#
+# @voices: #optional number of voices to use (default 1)
+#
+# @format: #optional sample format to use when using fixed settings
+#          (default s16)
+#
+# @buffer-len: #optional the buffer size in microseconds
+#
+# @buffer-count: #optional number of buffers
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevPerDirectionOptions',
+  'data': {
+    '*fixed-settings': 'bool',
+    '*frequency':      'int',
+    '*channels':       'int',
+    '*voices':         'int',
+    '*format':         'AudioFormat',
+    '*buffer-len':     'int',
+    '*buffer-count':   'int' } }
+
+##
+# @AudiodevCommonOptions
+#
+# Captures the common configuration of an audio backend.
+#
+# @id: identifier of the backend
+#
+# @driver: the backend driver to use
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# @timer-period: #optional timer period (in microseconds, 0: use lowest
+#                possible)
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevCommonOptions',
+  'data': {
+    'id':            'str',
+    'driver':        'AudiodevDriver',
+    'in':            'AudiodevPerDirectionOptions',
+    'out':           'AudiodevPerDirectionOptions',
+    '*timer-period': 'int' } }
+
+##
+# @AudiodevBackendOptions
+#
+# Options of an audio backend.
+#
+# Since: 2.4
+##
+{ 'union': 'Audiodev',
+  'base': 'AudiodevCommonOptions',
+  'discriminator': 'driver',
+  'data': {
+    'none':      'AudiodevNoOptions',
+    'alsa':      'AudiodevAlsaOptions',
+    'coreaudio': 'AudiodevNoOptions',
+    'dsound':    'AudiodevDsoundOptions',
+    'oss':       'AudiodevOssOptions',
+    'pa':        'AudiodevPaOptions',
+    'sdl':       'AudiodevNoOptions',
+    'spice':     'AudiodevNoOptions',
+    'wav':       'AudiodevWavOptions' } }
-- 
2.4.3

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

* [Qemu-devel] [PATCH v3 5/8] qapi: support nested structs in OptsVisitor
  2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
                   ` (3 preceding siblings ...)
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 4/8] qapi: qapi for audio backends Kővágó, Zoltán
@ 2015-06-18 16:43 ` Kővágó, Zoltán
  2015-06-18 17:15   ` Laszlo Ersek
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 6/8] opts: produce valid command line in qemu_opts_print Kővágó, Zoltán
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Roth, László Ersek, Gerd Hoffmann, Markus Armbruster

The current OptsVisitor flattens the whole structure, if there are same
named fields under different paths (like `in' and `out' in `Audiodev'),
the current visitor can't cope with them (for example setting
`frequency=44100' will set the in's frequency to 44100 and leave out's
frequency unspecified).

This patch fixes it, by always requiring a complete path in case of
nested structs.  Fields in the path are separated by dots, similar to C
structs (without pointers), like `in.frequency' or`out.frequency'.

You must provide a full path even in non-ambigous cases.  The previous
two commits hopefully ensures that this change doesn't create backward
compatibility problems.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>

---

Change from v2:
* only fully qualified paths are allowed

 qapi/opts-visitor.c                     | 114 ++++++++++++++++++++++++++------
 tests/qapi-schema/qapi-schema-test.json |   9 ++-
 tests/test-opts-visitor.c               |  34 ++++++++++
 3 files changed, 135 insertions(+), 22 deletions(-)

diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index f02059d..7a80442 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -71,6 +71,7 @@ struct OptsVisitor
      * schema, with a single mandatory scalar member. */
     ListMode list_mode;
     GQueue *repeated_opts;
+    char *repeated_name;
 
     /* When parsing a list of repeating options as integers, values of the form
      * "a-b", representing a closed interval, are allowed. Elements in the
@@ -86,6 +87,9 @@ struct OptsVisitor
      * not survive or escape the OptsVisitor object.
      */
     QemuOpt *fake_id_opt;
+
+    /* List of field names leading to the current structure. */
+    GQueue *nested_names;
 };
 
 
@@ -100,6 +104,7 @@ static void
 opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
 {
     GQueue *list;
+    assert(opt);
 
     list = g_hash_table_lookup(unprocessed_opts, opt->name);
     if (list == NULL) {
@@ -127,6 +132,9 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
     if (obj) {
         *obj = g_malloc0(size > 0 ? size : 1);
     }
+
+    g_queue_push_tail(ov->nested_names, (gpointer) name);
+
     if (ov->depth++ > 0) {
         return;
     }
@@ -169,6 +177,8 @@ opts_end_struct(Visitor *v, Error **errp)
     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
     GQueue *any;
 
+    g_queue_pop_tail(ov->nested_names);
+
     if (--ov->depth > 0) {
         return;
     }
@@ -198,15 +208,54 @@ opts_end_implicit_struct(Visitor *v, Error **errp)
 }
 
 
+static void
+sum_strlen(gpointer data, gpointer user_data)
+{
+    const char *str = data;
+    size_t *sum_len = user_data;
+
+    if (str) { /* skip NULLs */
+        *sum_len += strlen(str) + 1;
+    }
+}
+
+static void
+append_str(gpointer data, gpointer user_data)
+{
+    const char *str = data;
+    char *concat_str = user_data;
+
+    if (str) {
+        strcat(concat_str, str);
+        strcat(concat_str, ".");
+    }
+}
+
+/* lookup a name, using a fully qualified version */
 static GQueue *
-lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
+lookup_distinct(const OptsVisitor *ov, const char *name, char **out_key,
+                Error **errp)
 {
-    GQueue *list;
+    GQueue *list = NULL;
+    char *key;
+    size_t sum_len = strlen(name);
+
+    g_queue_foreach(ov->nested_names, sum_strlen, &sum_len);
+    key = g_malloc(sum_len+1);
+    key[0] = 0;
+    g_queue_foreach(ov->nested_names, append_str, key);
+    strcat(key, name);
+
+    list = g_hash_table_lookup(ov->unprocessed_opts, key);
+    if (list && out_key) {
+        *out_key = g_strdup(key);
+    }
 
-    list = g_hash_table_lookup(ov->unprocessed_opts, name);
     if (!list) {
         error_set(errp, QERR_MISSING_PARAMETER, name);
     }
+
+    g_free(key);
     return list;
 }
 
@@ -218,7 +267,7 @@ opts_start_list(Visitor *v, const char *name, Error **errp)
 
     /* we can't traverse a list in a list */
     assert(ov->list_mode == LM_NONE);
-    ov->repeated_opts = lookup_distinct(ov, name, errp);
+    ov->repeated_opts = lookup_distinct(ov, name, &ov->repeated_name, errp);
     if (ov->repeated_opts != NULL) {
         ov->list_mode = LM_STARTED;
     }
@@ -254,11 +303,9 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
         /* range has been completed, fall through in order to pop option */
 
     case LM_IN_PROGRESS: {
-        const QemuOpt *opt;
-
-        opt = g_queue_pop_head(ov->repeated_opts);
+        g_queue_pop_head(ov->repeated_opts);
         if (g_queue_is_empty(ov->repeated_opts)) {
-            g_hash_table_remove(ov->unprocessed_opts, opt->name);
+            g_hash_table_remove(ov->unprocessed_opts, ov->repeated_name);
             return NULL;
         }
         link = &(*list)->next;
@@ -284,22 +331,28 @@ opts_end_list(Visitor *v, Error **errp)
            ov->list_mode == LM_SIGNED_INTERVAL ||
            ov->list_mode == LM_UNSIGNED_INTERVAL);
     ov->repeated_opts = NULL;
+
+    g_free(ov->repeated_name);
+    ov->repeated_name = NULL;
+
     ov->list_mode = LM_NONE;
 }
 
 
 static const QemuOpt *
-lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
+lookup_scalar(const OptsVisitor *ov, const char *name, char** out_key,
+              Error **errp)
 {
     if (ov->list_mode == LM_NONE) {
         GQueue *list;
 
         /* the last occurrence of any QemuOpt takes effect when queried by name
          */
-        list = lookup_distinct(ov, name, errp);
+        list = lookup_distinct(ov, name, out_key, errp);
         return list ? g_queue_peek_tail(list) : NULL;
     }
     assert(ov->list_mode == LM_IN_PROGRESS);
+    assert(out_key == NULL || *out_key == NULL);
     return g_queue_peek_head(ov->repeated_opts);
 }
 
@@ -321,13 +374,15 @@ opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
 {
     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
     const QemuOpt *opt;
+    char *key = NULL;
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
     *obj = g_strdup(opt->str ? opt->str : "");
-    processed(ov, name);
+    processed(ov, key);
+    g_free(key);
 }
 
 
@@ -337,8 +392,9 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
 {
     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
     const QemuOpt *opt;
+    char *key = NULL;
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
@@ -355,13 +411,15 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
         } else {
             error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
                 "on|yes|y|off|no|n");
+            g_free(key);
             return;
         }
     } else {
         *obj = true;
     }
 
-    processed(ov, name);
+    processed(ov, key);
+    g_free(key);
 }
 
 
@@ -373,13 +431,14 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
     const char *str;
     long long val;
     char *endptr;
+    char *key = NULL;
 
     if (ov->list_mode == LM_SIGNED_INTERVAL) {
         *obj = ov->range_next.s;
         return;
     }
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
@@ -393,11 +452,13 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
     if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) {
         if (*endptr == '\0') {
             *obj = val;
-            processed(ov, name);
+            processed(ov, key);
+            g_free(key);
             return;
         }
         if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
             long long val2;
+            assert(key == NULL);
 
             str = endptr + 1;
             val2 = strtoll(str, &endptr, 0);
@@ -418,6 +479,7 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
     error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
               (ov->list_mode == LM_NONE) ? "an int64 value" :
                                            "an int64 value or range");
+    g_free(key);
 }
 
 
@@ -429,13 +491,14 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     const char *str;
     unsigned long long val;
     char *endptr;
+    char *key = NULL;
 
     if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
         *obj = ov->range_next.u;
         return;
     }
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
@@ -447,11 +510,13 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) {
         if (*endptr == '\0') {
             *obj = val;
-            processed(ov, name);
+            processed(ov, key);
+            g_free(key);
             return;
         }
         if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
             unsigned long long val2;
+            assert(key == NULL);
 
             str = endptr + 1;
             if (parse_uint_full(str, &val2, 0) == 0 &&
@@ -470,6 +535,7 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
               (ov->list_mode == LM_NONE) ? "a uint64 value" :
                                            "a uint64 value or range");
+    g_free(key);
 }
 
 
@@ -480,8 +546,9 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     const QemuOpt *opt;
     int64_t val;
     char *endptr;
+    char *key = NULL;
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
@@ -491,11 +558,13 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     if (val < 0 || *endptr) {
         error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
                   "a size value representible as a non-negative int64");
+        g_free(key);
         return;
     }
 
     *obj = val;
-    processed(ov, name);
+    processed(ov, key);
+    g_free(key);
 }
 
 
@@ -506,7 +575,7 @@ opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
 
     /* we only support a single mandatory scalar field in a list node */
     assert(ov->list_mode == LM_NONE);
-    *present = (lookup_distinct(ov, name, NULL) != NULL);
+    *present = (lookup_distinct(ov, name, NULL, NULL) != NULL);
 }
 
 
@@ -517,6 +586,8 @@ opts_visitor_new(const QemuOpts *opts)
 
     ov = g_malloc0(sizeof *ov);
 
+    ov->nested_names = g_queue_new();
+
     ov->visitor.start_struct = &opts_start_struct;
     ov->visitor.end_struct   = &opts_end_struct;
 
@@ -560,6 +631,7 @@ opts_visitor_cleanup(OptsVisitor *ov)
     if (ov->unprocessed_opts != NULL) {
         g_hash_table_destroy(ov->unprocessed_opts);
     }
+    g_queue_free(ov->nested_names);
     g_free(ov->fake_id_opt);
     g_free(ov);
 }
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index c7eaa86..a818eff 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -81,6 +81,11 @@
 { 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' },
   'returns': 'int' }
 
+# For testing hierarchy support in opts-visitor
+{ 'struct': 'UserDefOptionsSub',
+  'data': {
+    '*nint': 'int' } }
+
 # For testing integer range flattening in opts-visitor. The following schema
 # corresponds to the option format:
 #
@@ -94,7 +99,9 @@
     '*u64' : [ 'uint64' ],
     '*u16' : [ 'uint16' ],
     '*i64x':   'int'     ,
-    '*u64x':   'uint64'  } }
+    '*u64x':   'uint64'  ,
+    'sub0':    'UserDefOptionsSub',
+    'sub1':    'UserDefOptionsSub' } }
 
 # testing event
 { 'struct': 'EventStructOne',
diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
index ebeee5d..9199141 100644
--- a/tests/test-opts-visitor.c
+++ b/tests/test-opts-visitor.c
@@ -177,6 +177,34 @@ expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data)
     g_assert(f->userdef->u64->value == UINT64_MAX);
 }
 
+static void
+expect_both(OptsVisitorFixture *f, gconstpointer test_data)
+{
+    expect_ok(f, test_data);
+    g_assert(f->userdef->sub0->has_nint);
+    g_assert(f->userdef->sub0->nint == 13);
+    g_assert(f->userdef->sub1->has_nint);
+    g_assert(f->userdef->sub1->nint == 17);
+}
+
+static void
+expect_sub0(OptsVisitorFixture *f, gconstpointer test_data)
+{
+    expect_ok(f, test_data);
+    g_assert(f->userdef->sub0->has_nint);
+    g_assert(f->userdef->sub0->nint == 13);
+    g_assert(!f->userdef->sub1->has_nint);
+}
+
+static void
+expect_sub1(OptsVisitorFixture *f, gconstpointer test_data)
+{
+    expect_ok(f, test_data);
+    g_assert(!f->userdef->sub0->has_nint);
+    g_assert(f->userdef->sub1->has_nint);
+    g_assert(f->userdef->sub1->nint == 13);
+}
+
 /* test cases */
 
 int
@@ -270,6 +298,12 @@ main(int argc, char **argv)
     add_test("/visitor/opts/i64/range/2big/full", &expect_fail,
              "i64=-0x8000000000000000-0x7fffffffffffffff");
 
+    /* Test nested structs support */
+    add_test("/visitor/opts/nested/unqualified", &expect_fail, "nint=13");
+    add_test("/visitor/opts/nested/both",        &expect_both,
+             "sub0.nint=13,sub1.nint=17");
+    add_test("/visitor/opts/nested/sub0",        &expect_sub0, "sub0.nint=13");
+    add_test("/visitor/opts/nested/sub1",        &expect_sub1, "sub1.nint=13");
     g_test_run();
     return 0;
 }
-- 
2.4.3

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

* [Qemu-devel] [PATCH v3 6/8] opts: produce valid command line in qemu_opts_print
  2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
                   ` (4 preceding siblings ...)
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 5/8] qapi: support nested structs in OptsVisitor Kővágó, Zoltán
@ 2015-06-18 16:43 ` Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 7/8] audio: use qapi AudioFormat instead of audfmt_e Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 8/8] audio: -audiodev command line option Kővágó, Zoltán
  7 siblings, 0 replies; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Gerd Hoffmann, open list:Block layer core

This will let us print options in a format that the user would actually
write it on the command line (foo=bar,baz=asd,etc=def), without
prepending a spurious comma at the beginning of the list, or quoting
values unnecessarily.  This patch provides the following changes:
* write and id=, if the option has an id
* do not print separator before the first element
* do not quote string arguments which only contains letters or numbers
* properly escape commas (,) for QEMU, apostrophe (') for shell

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>

---

Chages from v2:
* print id=
* proper value escaping (apostrophe and comma)
* renamed d_sep -> separator

 block.c            |  2 +-
 util/qemu-option.c | 47 ++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/block.c b/block.c
index dd4f58d..c5d456c 100644
--- a/block.c
+++ b/block.c
@@ -3823,7 +3823,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
     }
 
     if (!quiet) {
-        printf("Formatting '%s', fmt=%s", filename, fmt);
+        printf("Formatting '%s', fmt=%s ", filename, fmt);
         qemu_opts_print(opts, " ");
         puts("");
     }
diff --git a/util/qemu-option.c b/util/qemu-option.c
index 840f5f7..911f02d 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -728,14 +728,53 @@ void qemu_opts_del(QemuOpts *opts)
     g_free(opts);
 }
 
-void qemu_opts_print(QemuOpts *opts, const char *sep)
+/* print value properly escaping it for the shell (at least for bash) */
+static void escaped_print(const char *value)
+{
+    const char *ptr;
+    bool need_quote = false;
+
+    for (ptr = value; *ptr; ++ptr) {
+        if (!qemu_isalnum(*ptr)) {
+            need_quote = true;
+            break;
+        }
+    }
+
+    if (need_quote) {
+        putchar('\'');
+        for (ptr = value; *ptr; ++ptr) {
+            if (*ptr == '\'') {
+                printf("'\\''");
+            } else if (*ptr == ',') {
+                printf(",,");
+            } else {
+                putchar(*ptr);
+            }
+        }
+        putchar('\'');
+    } else {
+        printf("%s", value);
+    }
+}
+
+void qemu_opts_print(QemuOpts *opts, const char *separator)
 {
     QemuOpt *opt;
     QemuOptDesc *desc = opts->list->desc;
+    const char *sep = "";
+
+    if (opts->id) {
+        printf("id=");
+        escaped_print(opts->id);
+        sep = separator;
+    }
 
     if (desc[0].name == NULL) {
         QTAILQ_FOREACH(opt, &opts->head, next) {
-            printf("%s%s=\"%s\"", sep, opt->name, opt->str);
+            printf("%s%s=", sep, opt->name);
+            escaped_print(opt->str);
+            sep = separator;
         }
         return;
     }
@@ -748,13 +787,15 @@ void qemu_opts_print(QemuOpts *opts, const char *sep)
             continue;
         }
         if (desc->type == QEMU_OPT_STRING) {
-            printf("%s%s='%s'", sep, desc->name, value);
+            printf("%s%s=", sep, desc->name);
+            escaped_print(value);
         } else if ((desc->type == QEMU_OPT_SIZE ||
                     desc->type == QEMU_OPT_NUMBER) && opt) {
             printf("%s%s=%" PRId64, sep, desc->name, opt->value.uint);
         } else {
             printf("%s%s=%s", sep, desc->name, value);
         }
+        sep = separator;
     }
 }
 
-- 
2.4.3

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

* [Qemu-devel] [PATCH v3 7/8] audio: use qapi AudioFormat instead of audfmt_e
  2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
                   ` (5 preceding siblings ...)
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 6/8] opts: produce valid command line in qemu_opts_print Kővágó, Zoltán
@ 2015-06-18 16:43 ` Kővágó, Zoltán
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 8/8] audio: -audiodev command line option Kővágó, Zoltán
  7 siblings, 0 replies; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Maydell, Michael Walle, Gerd Hoffmann

I had to include an enum for audio sampling formats into qapi, but that
meant duplicating the audfmt_e enum.  This patch replaces audfmt_e and
associated values with the qapi generated AudioFormat enum.

This patch is mostly a search-and-replace, except for switches where the
qapi generated AUDIO_FORMAT_MAX caused problems.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>

---

Changes from v2:
* replaced case AUDIO_FORMAT_MAX with default

 audio/alsaaudio.c         | 53 ++++++++++++++------------
 audio/audio.c             | 97 ++++++++++++++++++++++++++---------------------
 audio/audio.h             | 11 +-----
 audio/audio_win_int.c     | 18 ++++-----
 audio/ossaudio.c          | 30 +++++++--------
 audio/paaudio.c           | 28 +++++++-------
 audio/sdlaudio.c          | 26 ++++++-------
 audio/spiceaudio.c        |  4 +-
 audio/wavaudio.c          | 17 +++++----
 audio/wavcapture.c        |  2 +-
 hw/arm/omap2.c            |  2 +-
 hw/audio/ac97.c           |  2 +-
 hw/audio/adlib.c          |  2 +-
 hw/audio/cs4231a.c        |  6 +--
 hw/audio/es1370.c         |  4 +-
 hw/audio/gus.c            |  2 +-
 hw/audio/hda-codec.c      | 18 ++++-----
 hw/audio/lm4549.c         |  6 +--
 hw/audio/milkymist-ac97.c |  2 +-
 hw/audio/pcspk.c          |  2 +-
 hw/audio/sb16.c           | 14 +++----
 hw/audio/wm8750.c         |  4 +-
 hw/input/tsc210x.c        |  2 +-
 hw/usb/dev-audio.c        |  2 +-
 ui/vnc.c                  | 14 +++----
 25 files changed, 187 insertions(+), 181 deletions(-)

diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 6315b2d..2b28b99 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -88,7 +88,7 @@ struct alsa_params_req {
 
 struct alsa_params_obt {
     int freq;
-    audfmt_e fmt;
+    AudioFormat fmt;
     int endianness;
     int nchannels;
     snd_pcm_uframes_t samples;
@@ -295,16 +295,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
     return audio_pcm_sw_write (sw, buf, len);
 }
 
-static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
+static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
 {
     switch (fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         return SND_PCM_FORMAT_S8;
 
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         return SND_PCM_FORMAT_U8;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         if (endianness) {
             return SND_PCM_FORMAT_S16_BE;
         }
@@ -312,7 +312,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
             return SND_PCM_FORMAT_S16_LE;
         }
 
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         if (endianness) {
             return SND_PCM_FORMAT_U16_BE;
         }
@@ -320,7 +320,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
             return SND_PCM_FORMAT_U16_LE;
         }
 
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         if (endianness) {
             return SND_PCM_FORMAT_S32_BE;
         }
@@ -328,7 +328,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
             return SND_PCM_FORMAT_S32_LE;
         }
 
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         if (endianness) {
             return SND_PCM_FORMAT_U32_BE;
         }
@@ -345,58 +345,58 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
     }
 }
 
-static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
+static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
                            int *endianness)
 {
     switch (alsafmt) {
     case SND_PCM_FORMAT_S8:
         *endianness = 0;
-        *fmt = AUD_FMT_S8;
+        *fmt = AUDIO_FORMAT_S8;
         break;
 
     case SND_PCM_FORMAT_U8:
         *endianness = 0;
-        *fmt = AUD_FMT_U8;
+        *fmt = AUDIO_FORMAT_U8;
         break;
 
     case SND_PCM_FORMAT_S16_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case SND_PCM_FORMAT_U16_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     case SND_PCM_FORMAT_S16_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case SND_PCM_FORMAT_U16_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     case SND_PCM_FORMAT_S32_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_S32;
+        *fmt = AUDIO_FORMAT_S32;
         break;
 
     case SND_PCM_FORMAT_U32_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_U32;
+        *fmt = AUDIO_FORMAT_U32;
         break;
 
     case SND_PCM_FORMAT_S32_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_S32;
+        *fmt = AUDIO_FORMAT_S32;
         break;
 
     case SND_PCM_FORMAT_U32_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_U32;
+        *fmt = AUDIO_FORMAT_U32;
         break;
 
     default:
@@ -639,19 +639,22 @@ static int alsa_open (int in, struct alsa_params_req *req,
         bytes_per_sec = freq << (nchannels == 2);
 
         switch (obt->fmt) {
-        case AUD_FMT_S8:
-        case AUD_FMT_U8:
+        case AUDIO_FORMAT_S8:
+        case AUDIO_FORMAT_U8:
             break;
 
-        case AUD_FMT_S16:
-        case AUD_FMT_U16:
+        case AUDIO_FORMAT_S16:
+        case AUDIO_FORMAT_U16:
             bytes_per_sec <<= 1;
             break;
 
-        case AUD_FMT_S32:
-        case AUD_FMT_U32:
+        case AUDIO_FORMAT_S32:
+        case AUDIO_FORMAT_U32:
             bytes_per_sec <<= 2;
             break;
+
+        default:
+            abort();
         }
 
         threshold = (conf->threshold * bytes_per_sec) / 1000;
diff --git a/audio/audio.c b/audio/audio.c
index 5be4b15..334c935 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -75,7 +75,7 @@ static struct {
         .settings = {
             .freq = 44100,
             .nchannels = 2,
-            .fmt = AUD_FMT_S16,
+            .fmt = AUDIO_FORMAT_S16,
             .endianness =  AUDIO_HOST_ENDIANNESS,
         }
     },
@@ -87,7 +87,7 @@ static struct {
         .settings = {
             .freq = 44100,
             .nchannels = 2,
-            .fmt = AUD_FMT_S16,
+            .fmt = AUDIO_FORMAT_S16,
             .endianness = AUDIO_HOST_ENDIANNESS,
         }
     },
@@ -219,58 +219,61 @@ static char *audio_alloc_prefix (const char *s)
     return r;
 }
 
-static const char *audio_audfmt_to_string (audfmt_e fmt)
+static const char *audio_audfmt_to_string (AudioFormat fmt)
 {
     switch (fmt) {
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         return "U8";
 
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         return "U16";
 
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         return "S8";
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         return "S16";
 
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         return "U32";
 
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         return "S32";
+
+    default:
+        abort();
     }
 
     dolog ("Bogus audfmt %d returning S16\n", fmt);
     return "S16";
 }
 
-static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval,
+static AudioFormat audio_string_to_audfmt (const char *s, AudioFormat defval,
                                         int *defaultp)
 {
     if (!strcasecmp (s, "u8")) {
         *defaultp = 0;
-        return AUD_FMT_U8;
+        return AUDIO_FORMAT_U8;
     }
     else if (!strcasecmp (s, "u16")) {
         *defaultp = 0;
-        return AUD_FMT_U16;
+        return AUDIO_FORMAT_U16;
     }
     else if (!strcasecmp (s, "u32")) {
         *defaultp = 0;
-        return AUD_FMT_U32;
+        return AUDIO_FORMAT_U32;
     }
     else if (!strcasecmp (s, "s8")) {
         *defaultp = 0;
-        return AUD_FMT_S8;
+        return AUDIO_FORMAT_S8;
     }
     else if (!strcasecmp (s, "s16")) {
         *defaultp = 0;
-        return AUD_FMT_S16;
+        return AUDIO_FORMAT_S16;
     }
     else if (!strcasecmp (s, "s32")) {
         *defaultp = 0;
-        return AUD_FMT_S32;
+        return AUDIO_FORMAT_S32;
     }
     else {
         dolog ("Bogus audio format `%s' using %s\n",
@@ -280,8 +283,8 @@ static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval,
     }
 }
 
-static audfmt_e audio_get_conf_fmt (const char *envname,
-                                    audfmt_e defval,
+static AudioFormat audio_get_conf_fmt (const char *envname,
+                                    AudioFormat defval,
                                     int *defaultp)
 {
     const char *var = getenv (envname);
@@ -384,7 +387,7 @@ static void audio_print_options (const char *prefix,
 
         case AUD_OPT_FMT:
             {
-                audfmt_e *fmtp = opt->valp;
+                AudioFormat *fmtp = opt->valp;
                 printf (
                     "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
                     state,
@@ -471,7 +474,7 @@ static void audio_process_options (const char *prefix,
 
         case AUD_OPT_FMT:
             {
-                audfmt_e *fmtp = opt->valp;
+                AudioFormat *fmtp = opt->valp;
                 *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
             }
             break;
@@ -502,22 +505,22 @@ static void audio_print_settings (struct audsettings *as)
     dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         AUD_log (NULL, "S8");
         break;
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         AUD_log (NULL, "U8");
         break;
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         AUD_log (NULL, "S16");
         break;
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         AUD_log (NULL, "U16");
         break;
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         AUD_log (NULL, "S32");
         break;
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         AUD_log (NULL, "U32");
         break;
     default:
@@ -548,12 +551,12 @@ static int audio_validate_settings (struct audsettings *as)
     invalid |= as->endianness != 0 && as->endianness != 1;
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_S8:
+    case AUDIO_FORMAT_U8:
+    case AUDIO_FORMAT_S16:
+    case AUDIO_FORMAT_U16:
+    case AUDIO_FORMAT_S32:
+    case AUDIO_FORMAT_U32:
         break;
     default:
         invalid = 1;
@@ -569,25 +572,28 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
     int bits = 8, sign = 0;
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         sign = 1;
         /* fall through */
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         break;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         sign = 1;
         /* fall through */
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         bits = 16;
         break;
 
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         sign = 1;
         /* fall through */
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         bits = 32;
         break;
+
+    default:
+        abort();
     }
     return info->freq == as->freq
         && info->nchannels == as->nchannels
@@ -601,24 +607,27 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
     int bits = 8, sign = 0, shift = 0;
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         sign = 1;
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         break;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         sign = 1;
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         bits = 16;
         shift = 1;
         break;
 
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         sign = 1;
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         bits = 32;
         shift = 2;
         break;
+
+    default:
+        abort();
     }
 
     info->freq = as->freq;
diff --git a/audio/audio.h b/audio/audio.h
index e7ea397..e300511 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -29,15 +29,6 @@
 
 typedef void (*audio_callback_fn) (void *opaque, int avail);
 
-typedef enum {
-    AUD_FMT_U8,
-    AUD_FMT_S8,
-    AUD_FMT_U16,
-    AUD_FMT_S16,
-    AUD_FMT_U32,
-    AUD_FMT_S32
-} audfmt_e;
-
 #ifdef HOST_WORDS_BIGENDIAN
 #define AUDIO_HOST_ENDIANNESS 1
 #else
@@ -47,7 +38,7 @@ typedef enum {
 struct audsettings {
     int freq;
     int nchannels;
-    audfmt_e fmt;
+    AudioFormat fmt;
     int endianness;
 };
 
diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c
index e132405..a8cfa77 100644
--- a/audio/audio_win_int.c
+++ b/audio/audio_win_int.c
@@ -23,20 +23,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
     wfx->cbSize = 0;
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_S8:
+    case AUDIO_FORMAT_U8:
         wfx->wBitsPerSample = 8;
         break;
 
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_S16:
+    case AUDIO_FORMAT_U16:
         wfx->wBitsPerSample = 16;
         wfx->nAvgBytesPerSec <<= 1;
         wfx->nBlockAlign <<= 1;
         break;
 
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_S32:
+    case AUDIO_FORMAT_U32:
         wfx->wBitsPerSample = 32;
         wfx->nAvgBytesPerSec <<= 2;
         wfx->nBlockAlign <<= 2;
@@ -84,15 +84,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
 
     switch (wfx->wBitsPerSample) {
     case 8:
-        as->fmt = AUD_FMT_U8;
+        as->fmt = AUDIO_FORMAT_U8;
         break;
 
     case 16:
-        as->fmt = AUD_FMT_S16;
+        as->fmt = AUDIO_FORMAT_S16;
         break;
 
     case 32:
-        as->fmt = AUD_FMT_S32;
+        as->fmt = AUDIO_FORMAT_S32;
         break;
 
     default:
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 11e76a1..02a3a95 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -72,7 +72,7 @@ typedef struct OSSVoiceIn {
 
 struct oss_params {
     int freq;
-    audfmt_e fmt;
+    AudioFormat fmt;
     int nchannels;
     int nfrags;
     int fragsize;
@@ -150,16 +150,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
     return audio_pcm_sw_write (sw, buf, len);
 }
 
-static int aud_to_ossfmt (audfmt_e fmt, int endianness)
+static int aud_to_ossfmt (AudioFormat fmt, int endianness)
 {
     switch (fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         return AFMT_S8;
 
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         return AFMT_U8;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         if (endianness) {
             return AFMT_S16_BE;
         }
@@ -167,7 +167,7 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
             return AFMT_S16_LE;
         }
 
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         if (endianness) {
             return AFMT_U16_BE;
         }
@@ -184,37 +184,37 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
     }
 }
 
-static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
+static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness)
 {
     switch (ossfmt) {
     case AFMT_S8:
         *endianness = 0;
-        *fmt = AUD_FMT_S8;
+        *fmt = AUDIO_FORMAT_S8;
         break;
 
     case AFMT_U8:
         *endianness = 0;
-        *fmt = AUD_FMT_U8;
+        *fmt = AUDIO_FORMAT_U8;
         break;
 
     case AFMT_S16_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case AFMT_U16_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     case AFMT_S16_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case AFMT_U16_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     default:
@@ -502,7 +502,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
     int endianness;
     int err;
     int fd;
-    audfmt_e effective_fmt;
+    AudioFormat effective_fmt;
     struct audsettings obt_as;
     OSSConf *conf = drv_opaque;
 
@@ -671,7 +671,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     int endianness;
     int err;
     int fd;
-    audfmt_e effective_fmt;
+    AudioFormat effective_fmt;
     struct audsettings obt_as;
     OSSConf *conf = drv_opaque;
 
diff --git a/audio/paaudio.c b/audio/paaudio.c
index fea6071..cfdbdc6 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -384,21 +384,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
     return audio_pcm_sw_read (sw, buf, len);
 }
 
-static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
+static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
 {
     int format;
 
     switch (afmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_S8:
+    case AUDIO_FORMAT_U8:
         format = PA_SAMPLE_U8;
         break;
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_S16:
+    case AUDIO_FORMAT_U16:
         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
         break;
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_S32:
+    case AUDIO_FORMAT_U32:
         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
         break;
     default:
@@ -409,26 +409,26 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
     return format;
 }
 
-static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
+static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
 {
     switch (fmt) {
     case PA_SAMPLE_U8:
-        return AUD_FMT_U8;
+        return AUDIO_FORMAT_U8;
     case PA_SAMPLE_S16BE:
         *endianness = 1;
-        return AUD_FMT_S16;
+        return AUDIO_FORMAT_S16;
     case PA_SAMPLE_S16LE:
         *endianness = 0;
-        return AUD_FMT_S16;
+        return AUDIO_FORMAT_S16;
     case PA_SAMPLE_S32BE:
         *endianness = 1;
-        return AUD_FMT_S32;
+        return AUDIO_FORMAT_S32;
     case PA_SAMPLE_S32LE:
         *endianness = 0;
-        return AUD_FMT_S32;
+        return AUDIO_FORMAT_S32;
     default:
         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
-        return AUD_FMT_U8;
+        return AUDIO_FORMAT_U8;
     }
 }
 
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index 1140f2e..db0f95a 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -115,19 +115,19 @@ static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
     return sdl_post (s, forfn);
 }
 
-static int aud_to_sdlfmt (audfmt_e fmt)
+static int aud_to_sdlfmt (AudioFormat fmt)
 {
     switch (fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         return AUDIO_S8;
 
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         return AUDIO_U8;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         return AUDIO_S16LSB;
 
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         return AUDIO_U16LSB;
 
     default:
@@ -139,37 +139,37 @@ static int aud_to_sdlfmt (audfmt_e fmt)
     }
 }
 
-static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
+static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
 {
     switch (sdlfmt) {
     case AUDIO_S8:
         *endianness = 0;
-        *fmt = AUD_FMT_S8;
+        *fmt = AUDIO_FORMAT_S8;
         break;
 
     case AUDIO_U8:
         *endianness = 0;
-        *fmt = AUD_FMT_U8;
+        *fmt = AUDIO_FORMAT_U8;
         break;
 
     case AUDIO_S16LSB:
         *endianness = 0;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case AUDIO_U16LSB:
         *endianness = 0;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     case AUDIO_S16MSB:
         *endianness = 1;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case AUDIO_U16MSB:
         *endianness = 1;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     default:
@@ -341,7 +341,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
     SDL_AudioSpec req, obt;
     int endianness;
     int err;
-    audfmt_e effective_fmt;
+    AudioFormat effective_fmt;
     struct audsettings obt_as;
 
     req.freq = as->freq;
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index 5c6f726..f556b3b 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -127,7 +127,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ;
 #endif
     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN;
-    settings.fmt        = AUD_FMT_S16;
+    settings.fmt        = AUDIO_FORMAT_S16;
     settings.endianness = AUDIO_HOST_ENDIANNESS;
 
     audio_pcm_init_info (&hw->info, &settings);
@@ -255,7 +255,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     settings.freq       = SPICE_INTERFACE_RECORD_FREQ;
 #endif
     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN;
-    settings.fmt        = AUD_FMT_S16;
+    settings.fmt        = AUDIO_FORMAT_S16;
     settings.endianness = AUDIO_HOST_ENDIANNESS;
 
     audio_pcm_init_info (&hw->info, &settings);
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index c586020..81250e6 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -116,20 +116,23 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
 
     stereo = wav_as.nchannels == 2;
     switch (wav_as.fmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_S8:
+    case AUDIO_FORMAT_U8:
         bits16 = 0;
         break;
 
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_S16:
+    case AUDIO_FORMAT_U16:
         bits16 = 1;
         break;
 
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_S32:
+    case AUDIO_FORMAT_U32:
         dolog ("WAVE files can not handle 32bit formats\n");
         return -1;
+
+    default:
+        abort();
     }
 
     hdr[34] = bits16 ? 0x10 : 0x08;
@@ -224,7 +227,7 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
 static WAVConf glob_conf = {
     .settings.freq      = 44100,
     .settings.nchannels = 2,
-    .settings.fmt       = AUD_FMT_S16,
+    .settings.fmt       = AUDIO_FORMAT_S16,
     .wav_path           = "qemu.wav"
 };
 
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
index 6f6d792..b03c244 100644
--- a/audio/wavcapture.c
+++ b/audio/wavcapture.c
@@ -135,7 +135,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
 
     as.freq = freq;
     as.nchannels = 1 << stereo;
-    as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
+    as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
     as.endianness = 0;
 
     ops.notify = wav_notify;
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index e39b317..3b14a5d 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -269,7 +269,7 @@ static void omap_eac_format_update(struct omap_eac_s *s)
      * does I2S specify it?  */
     /* All register writes are 16 bits so we we store 16-bit samples
      * in the buffers regardless of AGCFR[B8_16] value.  */
-    fmt.fmt = AUD_FMT_U16;
+    fmt.fmt = AUDIO_FORMAT_U16;
 
     s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice,
                     "eac.codec.in", s, omap_eac_in_cb, &fmt);
diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c
index b173835..fa75f33 100644
--- a/hw/audio/ac97.c
+++ b/hw/audio/ac97.c
@@ -360,7 +360,7 @@ static void open_voice (AC97LinkState *s, int index, int freq)
 
     as.freq = freq;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = 0;
 
     if (freq > 0) {
diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c
index 656eb37..f8f0f55 100644
--- a/hw/audio/adlib.c
+++ b/hw/audio/adlib.c
@@ -323,7 +323,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
 
     as.freq = s->freq;
     as.nchannels = SHIFT;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = AUDIO_HOST_ENDIANNESS;
 
     AUD_register_card ("adlib", &s->card);
diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c
index f96f561..626a173 100644
--- a/hw/audio/cs4231a.c
+++ b/hw/audio/cs4231a.c
@@ -284,7 +284,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
 
     switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
     case 0:
-        as.fmt = AUD_FMT_U8;
+        as.fmt = AUDIO_FORMAT_U8;
         s->shift = as.nchannels == 2;
         break;
 
@@ -294,7 +294,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
     case 3:
         s->tab = ALawDecompressTable;
     x_law:
-        as.fmt = AUD_FMT_S16;
+        as.fmt = AUDIO_FORMAT_S16;
         as.endianness = AUDIO_HOST_ENDIANNESS;
         s->shift = as.nchannels == 2;
         break;
@@ -302,7 +302,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
     case 6:
         as.endianness = 1;
     case 2:
-        as.fmt = AUD_FMT_S16;
+        as.fmt = AUDIO_FORMAT_S16;
         s->shift = as.nchannels;
         break;
 
diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
index 8e7bcf5..f6e74cb 100644
--- a/hw/audio/es1370.c
+++ b/hw/audio/es1370.c
@@ -414,14 +414,14 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
                     i,
                     new_freq,
                     1 << (new_fmt & 1),
-                    (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
+                    (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8,
                     d->shift);
             if (new_freq) {
                 struct audsettings as;
 
                 as.freq = new_freq;
                 as.nchannels = 1 << (new_fmt & 1);
-                as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8;
+                as.fmt = (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
                 as.endianness = 0;
 
                 if (i == ADC_CHANNEL) {
diff --git a/hw/audio/gus.c b/hw/audio/gus.c
index 86223a9..6107824 100644
--- a/hw/audio/gus.c
+++ b/hw/audio/gus.c
@@ -242,7 +242,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp)
 
     as.freq = s->freq;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = GUS_ENDIANNESS;
 
     s->voice = AUD_open_out (
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 3c03ff5..8693b7a 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -97,9 +97,9 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
     }
 
     switch (format & AC_FMT_BITS_MASK) {
-    case AC_FMT_BITS_8:  as->fmt = AUD_FMT_S8;  break;
-    case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
-    case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
+    case AC_FMT_BITS_8:  as->fmt = AUDIO_FORMAT_S8;  break;
+    case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break;
+    case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break;
     }
 
     as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
@@ -128,12 +128,12 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
 /* -------------------------------------------------------------------------- */
 
 static const char *fmt2name[] = {
-    [ AUD_FMT_U8  ] = "PCM-U8",
-    [ AUD_FMT_S8  ] = "PCM-S8",
-    [ AUD_FMT_U16 ] = "PCM-U16",
-    [ AUD_FMT_S16 ] = "PCM-S16",
-    [ AUD_FMT_U32 ] = "PCM-U32",
-    [ AUD_FMT_S32 ] = "PCM-S32",
+    [ AUDIO_FORMAT_U8  ] = "PCM-U8",
+    [ AUDIO_FORMAT_S8  ] = "PCM-S8",
+    [ AUDIO_FORMAT_U16 ] = "PCM-U16",
+    [ AUDIO_FORMAT_S16 ] = "PCM-S16",
+    [ AUDIO_FORMAT_U32 ] = "PCM-U32",
+    [ AUDIO_FORMAT_S32 ] = "PCM-S32",
 };
 
 typedef struct HDAAudioState HDAAudioState;
diff --git a/hw/audio/lm4549.c b/hw/audio/lm4549.c
index 380ef60..9d4f4b5 100644
--- a/hw/audio/lm4549.c
+++ b/hw/audio/lm4549.c
@@ -185,7 +185,7 @@ void lm4549_write(lm4549_state *s,
         struct audsettings as;
         as.freq = value;
         as.nchannels = 2;
-        as.fmt = AUD_FMT_S16;
+        as.fmt = AUDIO_FORMAT_S16;
         as.endianness = 0;
 
         s->voice = AUD_open_out(
@@ -255,7 +255,7 @@ static int lm4549_post_load(void *opaque, int version_id)
     struct audsettings as;
     as.freq = freq;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = 0;
 
     s->voice = AUD_open_out(
@@ -292,7 +292,7 @@ void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque)
     /* Open a default voice */
     as.freq = 48000;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = 0;
 
     s->voice = AUD_open_out(
diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c
index 28f55e8..15169e2 100644
--- a/hw/audio/milkymist-ac97.c
+++ b/hw/audio/milkymist-ac97.c
@@ -297,7 +297,7 @@ static int milkymist_ac97_init(SysBusDevice *dev)
 
     as.freq = 48000;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = 1;
 
     s->voice_in = AUD_open_in(&s->card, s->voice_in,
diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c
index 5266fb5..302debf 100644
--- a/hw/audio/pcspk.c
+++ b/hw/audio/pcspk.c
@@ -112,7 +112,7 @@ static void pcspk_callback(void *opaque, int free)
 static int pcspk_audio_init(ISABus *bus)
 {
     PCSpkState *s = pcspk_state;
-    struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
+    struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUDIO_FORMAT_U8, 0};
 
     AUD_register_card(s_spk, &s->card);
 
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
index b052de5..a159dcc 100644
--- a/hw/audio/sb16.c
+++ b/hw/audio/sb16.c
@@ -66,7 +66,7 @@ typedef struct SB16State {
     int fmt_stereo;
     int fmt_signed;
     int fmt_bits;
-    audfmt_e fmt;
+    AudioFormat fmt;
     int dma_auto;
     int block_size;
     int fifo;
@@ -221,7 +221,7 @@ static void continue_dma8 (SB16State *s)
 
 static void dma_cmd8 (SB16State *s, int mask, int dma_len)
 {
-    s->fmt = AUD_FMT_U8;
+    s->fmt = AUDIO_FORMAT_U8;
     s->use_hdma = 0;
     s->fmt_bits = 8;
     s->fmt_signed = 0;
@@ -316,18 +316,18 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
 
     if (16 == s->fmt_bits) {
         if (s->fmt_signed) {
-            s->fmt = AUD_FMT_S16;
+            s->fmt = AUDIO_FORMAT_S16;
         }
         else {
-            s->fmt = AUD_FMT_U16;
+            s->fmt = AUDIO_FORMAT_U16;
         }
     }
     else {
         if (s->fmt_signed) {
-            s->fmt = AUD_FMT_S8;
+            s->fmt = AUDIO_FORMAT_S8;
         }
         else {
-            s->fmt = AUD_FMT_U8;
+            s->fmt = AUDIO_FORMAT_U8;
         }
     }
 
@@ -839,7 +839,7 @@ static void legacy_reset (SB16State *s)
 
     as.freq = s->freq;
     as.nchannels = 1;
-    as.fmt = AUD_FMT_U8;
+    as.fmt = AUDIO_FORMAT_U8;
     as.endianness = 0;
 
     s->voice = AUD_open_out (
diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c
index b50b331..4c4333c 100644
--- a/hw/audio/wm8750.c
+++ b/hw/audio/wm8750.c
@@ -201,7 +201,7 @@ static void wm8750_set_format(WM8750State *s)
     in_fmt.endianness = 0;
     in_fmt.nchannels = 2;
     in_fmt.freq = s->adc_hz;
-    in_fmt.fmt = AUD_FMT_S16;
+    in_fmt.fmt = AUDIO_FORMAT_S16;
 
     s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
                     CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
@@ -214,7 +214,7 @@ static void wm8750_set_format(WM8750State *s)
     out_fmt.endianness = 0;
     out_fmt.nchannels = 2;
     out_fmt.freq = s->dac_hz;
-    out_fmt.fmt = AUD_FMT_S16;
+    out_fmt.fmt = AUDIO_FORMAT_S16;
 
     s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
                     CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c
index fae3385..3cf938b 100644
--- a/hw/input/tsc210x.c
+++ b/hw/input/tsc210x.c
@@ -315,7 +315,7 @@ static void tsc2102_audio_output_update(TSC210xState *s)
     fmt.endianness = 0;
     fmt.nchannels = 2;
     fmt.freq = s->codec.tx_rate;
-    fmt.fmt = AUD_FMT_S16;
+    fmt.fmt = AUDIO_FORMAT_S16;
 
     s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
                     "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index f092bb8..0171579 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -646,7 +646,7 @@ static void usb_audio_realize(USBDevice *dev, Error **errp)
     s->out.vol[1]        = 240; /* 0 dB */
     s->out.as.freq       = USBAUDIO_SAMPLE_RATE;
     s->out.as.nchannels  = 2;
-    s->out.as.fmt        = AUD_FMT_S16;
+    s->out.as.fmt        = AUDIO_FORMAT_S16;
     s->out.as.endianness = 0;
     streambuf_init(&s->out.buf, s->buffer);
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 69b605c..42cefb7 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2379,12 +2379,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
                 if (len == 4)
                     return 10;
                 switch (read_u8(data, 4)) {
-                case 0: vs->as.fmt = AUD_FMT_U8; break;
-                case 1: vs->as.fmt = AUD_FMT_S8; break;
-                case 2: vs->as.fmt = AUD_FMT_U16; break;
-                case 3: vs->as.fmt = AUD_FMT_S16; break;
-                case 4: vs->as.fmt = AUD_FMT_U32; break;
-                case 5: vs->as.fmt = AUD_FMT_S32; break;
+                case 0: vs->as.fmt = AUDIO_FORMAT_U8; break;
+                case 1: vs->as.fmt = AUDIO_FORMAT_S8; break;
+                case 2: vs->as.fmt = AUDIO_FORMAT_U16; break;
+                case 3: vs->as.fmt = AUDIO_FORMAT_S16; break;
+                case 4: vs->as.fmt = AUDIO_FORMAT_U32; break;
+                case 5: vs->as.fmt = AUDIO_FORMAT_S32; break;
                 default:
                     VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
                     vnc_client_error(vs);
@@ -3065,7 +3065,7 @@ void vnc_init_state(VncState *vs)
 
     vs->as.freq = 44100;
     vs->as.nchannels = 2;
-    vs->as.fmt = AUD_FMT_S16;
+    vs->as.fmt = AUDIO_FORMAT_S16;
     vs->as.endianness = 0;
 
     qemu_mutex_init(&vs->output_mutex);
-- 
2.4.3

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

* [Qemu-devel] [PATCH v3 8/8] audio: -audiodev command line option
  2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
                   ` (6 preceding siblings ...)
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 7/8] audio: use qapi AudioFormat instead of audfmt_e Kővágó, Zoltán
@ 2015-06-18 16:43 ` Kővágó, Zoltán
  7 siblings, 0 replies; 13+ messages in thread
From: Kővágó, Zoltán @ 2015-06-18 16:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Gerd Hoffmann

This patch adds an -audiodev command line option, and deprecates the
QEMU_* environment variables for audio backend configuration.  It's
syntax is similar to existing options (-netdev, -device, etc): -audiodev
driver_name,property=value,...

Audio drivers now get an Audiodev * as config paramters, instead of the
global audio_option structs.  There is some code in audio/audio_legacy.c
that converts the old environment variables to audiodev options (this
way backends do not have to worry about legacy options).  It also
contains a replacement of -audio-help, which prints out the equivalent
-audiodev based config of the currently specified environment variables.

Although now it's possible to specify multiple -audiodev options on
command line, multiple audio backends are not supported yet.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>

---

Changes from v2:
* fixed alsa threshold
* updated to audio qapi changes

Changes from v1:
* updated to everything usecs without suffix
* better rounding when converting between usecs and frames/samples/bytes
* bugfixes in audio_legacy.c
* fixed code broken by qemu_opts changes by Markus

 audio/Makefile.objs     |   2 +-
 audio/alsaaudio.c       | 311 ++++++--------------
 audio/audio.c           | 741 +++++++++++++-----------------------------------
 audio/audio.h           |  23 +-
 audio/audio_int.h       |   7 +-
 audio/audio_legacy.c    | 328 +++++++++++++++++++++
 audio/audio_template.h  |  13 +-
 audio/coreaudio.c       |  49 +---
 audio/dsound_template.h |   6 +-
 audio/dsoundaudio.c     |  60 ++--
 audio/noaudio.c         |   3 +-
 audio/ossaudio.c        | 154 +++-------
 audio/paaudio.c         |  80 ++----
 audio/sdlaudio.c        |  24 +-
 audio/spiceaudio.c      |   7 +-
 audio/wavaudio.c        |  60 +---
 qemu-options.hx         | 230 ++++++++++++++-
 vl.c                    |   9 +-
 18 files changed, 997 insertions(+), 1110 deletions(-)
 create mode 100644 audio/audio_legacy.c

diff --git a/audio/Makefile.objs b/audio/Makefile.objs
index 481d1aa..9d8f579 100644
--- a/audio/Makefile.objs
+++ b/audio/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
+common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
 common-obj-$(CONFIG_SDL) += sdlaudio.o
 common-obj-$(CONFIG_OSS) += ossaudio.o
 common-obj-$(CONFIG_SPICE) += spiceaudio.o
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 2b28b99..cfe4aec 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -22,6 +22,7 @@
  * THE SOFTWARE.
  */
 #include <alsa/asoundlib.h>
+#include "qapi-visit.h"
 #include "qemu-common.h"
 #include "qemu/main-loop.h"
 #include "audio.h"
@@ -34,28 +35,9 @@
 #define AUDIO_CAP "alsa"
 #include "audio_int.h"
 
-typedef struct ALSAConf {
-    int size_in_usec_in;
-    int size_in_usec_out;
-    const char *pcm_name_in;
-    const char *pcm_name_out;
-    unsigned int buffer_size_in;
-    unsigned int period_size_in;
-    unsigned int buffer_size_out;
-    unsigned int period_size_out;
-    unsigned int threshold;
-
-    int buffer_size_in_overridden;
-    int period_size_in_overridden;
-
-    int buffer_size_out_overridden;
-    int period_size_out_overridden;
-} ALSAConf;
-
 struct pollhlp {
     snd_pcm_t *handle;
     struct pollfd *pfds;
-    ALSAConf *conf;
     int count;
     int mask;
 };
@@ -67,6 +49,7 @@ typedef struct ALSAVoiceOut {
     void *pcm_buf;
     snd_pcm_t *handle;
     struct pollhlp pollhlp;
+    Audiodev *dev;
 } ALSAVoiceOut;
 
 typedef struct ALSAVoiceIn {
@@ -74,16 +57,13 @@ typedef struct ALSAVoiceIn {
     snd_pcm_t *handle;
     void *pcm_buf;
     struct pollhlp pollhlp;
+    Audiodev *dev;
 } ALSAVoiceIn;
 
 struct alsa_params_req {
     int freq;
     snd_pcm_format_t fmt;
     int nchannels;
-    int size_in_usec;
-    int override_mask;
-    unsigned int buffer_size;
-    unsigned int period_size;
 };
 
 struct alsa_params_obt {
@@ -409,7 +389,8 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
 
 static void alsa_dump_info (struct alsa_params_req *req,
                             struct alsa_params_obt *obt,
-                            snd_pcm_format_t obtfmt)
+                            snd_pcm_format_t obtfmt,
+                            AudiodevPerDirectionOptions *pdo)
 {
     dolog ("parameter | requested value | obtained value\n");
     dolog ("format    |      %10d |     %10d\n", req->fmt, obtfmt);
@@ -417,8 +398,9 @@ static void alsa_dump_info (struct alsa_params_req *req,
            req->nchannels, obt->nchannels);
     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
     dolog ("============================================\n");
-    dolog ("requested: buffer size %d period size %d\n",
-           req->buffer_size, req->period_size);
+    dolog ("requested: buffer size %" PRId64 " buffer count %" PRId64 "\n",
+           pdo->has_buffer_len ? pdo->buffer_len : 0,
+           pdo->has_buffer_len ? pdo->buffer_len : 0);
     dolog ("obtained: samples %ld\n", obt->samples);
 }
 
@@ -452,23 +434,25 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
     }
 }
 
-static int alsa_open (int in, struct alsa_params_req *req,
-                      struct alsa_params_obt *obt, snd_pcm_t **handlep,
-                      ALSAConf *conf)
+static int alsa_open(bool in, struct alsa_params_req *req,
+                     struct alsa_params_obt *obt, snd_pcm_t **handlep,
+                     Audiodev *dev)
 {
+    AudiodevPerDirectionOptions *pdo = in ? dev->in : dev->out;
+    AudiodevAlsaOptions *aopts = dev->alsa;
+    AudiodevAlsaPerDirectionOptions *apdo =
+        in ? aopts->alsa_in : aopts->alsa_out;
     snd_pcm_t *handle;
     snd_pcm_hw_params_t *hw_params;
     int err;
-    int size_in_usec;
     unsigned int freq, nchannels;
-    const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
+    const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
     snd_pcm_uframes_t obt_buffer_size;
     const char *typ = in ? "ADC" : "DAC";
     snd_pcm_format_t obtfmt;
 
     freq = req->freq;
     nchannels = req->nchannels;
-    size_in_usec = req->size_in_usec;
 
     snd_pcm_hw_params_alloca (&hw_params);
 
@@ -528,79 +512,49 @@ static int alsa_open (int in, struct alsa_params_req *req,
         goto err;
     }
 
-    if (req->buffer_size) {
-        unsigned long obt;
+    if (pdo->buffer_count) {
+        if (pdo->buffer_len) {
+            int64_t req = pdo->buffer_len * pdo->buffer_count;
 
-        if (size_in_usec) {
             int dir = 0;
-            unsigned int btime = req->buffer_size;
+            unsigned int btime = req;
 
-            err = snd_pcm_hw_params_set_buffer_time_near (
-                handle,
-                hw_params,
-                &btime,
-                &dir
-                );
-            obt = btime;
-        }
-        else {
-            snd_pcm_uframes_t bsize = req->buffer_size;
+            err = snd_pcm_hw_params_set_buffer_time_near(
+                handle, hw_params, &btime, &dir);
 
-            err = snd_pcm_hw_params_set_buffer_size_near (
-                handle,
-                hw_params,
-                &bsize
-                );
-            obt = bsize;
-        }
-        if (err < 0) {
-            alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
-                          size_in_usec ? "time" : "size", req->buffer_size);
-            goto err;
-        }
+            if (err < 0) {
+                alsa_logerr2(err, typ,
+                             "Failed to set buffer time to %" PRId64 "\n",
+                             req);
+                goto err;
+            }
 
-        if ((req->override_mask & 2) && (obt - req->buffer_size))
-            dolog ("Requested buffer %s %u was rejected, using %lu\n",
-                   size_in_usec ? "time" : "size", req->buffer_size, obt);
+            if (pdo->has_buffer_count && btime != req) {
+                dolog("Requested buffer time %" PRId64
+                      " was rejected, using %u\n", req, btime);
+            }
+        } else {
+            dolog("Can't set buffer_count without buffer_size!\n");
+        }
     }
 
-    if (req->period_size) {
-        unsigned long obt;
+    if (pdo->buffer_len) {
+        int dir = 0;
+        unsigned int ptime = pdo->buffer_len;
 
-        if (size_in_usec) {
-            int dir = 0;
-            unsigned int ptime = req->period_size;
-
-            err = snd_pcm_hw_params_set_period_time_near (
-                handle,
-                hw_params,
-                &ptime,
-                &dir
-                );
-            obt = ptime;
-        }
-        else {
-            int dir = 0;
-            snd_pcm_uframes_t psize = req->period_size;
-
-            err = snd_pcm_hw_params_set_period_size_near (
-                handle,
-                hw_params,
-                &psize,
-                &dir
-                );
-            obt = psize;
-        }
+        err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
+                                                     &dir);
 
         if (err < 0) {
-            alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
-                          size_in_usec ? "time" : "size", req->period_size);
+            alsa_logerr2(err, typ, "Failed to set period time to %" PRId64 "\n",
+                         pdo->buffer_len);
             goto err;
         }
 
-        if (((req->override_mask & 1) && (obt - req->period_size)))
-            dolog ("Requested period %s %u was rejected, using %lu\n",
-                   size_in_usec ? "time" : "size", req->period_size, obt);
+        if (pdo->has_buffer_len && ptime != pdo->buffer_len) {
+            dolog("Requested period time %" PRId64 " was rejected, using %d\n",
+                  pdo->buffer_len, ptime);
+        }
     }
 
     err = snd_pcm_hw_params (handle, hw_params);
@@ -632,33 +586,10 @@ static int alsa_open (int in, struct alsa_params_req *req,
         goto err;
     }
 
-    if (!in && conf->threshold) {
-        snd_pcm_uframes_t threshold;
-        int bytes_per_sec;
-
-        bytes_per_sec = freq << (nchannels == 2);
-
-        switch (obt->fmt) {
-        case AUDIO_FORMAT_S8:
-        case AUDIO_FORMAT_U8:
-            break;
-
-        case AUDIO_FORMAT_S16:
-        case AUDIO_FORMAT_U16:
-            bytes_per_sec <<= 1;
-            break;
-
-        case AUDIO_FORMAT_S32:
-        case AUDIO_FORMAT_U32:
-            bytes_per_sec <<= 2;
-            break;
-
-        default:
-            abort();
-        }
-
-        threshold = (conf->threshold * bytes_per_sec) / 1000;
-        alsa_set_threshold (handle, threshold);
+    if (!in && aopts->has_threshold && aopts->threshold) {
+        struct audsettings as = { .freq = freq };
+        alsa_set_threshold(handle,
+                           audio_buffer_frames(pdo, &as, aopts->threshold));
     }
 
     obt->nchannels = nchannels;
@@ -671,11 +602,11 @@ static int alsa_open (int in, struct alsa_params_req *req,
          obt->nchannels != req->nchannels ||
          obt->freq != req->freq) {
         dolog ("Audio parameters for %s\n", typ);
-        alsa_dump_info (req, obt, obtfmt);
+        alsa_dump_info (req, obt, obtfmt, pdo);
     }
 
 #ifdef DEBUG
-    alsa_dump_info (req, obt, obtfmt);
+    alsa_dump_info (req, obt, obtfmt, pdo);
 #endif
     return 0;
 
@@ -801,19 +732,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
     struct alsa_params_obt obt;
     snd_pcm_t *handle;
     struct audsettings obt_as;
-    ALSAConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
 
     req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.period_size = conf->period_size_out;
-    req.buffer_size = conf->buffer_size_out;
-    req.size_in_usec = conf->size_in_usec_out;
-    req.override_mask =
-        (conf->period_size_out_overridden ? 1 : 0) |
-        (conf->buffer_size_out_overridden ? 2 : 0);
 
-    if (alsa_open (0, &req, &obt, &handle, conf)) {
+    if (alsa_open (0, &req, &obt, &handle, dev)) {
         return -1;
     }
 
@@ -834,7 +759,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     alsa->handle = handle;
-    alsa->pollhlp.conf = conf;
+    alsa->dev = dev;
     return 0;
 }
 
@@ -874,16 +799,12 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
 static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->alsa->alsa_out;
 
     switch (cmd) {
     case VOICE_ENABLE:
         {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
+            bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
 
             ldebug ("enabling voice\n");
             if (poll_mode && alsa_poll_out (hw)) {
@@ -912,19 +833,13 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     struct alsa_params_obt obt;
     snd_pcm_t *handle;
     struct audsettings obt_as;
-    ALSAConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
 
     req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.period_size = conf->period_size_in;
-    req.buffer_size = conf->buffer_size_in;
-    req.size_in_usec = conf->size_in_usec_in;
-    req.override_mask =
-        (conf->period_size_in_overridden ? 1 : 0) |
-        (conf->buffer_size_in_overridden ? 2 : 0);
 
-    if (alsa_open (1, &req, &obt, &handle, conf)) {
+    if (alsa_open (1, &req, &obt, &handle, dev)) {
         return -1;
     }
 
@@ -945,7 +860,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     }
 
     alsa->handle = handle;
-    alsa->pollhlp.conf = conf;
+    alsa->dev = dev;
     return 0;
 }
 
@@ -1087,16 +1002,12 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
 static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 {
     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->alsa->alsa_in;
 
     switch (cmd) {
     case VOICE_ENABLE:
         {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
+            bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
 
             ldebug ("enabling voice\n");
             if (poll_mode && alsa_poll_in (hw)) {
@@ -1119,88 +1030,35 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return -1;
 }
 
-static ALSAConf glob_conf = {
-    .buffer_size_out = 4096,
-    .period_size_out = 1024,
-    .pcm_name_out = "default",
-    .pcm_name_in = "default",
-};
-
-static void *alsa_audio_init (void)
+static void *alsa_audio_init(Audiodev *dev)
 {
-    ALSAConf *conf = g_malloc(sizeof(ALSAConf));
-    *conf = glob_conf;
-    return conf;
+    assert(dev->kind == AUDIODEV_DRIVER_ALSA);
+
+    /* need to define them, as otherwise alsa produces no sound
+     * doesn't set has_* so alsa_open can identify it wasn't set by the user */
+    if (!dev->out->has_buffer_count) {
+        dev->out->buffer_count = 4;
+    }
+    if (!dev->out->has_buffer_len) {
+        dev->out->buffer_len = 23219; /* 1024 frames assuming 44100Hz */
+    }
+
+    /* OptsVisitor sets unspecified optional fields to zero, but do not depend
+     * on it... */
+    if (!dev->in->has_buffer_count) {
+        dev->in->buffer_count = 0;
+    }
+    if (!dev->in->has_buffer_len) {
+        dev->in->buffer_len = 0;
+    }
+
+    return dev;
 }
 
 static void alsa_audio_fini (void *opaque)
 {
-    g_free(opaque);
 }
 
-static struct audio_option alsa_options[] = {
-    {
-        .name        = "DAC_SIZE_IN_USEC",
-        .tag         = AUD_OPT_BOOL,
-        .valp        = &glob_conf.size_in_usec_out,
-        .descr       = "DAC period/buffer size in microseconds (otherwise in frames)"
-    },
-    {
-        .name        = "DAC_PERIOD_SIZE",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.period_size_out,
-        .descr       = "DAC period size (0 to go with system default)",
-        .overriddenp = &glob_conf.period_size_out_overridden
-    },
-    {
-        .name        = "DAC_BUFFER_SIZE",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.buffer_size_out,
-        .descr       = "DAC buffer size (0 to go with system default)",
-        .overriddenp = &glob_conf.buffer_size_out_overridden
-    },
-    {
-        .name        = "ADC_SIZE_IN_USEC",
-        .tag         = AUD_OPT_BOOL,
-        .valp        = &glob_conf.size_in_usec_in,
-        .descr       =
-        "ADC period/buffer size in microseconds (otherwise in frames)"
-    },
-    {
-        .name        = "ADC_PERIOD_SIZE",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.period_size_in,
-        .descr       = "ADC period size (0 to go with system default)",
-        .overriddenp = &glob_conf.period_size_in_overridden
-    },
-    {
-        .name        = "ADC_BUFFER_SIZE",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.buffer_size_in,
-        .descr       = "ADC buffer size (0 to go with system default)",
-        .overriddenp = &glob_conf.buffer_size_in_overridden
-    },
-    {
-        .name        = "THRESHOLD",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.threshold,
-        .descr       = "(undocumented)"
-    },
-    {
-        .name        = "DAC_DEV",
-        .tag         = AUD_OPT_STR,
-        .valp        = &glob_conf.pcm_name_out,
-        .descr       = "DAC device name (for instance dmix)"
-    },
-    {
-        .name        = "ADC_DEV",
-        .tag         = AUD_OPT_STR,
-        .valp        = &glob_conf.pcm_name_in,
-        .descr       = "ADC device name"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops alsa_pcm_ops = {
     .init_out = alsa_init_out,
     .fini_out = alsa_fini_out,
@@ -1218,7 +1076,6 @@ static struct audio_pcm_ops alsa_pcm_ops = {
 struct audio_driver alsa_audio_driver = {
     .name           = "alsa",
     .descr          = "ALSA http://www.alsa-project.org",
-    .options        = alsa_options,
     .init           = alsa_audio_init,
     .fini           = alsa_audio_fini,
     .pcm_ops        = &alsa_pcm_ops,
diff --git a/audio/audio.c b/audio/audio.c
index 334c935..76d4b8f 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -24,7 +24,10 @@
 #include "hw/hw.h"
 #include "audio.h"
 #include "monitor/monitor.h"
+#include "qapi-visit.h"
+#include "qapi/opts-visitor.h"
 #include "qemu/timer.h"
+#include "qemu/config-file.h"
 #include "sysemu/sysemu.h"
 
 #define AUDIO_CAP "audio"
@@ -42,59 +45,14 @@
    The 1st one is the one used by default, that is the reason
     that we generate the list.
 */
-static struct audio_driver *drvtab[] = {
+struct audio_driver *drvtab[] = {
 #ifdef CONFIG_SPICE
     &spice_audio_driver,
 #endif
     CONFIG_AUDIO_DRIVERS
     &no_audio_driver,
-    &wav_audio_driver
-};
-
-struct fixed_settings {
-    int enabled;
-    int nb_voices;
-    int greedy;
-    struct audsettings settings;
-};
-
-static struct {
-    struct fixed_settings fixed_out;
-    struct fixed_settings fixed_in;
-    union {
-        int hertz;
-        int64_t ticks;
-    } period;
-    int try_poll_in;
-    int try_poll_out;
-} conf = {
-    .fixed_out = { /* DAC fixed settings */
-        .enabled = 1,
-        .nb_voices = 1,
-        .greedy = 1,
-        .settings = {
-            .freq = 44100,
-            .nchannels = 2,
-            .fmt = AUDIO_FORMAT_S16,
-            .endianness =  AUDIO_HOST_ENDIANNESS,
-        }
-    },
-
-    .fixed_in = { /* ADC fixed settings */
-        .enabled = 1,
-        .nb_voices = 1,
-        .greedy = 1,
-        .settings = {
-            .freq = 44100,
-            .nchannels = 2,
-            .fmt = AUDIO_FORMAT_S16,
-            .endianness = AUDIO_HOST_ENDIANNESS,
-        }
-    },
-
-    .period = { .hertz = 100 },
-    .try_poll_in = 1,
-    .try_poll_out = 1,
+    &wav_audio_driver,
+    NULL
 };
 
 static AudioState glob_audio_state;
@@ -113,9 +71,6 @@ const struct mixeng_volume nominal_volume = {
 #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
 #error No its not
 #else
-static void audio_print_options (const char *prefix,
-                                 struct audio_option *opt);
-
 int audio_bug (const char *funcname, int cond)
 {
     if (cond) {
@@ -123,16 +78,9 @@ int audio_bug (const char *funcname, int cond)
 
         AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
         if (!shown) {
-            struct audio_driver *d;
-
             shown = 1;
             AUD_log (NULL, "Save all your work and restart without audio\n");
-            AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n");
             AUD_log (NULL, "I am sorry\n");
-            d = glob_audio_state.drv;
-            if (d) {
-                audio_print_options (d->name, d->options);
-            }
         }
         AUD_log (NULL, "Context:\n");
 
@@ -194,139 +142,6 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size)
     return g_malloc0 (len);
 }
 
-static char *audio_alloc_prefix (const char *s)
-{
-    const char qemu_prefix[] = "QEMU_";
-    size_t len, i;
-    char *r, *u;
-
-    if (!s) {
-        return NULL;
-    }
-
-    len = strlen (s);
-    r = g_malloc (len + sizeof (qemu_prefix));
-
-    u = r + sizeof (qemu_prefix) - 1;
-
-    pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix);
-    pstrcat (r, len + sizeof (qemu_prefix), s);
-
-    for (i = 0; i < len; ++i) {
-        u[i] = qemu_toupper(u[i]);
-    }
-
-    return r;
-}
-
-static const char *audio_audfmt_to_string (AudioFormat fmt)
-{
-    switch (fmt) {
-    case AUDIO_FORMAT_U8:
-        return "U8";
-
-    case AUDIO_FORMAT_U16:
-        return "U16";
-
-    case AUDIO_FORMAT_S8:
-        return "S8";
-
-    case AUDIO_FORMAT_S16:
-        return "S16";
-
-    case AUDIO_FORMAT_U32:
-        return "U32";
-
-    case AUDIO_FORMAT_S32:
-        return "S32";
-
-    default:
-        abort();
-    }
-
-    dolog ("Bogus audfmt %d returning S16\n", fmt);
-    return "S16";
-}
-
-static AudioFormat audio_string_to_audfmt (const char *s, AudioFormat defval,
-                                        int *defaultp)
-{
-    if (!strcasecmp (s, "u8")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_U8;
-    }
-    else if (!strcasecmp (s, "u16")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_U16;
-    }
-    else if (!strcasecmp (s, "u32")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_U32;
-    }
-    else if (!strcasecmp (s, "s8")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_S8;
-    }
-    else if (!strcasecmp (s, "s16")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_S16;
-    }
-    else if (!strcasecmp (s, "s32")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_S32;
-    }
-    else {
-        dolog ("Bogus audio format `%s' using %s\n",
-               s, audio_audfmt_to_string (defval));
-        *defaultp = 1;
-        return defval;
-    }
-}
-
-static AudioFormat audio_get_conf_fmt (const char *envname,
-                                    AudioFormat defval,
-                                    int *defaultp)
-{
-    const char *var = getenv (envname);
-    if (!var) {
-        *defaultp = 1;
-        return defval;
-    }
-    return audio_string_to_audfmt (var, defval, defaultp);
-}
-
-static int audio_get_conf_int (const char *key, int defval, int *defaultp)
-{
-    int val;
-    char *strval;
-
-    strval = getenv (key);
-    if (strval) {
-        *defaultp = 0;
-        val = atoi (strval);
-        return val;
-    }
-    else {
-        *defaultp = 1;
-        return defval;
-    }
-}
-
-static const char *audio_get_conf_str (const char *key,
-                                       const char *defval,
-                                       int *defaultp)
-{
-    const char *val = getenv (key);
-    if (!val) {
-        *defaultp = 1;
-        return defval;
-    }
-    else {
-        *defaultp = 0;
-        return val;
-    }
-}
-
 void AUD_vlog (const char *cap, const char *fmt, va_list ap)
 {
     if (cap) {
@@ -345,161 +160,6 @@ void AUD_log (const char *cap, const char *fmt, ...)
     va_end (ap);
 }
 
-static void audio_print_options (const char *prefix,
-                                 struct audio_option *opt)
-{
-    char *uprefix;
-
-    if (!prefix) {
-        dolog ("No prefix specified\n");
-        return;
-    }
-
-    if (!opt) {
-        dolog ("No options\n");
-        return;
-    }
-
-    uprefix = audio_alloc_prefix (prefix);
-
-    for (; opt->name; opt++) {
-        const char *state = "default";
-        printf ("  %s_%s: ", uprefix, opt->name);
-
-        if (opt->overriddenp && *opt->overriddenp) {
-            state = "current";
-        }
-
-        switch (opt->tag) {
-        case AUD_OPT_BOOL:
-            {
-                int *intp = opt->valp;
-                printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
-            }
-            break;
-
-        case AUD_OPT_INT:
-            {
-                int *intp = opt->valp;
-                printf ("integer, %s = %d\n", state, *intp);
-            }
-            break;
-
-        case AUD_OPT_FMT:
-            {
-                AudioFormat *fmtp = opt->valp;
-                printf (
-                    "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
-                    state,
-                    audio_audfmt_to_string (*fmtp)
-                    );
-            }
-            break;
-
-        case AUD_OPT_STR:
-            {
-                const char **strp = opt->valp;
-                printf ("string, %s = %s\n",
-                        state,
-                        *strp ? *strp : "(not set)");
-            }
-            break;
-
-        default:
-            printf ("???\n");
-            dolog ("Bad value tag for option %s_%s %d\n",
-                   uprefix, opt->name, opt->tag);
-            break;
-        }
-        printf ("    %s\n", opt->descr);
-    }
-
-    g_free (uprefix);
-}
-
-static void audio_process_options (const char *prefix,
-                                   struct audio_option *opt)
-{
-    char *optname;
-    const char qemu_prefix[] = "QEMU_";
-    size_t preflen, optlen;
-
-    if (audio_bug (AUDIO_FUNC, !prefix)) {
-        dolog ("prefix = NULL\n");
-        return;
-    }
-
-    if (audio_bug (AUDIO_FUNC, !opt)) {
-        dolog ("opt = NULL\n");
-        return;
-    }
-
-    preflen = strlen (prefix);
-
-    for (; opt->name; opt++) {
-        size_t len, i;
-        int def;
-
-        if (!opt->valp) {
-            dolog ("Option value pointer for `%s' is not set\n",
-                   opt->name);
-            continue;
-        }
-
-        len = strlen (opt->name);
-        /* len of opt->name + len of prefix + size of qemu_prefix
-         * (includes trailing zero) + zero + underscore (on behalf of
-         * sizeof) */
-        optlen = len + preflen + sizeof (qemu_prefix) + 1;
-        optname = g_malloc (optlen);
-
-        pstrcpy (optname, optlen, qemu_prefix);
-
-        /* copy while upper-casing, including trailing zero */
-        for (i = 0; i <= preflen; ++i) {
-            optname[i + sizeof (qemu_prefix) - 1] = qemu_toupper(prefix[i]);
-        }
-        pstrcat (optname, optlen, "_");
-        pstrcat (optname, optlen, opt->name);
-
-        def = 1;
-        switch (opt->tag) {
-        case AUD_OPT_BOOL:
-        case AUD_OPT_INT:
-            {
-                int *intp = opt->valp;
-                *intp = audio_get_conf_int (optname, *intp, &def);
-            }
-            break;
-
-        case AUD_OPT_FMT:
-            {
-                AudioFormat *fmtp = opt->valp;
-                *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
-            }
-            break;
-
-        case AUD_OPT_STR:
-            {
-                const char **strp = opt->valp;
-                *strp = audio_get_conf_str (optname, *strp, &def);
-            }
-            break;
-
-        default:
-            dolog ("Bad value tag for option `%s' - %d\n",
-                   optname, opt->tag);
-            break;
-        }
-
-        if (!opt->overriddenp) {
-            opt->overriddenp = &opt->overridden;
-        }
-        *opt->overriddenp = !def;
-        g_free (optname);
-    }
-}
-
 static void audio_print_settings (struct audsettings *as)
 {
     dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
@@ -1120,7 +780,7 @@ static void audio_reset_timer (AudioState *s)
 {
     if (audio_is_timer_needed ()) {
         timer_mod (s->ts,
-            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
+            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
     }
     else {
         timer_del (s->ts);
@@ -1196,7 +856,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
             if (!hw->enabled) {
                 hw->enabled = 1;
                 if (s->vm_running) {
-                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
+                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
                     audio_reset_timer (s);
                 }
             }
@@ -1241,7 +901,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
             if (!hw->enabled) {
                 hw->enabled = 1;
                 if (s->vm_running) {
-                    hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
+                    hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
                     audio_reset_timer (s);
                 }
             }
@@ -1558,168 +1218,10 @@ void audio_run (const char *msg)
 #endif
 }
 
-static struct audio_option audio_options[] = {
-    /* DAC */
-    {
-        .name  = "DAC_FIXED_SETTINGS",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.fixed_out.enabled,
-        .descr = "Use fixed settings for host DAC"
-    },
-    {
-        .name  = "DAC_FIXED_FREQ",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_out.settings.freq,
-        .descr = "Frequency for fixed host DAC"
-    },
-    {
-        .name  = "DAC_FIXED_FMT",
-        .tag   = AUD_OPT_FMT,
-        .valp  = &conf.fixed_out.settings.fmt,
-        .descr = "Format for fixed host DAC"
-    },
-    {
-        .name  = "DAC_FIXED_CHANNELS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_out.settings.nchannels,
-        .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)"
-    },
-    {
-        .name  = "DAC_VOICES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_out.nb_voices,
-        .descr = "Number of voices for DAC"
-    },
-    {
-        .name  = "DAC_TRY_POLL",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.try_poll_out,
-        .descr = "Attempt using poll mode for DAC"
-    },
-    /* ADC */
-    {
-        .name  = "ADC_FIXED_SETTINGS",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.fixed_in.enabled,
-        .descr = "Use fixed settings for host ADC"
-    },
-    {
-        .name  = "ADC_FIXED_FREQ",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_in.settings.freq,
-        .descr = "Frequency for fixed host ADC"
-    },
-    {
-        .name  = "ADC_FIXED_FMT",
-        .tag   = AUD_OPT_FMT,
-        .valp  = &conf.fixed_in.settings.fmt,
-        .descr = "Format for fixed host ADC"
-    },
-    {
-        .name  = "ADC_FIXED_CHANNELS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_in.settings.nchannels,
-        .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)"
-    },
-    {
-        .name  = "ADC_VOICES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_in.nb_voices,
-        .descr = "Number of voices for ADC"
-    },
-    {
-        .name  = "ADC_TRY_POLL",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.try_poll_in,
-        .descr = "Attempt using poll mode for ADC"
-    },
-    /* Misc */
-    {
-        .name  = "TIMER_PERIOD",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.period.hertz,
-        .descr = "Timer period in HZ (0 - use lowest possible)"
-    },
-    { /* End of list */ }
-};
-
-static void audio_pp_nb_voices (const char *typ, int nb)
+static int audio_driver_init(AudioState *s, struct audio_driver *drv,
+                             Audiodev *dev)
 {
-    switch (nb) {
-    case 0:
-        printf ("Does not support %s\n", typ);
-        break;
-    case 1:
-        printf ("One %s voice\n", typ);
-        break;
-    case INT_MAX:
-        printf ("Theoretically supports many %s voices\n", typ);
-        break;
-    default:
-        printf ("Theoretically supports up to %d %s voices\n", nb, typ);
-        break;
-    }
-
-}
-
-void AUD_help (void)
-{
-    size_t i;
-
-    audio_process_options ("AUDIO", audio_options);
-    for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-        struct audio_driver *d = drvtab[i];
-        if (d->options) {
-            audio_process_options (d->name, d->options);
-        }
-    }
-
-    printf ("Audio options:\n");
-    audio_print_options ("AUDIO", audio_options);
-    printf ("\n");
-
-    printf ("Available drivers:\n");
-
-    for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-        struct audio_driver *d = drvtab[i];
-
-        printf ("Name: %s\n", d->name);
-        printf ("Description: %s\n", d->descr);
-
-        audio_pp_nb_voices ("playback", d->max_voices_out);
-        audio_pp_nb_voices ("capture", d->max_voices_in);
-
-        if (d->options) {
-            printf ("Options:\n");
-            audio_print_options (d->name, d->options);
-        }
-        else {
-            printf ("No options\n");
-        }
-        printf ("\n");
-    }
-
-    printf (
-        "Options are settable through environment variables.\n"
-        "Example:\n"
-#ifdef _WIN32
-        "  set QEMU_AUDIO_DRV=wav\n"
-        "  set QEMU_WAV_PATH=c:\\tune.wav\n"
-#else
-        "  export QEMU_AUDIO_DRV=wav\n"
-        "  export QEMU_WAV_PATH=$HOME/tune.wav\n"
-        "(for csh replace export with setenv in the above)\n"
-#endif
-        "  qemu ...\n\n"
-        );
-}
-
-static int audio_driver_init (AudioState *s, struct audio_driver *drv)
-{
-    if (drv->options) {
-        audio_process_options (drv->name, drv->options);
-    }
-    s->drv_opaque = drv->init ();
+    s->drv_opaque = drv->init(dev);
 
     if (s->drv_opaque) {
         audio_init_nb_voices_out (drv);
@@ -1743,11 +1245,11 @@ static void audio_vm_change_state_handler (void *opaque, int running,
 
     s->vm_running = running;
     while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
-        hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out);
+        hwo->pcm_ops->ctl_out (hwo, op);
     }
 
     while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
-        hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
+        hwi->pcm_ops->ctl_in (hwi, op);
     }
     audio_reset_timer (s);
 }
@@ -1786,6 +1288,8 @@ static void audio_atexit (void)
     if (s->drv) {
         s->drv->fini (s->drv_opaque);
     }
+
+    qapi_free_Audiodev(s->dev);
 }
 
 static const VMStateDescription vmstate_audio = {
@@ -1797,18 +1301,37 @@ static const VMStateDescription vmstate_audio = {
     }
 };
 
-static void audio_init (void)
+static Audiodev *parse_option(QemuOpts *opts, Error **errp);
+static int audio_init(Audiodev *dev)
 {
     size_t i;
     int done = 0;
-    const char *drvname;
+    const char *drvname = NULL;
     VMChangeStateEntry *e;
     AudioState *s = &glob_audio_state;
+    QemuOptsList *list = NULL; /* silence gcc warning about uninitialized
+                                * variable */
 
     if (s->drv) {
-        return;
+        if (dev) {
+            dolog("Cannot create more than one audio backend, sorry\n");
+            qapi_free_Audiodev(dev);
+        }
+        return -1;
     }
 
+    if (dev) {
+        drvname = AudiodevDriver_lookup[dev->kind];
+    } else {
+        audio_handle_legacy_opts();
+        list = qemu_find_opts("audiodev");
+        dev = parse_option(QTAILQ_FIRST(&list->head), &error_abort);
+        if (!dev) {
+            exit(1);
+        }
+    }
+    s->dev = dev;
+
     QLIST_INIT (&s->hw_head_out);
     QLIST_INIT (&s->hw_head_in);
     QLIST_INIT (&s->cap_head);
@@ -1819,10 +1342,8 @@ static void audio_init (void)
         hw_error("Could not create audio timer\n");
     }
 
-    audio_process_options ("AUDIO", audio_options);
-
-    s->nb_hw_voices_out = conf.fixed_out.nb_voices;
-    s->nb_hw_voices_in = conf.fixed_in.nb_voices;
+    s->nb_hw_voices_out = dev->out->voices;
+    s->nb_hw_voices_in = dev->in->voices;
 
     if (s->nb_hw_voices_out <= 0) {
         dolog ("Bogus number of playback voices %d, setting to 1\n",
@@ -1836,17 +1357,12 @@ static void audio_init (void)
         s->nb_hw_voices_in = 0;
     }
 
-    {
-        int def;
-        drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
-    }
-
     if (drvname) {
         int found = 0;
 
-        for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
+        for (i = 0; drvtab[i]; i++) {
             if (!strcmp (drvname, drvtab[i]->name)) {
-                done = !audio_driver_init (s, drvtab[i]);
+                done = !audio_driver_init (s, drvtab[i], dev);
                 found = 1;
                 break;
             }
@@ -1854,20 +1370,24 @@ static void audio_init (void)
 
         if (!found) {
             dolog ("Unknown audio driver `%s'\n", drvname);
-            dolog ("Run with -audio-help to list available drivers\n");
         }
-    }
-
-    if (!done) {
-        for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) {
-            if (drvtab[i]->can_be_default) {
-                done = !audio_driver_init (s, drvtab[i]);
+    } else {
+        for (i = 0; !done && drvtab[i]; i++) {
+            QemuOpts *opts = qemu_opts_find(list, drvtab[i]->name);
+            if (opts) {
+                qapi_free_Audiodev(dev);
+                dev = parse_option(opts, &error_abort);
+                if (!dev) {
+                    exit(1);
+                }
+                s->dev = dev;
+                done = !audio_driver_init(s, drvtab[i], dev);
             }
         }
     }
 
     if (!done) {
-        done = !audio_driver_init (s, &no_audio_driver);
+        done = !audio_driver_init (s, &no_audio_driver, dev);
         if (!done) {
             hw_error("Could not initialize audio subsystem\n");
         }
@@ -1876,16 +1396,16 @@ static void audio_init (void)
         }
     }
 
-    if (conf.period.hertz <= 0) {
-        if (conf.period.hertz < 0) {
-            dolog ("warning: Timer period is negative - %d "
-                   "treating as zero\n",
-                   conf.period.hertz);
+    if (dev->timer_period <= 0) {
+        if (dev->timer_period < 0) {
+            dolog ("warning: Timer period is negative - %" PRId64
+                   " treating as zero\n",
+                   dev->timer_period);
         }
-        conf.period.ticks = 1;
+        s->period_ticks = 1;
     } else {
-        conf.period.ticks =
-            muldiv64 (1, get_ticks_per_sec (), conf.period.hertz);
+        s->period_ticks =
+            muldiv64(dev->timer_period, get_ticks_per_sec(), 1000000);
     }
 
     e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
@@ -1896,11 +1416,12 @@ static void audio_init (void)
 
     QLIST_INIT (&s->card_head);
     vmstate_register (NULL, 0, &vmstate_audio, s);
+    return 0;
 }
 
 void AUD_register_card (const char *name, QEMUSoundCard *card)
 {
-    audio_init ();
+    audio_init(NULL);
     card->name = g_strdup (name);
     memset (&card->entries, 0, sizeof (card->entries));
     QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
@@ -2070,3 +1591,137 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
         }
     }
 }
+
+QemuOptsList qemu_audiodev_opts = {
+    .name = "audiodev",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_audiodev_opts.head),
+    .implied_opt_name = "driver",
+    .desc = {
+        /*
+         * no elements => accept any params
+         * sanity checking will happen later
+         */
+        { /* end of list */ }
+    },
+};
+
+static void set_per_direction_defaults(AudiodevPerDirectionOptions *pdo)
+{
+    if (!pdo->has_fixed_settings) {
+        pdo->has_fixed_settings = true;
+        pdo->fixed_settings = true;
+    }
+    if (!pdo->has_frequency) {
+        pdo->has_frequency = true;
+        pdo->frequency = 44100;
+    }
+    if (!pdo->has_channels) {
+        pdo->has_channels = true;
+        pdo->channels = 2;
+    }
+    if (!pdo->has_voices) {
+        pdo->has_voices = true;
+        pdo->voices = 1;
+    }
+    if (!pdo->has_format) {
+        pdo->has_format = true;
+        pdo->format = AUDIO_FORMAT_S16;
+    }
+}
+
+static Audiodev *parse_option(QemuOpts *opts, Error **errp)
+{
+    Error *local_err = NULL;
+    OptsVisitor *ov = opts_visitor_new(opts);
+    Audiodev *dev = NULL;
+    visit_type_Audiodev(opts_get_visitor(ov), &dev, NULL, &local_err);
+    opts_visitor_cleanup(ov);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return NULL;
+    }
+
+    set_per_direction_defaults(dev->in);
+    set_per_direction_defaults(dev->out);
+
+    if (!dev->has_timer_period) {
+        dev->has_timer_period = true;
+        dev->timer_period = 10000; /* 100Hz -> 10ms */
+    }
+
+    return dev;
+}
+
+static int each_option(void *opaque, QemuOpts *opts, Error **errp)
+{
+    Audiodev *dev = parse_option(opts, errp);
+    if (!dev) {
+        return -1;
+    }
+    return audio_init(dev);
+}
+
+void audio_set_options(void)
+{
+    if (qemu_opts_foreach(qemu_find_opts("audiodev"), each_option, NULL,
+                          &error_abort)) {
+        exit(1);
+    }
+}
+
+audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
+{
+    return (audsettings) {
+        .freq = pdo->frequency,
+        .nchannels = pdo->channels,
+        .fmt = pdo->format,
+        .endianness = AUDIO_HOST_ENDIANNESS,
+    };
+}
+
+int audioformat_bytes_per_sample(AudioFormat fmt)
+{
+    switch (fmt) {
+    case AUDIO_FORMAT_U8:
+    case AUDIO_FORMAT_S8:
+        return 1;
+
+    case AUDIO_FORMAT_U16:
+    case AUDIO_FORMAT_S16:
+        return 2;
+
+    case AUDIO_FORMAT_U32:
+    case AUDIO_FORMAT_S32:
+        return 4;
+
+    case AUDIO_FORMAT_MAX:
+        ;
+    }
+    abort();
+}
+
+
+/* frames = freq * usec / 1e6 */
+int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
+                        audsettings *as, int def_usecs)
+{
+    uint64_t usecs = pdo->has_buffer_len ? pdo->buffer_len : def_usecs;
+    return (as->freq * usecs + 500000) / 1000000;
+}
+
+/* samples = channels * frames = channels * freq * usec / 1e6 */
+int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
+                         audsettings *as, int def_usecs)
+{
+    return as->nchannels * audio_buffer_frames(pdo, as, def_usecs);
+}
+
+/* bytes = bytes_per_sample * samples =
+ *   bytes_per_sample * channels * freq * usec / 1e6 */
+int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
+                       audsettings *as, int def_usecs)
+{
+    return audio_buffer_samples(pdo, as, def_usecs) *
+        audioformat_bytes_per_sample(as->fmt);
+}
diff --git a/audio/audio.h b/audio/audio.h
index e300511..177a673 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -24,7 +24,10 @@
 #ifndef QEMU_AUDIO_H
 #define QEMU_AUDIO_H
 
+#include <stdarg.h>
 #include "config-host.h"
+#include "qapi-types.h"
+#include "qemu/option.h"
 #include "qemu/queue.h"
 
 typedef void (*audio_callback_fn) (void *opaque, int avail);
@@ -35,12 +38,21 @@ typedef void (*audio_callback_fn) (void *opaque, int avail);
 #define AUDIO_HOST_ENDIANNESS 0
 #endif
 
-struct audsettings {
+typedef struct audsettings {
     int freq;
     int nchannels;
     AudioFormat fmt;
     int endianness;
-};
+} audsettings;
+
+audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
+int audioformat_bytes_per_sample(AudioFormat fmt);
+int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
+                        audsettings *as, int def_usecs);
+int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
+                         audsettings *as, int def_usecs);
+int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
+                       audsettings *as, int def_usecs);
 
 typedef enum {
     AUD_CNOTIFY_ENABLE,
@@ -77,10 +89,11 @@ typedef struct QEMUAudioTimeStamp {
     uint64_t old_ts;
 } QEMUAudioTimeStamp;
 
+extern QemuOptsList qemu_audiodev_opts;
+
 void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
 void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
 
-void AUD_help (void);
 void AUD_register_card (const char *name, QEMUSoundCard *card);
 void AUD_remove_card (QEMUSoundCard *card);
 CaptureVoiceOut *AUD_add_capture (
@@ -154,4 +167,8 @@ static inline void *advance (void *p, int incr)
 int wav_start_capture (CaptureState *s, const char *path, int freq,
                        int bits, int nchannels);
 
+void audio_set_options(void);
+void audio_handle_legacy_opts(void);
+void audio_legacy_help(void);
+
 #endif  /* audio.h */
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 566df5e..c4539e7 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -143,8 +143,7 @@ struct SWVoiceIn {
 struct audio_driver {
     const char *name;
     const char *descr;
-    struct audio_option *options;
-    void *(*init) (void);
+    void *(*init) (Audiodev *);
     void (*fini) (void *);
     struct audio_pcm_ops *pcm_ops;
     int can_be_default;
@@ -190,6 +189,7 @@ struct SWVoiceCap {
 
 struct AudioState {
     struct audio_driver *drv;
+    Audiodev *dev;
     void *drv_opaque;
 
     QEMUTimer *ts;
@@ -200,6 +200,7 @@ struct AudioState {
     int nb_hw_voices_out;
     int nb_hw_voices_in;
     int vm_running;
+    int64_t period_ticks;
 };
 
 extern struct audio_driver no_audio_driver;
@@ -213,6 +214,8 @@ extern struct audio_driver pa_audio_driver;
 extern struct audio_driver spice_audio_driver;
 extern const struct mixeng_volume nominal_volume;
 
+extern struct audio_driver *drvtab[];
+
 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
 void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
 
diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c
new file mode 100644
index 0000000..88f577d
--- /dev/null
+++ b/audio/audio_legacy.c
@@ -0,0 +1,328 @@
+#include "audio.h"
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+
+#define AUDIO_CAP "audio-legacy"
+#include "audio_int.h"
+
+typedef enum EnvTransform {
+    ENV_TRANSFORM_NONE,
+    ENV_TRANSFORM_BOOL,
+    ENV_TRANSFORM_FMT,
+    ENV_TRANSFORM_FRAMES_TO_USECS_IN,
+    ENV_TRANSFORM_FRAMES_TO_USECS_OUT,
+    ENV_TRANSFORM_SAMPLES_TO_USECS_IN,
+    ENV_TRANSFORM_SAMPLES_TO_USECS_OUT,
+    ENV_TRANSFORM_BYTES_TO_USECS_IN,
+    ENV_TRANSFORM_BYTES_TO_USECS_OUT,
+    ENV_TRANSFORM_MILLIS_TO_USECS,
+    ENV_TRANSFORM_HZ_TO_USECS,
+} EnvTransform;
+
+typedef struct SimpleEnvMap {
+    const char *name;
+    const char *option;
+    EnvTransform transform;
+} SimpleEnvMap;
+
+SimpleEnvMap global_map[] = {
+    /* DAC/out settings */
+    { "QEMU_AUDIO_DAC_FIXED_SETTINGS", "out.fixed-settings",
+      ENV_TRANSFORM_BOOL },
+    { "QEMU_AUDIO_DAC_FIXED_FREQ", "out.frequency" },
+    { "QEMU_AUDIO_DAC_FIXED_FMT", "out.format", ENV_TRANSFORM_FMT },
+    { "QEMU_AUDIO_DAC_FIXED_CHANNELS", "out.channels" },
+    { "QEMU_AUDIO_DAC_VOICES", "out.voices" },
+
+    /* ADC/in settings */
+    { "QEMU_AUDIO_ADC_FIXED_SETTINGS", "in.fixed-settings",
+      ENV_TRANSFORM_BOOL },
+    { "QEMU_AUDIO_ADC_FIXED_FREQ", "in.frequency" },
+    { "QEMU_AUDIO_ADC_FIXED_FMT", "in.format", ENV_TRANSFORM_FMT },
+    { "QEMU_AUDIO_ADC_FIXED_CHANNELS", "in.channels" },
+    { "QEMU_AUDIO_ADC_VOICES", "in.voices" },
+
+    /* general */
+    { "QEMU_AUDIO_TIMER_PERIOD", "timer-period", ENV_TRANSFORM_HZ_TO_USECS },
+    { /* End of list */ }
+};
+
+SimpleEnvMap alsa_map[] = {
+    { "QEMU_AUDIO_DAC_TRY_POLL", "alsa-out.try-poll", ENV_TRANSFORM_BOOL },
+    { "QEMU_AUDIO_ADC_TRY_POLL", "alsa-in.try-poll", ENV_TRANSFORM_BOOL },
+
+    { "QEMU_ALSA_THRESHOLD", "threshold", ENV_TRANSFORM_MILLIS_TO_USECS },
+    { "QEMU_ALSA_DAC_DEV", "alsa-out.dev" },
+    { "QEMU_ALSA_ADC_DEV", "alsa-in.dev" },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap coreaudio_map[] = {
+    { "QEMU_COREAUDIO_BUFFER_SIZE", "buffer-len",
+      ENV_TRANSFORM_FRAMES_TO_USECS_OUT },
+    { "QEMU_COREAUDIO_BUFFER_COUNT", "buffer-count" },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap dsound_map[] = {
+    { "QEMU_DSOUND_LATENCY_MILLIS", "latency", ENV_TRANSFORM_MILLIS_TO_USECS },
+    { "QEMU_DSOUND_BUFSIZE_OUT", "out.buffer-len",
+      ENV_TRANSFORM_BYTES_TO_USECS_OUT },
+    { "QEMU_DSOUND_BUFSIZE_IN", "in.buffer-len",
+      ENV_TRANSFORM_BYTES_TO_USECS_IN },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap oss_map[] = {
+    { "QEMU_AUDIO_DAC_TRY_POLL", "oss-out.try-poll", ENV_TRANSFORM_BOOL },
+    { "QEMU_AUDIO_ADC_TRY_POLL", "oss-in.try-poll", ENV_TRANSFORM_BOOL },
+
+    { "QEMU_OSS_FRAGSIZE", "buffer-len", ENV_TRANSFORM_BYTES_TO_USECS_OUT },
+    { "QEMU_OSS_NFRAGS", "buffer-count" },
+    { "QEMU_OSS_MMAP", "try-mmap", ENV_TRANSFORM_BOOL },
+    { "QEMU_OSS_DAC_DEV", "oss-out.dev" },
+    { "QEMU_OSS_ADC_DEV", "oss-in.dev" },
+    { "QEMU_OSS_EXCLUSIVE", "exclusive", ENV_TRANSFORM_BOOL },
+    { "QEMU_OSS_POLICY", "dsp-policy" },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap pa_map[] = {
+    { "QEMU_PA_SAMPLES", "buffer", ENV_TRANSFORM_SAMPLES_TO_USECS_OUT },
+    { "QEMU_PA_SERVER", "server" },
+    { "QEMU_PA_SINK", "sink" },
+    { "QEMU_PA_SOURCE", "source" },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap sdl_map[] = {
+    { "QEMU_SDL_SAMPLES", "buffer-len", ENV_TRANSFORM_SAMPLES_TO_USECS_OUT },
+    { /* End of list */ }
+};
+
+SimpleEnvMap wav_map[] = {
+    { "QEMU_WAV_FREQUENCY", "out.frequency" },
+    { "QEMU_WAV_FORMAT", "out.format", ENV_TRANSFORM_FMT },
+    { "QEMU_WAV_DAC_FIXED_CHANNELS", "out.channels" },
+    { "QEMU_WAV_PATH", "path" },
+    { /* End of list */ }
+};
+
+static unsigned long long toull(const char *str)
+{
+    unsigned long long ret;
+    if (parse_uint_full(str, &ret, 10)) {
+        dolog("Invalid boolean value `%s'\n", str);
+        exit(1);
+    }
+    return ret;
+}
+
+/* non reentrant typesafe or anything, but enough in this small c file */
+static const char *tostr(unsigned long long val)
+{
+    #define LEN ((CHAR_BIT * sizeof(int) - 1) / 3 + 2)
+    static char ret[LEN];
+    snprintf(ret, LEN, "%llu", val);
+    return ret;
+}
+
+static uint64_t frames_to_usecs(QemuOpts *opts, uint64_t frames, bool in)
+{
+    const char *opt = in ? "in.frequency" : "out.frequency";
+    uint64_t freq = qemu_opt_get_number(opts, opt, 44100);
+    return (frames * 1000000 + freq/2) / freq;
+}
+
+static uint64_t samples_to_usecs(QemuOpts *opts, uint64_t samples, bool in)
+{
+    const char *opt = in ? "in.channels" : "out.channels";
+    uint64_t channels = qemu_opt_get_number(opts, opt, 2);
+    return frames_to_usecs(opts, samples/channels, in);
+}
+
+static uint64_t bytes_to_usecs(QemuOpts *opts, uint64_t bytes, bool in)
+{
+    const char *opt = in ? "in.format" : "out.format";
+    const char *val = qemu_opt_get(opts, opt);
+    uint64_t bytes_per_sample = (val ? toull(val) : 16) / 8;
+    return samples_to_usecs(opts, bytes * bytes_per_sample, in);
+}
+
+static const char *transform_val(QemuOpts *opts, const char *val,
+                                 EnvTransform transform)
+{
+    switch (transform) {
+    case ENV_TRANSFORM_NONE:
+        return val;
+
+    case ENV_TRANSFORM_BOOL:
+        return toull(val) ? "on" : "off";
+
+    case ENV_TRANSFORM_FMT:
+        if (strcasecmp(val, "u8") == 0) {
+            return "u8";
+        } else if (strcasecmp(val, "u16") == 0) {
+            return "u16";
+        } else if (strcasecmp(val, "u32") == 0) {
+            return "u32";
+        } else if (strcasecmp(val, "s8") == 0) {
+            return "s8";
+        } else if (strcasecmp(val, "s16") == 0) {
+            return "s16";
+        } else if (strcasecmp(val, "s32") == 0) {
+            return "s32";
+        } else {
+            dolog("Invalid audio format `%s'\n", val);
+            exit(1);
+        }
+
+    case ENV_TRANSFORM_FRAMES_TO_USECS_IN:
+        return tostr(frames_to_usecs(opts, toull(val), true));
+    case ENV_TRANSFORM_FRAMES_TO_USECS_OUT:
+        return tostr(frames_to_usecs(opts, toull(val), false));
+
+    case ENV_TRANSFORM_SAMPLES_TO_USECS_IN:
+        return tostr(samples_to_usecs(opts, toull(val), true));
+    case ENV_TRANSFORM_SAMPLES_TO_USECS_OUT:
+        return tostr(samples_to_usecs(opts, toull(val), false));
+
+    case ENV_TRANSFORM_BYTES_TO_USECS_IN:
+        return tostr(bytes_to_usecs(opts, toull(val), true));
+    case ENV_TRANSFORM_BYTES_TO_USECS_OUT:
+        return tostr(bytes_to_usecs(opts, toull(val), false));
+
+    case ENV_TRANSFORM_MILLIS_TO_USECS:
+        return tostr(toull(val) * 1000);
+
+    case ENV_TRANSFORM_HZ_TO_USECS:
+        return tostr(1000000 / toull(val));
+    }
+
+    abort(); /* it's unreachable, gcc */
+}
+
+static void handle_env_opts(QemuOpts *opts, SimpleEnvMap *map)
+{
+    while (map->name) {
+        const char *val = getenv(map->name);
+
+        if (val) {
+            qemu_opt_set(opts, map->option,
+                         transform_val(opts, val, map->transform),
+                         &error_abort);
+        }
+
+        ++map;
+    }
+}
+
+static void handle_alsa_side(QemuOpts *opts, int period, int buffer,
+                             const char *usec_env, const char *period_env,
+                             const char *buffer_env, const char *usec_opt,
+                             const char *count_opt, bool in)
+{
+    char *usec_s, *period_s, *buffer_s;
+    bool usec = false;
+
+    usec_s = getenv(usec_env);
+    if (usec_s) {
+        usec = toull(usec_s);
+    }
+
+    period_s = getenv(period_env);
+    if (period_s) {
+        period = toull(period_s);
+    }
+    if (!usec) {
+        period = frames_to_usecs(opts, period, in);
+    }
+    if (period_s) {
+        qemu_opt_set(opts, usec_opt, tostr(period), &error_abort);
+    }
+
+    buffer_s = getenv(buffer_env);
+    if (buffer_s) {
+        buffer = toull(buffer_s);
+        if (!usec) {
+            buffer = frames_to_usecs(opts, buffer, in);
+        }
+        printf("buffer %d period %d\n", buffer, period);
+        qemu_opt_set(opts, count_opt, tostr((buffer+period/2)/period),
+                     &error_abort);
+    }
+}
+
+static void handle_alsa(QemuOpts *opts)
+{
+    handle_alsa_side(opts, 1024, 4096,
+                     "QEMU_ALSA_DAC_SIZE_IN_USEC", "QEMU_ALSA_DAC_PERIOD_SIZE",
+                     "QEMU_ALSA_DAC_BUFFER_SIZE",
+                     "out.buffer-len", "out.buffer-count", false);
+    handle_alsa_side(opts, 0, 0,
+                     "QEMU_ALSA_ADC_SIZE_IN_USEC", "QEMU_ALSA_ADC_PERIOD_SIZE",
+                     "QEMU_ALSA_ADC_BUFFER_SIZE",
+                     "in.buffer-len", "in.buffer-count", true);
+}
+
+static void legacy_opt(const char *drv)
+{
+    QemuOpts *opts;
+    opts = qemu_opts_create(qemu_find_opts("audiodev"), drv, true,
+                            &error_abort);
+    qemu_opt_set(opts, "driver", drv, &error_abort);
+
+    handle_env_opts(opts, global_map);
+
+    if (strcmp(drv, "alsa") == 0) {
+        handle_env_opts(opts, alsa_map);
+        handle_alsa(opts);
+    } else if (strcmp(drv, "oss") == 0) {
+        handle_env_opts(opts, oss_map);
+    } else if (strcmp(drv, "pa") == 0) {
+        handle_env_opts(opts, pa_map);
+    } else if (strcmp(drv, "sdl") == 0) {
+        handle_env_opts(opts, sdl_map);
+    } else if (strcmp(drv, "wav") == 0) {
+        handle_env_opts(opts, wav_map);
+    }
+}
+
+void audio_handle_legacy_opts(void)
+{
+    const char *drv = getenv("QEMU_AUDIO_DRV");
+
+    if (drv) {
+        legacy_opt(drv);
+    } else {
+        struct audio_driver **drv;
+        for (drv = drvtab; *drv; ++drv) {
+            if ((*drv)->can_be_default) {
+                legacy_opt((*drv)->name);
+            }
+        }
+    }
+}
+
+static int legacy_help_each(void *opaque, QemuOpts *opts, Error **errp)
+{
+    printf("-audiodev ");
+    qemu_opts_print(opts, ",");
+    printf("\n");
+    return 0;
+}
+
+void audio_legacy_help(void)
+{
+    printf("Environment variable based configuration deprecated.\n");
+    printf("Please use the new -audiodev option.\n");
+
+    audio_handle_legacy_opts();
+    printf("\nEquivalent -audiodev to your current environment variables:\n");
+    qemu_opts_foreach(qemu_find_opts("audiodev"), legacy_help_each, NULL, NULL);
+}
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 99b27b2..096b2b3 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -302,8 +302,10 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
 static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
 {
     HW *hw;
+    AudioState *s = &glob_audio_state;
+    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
-    if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
+    if (pdo->fixed_settings) {
         hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
         if (hw) {
             return hw;
@@ -331,9 +333,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
     SW *sw;
     HW *hw;
     struct audsettings hw_as;
+    AudioState *s = &glob_audio_state;
+    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
-    if (glue (conf.fixed_, TYPE).enabled) {
-        hw_as = glue (conf.fixed_, TYPE).settings;
+    if (pdo->fixed_settings) {
+        hw_as = audiodev_to_audsettings(pdo);
     }
     else {
         hw_as = *as;
@@ -398,6 +402,7 @@ SW *glue (AUD_open_, TYPE) (
     )
 {
     AudioState *s = &glob_audio_state;
+    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
     if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
         dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -422,7 +427,7 @@ SW *glue (AUD_open_, TYPE) (
         return sw;
     }
 
-    if (!glue (conf.fixed_, TYPE).enabled && sw) {
+    if (!pdo->fixed_settings && sw) {
         glue (AUD_close_, TYPE) (card, sw);
         sw = NULL;
     }
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index 6dfd63e..dfa5e79 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -34,11 +34,6 @@
 
 static int isAtexit;
 
-typedef struct {
-    int buffer_frames;
-    int nbuffers;
-} CoreaudioConf;
-
 typedef struct coreaudioVoiceOut {
     HWVoiceOut hw;
     pthread_mutex_t mutex;
@@ -292,7 +287,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
     int err;
     const char *typ = "playback";
     AudioValueRange frameRange;
-    CoreaudioConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
+    AudiodevPerDirectionOptions *pdo = dev->out;
+    int frames;
 
     /* create mutex */
     err = pthread_mutex_init(&core->mutex, NULL);
@@ -334,16 +331,17 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
         return -1;
     }
 
-    if (frameRange.mMinimum > conf->buffer_frames) {
+    frames = audio_buffer_frames(pdo, as, 11610);
+    if (frameRange.mMinimum > frames) {
         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
     }
-    else if (frameRange.mMaximum < conf->buffer_frames) {
+    else if (frameRange.mMaximum < frames) {
         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
     }
     else {
-        core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
+        core->audioDevicePropertyBufferFrameSize = frames;
     }
 
     /* set Buffer Frame Size */
@@ -377,7 +375,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
                            "Could not get device buffer frame size\n");
         return -1;
     }
-    hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
+    hw->samples = (pdo->has_buffer_count ? pdo->buffer_count : 4) *
+        core->audioDevicePropertyBufferFrameSize;
 
     /* get StreamFormat */
     propertySize = sizeof(core->outputStreamBasicDescription);
@@ -497,41 +496,16 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static CoreaudioConf glob_conf = {
-    .buffer_frames = 512,
-    .nbuffers = 4,
-};
-
-static void *coreaudio_audio_init (void)
+static void *coreaudio_audio_init(Audiodev *dev)
 {
-    CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
-    *conf = glob_conf;
-
     atexit(coreaudio_atexit);
-    return conf;
+    return dev;
 }
 
 static void coreaudio_audio_fini (void *opaque)
 {
-    g_free(opaque);
 }
 
-static struct audio_option coreaudio_options[] = {
-    {
-        .name  = "BUFFER_SIZE",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.buffer_frames,
-        .descr = "Size of the buffer in frames"
-    },
-    {
-        .name  = "BUFFER_COUNT",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.nbuffers,
-        .descr = "Number of buffers"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops coreaudio_pcm_ops = {
     .init_out = coreaudio_init_out,
     .fini_out = coreaudio_fini_out,
@@ -543,7 +517,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
 struct audio_driver coreaudio_audio_driver = {
     .name           = "coreaudio",
     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
-    .options        = coreaudio_options,
     .init           = coreaudio_audio_init,
     .fini           = coreaudio_audio_fini,
     .pcm_ops        = &coreaudio_pcm_ops,
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index b439f33..96181ef 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -167,17 +167,18 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
     dsound *s = drv_opaque;
     WAVEFORMATEX wfx;
     struct audsettings obt_as;
-    DSoundConf *conf = &s->conf;
 #ifdef DSBTYPE_IN
     const char *typ = "ADC";
     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
     DSCBUFFERDESC bd;
     DSCBCAPS bc;
+    AudiodevPerDirectionOptions *pdo = s->dev->in;
 #else
     const char *typ = "DAC";
     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
     DSBUFFERDESC bd;
     DSBCAPS bc;
+    AudiodevPerDirectionOptions *pdo = s->dev->out;
 #endif
 
     if (!s->FIELD2) {
@@ -193,8 +194,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
     memset (&bd, 0, sizeof (bd));
     bd.dwSize = sizeof (bd);
     bd.lpwfxFormat = &wfx;
+    bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
 #ifdef DSBTYPE_IN
-    bd.dwBufferBytes = conf->bufsize_in;
     hr = IDirectSoundCapture_CreateCaptureBuffer (
         s->dsound_capture,
         &bd,
@@ -203,7 +204,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
         );
 #else
     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
-    bd.dwBufferBytes = conf->bufsize_out;
     hr = IDirectSound_CreateSoundBuffer (
         s->dsound,
         &bd,
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index e9472c1..7e7b3f2 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -42,16 +42,10 @@
 /* #define DEBUG_DSOUND */
 
 typedef struct {
-    int bufsize_in;
-    int bufsize_out;
-    int latency_millis;
-} DSoundConf;
-
-typedef struct {
     LPDIRECTSOUND dsound;
     LPDIRECTSOUNDCAPTURE dsound_capture;
     struct audsettings settings;
-    DSoundConf conf;
+    Audiodev *dev;
 } dsound;
 
 typedef struct {
@@ -247,9 +241,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
     dsound_log_hresult (hr);
 }
 
-static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
+static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
 {
-    return (millis * info->bytes_per_second) / 1000;
+    return muldiv64(usecs, info->bytes_per_second, 1000000);
 }
 
 #ifdef DEBUG_DSOUND
@@ -477,7 +471,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
     LPVOID p1, p2;
     int bufsize;
     dsound *s = ds->s;
-    DSoundConf *conf = &s->conf;
+    AudiodevDsoundOptions *dso = s->dev->dsound;
 
     if (!dsb) {
         dolog ("Attempt to run empty with playback buffer\n");
@@ -500,14 +494,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
     len = live << hwshift;
 
     if (ds->first_time) {
-        if (conf->latency_millis) {
+        if (dso->latency) {
             DWORD cur_blat;
 
             cur_blat = audio_ring_dist (wpos, ppos, bufsize);
             ds->first_time = 0;
             old_pos = wpos;
             old_pos +=
-                millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
+                usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
             old_pos %= bufsize;
             old_pos &= ~hw->info.align;
         }
@@ -746,12 +740,6 @@ static int dsound_run_in (HWVoiceIn *hw)
     return decr;
 }
 
-static DSoundConf glob_conf = {
-    .bufsize_in         = 16384,
-    .bufsize_out        = 16384,
-    .latency_millis     = 10
-};
-
 static void dsound_audio_fini (void *opaque)
 {
     HRESULT hr;
@@ -782,13 +770,22 @@ static void dsound_audio_fini (void *opaque)
     g_free(s);
 }
 
-static void *dsound_audio_init (void)
+static void *dsound_audio_init(Audiodev *dev)
 {
     int err;
     HRESULT hr;
     dsound *s = g_malloc0(sizeof(dsound));
+    AudiodevDsoundOptions *dso;
+
+    assert(dev->kind == AUDIODEV_DRIVER_DSOUND);
+    s->dev = dev;
+    dso = dev->dsound;
+
+    if (!dso->has_latency) {
+        dso->has_latency = true;
+        dso->latency = 10000; /* 10 ms */
+    }
 
-    s->conf = glob_conf;
     hr = CoInitialize (NULL);
     if (FAILED (hr)) {
         dsound_logerr (hr, "Could not initialize COM\n");
@@ -853,28 +850,6 @@ static void *dsound_audio_init (void)
     return s;
 }
 
-static struct audio_option dsound_options[] = {
-    {
-        .name  = "LATENCY_MILLIS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.latency_millis,
-        .descr = "(undocumented)"
-    },
-    {
-        .name  = "BUFSIZE_OUT",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.bufsize_out,
-        .descr = "(undocumented)"
-    },
-    {
-        .name  = "BUFSIZE_IN",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.bufsize_in,
-        .descr = "(undocumented)"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops dsound_pcm_ops = {
     .init_out = dsound_init_out,
     .fini_out = dsound_fini_out,
@@ -892,7 +867,6 @@ static struct audio_pcm_ops dsound_pcm_ops = {
 struct audio_driver dsound_audio_driver = {
     .name           = "dsound",
     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
-    .options        = dsound_options,
     .init           = dsound_audio_init,
     .fini           = dsound_audio_fini,
     .pcm_ops        = &dsound_pcm_ops,
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 50db1f3..4c94a26 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -134,7 +134,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return 0;
 }
 
-static void *no_audio_init (void)
+static void *no_audio_init (Audiodev *dev)
 {
     return &no_audio_init;
 }
@@ -161,7 +161,6 @@ static struct audio_pcm_ops no_pcm_ops = {
 struct audio_driver no_audio_driver = {
     .name           = "none",
     .descr          = "Timer based audio emulation",
-    .options        = NULL,
     .init           = no_audio_init,
     .fini           = no_audio_fini,
     .pcm_ops        = &no_pcm_ops,
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 02a3a95..a5e7f7c 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -29,6 +29,7 @@
 #include "qemu-common.h"
 #include "qemu/main-loop.h"
 #include "qemu/host-utils.h"
+#include "qapi-visit.h"
 #include "audio.h"
 #include "trace.h"
 
@@ -39,16 +40,6 @@
 #define USE_DSP_POLICY
 #endif
 
-typedef struct OSSConf {
-    int try_mmap;
-    int nfrags;
-    int fragsize;
-    const char *devpath_out;
-    const char *devpath_in;
-    int exclusive;
-    int policy;
-} OSSConf;
-
 typedef struct OSSVoiceOut {
     HWVoiceOut hw;
     void *pcm_buf;
@@ -58,7 +49,7 @@ typedef struct OSSVoiceOut {
     int fragsize;
     int mmapped;
     int pending;
-    OSSConf *conf;
+    Audiodev *dev;
 } OSSVoiceOut;
 
 typedef struct OSSVoiceIn {
@@ -67,12 +58,12 @@ typedef struct OSSVoiceIn {
     int fd;
     int nfrags;
     int fragsize;
-    OSSConf *conf;
+    Audiodev *dev;
 } OSSVoiceIn;
 
 struct oss_params {
     int freq;
-    AudioFormat fmt;
+    int fmt;
     int nchannels;
     int nfrags;
     int fragsize;
@@ -264,19 +255,26 @@ static int oss_get_version (int fd, int *version, const char *typ)
 }
 #endif
 
-static int oss_open (int in, struct oss_params *req,
-                     struct oss_params *obt, int *pfd, OSSConf* conf)
+static int oss_open(int in, struct oss_params *req, audsettings *as,
+                    struct oss_params *obt, int *pfd, Audiodev *dev)
 {
+    AudiodevOssOptions *oopts = dev->oss;
+    AudiodevOssPerDirectionOptions *opdo = in ? oopts->oss_in : oopts->oss_out;
+    AudiodevPerDirectionOptions *pdo = in ? dev->in : dev->out;
     int fd;
-    int oflags = conf->exclusive ? O_EXCL : 0;
+    int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
     audio_buf_info abinfo;
     int fmt, freq, nchannels;
     int setfragment = 1;
-    const char *dspname = in ? conf->devpath_in : conf->devpath_out;
+    const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
     const char *typ = in ? "ADC" : "DAC";
+#ifdef USE_DSP_POLICY
+    int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
+#endif
 
     /* Kludge needed to have working mmap on Linux */
-    oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
+    oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
+        O_RDWR : (in ? O_RDONLY : O_WRONLY);
 
     fd = open (dspname, oflags | O_NONBLOCK);
     if (-1 == fd) {
@@ -287,6 +285,8 @@ static int oss_open (int in, struct oss_params *req,
     freq = req->freq;
     nchannels = req->nchannels;
     fmt = req->fmt;
+    req->nfrags = pdo->has_buffer_count ? pdo->buffer_count : 4;
+    req->fragsize = audio_buffer_bytes(pdo, as, 23220);
 
     if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
         oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
@@ -310,18 +310,18 @@ static int oss_open (int in, struct oss_params *req,
     }
 
 #ifdef USE_DSP_POLICY
-    if (conf->policy >= 0) {
+    if (policy >= 0) {
         int version;
 
         if (!oss_get_version (fd, &version, typ)) {
             trace_oss_version(version);
 
             if (version >= 0x040000) {
-                int policy = conf->policy;
-                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
+                int policy2 = policy;
+                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy2)) {
                     oss_logerr2 (errno, typ,
                                  "Failed to set timing policy to %d\n",
-                                 conf->policy);
+                                 policy);
                     goto err;
                 }
                 setfragment = 0;
@@ -504,17 +504,16 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
     int fd;
     AudioFormat effective_fmt;
     struct audsettings obt_as;
-    OSSConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
+    AudiodevOssOptions *oopts = dev->oss;
 
     oss->fd = -1;
 
     req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.fragsize = conf->fragsize;
-    req.nfrags = conf->nfrags;
 
-    if (oss_open (0, &req, &obt, &fd, conf)) {
+    if (oss_open(0, &req, as, &obt, &fd, dev)) {
         return -1;
     }
 
@@ -541,7 +540,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 
     oss->mmapped = 0;
-    if (conf->try_mmap) {
+    if (oopts->has_try_mmap && oopts->try_mmap) {
         oss->pcm_buf = mmap (
             NULL,
             hw->samples << hw->info.shift,
@@ -601,7 +600,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     oss->fd = fd;
-    oss->conf = conf;
+    oss->dev = dev;
     return 0;
 }
 
@@ -609,16 +608,12 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
     int trig;
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+    AudiodevOssPerDirectionOptions *opdo = oss->dev->oss->oss_out;
 
     switch (cmd) {
     case VOICE_ENABLE:
         {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
+            bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
 
             ldebug ("enabling voice\n");
             if (poll_mode) {
@@ -673,16 +668,14 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     int fd;
     AudioFormat effective_fmt;
     struct audsettings obt_as;
-    OSSConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
 
     oss->fd = -1;
 
     req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.fragsize = conf->fragsize;
-    req.nfrags = conf->nfrags;
-    if (oss_open (1, &req, &obt, &fd, conf)) {
+    if (oss_open(1, &req, as, &obt, &fd, dev)) {
         return -1;
     }
 
@@ -716,7 +709,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     }
 
     oss->fd = fd;
-    oss->conf = conf;
+    oss->dev = dev;
     return 0;
 }
 
@@ -807,16 +800,12 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
 static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
 {
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+    AudiodevOssPerDirectionOptions *opdo = oss->dev->oss->oss_out;
 
     switch (cmd) {
     case VOICE_ENABLE:
         {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
+            bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
 
             if (poll_mode) {
                 oss_poll_in (hw);
@@ -836,81 +825,25 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return 0;
 }
 
-static OSSConf glob_conf = {
-    .try_mmap = 0,
-    .nfrags = 4,
-    .fragsize = 4096,
-    .devpath_out = "/dev/dsp",
-    .devpath_in = "/dev/dsp",
-    .exclusive = 0,
-    .policy = 5
-};
-
-static void *oss_audio_init (void)
+static void *oss_audio_init(Audiodev *dev)
 {
-    OSSConf *conf = g_malloc(sizeof(OSSConf));
-    *conf = glob_conf;
+    AudiodevOssOptions *oopts;
+    assert(dev->kind == AUDIODEV_DRIVER_OSS);
 
-    if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
-        access(conf->devpath_out, R_OK | W_OK) < 0) {
+    oopts = dev->oss;
+    if (access(oopts->oss_in->has_dev ? oopts->oss_in->dev : "/dev/dsp",
+               R_OK | W_OK) < 0 ||
+        access(oopts->oss_out->has_dev ? oopts->oss_out->dev : "/dev/dsp",
+               R_OK | W_OK) < 0) {
         return NULL;
     }
-    return conf;
+    return dev;
 }
 
 static void oss_audio_fini (void *opaque)
 {
-    g_free(opaque);
 }
 
-static struct audio_option oss_options[] = {
-    {
-        .name  = "FRAGSIZE",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.fragsize,
-        .descr = "Fragment size in bytes"
-    },
-    {
-        .name  = "NFRAGS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.nfrags,
-        .descr = "Number of fragments"
-    },
-    {
-        .name  = "MMAP",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &glob_conf.try_mmap,
-        .descr = "Try using memory mapped access"
-    },
-    {
-        .name  = "DAC_DEV",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.devpath_out,
-        .descr = "Path to DAC device"
-    },
-    {
-        .name  = "ADC_DEV",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.devpath_in,
-        .descr = "Path to ADC device"
-    },
-    {
-        .name  = "EXCLUSIVE",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &glob_conf.exclusive,
-        .descr = "Open device in exclusive mode (vmix wont work)"
-    },
-#ifdef USE_DSP_POLICY
-    {
-        .name  = "POLICY",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.policy,
-        .descr = "Set the timing policy of the device, -1 to use fragment mode",
-    },
-#endif
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops oss_pcm_ops = {
     .init_out = oss_init_out,
     .fini_out = oss_fini_out,
@@ -928,7 +861,6 @@ static struct audio_pcm_ops oss_pcm_ops = {
 struct audio_driver oss_audio_driver = {
     .name           = "oss",
     .descr          = "OSS http://www.opensound.com",
-    .options        = oss_options,
     .init           = oss_audio_init,
     .fini           = oss_audio_fini,
     .pcm_ops        = &oss_pcm_ops,
diff --git a/audio/paaudio.c b/audio/paaudio.c
index cfdbdc6..a9a6a5c 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -1,6 +1,7 @@
 /* public domain */
 #include "qemu-common.h"
 #include "audio.h"
+#include "qapi-visit.h"
 
 #include <pulse/pulseaudio.h>
 
@@ -9,14 +10,7 @@
 #include "audio_pt_int.h"
 
 typedef struct {
-    int samples;
-    char *server;
-    char *sink;
-    char *source;
-} PAConf;
-
-typedef struct {
-    PAConf conf;
+    Audiodev *dev;
     pa_threaded_mainloop *mainloop;
     pa_context *context;
 } paaudio;
@@ -31,6 +25,7 @@ typedef struct {
     void *pcm_buf;
     struct audio_pt pt;
     paaudio *g;
+    int samples;
 } PAVoiceOut;
 
 typedef struct {
@@ -45,6 +40,7 @@ typedef struct {
     const void *read_data;
     size_t read_index, read_length;
     paaudio *g;
+    int samples;
 } PAVoiceIn;
 
 static void qpa_audio_fini(void *opaque);
@@ -226,7 +222,7 @@ static void *qpa_thread_out (void *arg)
             }
         }
 
-        decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
+        decr = to_mix = audio_MIN (pa->live, pa->samples >> 2);
         rpos = pa->rpos;
 
         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -318,7 +314,7 @@ static void *qpa_thread_in (void *arg)
             }
         }
 
-        incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
+        incr = to_grab = audio_MIN (pa->dead, pa->samples >> 2);
         wpos = pa->wpos;
 
         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -545,6 +541,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
     struct audsettings obt_as = *as;
     PAVoiceOut *pa = (PAVoiceOut *) hw;
     paaudio *g = pa->g = drv_opaque;
+    AudiodevPaOptions *popts = g->dev->pa;
 
     ss.format = audfmt_to_pa (as->fmt, as->endianness);
     ss.channels = as->nchannels;
@@ -565,7 +562,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
         g,
         "qemu",
         PA_STREAM_PLAYBACK,
-        g->conf.sink,
+        popts->has_sink ? popts->sink : NULL,
         &ss,
         NULL,                   /* channel map */
         &ba,                    /* buffering attributes */
@@ -577,7 +574,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = g->conf.samples;
+    hw->samples = pa->samples = audio_buffer_samples(g->dev->out, &obt_as,
+                                                     46440);
     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
     pa->rpos = hw->rpos;
     if (!pa->pcm_buf) {
@@ -611,6 +609,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     struct audsettings obt_as = *as;
     PAVoiceIn *pa = (PAVoiceIn *) hw;
     paaudio *g = pa->g = drv_opaque;
+    AudiodevPaOptions *popts = g->dev->pa;
 
     ss.format = audfmt_to_pa (as->fmt, as->endianness);
     ss.channels = as->nchannels;
@@ -622,7 +621,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
         g,
         "qemu",
         PA_STREAM_RECORD,
-        g->conf.source,
+        popts->has_source ? popts->source : NULL,
         &ss,
         NULL,                   /* channel map */
         NULL,                   /* buffering attributes */
@@ -634,7 +633,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = g->conf.samples;
+    hw->samples = pa->samples = audio_buffer_samples(g->dev->in, &obt_as,
+                                                     46440);
     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
     pa->wpos = hw->wpos;
     if (!pa->pcm_buf) {
@@ -808,14 +808,19 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 }
 
 /* common */
-static PAConf glob_conf = {
-    .samples = 4096,
-};
-
-static void *qpa_audio_init (void)
+static void *qpa_audio_init(Audiodev *dev)
 {
-    paaudio *g = g_malloc(sizeof(paaudio));
-    g->conf = glob_conf;
+    paaudio *g;
+    AudiodevPaOptions *popts;
+    const char *server;
+
+    assert(dev->kind == AUDIODEV_DRIVER_PA);
+
+    g = g_malloc(sizeof(paaudio));
+    popts = dev->pa;
+    server = popts->has_server ? popts->server : NULL;
+
+    g->dev = dev;
     g->mainloop = NULL;
     g->context = NULL;
 
@@ -825,14 +830,14 @@ static void *qpa_audio_init (void)
     }
 
     g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
-                                 g->conf.server);
+                                 server);
     if (!g->context) {
         goto fail;
     }
 
     pa_context_set_state_callback (g->context, context_state_cb, g);
 
-    if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
+    if (pa_context_connect (g->context, server, 0, NULL) < 0) {
         qpa_logerr (pa_context_errno (g->context),
                     "pa_context_connect() failed\n");
         goto fail;
@@ -895,34 +900,6 @@ static void qpa_audio_fini (void *opaque)
     g_free(g);
 }
 
-struct audio_option qpa_options[] = {
-    {
-        .name  = "SAMPLES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.samples,
-        .descr = "buffer size in samples"
-    },
-    {
-        .name  = "SERVER",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.server,
-        .descr = "server address"
-    },
-    {
-        .name  = "SINK",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.sink,
-        .descr = "sink device name"
-    },
-    {
-        .name  = "SOURCE",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.source,
-        .descr = "source device name"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops qpa_pcm_ops = {
     .init_out = qpa_init_out,
     .fini_out = qpa_fini_out,
@@ -940,7 +917,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
 struct audio_driver pa_audio_driver = {
     .name           = "pa",
     .descr          = "http://www.pulseaudio.org/",
-    .options        = qpa_options,
     .init           = qpa_audio_init,
     .fini           = qpa_audio_fini,
     .pcm_ops        = &qpa_pcm_ops,
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index db0f95a..796238a 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -44,18 +44,13 @@ typedef struct SDLVoiceOut {
     int decr;
 } SDLVoiceOut;
 
-static struct {
-    int nb_samples;
-} conf = {
-    .nb_samples = 1024
-};
-
 static struct SDLAudioState {
     int exit;
     SDL_mutex *mutex;
     SDL_sem *sem;
     int initialized;
     bool driver_created;
+    Audiodev *dev;
 } glob_sdl;
 typedef struct SDLAudioState SDLAudioState;
 
@@ -347,7 +342,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
     req.freq = as->freq;
     req.format = aud_to_sdlfmt (as->fmt);
     req.channels = as->nchannels;
-    req.samples = conf.nb_samples;
+    req.samples = audio_buffer_samples(s->dev->out, as, 11610);
     req.callback = sdl_callback;
     req.userdata = sdl;
 
@@ -391,7 +386,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static void *sdl_audio_init (void)
+static void *sdl_audio_init(Audiodev *dev)
 {
     SDLAudioState *s = &glob_sdl;
     if (s->driver_created) {
@@ -420,6 +415,7 @@ static void *sdl_audio_init (void)
     }
 
     s->driver_created = true;
+    s->dev = dev;
     return s;
 }
 
@@ -431,18 +427,9 @@ static void sdl_audio_fini (void *opaque)
     SDL_DestroyMutex (s->mutex);
     SDL_QuitSubSystem (SDL_INIT_AUDIO);
     s->driver_created = false;
+    s->dev = NULL;
 }
 
-static struct audio_option sdl_options[] = {
-    {
-        .name  = "SAMPLES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.nb_samples,
-        .descr = "Size of SDL buffer in samples"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops sdl_pcm_ops = {
     .init_out = sdl_init_out,
     .fini_out = sdl_fini_out,
@@ -454,7 +441,6 @@ static struct audio_pcm_ops sdl_pcm_ops = {
 struct audio_driver sdl_audio_driver = {
     .name           = "sdl",
     .descr          = "SDL http://www.libsdl.org",
-    .options        = sdl_options,
     .init           = sdl_audio_init,
     .fini           = sdl_audio_fini,
     .pcm_ops        = &sdl_pcm_ops,
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index f556b3b..441fbcb 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -74,7 +74,7 @@ static const SpiceRecordInterface record_sif = {
     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
 };
 
-static void *spice_audio_init (void)
+static void *spice_audio_init(Audiodev *dev)
 {
     if (!using_spice) {
         return NULL;
@@ -370,10 +370,6 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
     return 0;
 }
 
-static struct audio_option audio_options[] = {
-    { /* end of list */ },
-};
-
 static struct audio_pcm_ops audio_callbacks = {
     .init_out = line_out_init,
     .fini_out = line_out_fini,
@@ -391,7 +387,6 @@ static struct audio_pcm_ops audio_callbacks = {
 struct audio_driver spice_audio_driver = {
     .name           = "spice",
     .descr          = "spice audio driver",
-    .options        = audio_options,
     .init           = spice_audio_init,
     .fini           = spice_audio_fini,
     .pcm_ops        = &audio_callbacks,
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 81250e6..1af6d23 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -23,6 +23,7 @@
  */
 #include "hw/hw.h"
 #include "qemu/timer.h"
+#include "qapi-visit.h"
 #include "audio.h"
 
 #define AUDIO_CAP "wav"
@@ -36,11 +37,6 @@ typedef struct WAVVoiceOut {
     int total_samples;
 } WAVVoiceOut;
 
-typedef struct {
-    struct audsettings settings;
-    const char *wav_path;
-} WAVConf;
-
 static int wav_run_out (HWVoiceOut *hw, int live)
 {
     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
@@ -111,8 +107,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
     };
-    WAVConf *conf = drv_opaque;
-    struct audsettings wav_as = conf->settings;
+    Audiodev *dev = drv_opaque;
+    AudiodevWavOptions *wopts = dev->wav;
+    struct audsettings wav_as = audiodev_to_audsettings(dev->out);
+    const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
 
     stereo = wav_as.nchannels == 2;
     switch (wav_as.fmt) {
@@ -153,10 +151,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
 
-    wav->f = fopen (conf->wav_path, "wb");
+    wav->f = fopen(wav_path, "wb");
     if (!wav->f) {
         dolog ("Failed to open wave file `%s'\nReason: %s\n",
-               conf->wav_path, strerror (errno));
+               wav_path, strerror(errno));
         g_free (wav->pcm_buf);
         wav->pcm_buf = NULL;
         return -1;
@@ -224,54 +222,17 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static WAVConf glob_conf = {
-    .settings.freq      = 44100,
-    .settings.nchannels = 2,
-    .settings.fmt       = AUDIO_FORMAT_S16,
-    .wav_path           = "qemu.wav"
-};
-
-static void *wav_audio_init (void)
+static void *wav_audio_init(Audiodev *dev)
 {
-    WAVConf *conf = g_malloc(sizeof(WAVConf));
-    *conf = glob_conf;
-    return conf;
+    assert(dev->kind == AUDIODEV_DRIVER_WAV);
+    return dev;
 }
 
 static void wav_audio_fini (void *opaque)
 {
     ldebug ("wav_fini");
-    g_free(opaque);
 }
 
-static struct audio_option wav_options[] = {
-    {
-        .name  = "FREQUENCY",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.settings.freq,
-        .descr = "Frequency"
-    },
-    {
-        .name  = "FORMAT",
-        .tag   = AUD_OPT_FMT,
-        .valp  = &glob_conf.settings.fmt,
-        .descr = "Format"
-    },
-    {
-        .name  = "DAC_FIXED_CHANNELS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.settings.nchannels,
-        .descr = "Number of channels (1 - mono, 2 - stereo)"
-    },
-    {
-        .name  = "PATH",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.wav_path,
-        .descr = "Path to wave file"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops wav_pcm_ops = {
     .init_out = wav_init_out,
     .fini_out = wav_fini_out,
@@ -283,7 +244,6 @@ static struct audio_pcm_ops wav_pcm_ops = {
 struct audio_driver wav_audio_driver = {
     .name           = "wav",
     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
-    .options        = wav_options,
     .init           = wav_audio_init,
     .fini           = wav_audio_fini,
     .pcm_ops        = &wav_pcm_ops,
diff --git a/qemu-options.hx b/qemu-options.hx
index 5438f98..2ce03c4 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -185,8 +185,8 @@ Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.:
 qemu-system-i386 -global ide-drive.physical_block_size=4096 -drive file=file,if=ide,index=0,media=disk
 @end example
 
-In particular, you can use this to set driver properties for devices which are 
-created automatically by the machine model. To create a device which is not 
+In particular, you can use this to set driver properties for devices which are
+created automatically by the machine model. To create a device which is not
 created automatically and set properties on it, use -@option{device}.
 
 The two syntaxes are equivalent.  The longer one works for drivers whose name
@@ -312,14 +312,234 @@ The default is @code{en-us}.
 ETEXI
 
 
+HXCOMM Deprecated by -audiodev
 DEF("audio-help", 0, QEMU_OPTION_audio_help,
-    "-audio-help     print list of audio drivers and their options\n",
+    "-audio-help     show -audiodev equivalent of the current audio settings\n",
     QEMU_ARCH_ALL)
 STEXI
 @item -audio-help
 @findex -audio-help
-Will show the audio subsystem help: list of drivers, tunable
-parameters.
+Will show the -audiodev equivalent of the currently specified
+(deprecated) environment variables.
+ETEXI
+
+DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
+    "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n"
+    "                specifies the audio backend to use\n"
+    "                id= identifier of the backend\n"
+    "                timer-period= timer period in microseconds\n"
+    "                in|out.fixed-settings= use fixed settings for host audio\n"
+    "                in|out.frequency= frequency to use with fixed settings\n"
+    "                in|out.channels= number of channels to use with fixed settings\n"
+    "                in|out.format= sample format to use with fixed settings\n"
+    "                valid values: s8, s16, s32, u8, u16, u32\n"
+    "                in|out.voices= number of voices to use\n"
+    "                in|out.buffer-len= size of buffer in microseconds\n"
+    "                in|out.buffer-count= number of buffers\n"
+    "-audiodev none,id=id,[,prop[=value][,...]]\n"
+    "                dummy driver that discards all output\n"
+#ifdef CONFIG_ALSA
+    "-audiodev alsa,id=id[,prop[=value][,...]]\n"
+    "                alsa-in|alsa-out.dev= name of the audio device to use\n"
+    "                alsa-in|alsa-out.try-poll= attempt to use poll mode\n"
+    "                threshold= threshold (in microseconds) when playback starts\n"
+#endif
+#ifdef CONFIG_COREAUDIO
+    "-audiodev coreaudio,id=id[,prop[=value][,...]]\n"
+#endif
+#ifdef CONFIG_DSOUND
+    "-audiodev dsound,id=id[,prop[=value][,...]]\n"
+    "                latency= add extra latency to playback in microseconds\n"
+#endif
+#ifdef CONFIG_OSS
+    "-audiodev oss,id=id[,prop[=value][,...]]\n"
+    "                oss-in|oss-out.dev= path of the audio device to use\n"
+    "                oss-in|oss-out.try-poll= attempt to use poll mode\n"
+    "                try-mmap= try using memory mapped access\n"
+    "                exclusive= open device in exclusive mode\n"
+    "                dsp-policy= set timing policy (0..10), -1 to use fragment mode\n"
+#endif
+#ifdef CONFIG_PA
+    "-audiodev pa,id=id[,prop[=value][,...]]\n"
+    "                server= PulseAudio server address\n"
+    "                sink= sink device name\n"
+    "                source= source device name\n"
+#endif
+#ifdef CONFIG_SDL
+    "-audiodev sdl,id=id[,prop[=value][,...]]\n"
+#endif
+#ifdef CONFIG_SPICE
+    "-audiodev spice,id=id[,prop[=value][,...]]\n"
+#endif
+    "-audiodev wav,id=id[,prop[=value][,...]]\n"
+    "                path= path of wav file to record\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -audiodev [driver=]@var{driver},id=@var{id}[,@var{prop}[=@var{value}][,...]]
+@findex -audiodev
+Adds a new audio backend @var{driver} identified by @var{id}.  There are
+global and driver specific properties.  Some values can be set
+differently for input and output, they're marked with @code{in|out.}.
+You can set the input's property with @code{in.@var{prop}} and the
+output's property with @code{out.@var{prop}}. For example:
+@example
+-audiodev alsa,in.frequency=44110,out.frequency=8000
+-audiodev alsa,out.channels=1 # leaves in.channels unspecified
+@end example
+
+Valid global options are:
+
+@table @option
+@item id=@var{identifier}
+Identifies the audio backend.
+
+@item timer-period=@var{period}
+Sets the timer @var{period} used by the audio subsystem in microseconds.
+Default is 10000 (10 ms).
+
+@item in|out.fixed-settings=on|off
+Use fixed settings for host audio.  When off, it will change based on
+how the guest opens the sound card.  In this case you must not specify
+@var{frequency}, @var{channels} or @var{format}.  Default is on.
+
+@item in|out.frequency=@var{frequency}
+Specify the @var{frequency} to use when using @var{fixed-settings}.
+Default is 44100Hz.
+
+@item in|out.channels=@var{channels}
+Specify the number of @var{channels} to use when using
+@var{fixed-settings}. Default is 2 (stereo).
+
+@item in|out.format=@var{format}
+Specify the sample @var{format} to use when using @var{fixed-settings}.
+Valid values are: @code{s8}, @code{s16}, @code{s32}, @code{u8},
+@code{u16}, @code{u32}. Default is @code{s16}.
+
+@item in|out.voices=@var{voices}
+Specify the number of @var{voices} to use.  Default is 1.
+
+@item in|out.buffer=@var{usecs}
+Sets the size of the buffer in microseconds.
+
+@item in|out.buffer-count=@var{count}
+Sets the @var{count} of the buffers.
+
+@end table
+
+@item -audiodev none,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a dummy backend that discards all outputs.  This backend has no
+backend specific properties.
+
+@item -audiodev alsa,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates backend using the ALSA.  This backend is only available on
+Linux.
+
+ALSA specific options are:
+
+@table @option
+@item alsa-in|alsa-out.dev=@var{device}
+Specify the ALSA @var{device} to use for input and/or output.  Default
+is @code{default}.
+
+@item alsa-in|alsa-out.try-poll=on|off
+Attempt to use poll mode with the device.  Default is on.
+
+@item threshold=@var{threshold}
+Threshold (in microseconds) when playback starts.  Default is 0.
+
+@end table
+
+@item -audiodev coreaudio,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using Apple's Core Audio.  This backend is only
+available on Mac OS and only supports playback.  This backend has no
+backend specific properties.
+
+@item -audiodev dsound,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using Microsoft's DirectSound.  This backend is only
+available on Windows and only supports playback.
+
+Backend specific options are:
+
+@table @option
+
+@item latency=@var{usecs}
+Add extra @var{usecs} microseconds latency to playback.  Default is
+10000 (10 ms).
+
+@end table
+
+@item -audiodev oss,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using OSS.  This backend is available on most
+Unix-like systems.
+
+OSS specific options are:
+
+@table @option
+
+@item oss-in|oss-out.dev=@var{device}
+Specify the file name of the OSS @var{device} to use.  Default is
+@code{/dev/dsp}.
+
+@item oss-in|oss-out.try-poll=on|of
+Attempt to use poll mode with the device.  Default is on.
+
+@item try-mmap=on|off
+Try using memory mapped device access.  Default is off.
+
+@item exclusive=on|off
+Open the device in exclusive mode (vmix won't work in this case).
+Default is off.
+
+@item dsp-policy=@var{policy}
+Sets the timing policy (between 0 and 10, where smaller number means
+smaller latency but higher CPU usage).  Use -1 to use buffer sizes
+specified by @code{buffer} and @code{buffer-count}.  This option is
+ignored if you do not have OSS 4. Default is 5.
+
+@end table
+
+@item -audiodev pa,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using PulseAudio.  This backend is available on most
+systems.
+
+PulseAudio specific options are:
+
+@table @option
+
+@item server=@var{server}
+Sets the PulseAudio @var{server} to connect to.
+
+@item sink=@var{sink}
+Use the specified @var{sink} for playback.
+
+@item source=@var{source}
+Use the specified @var{source} for recording.
+
+@end table
+
+@item -audiodev sdl,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using SDL.  This backend is available on most systems,
+but you should use your platform's native backend if possible.  This
+backend has no backend specific properties.
+
+@item -audiodev spice,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend that sends audio through SPICE.  This backend requires
+@code{-spice} and automatically selected in that case, so usually you
+can ignore this option.  This backend has no backend specific
+properties.
+
+@item -audiodev wav,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend that writes audio to a WAV file.
+
+Backend specific options are:
+
+@table @option
+
+@item path=@var{path}
+Write recorded audio into the specified file.  Default is
+@code{qemu.wav}.
+
+@end table
 ETEXI
 
 DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw,
diff --git a/vl.c b/vl.c
index 2201e27..c3d6ef9 100644
--- a/vl.c
+++ b/vl.c
@@ -2873,6 +2873,7 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_trace_opts);
     qemu_add_opts(&qemu_option_rom_opts);
     qemu_add_opts(&qemu_machine_opts);
+    qemu_add_opts(&qemu_audiodev_opts);
     qemu_add_opts(&qemu_mem_opts);
     qemu_add_opts(&qemu_smp_opts);
     qemu_add_opts(&qemu_boot_opts);
@@ -3170,9 +3171,14 @@ int main(int argc, char **argv, char **envp)
                 add_device_config(DEV_BT, optarg);
                 break;
             case QEMU_OPTION_audio_help:
-                AUD_help ();
+                audio_legacy_help();
                 exit (0);
                 break;
+            case QEMU_OPTION_audiodev:
+                if (!qemu_opts_parse(qemu_find_opts("audiodev"), optarg, 1)) {
+                    exit(1);
+                }
+                break;
             case QEMU_OPTION_soundhw:
                 select_soundhw (optarg);
                 break;
@@ -4339,6 +4345,7 @@ int main(int argc, char **argv, char **envp)
 
     realtime_init();
 
+    audio_set_options();
     audio_init();
 
     cpu_synchronize_all_post_init();
-- 
2.4.3

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

* Re: [Qemu-devel] [PATCH v3 5/8] qapi: support nested structs in OptsVisitor
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 5/8] qapi: support nested structs in OptsVisitor Kővágó, Zoltán
@ 2015-06-18 17:15   ` Laszlo Ersek
  2015-06-18 17:35     ` Kővágó Zoltán
  0 siblings, 1 reply; 13+ messages in thread
From: Laszlo Ersek @ 2015-06-18 17:15 UTC (permalink / raw)
  To: "Kővágó, Zoltán"
  Cc: Michael Roth, Markus Armbruster, qemu-devel, Gerd Hoffmann

On 06/18/15 18:43, Kővágó, Zoltán wrote:
> The current OptsVisitor flattens the whole structure, if there are same
> named fields under different paths (like `in' and `out' in `Audiodev'),
> the current visitor can't cope with them (for example setting
> `frequency=44100' will set the in's frequency to 44100 and leave out's
> frequency unspecified).
> 
> This patch fixes it, by always requiring a complete path in case of
> nested structs.  Fields in the path are separated by dots, similar to C
> structs (without pointers), like `in.frequency' or`out.frequency'.
> 
> You must provide a full path even in non-ambigous cases.  The previous
> two commits hopefully ensures that this change doesn't create backward
> compatibility problems.
> 
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> 
> ---
> 
> Change from v2:
> * only fully qualified paths are allowed
> 
>  qapi/opts-visitor.c                     | 114 ++++++++++++++++++++++++++------
>  tests/qapi-schema/qapi-schema-test.json |   9 ++-
>  tests/test-opts-visitor.c               |  34 ++++++++++
>  3 files changed, 135 insertions(+), 22 deletions(-)
> 
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index f02059d..7a80442 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -71,6 +71,7 @@ struct OptsVisitor
>       * schema, with a single mandatory scalar member. */
>      ListMode list_mode;
>      GQueue *repeated_opts;
> +    char *repeated_name;
>  
>      /* When parsing a list of repeating options as integers, values of the form
>       * "a-b", representing a closed interval, are allowed. Elements in the
> @@ -86,6 +87,9 @@ struct OptsVisitor
>       * not survive or escape the OptsVisitor object.
>       */
>      QemuOpt *fake_id_opt;
> +
> +    /* List of field names leading to the current structure. */
> +    GQueue *nested_names;
>  };
>  
>  
> @@ -100,6 +104,7 @@ static void
>  opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
>  {
>      GQueue *list;
> +    assert(opt);
>  
>      list = g_hash_table_lookup(unprocessed_opts, opt->name);
>      if (list == NULL) {
> @@ -127,6 +132,9 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
>      if (obj) {
>          *obj = g_malloc0(size > 0 ? size : 1);
>      }
> +
> +    g_queue_push_tail(ov->nested_names, (gpointer) name);
> +
>      if (ov->depth++ > 0) {
>          return;
>      }
> @@ -169,6 +177,8 @@ opts_end_struct(Visitor *v, Error **errp)
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>      GQueue *any;
>  
> +    g_queue_pop_tail(ov->nested_names);
> +
>      if (--ov->depth > 0) {
>          return;
>      }
> @@ -198,15 +208,54 @@ opts_end_implicit_struct(Visitor *v, Error **errp)
>  }
>  
>  
> +static void
> +sum_strlen(gpointer data, gpointer user_data)
> +{
> +    const char *str = data;
> +    size_t *sum_len = user_data;
> +
> +    if (str) { /* skip NULLs */
> +        *sum_len += strlen(str) + 1;
> +    }
> +}
> +
> +static void
> +append_str(gpointer data, gpointer user_data)
> +{
> +    const char *str = data;
> +    char *concat_str = user_data;
> +
> +    if (str) {
> +        strcat(concat_str, str);
> +        strcat(concat_str, ".");
> +    }
> +}
> +
> +/* lookup a name, using a fully qualified version */
>  static GQueue *
> -lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
> +lookup_distinct(const OptsVisitor *ov, const char *name, char **out_key,
> +                Error **errp)
>  {
> -    GQueue *list;
> +    GQueue *list = NULL;
> +    char *key;
> +    size_t sum_len = strlen(name);
> +
> +    g_queue_foreach(ov->nested_names, sum_strlen, &sum_len);
> +    key = g_malloc(sum_len+1);
> +    key[0] = 0;
> +    g_queue_foreach(ov->nested_names, append_str, key);
> +    strcat(key, name);
> +
> +    list = g_hash_table_lookup(ov->unprocessed_opts, key);
> +    if (list && out_key) {
> +        *out_key = g_strdup(key);
> +    }
>  
> -    list = g_hash_table_lookup(ov->unprocessed_opts, name);
>      if (!list) {
>          error_set(errp, QERR_MISSING_PARAMETER, name);
>      }
> +
> +    g_free(key);
>      return list;
>  }
>  
> @@ -218,7 +267,7 @@ opts_start_list(Visitor *v, const char *name, Error **errp)
>  
>      /* we can't traverse a list in a list */
>      assert(ov->list_mode == LM_NONE);
> -    ov->repeated_opts = lookup_distinct(ov, name, errp);
> +    ov->repeated_opts = lookup_distinct(ov, name, &ov->repeated_name, errp);
>      if (ov->repeated_opts != NULL) {
>          ov->list_mode = LM_STARTED;
>      }
> @@ -254,11 +303,9 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
>          /* range has been completed, fall through in order to pop option */
>  
>      case LM_IN_PROGRESS: {
> -        const QemuOpt *opt;
> -
> -        opt = g_queue_pop_head(ov->repeated_opts);
> +        g_queue_pop_head(ov->repeated_opts);
>          if (g_queue_is_empty(ov->repeated_opts)) {
> -            g_hash_table_remove(ov->unprocessed_opts, opt->name);
> +            g_hash_table_remove(ov->unprocessed_opts, ov->repeated_name);
>              return NULL;
>          }
>          link = &(*list)->next;
> @@ -284,22 +331,28 @@ opts_end_list(Visitor *v, Error **errp)
>             ov->list_mode == LM_SIGNED_INTERVAL ||
>             ov->list_mode == LM_UNSIGNED_INTERVAL);
>      ov->repeated_opts = NULL;
> +
> +    g_free(ov->repeated_name);
> +    ov->repeated_name = NULL;
> +
>      ov->list_mode = LM_NONE;
>  }
>  
>  
>  static const QemuOpt *
> -lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
> +lookup_scalar(const OptsVisitor *ov, const char *name, char** out_key,
> +              Error **errp)
>  {
>      if (ov->list_mode == LM_NONE) {
>          GQueue *list;
>  
>          /* the last occurrence of any QemuOpt takes effect when queried by name
>           */
> -        list = lookup_distinct(ov, name, errp);
> +        list = lookup_distinct(ov, name, out_key, errp);
>          return list ? g_queue_peek_tail(list) : NULL;
>      }
>      assert(ov->list_mode == LM_IN_PROGRESS);
> +    assert(out_key == NULL || *out_key == NULL);
>      return g_queue_peek_head(ov->repeated_opts);
>  }
>  
> @@ -321,13 +374,15 @@ opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
>  {
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>      const QemuOpt *opt;
> +    char *key = NULL;
>  
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
>      *obj = g_strdup(opt->str ? opt->str : "");
> -    processed(ov, name);
> +    processed(ov, key);
> +    g_free(key);
>  }
>  
>  
> @@ -337,8 +392,9 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
>  {
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>      const QemuOpt *opt;
> +    char *key = NULL;
>  
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
> @@ -355,13 +411,15 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
>          } else {
>              error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>                  "on|yes|y|off|no|n");
> +            g_free(key);
>              return;
>          }
>      } else {
>          *obj = true;
>      }
>  
> -    processed(ov, name);
> +    processed(ov, key);
> +    g_free(key);
>  }
>  
>  
> @@ -373,13 +431,14 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>      const char *str;
>      long long val;
>      char *endptr;
> +    char *key = NULL;
>  
>      if (ov->list_mode == LM_SIGNED_INTERVAL) {
>          *obj = ov->range_next.s;
>          return;
>      }
>  
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
> @@ -393,11 +452,13 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>      if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) {
>          if (*endptr == '\0') {
>              *obj = val;
> -            processed(ov, name);
> +            processed(ov, key);
> +            g_free(key);
>              return;
>          }
>          if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
>              long long val2;
> +            assert(key == NULL);
>  
>              str = endptr + 1;
>              val2 = strtoll(str, &endptr, 0);
> @@ -418,6 +479,7 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>      error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>                (ov->list_mode == LM_NONE) ? "an int64 value" :
>                                             "an int64 value or range");
> +    g_free(key);
>  }
>  
>  
> @@ -429,13 +491,14 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      const char *str;
>      unsigned long long val;
>      char *endptr;
> +    char *key = NULL;
>  
>      if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
>          *obj = ov->range_next.u;
>          return;
>      }
>  
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
> @@ -447,11 +510,13 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) {
>          if (*endptr == '\0') {
>              *obj = val;
> -            processed(ov, name);
> +            processed(ov, key);
> +            g_free(key);
>              return;
>          }
>          if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
>              unsigned long long val2;
> +            assert(key == NULL);
>  
>              str = endptr + 1;
>              if (parse_uint_full(str, &val2, 0) == 0 &&
> @@ -470,6 +535,7 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>                (ov->list_mode == LM_NONE) ? "a uint64 value" :
>                                             "a uint64 value or range");
> +    g_free(key);
>  }
>  
>  
> @@ -480,8 +546,9 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      const QemuOpt *opt;
>      int64_t val;
>      char *endptr;
> +    char *key = NULL;
>  
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
> @@ -491,11 +558,13 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      if (val < 0 || *endptr) {
>          error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>                    "a size value representible as a non-negative int64");
> +        g_free(key);
>          return;
>      }
>  
>      *obj = val;
> -    processed(ov, name);
> +    processed(ov, key);
> +    g_free(key);
>  }
>  
>  
> @@ -506,7 +575,7 @@ opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
>  
>      /* we only support a single mandatory scalar field in a list node */
>      assert(ov->list_mode == LM_NONE);
> -    *present = (lookup_distinct(ov, name, NULL) != NULL);
> +    *present = (lookup_distinct(ov, name, NULL, NULL) != NULL);
>  }
>  
>  
> @@ -517,6 +586,8 @@ opts_visitor_new(const QemuOpts *opts)
>  
>      ov = g_malloc0(sizeof *ov);
>  
> +    ov->nested_names = g_queue_new();
> +
>      ov->visitor.start_struct = &opts_start_struct;
>      ov->visitor.end_struct   = &opts_end_struct;
>  
> @@ -560,6 +631,7 @@ opts_visitor_cleanup(OptsVisitor *ov)
>      if (ov->unprocessed_opts != NULL) {
>          g_hash_table_destroy(ov->unprocessed_opts);
>      }
> +    g_queue_free(ov->nested_names);
>      g_free(ov->fake_id_opt);
>      g_free(ov);
>  }
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index c7eaa86..a818eff 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -81,6 +81,11 @@
>  { 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' },
>    'returns': 'int' }
>  
> +# For testing hierarchy support in opts-visitor
> +{ 'struct': 'UserDefOptionsSub',
> +  'data': {
> +    '*nint': 'int' } }
> +
>  # For testing integer range flattening in opts-visitor. The following schema
>  # corresponds to the option format:
>  #
> @@ -94,7 +99,9 @@
>      '*u64' : [ 'uint64' ],
>      '*u16' : [ 'uint16' ],
>      '*i64x':   'int'     ,
> -    '*u64x':   'uint64'  } }
> +    '*u64x':   'uint64'  ,
> +    'sub0':    'UserDefOptionsSub',
> +    'sub1':    'UserDefOptionsSub' } }
>  
>  # testing event
>  { 'struct': 'EventStructOne',
> diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
> index ebeee5d..9199141 100644
> --- a/tests/test-opts-visitor.c
> +++ b/tests/test-opts-visitor.c
> @@ -177,6 +177,34 @@ expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data)
>      g_assert(f->userdef->u64->value == UINT64_MAX);
>  }
>  
> +static void
> +expect_both(OptsVisitorFixture *f, gconstpointer test_data)
> +{
> +    expect_ok(f, test_data);
> +    g_assert(f->userdef->sub0->has_nint);
> +    g_assert(f->userdef->sub0->nint == 13);
> +    g_assert(f->userdef->sub1->has_nint);
> +    g_assert(f->userdef->sub1->nint == 17);
> +}
> +
> +static void
> +expect_sub0(OptsVisitorFixture *f, gconstpointer test_data)
> +{
> +    expect_ok(f, test_data);
> +    g_assert(f->userdef->sub0->has_nint);
> +    g_assert(f->userdef->sub0->nint == 13);
> +    g_assert(!f->userdef->sub1->has_nint);
> +}
> +
> +static void
> +expect_sub1(OptsVisitorFixture *f, gconstpointer test_data)
> +{
> +    expect_ok(f, test_data);
> +    g_assert(!f->userdef->sub0->has_nint);
> +    g_assert(f->userdef->sub1->has_nint);
> +    g_assert(f->userdef->sub1->nint == 13);
> +}
> +
>  /* test cases */
>  
>  int
> @@ -270,6 +298,12 @@ main(int argc, char **argv)
>      add_test("/visitor/opts/i64/range/2big/full", &expect_fail,
>               "i64=-0x8000000000000000-0x7fffffffffffffff");
>  
> +    /* Test nested structs support */
> +    add_test("/visitor/opts/nested/unqualified", &expect_fail, "nint=13");
> +    add_test("/visitor/opts/nested/both",        &expect_both,
> +             "sub0.nint=13,sub1.nint=17");
> +    add_test("/visitor/opts/nested/sub0",        &expect_sub0, "sub0.nint=13");
> +    add_test("/visitor/opts/nested/sub1",        &expect_sub1, "sub1.nint=13");
>      g_test_run();
>      return 0;
>  }
> 

Re patch v3 1/8 -- apparently I was not around when the
start_implicit_struct() and end_implicit_struct() visitor callbacks were
introduced, so I don't know what they are good for, but on the surface,
that patch might even be correct. I can't tell.

Re patch v3 5/8, ie. this patch -- if there has been some kind of
"master blurb" for this feature, ie. justification for nested structs,
I'm unaware of it. (I'm not getting qemu-devel email, just personal
Cc's.) Can you please send me a link into the archive where this feature
is justifed / discussed?

I have one related, and one independent note here:

(1) The related note -- OptsVisitor was introduced in commit eb7ee2cbeb.
You can see in the commit message that "union of flat structures" was a
foundational design trait for OptsVisitor, so if you want nested
structs, it's not a "bug to fix", but a new feature to add.

(Namely, refer to the allowed members of "struct for discriminator case
1" -- they can only be scalar members, or restricted format lists for
repeating optargs. Structure nesting is not allowed on any level.)

The objective was to support then-existent command lines (ie. parse
options into QAPI schemas). As far as I (vaguely) recall, Eric has since
(independently) flattened a bunch of QAPI structs as well. So I'm unsure
if and why nested structs are "again" a "thing".

(In any case this should not be taken as my opposition; I'm just
uninformed. That link I mentioned above could inform me.)

(2) The independent note. OptsVisitor is one of the
hardest-to-understand things I've implemented. It has always been hard
to understand for myself. As far as I recall, I struggled in my mind to
cover all corner cases and prove stuff right. Later I was "asked" to add
range support to it (ie. turn a range like 3-7 into a list of integers
3, 4, 5, 6, 7) and I was sweating bloody bullets to avoid regressions.

So, this code requires careful tiptoeing. I do not want to discourage
changes to it (and I certainly encourage reviewers to look at your
patches). I'm just saying I personally can't be your reviewer. I lost
this context long ago, I dread looking at this code again, and I don't
feel safe saying either yay or nay.

You could *almost* accuse me of having thrown this code over the fence.
My only excuse (a good one though) is that I only wrote this code
because I was asked to. (If you're familiar with the Bioshock universe,
I heard "would you kindly".)

I'm told that this code has proved somewhat useful, which I'm glad
about, but I really can't support it. Sorry about that. So, I'm neither
acking nor nacking; don't wait for my feedback.

(Apologies for the wall of text, but I guess it's better to say "sorry
can't help you" in too many words than not reacting at all.)

Thanks
László (since we're using Hungarian accents in this thread :))

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

* Re: [Qemu-devel] [PATCH v3 5/8] qapi: support nested structs in OptsVisitor
  2015-06-18 17:15   ` Laszlo Ersek
@ 2015-06-18 17:35     ` Kővágó Zoltán
  0 siblings, 0 replies; 13+ messages in thread
From: Kővágó Zoltán @ 2015-06-18 17:35 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: Michael Roth, Markus Armbruster, qemu-devel, Gerd Hoffmann

2015-06-18 19:15 keltezéssel, Laszlo Ersek írta:
> On 06/18/15 18:43, Kővágó, Zoltán wrote:
>> The current OptsVisitor flattens the whole structure, if there are same
>> named fields under different paths (like `in' and `out' in `Audiodev'),
>> the current visitor can't cope with them (for example setting
>> `frequency=44100' will set the in's frequency to 44100 and leave out's
>> frequency unspecified).
>>
>> This patch fixes it, by always requiring a complete path in case of
>> nested structs.  Fields in the path are separated by dots, similar to C
>> structs (without pointers), like `in.frequency' or`out.frequency'.
>>
>> You must provide a full path even in non-ambigous cases.  The previous
>> two commits hopefully ensures that this change doesn't create backward
>> compatibility problems.
>>
>> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
>>
>> ---
>>
>> Change from v2:
>> * only fully qualified paths are allowed
>>
>>   qapi/opts-visitor.c                     | 114 ++++++++++++++++++++++++++------
>>   tests/qapi-schema/qapi-schema-test.json |   9 ++-
>>   tests/test-opts-visitor.c               |  34 ++++++++++
>>   3 files changed, 135 insertions(+), 22 deletions(-)
>>
>> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
>> index f02059d..7a80442 100644
>> --- a/qapi/opts-visitor.c
>> +++ b/qapi/opts-visitor.c
>> @@ -71,6 +71,7 @@ struct OptsVisitor
>>        * schema, with a single mandatory scalar member. */
>>       ListMode list_mode;
>>       GQueue *repeated_opts;
>> +    char *repeated_name;
>>
>>       /* When parsing a list of repeating options as integers, values of the form
>>        * "a-b", representing a closed interval, are allowed. Elements in the
>> @@ -86,6 +87,9 @@ struct OptsVisitor
>>        * not survive or escape the OptsVisitor object.
>>        */
>>       QemuOpt *fake_id_opt;
>> +
>> +    /* List of field names leading to the current structure. */
>> +    GQueue *nested_names;
>>   };
>>
>>
>> @@ -100,6 +104,7 @@ static void
>>   opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
>>   {
>>       GQueue *list;
>> +    assert(opt);
>>
>>       list = g_hash_table_lookup(unprocessed_opts, opt->name);
>>       if (list == NULL) {
>> @@ -127,6 +132,9 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
>>       if (obj) {
>>           *obj = g_malloc0(size > 0 ? size : 1);
>>       }
>> +
>> +    g_queue_push_tail(ov->nested_names, (gpointer) name);
>> +
>>       if (ov->depth++ > 0) {
>>           return;
>>       }
>> @@ -169,6 +177,8 @@ opts_end_struct(Visitor *v, Error **errp)
>>       OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>>       GQueue *any;
>>
>> +    g_queue_pop_tail(ov->nested_names);
>> +
>>       if (--ov->depth > 0) {
>>           return;
>>       }
>> @@ -198,15 +208,54 @@ opts_end_implicit_struct(Visitor *v, Error **errp)
>>   }
>>
>>
>> +static void
>> +sum_strlen(gpointer data, gpointer user_data)
>> +{
>> +    const char *str = data;
>> +    size_t *sum_len = user_data;
>> +
>> +    if (str) { /* skip NULLs */
>> +        *sum_len += strlen(str) + 1;
>> +    }
>> +}
>> +
>> +static void
>> +append_str(gpointer data, gpointer user_data)
>> +{
>> +    const char *str = data;
>> +    char *concat_str = user_data;
>> +
>> +    if (str) {
>> +        strcat(concat_str, str);
>> +        strcat(concat_str, ".");
>> +    }
>> +}
>> +
>> +/* lookup a name, using a fully qualified version */
>>   static GQueue *
>> -lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
>> +lookup_distinct(const OptsVisitor *ov, const char *name, char **out_key,
>> +                Error **errp)
>>   {
>> -    GQueue *list;
>> +    GQueue *list = NULL;
>> +    char *key;
>> +    size_t sum_len = strlen(name);
>> +
>> +    g_queue_foreach(ov->nested_names, sum_strlen, &sum_len);
>> +    key = g_malloc(sum_len+1);
>> +    key[0] = 0;
>> +    g_queue_foreach(ov->nested_names, append_str, key);
>> +    strcat(key, name);
>> +
>> +    list = g_hash_table_lookup(ov->unprocessed_opts, key);
>> +    if (list && out_key) {
>> +        *out_key = g_strdup(key);
>> +    }
>>
>> -    list = g_hash_table_lookup(ov->unprocessed_opts, name);
>>       if (!list) {
>>           error_set(errp, QERR_MISSING_PARAMETER, name);
>>       }
>> +
>> +    g_free(key);
>>       return list;
>>   }
>>
>> @@ -218,7 +267,7 @@ opts_start_list(Visitor *v, const char *name, Error **errp)
>>
>>       /* we can't traverse a list in a list */
>>       assert(ov->list_mode == LM_NONE);
>> -    ov->repeated_opts = lookup_distinct(ov, name, errp);
>> +    ov->repeated_opts = lookup_distinct(ov, name, &ov->repeated_name, errp);
>>       if (ov->repeated_opts != NULL) {
>>           ov->list_mode = LM_STARTED;
>>       }
>> @@ -254,11 +303,9 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
>>           /* range has been completed, fall through in order to pop option */
>>
>>       case LM_IN_PROGRESS: {
>> -        const QemuOpt *opt;
>> -
>> -        opt = g_queue_pop_head(ov->repeated_opts);
>> +        g_queue_pop_head(ov->repeated_opts);
>>           if (g_queue_is_empty(ov->repeated_opts)) {
>> -            g_hash_table_remove(ov->unprocessed_opts, opt->name);
>> +            g_hash_table_remove(ov->unprocessed_opts, ov->repeated_name);
>>               return NULL;
>>           }
>>           link = &(*list)->next;
>> @@ -284,22 +331,28 @@ opts_end_list(Visitor *v, Error **errp)
>>              ov->list_mode == LM_SIGNED_INTERVAL ||
>>              ov->list_mode == LM_UNSIGNED_INTERVAL);
>>       ov->repeated_opts = NULL;
>> +
>> +    g_free(ov->repeated_name);
>> +    ov->repeated_name = NULL;
>> +
>>       ov->list_mode = LM_NONE;
>>   }
>>
>>
>>   static const QemuOpt *
>> -lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
>> +lookup_scalar(const OptsVisitor *ov, const char *name, char** out_key,
>> +              Error **errp)
>>   {
>>       if (ov->list_mode == LM_NONE) {
>>           GQueue *list;
>>
>>           /* the last occurrence of any QemuOpt takes effect when queried by name
>>            */
>> -        list = lookup_distinct(ov, name, errp);
>> +        list = lookup_distinct(ov, name, out_key, errp);
>>           return list ? g_queue_peek_tail(list) : NULL;
>>       }
>>       assert(ov->list_mode == LM_IN_PROGRESS);
>> +    assert(out_key == NULL || *out_key == NULL);
>>       return g_queue_peek_head(ov->repeated_opts);
>>   }
>>
>> @@ -321,13 +374,15 @@ opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
>>   {
>>       OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>>       const QemuOpt *opt;
>> +    char *key = NULL;
>>
>> -    opt = lookup_scalar(ov, name, errp);
>> +    opt = lookup_scalar(ov, name, &key, errp);
>>       if (!opt) {
>>           return;
>>       }
>>       *obj = g_strdup(opt->str ? opt->str : "");
>> -    processed(ov, name);
>> +    processed(ov, key);
>> +    g_free(key);
>>   }
>>
>>
>> @@ -337,8 +392,9 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
>>   {
>>       OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>>       const QemuOpt *opt;
>> +    char *key = NULL;
>>
>> -    opt = lookup_scalar(ov, name, errp);
>> +    opt = lookup_scalar(ov, name, &key, errp);
>>       if (!opt) {
>>           return;
>>       }
>> @@ -355,13 +411,15 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
>>           } else {
>>               error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>>                   "on|yes|y|off|no|n");
>> +            g_free(key);
>>               return;
>>           }
>>       } else {
>>           *obj = true;
>>       }
>>
>> -    processed(ov, name);
>> +    processed(ov, key);
>> +    g_free(key);
>>   }
>>
>>
>> @@ -373,13 +431,14 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>>       const char *str;
>>       long long val;
>>       char *endptr;
>> +    char *key = NULL;
>>
>>       if (ov->list_mode == LM_SIGNED_INTERVAL) {
>>           *obj = ov->range_next.s;
>>           return;
>>       }
>>
>> -    opt = lookup_scalar(ov, name, errp);
>> +    opt = lookup_scalar(ov, name, &key, errp);
>>       if (!opt) {
>>           return;
>>       }
>> @@ -393,11 +452,13 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>>       if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) {
>>           if (*endptr == '\0') {
>>               *obj = val;
>> -            processed(ov, name);
>> +            processed(ov, key);
>> +            g_free(key);
>>               return;
>>           }
>>           if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
>>               long long val2;
>> +            assert(key == NULL);
>>
>>               str = endptr + 1;
>>               val2 = strtoll(str, &endptr, 0);
>> @@ -418,6 +479,7 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>>       error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>>                 (ov->list_mode == LM_NONE) ? "an int64 value" :
>>                                              "an int64 value or range");
>> +    g_free(key);
>>   }
>>
>>
>> @@ -429,13 +491,14 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>>       const char *str;
>>       unsigned long long val;
>>       char *endptr;
>> +    char *key = NULL;
>>
>>       if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
>>           *obj = ov->range_next.u;
>>           return;
>>       }
>>
>> -    opt = lookup_scalar(ov, name, errp);
>> +    opt = lookup_scalar(ov, name, &key, errp);
>>       if (!opt) {
>>           return;
>>       }
>> @@ -447,11 +510,13 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>>       if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) {
>>           if (*endptr == '\0') {
>>               *obj = val;
>> -            processed(ov, name);
>> +            processed(ov, key);
>> +            g_free(key);
>>               return;
>>           }
>>           if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
>>               unsigned long long val2;
>> +            assert(key == NULL);
>>
>>               str = endptr + 1;
>>               if (parse_uint_full(str, &val2, 0) == 0 &&
>> @@ -470,6 +535,7 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>>       error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>>                 (ov->list_mode == LM_NONE) ? "a uint64 value" :
>>                                              "a uint64 value or range");
>> +    g_free(key);
>>   }
>>
>>
>> @@ -480,8 +546,9 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>>       const QemuOpt *opt;
>>       int64_t val;
>>       char *endptr;
>> +    char *key = NULL;
>>
>> -    opt = lookup_scalar(ov, name, errp);
>> +    opt = lookup_scalar(ov, name, &key, errp);
>>       if (!opt) {
>>           return;
>>       }
>> @@ -491,11 +558,13 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>>       if (val < 0 || *endptr) {
>>           error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>>                     "a size value representible as a non-negative int64");
>> +        g_free(key);
>>           return;
>>       }
>>
>>       *obj = val;
>> -    processed(ov, name);
>> +    processed(ov, key);
>> +    g_free(key);
>>   }
>>
>>
>> @@ -506,7 +575,7 @@ opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
>>
>>       /* we only support a single mandatory scalar field in a list node */
>>       assert(ov->list_mode == LM_NONE);
>> -    *present = (lookup_distinct(ov, name, NULL) != NULL);
>> +    *present = (lookup_distinct(ov, name, NULL, NULL) != NULL);
>>   }
>>
>>
>> @@ -517,6 +586,8 @@ opts_visitor_new(const QemuOpts *opts)
>>
>>       ov = g_malloc0(sizeof *ov);
>>
>> +    ov->nested_names = g_queue_new();
>> +
>>       ov->visitor.start_struct = &opts_start_struct;
>>       ov->visitor.end_struct   = &opts_end_struct;
>>
>> @@ -560,6 +631,7 @@ opts_visitor_cleanup(OptsVisitor *ov)
>>       if (ov->unprocessed_opts != NULL) {
>>           g_hash_table_destroy(ov->unprocessed_opts);
>>       }
>> +    g_queue_free(ov->nested_names);
>>       g_free(ov->fake_id_opt);
>>       g_free(ov);
>>   }
>> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
>> index c7eaa86..a818eff 100644
>> --- a/tests/qapi-schema/qapi-schema-test.json
>> +++ b/tests/qapi-schema/qapi-schema-test.json
>> @@ -81,6 +81,11 @@
>>   { 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' },
>>     'returns': 'int' }
>>
>> +# For testing hierarchy support in opts-visitor
>> +{ 'struct': 'UserDefOptionsSub',
>> +  'data': {
>> +    '*nint': 'int' } }
>> +
>>   # For testing integer range flattening in opts-visitor. The following schema
>>   # corresponds to the option format:
>>   #
>> @@ -94,7 +99,9 @@
>>       '*u64' : [ 'uint64' ],
>>       '*u16' : [ 'uint16' ],
>>       '*i64x':   'int'     ,
>> -    '*u64x':   'uint64'  } }
>> +    '*u64x':   'uint64'  ,
>> +    'sub0':    'UserDefOptionsSub',
>> +    'sub1':    'UserDefOptionsSub' } }
>>
>>   # testing event
>>   { 'struct': 'EventStructOne',
>> diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
>> index ebeee5d..9199141 100644
>> --- a/tests/test-opts-visitor.c
>> +++ b/tests/test-opts-visitor.c
>> @@ -177,6 +177,34 @@ expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data)
>>       g_assert(f->userdef->u64->value == UINT64_MAX);
>>   }
>>
>> +static void
>> +expect_both(OptsVisitorFixture *f, gconstpointer test_data)
>> +{
>> +    expect_ok(f, test_data);
>> +    g_assert(f->userdef->sub0->has_nint);
>> +    g_assert(f->userdef->sub0->nint == 13);
>> +    g_assert(f->userdef->sub1->has_nint);
>> +    g_assert(f->userdef->sub1->nint == 17);
>> +}
>> +
>> +static void
>> +expect_sub0(OptsVisitorFixture *f, gconstpointer test_data)
>> +{
>> +    expect_ok(f, test_data);
>> +    g_assert(f->userdef->sub0->has_nint);
>> +    g_assert(f->userdef->sub0->nint == 13);
>> +    g_assert(!f->userdef->sub1->has_nint);
>> +}
>> +
>> +static void
>> +expect_sub1(OptsVisitorFixture *f, gconstpointer test_data)
>> +{
>> +    expect_ok(f, test_data);
>> +    g_assert(!f->userdef->sub0->has_nint);
>> +    g_assert(f->userdef->sub1->has_nint);
>> +    g_assert(f->userdef->sub1->nint == 13);
>> +}
>> +
>>   /* test cases */
>>
>>   int
>> @@ -270,6 +298,12 @@ main(int argc, char **argv)
>>       add_test("/visitor/opts/i64/range/2big/full", &expect_fail,
>>                "i64=-0x8000000000000000-0x7fffffffffffffff");
>>
>> +    /* Test nested structs support */
>> +    add_test("/visitor/opts/nested/unqualified", &expect_fail, "nint=13");
>> +    add_test("/visitor/opts/nested/both",        &expect_both,
>> +             "sub0.nint=13,sub1.nint=17");
>> +    add_test("/visitor/opts/nested/sub0",        &expect_sub0, "sub0.nint=13");
>> +    add_test("/visitor/opts/nested/sub1",        &expect_sub1, "sub1.nint=13");
>>       g_test_run();
>>       return 0;
>>   }
>>
>
> Re patch v3 1/8 -- apparently I was not around when the
> start_implicit_struct() and end_implicit_struct() visitor callbacks were
> introduced, so I don't know what they are good for, but on the surface,
> that patch might even be correct. I can't tell.

Actually I'm not completely sure that the visitor code generated are 
correct in case of implicit structs.  In case of normal structs, there's 
an if (*obj) { ... } check, but in case of implicit structs it just 
visits the fields without checking (which is problematic, since the 
default visit_start_implicit_struct is just a no-op, causing segfaults...)

> Re patch v3 5/8, ie. this patch -- if there has been some kind of
> "master blurb" for this feature, ie. justification for nested structs,
> I'm unaware of it. (I'm not getting qemu-devel email, just personal
> Cc's.) Can you please send me a link into the archive where this feature
> is justifed / discussed?

This is needed for my -audiodev option, see v2 patch thread: 
http://lists.nongnu.org/archive/html/qemu-devel/2015-06/msg04189.html
And probably this as an example where it's useful:
http://lists.nongnu.org/archive/html/qemu-devel/2015-06/msg04186.html

> I have one related, and one independent note here:
>
> (1) The related note -- OptsVisitor was introduced in commit eb7ee2cbeb.
> You can see in the commit message that "union of flat structures" was a
> foundational design trait for OptsVisitor, so if you want nested
> structs, it's not a "bug to fix", but a new feature to add.
>
> (Namely, refer to the allowed members of "struct for discriminator case
> 1" -- they can only be scalar members, or restricted format lists for
> repeating optargs. Structure nesting is not allowed on any level.)
>
> The objective was to support then-existent command lines (ie. parse
> options into QAPI schemas). As far as I (vaguely) recall, Eric has since
> (independently) flattened a bunch of QAPI structs as well. So I'm unsure
> if and why nested structs are "again" a "thing".
>
> (In any case this should not be taken as my opposition; I'm just
> uninformed. That link I mentioned above could inform me.)

Check my second link, it contains a good example where it's useful (to 
specify input and output audio settings independently, like in.frequency 
and out.frequency.  Of course we could do like in-frequency and 
out-frequency and a bunch of copy paste, but this is a much better way 
imho, even if it requires some changes to how existing things work).

> (2) The independent note. OptsVisitor is one of the
> hardest-to-understand things I've implemented. It has always been hard
> to understand for myself. As far as I recall, I struggled in my mind to
> cover all corner cases and prove stuff right. Later I was "asked" to add
> range support to it (ie. turn a range like 3-7 into a list of integers
> 3, 4, 5, 6, 7) and I was sweating bloody bullets to avoid regressions.
>
> So, this code requires careful tiptoeing. I do not want to discourage
> changes to it (and I certainly encourage reviewers to look at your
> patches). I'm just saying I personally can't be your reviewer. I lost
> this context long ago, I dread looking at this code again, and I don't
> feel safe saying either yay or nay.
>
> You could *almost* accuse me of having thrown this code over the fence.
> My only excuse (a good one though) is that I only wrote this code
> because I was asked to. (If you're familiar with the Bioshock universe,
> I heard "would you kindly".)
>
> I'm told that this code has proved somewhat useful, which I'm glad
> about, but I really can't support it. Sorry about that. So, I'm neither
> acking nor nacking; don't wait for my feedback.
>
> (Apologies for the wall of text, but I guess it's better to say "sorry
> can't help you" in too many words than not reacting at all.)

All right, no problem.

>
> Thanks
> László (since we're using Hungarian accents in this thread :))
>

Thanks
Zoltán :)

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

* Re: [Qemu-devel] [PATCH v3 3/8] qapi: change Netdev and NetLegacy into a flat union
  2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 3/8] qapi: change Netdev and NetLegacy " Kővágó, Zoltán
@ 2015-06-19 14:06   ` Stefan Hajnoczi
  2015-06-19 14:56     ` Kővágó Zoltán
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Hajnoczi @ 2015-06-19 14:06 UTC (permalink / raw)
  To: Kővágó, Zoltán
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang, qemu-devel,
	Vincenzo Maffione, Alexander Graf, Max Filippov, Gerd Hoffmann,
	Dmitry Fleytman, Edgar E. Iglesias, Rob Herring,
	Markus Armbruster, Scott Feldman, Jiri Pirko, Jan Kiszka,
	Giuseppe Lettieri, Luiz Capitulino, Luigi Rizzo, David Gibson,
	Peter Crosthwaite, Michael Walle, open list:sPAPR (pseries)

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

On Thu, Jun 18, 2015 at 06:43:45PM +0200, Kővágó, Zoltán wrote:
> @@ -713,8 +710,6 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
>      const char *vhostfdname;
>      char ifname[128];
>  
> -    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
> -    tap = opts->tap;
...
> @@ -109,14 +109,11 @@ static int net_vde_init(NetClientState *peer, const char *model,
>      return 0;
>  }
>  
> -int net_init_vde(const NetClientOptions *opts, const char *name,
> +int net_init_vde(const void *opts, const char *name,
>                   NetClientState *peer, Error **errp)
>  {
>      /* FIXME error_setg(errp, ...) on failure */
> -    const NetdevVdeOptions *vde;
> -
> -    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
> -    vde = opts->vde;
> +    const NetdevVdeOptions *vde = opts;
>  
>      /* missing optional values have been initialized to "all bits zero" */
>      if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
...
> @@ -228,16 +228,13 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
>      return 0;
>  }
>  
> -int net_init_vhost_user(const NetClientOptions *opts, const char *name,
> +int net_init_vhost_user(const void *opts, const char *name,
>                          NetClientState *peer, Error **errp)
>  {
>      uint32_t queues;
> -    const NetdevVhostUserOptions *vhost_user_opts;
> +    const NetdevVhostUserOptions *vhost_user_opts = opts;
>      CharDriverState *chr;
>  
> -    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
> -    vhost_user_opts = opts->vhost_user;
> -

Why drop the assertion?

[-- Attachment #2: Type: application/pgp-signature, Size: 473 bytes --]

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

* Re: [Qemu-devel] [PATCH v3 3/8] qapi: change Netdev and NetLegacy into a flat union
  2015-06-19 14:06   ` Stefan Hajnoczi
@ 2015-06-19 14:56     ` Kővágó Zoltán
  0 siblings, 0 replies; 13+ messages in thread
From: Kővágó Zoltán @ 2015-06-19 14:56 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang, qemu-devel,
	Vincenzo Maffione, Alexander Graf, Max Filippov, Gerd Hoffmann,
	Dmitry Fleytman, Edgar E. Iglesias, Rob Herring,
	Markus Armbruster, Scott Feldman, Jiri Pirko, Jan Kiszka,
	Giuseppe Lettieri, Luiz Capitulino, Luigi Rizzo, David Gibson,
	Peter Crosthwaite, Michael Walle, open list:sPAPR (pseries)

2015-06-19 16:06 keltezéssel, Stefan Hajnoczi írta:
> On Thu, Jun 18, 2015 at 06:43:45PM +0200, Kővágó, Zoltán wrote:
>> @@ -713,8 +710,6 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
>>       const char *vhostfdname;
>>       char ifname[128];
>>
>> -    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
>> -    tap = opts->tap;
> ...
>> @@ -109,14 +109,11 @@ static int net_vde_init(NetClientState *peer, const char *model,
>>       return 0;
>>   }
>>
>> -int net_init_vde(const NetClientOptions *opts, const char *name,
>> +int net_init_vde(const void *opts, const char *name,
>>                    NetClientState *peer, Error **errp)
>>   {
>>       /* FIXME error_setg(errp, ...) on failure */
>> -    const NetdevVdeOptions *vde;
>> -
>> -    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
>> -    vde = opts->vde;
>> +    const NetdevVdeOptions *vde = opts;
>>
>>       /* missing optional values have been initialized to "all bits zero" */
>>       if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
> ...
>> @@ -228,16 +228,13 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
>>       return 0;
>>   }
>>
>> -int net_init_vhost_user(const NetClientOptions *opts, const char *name,
>> +int net_init_vhost_user(const void *opts, const char *name,
>>                           NetClientState *peer, Error **errp)
>>   {
>>       uint32_t queues;
>> -    const NetdevVhostUserOptions *vhost_user_opts;
>> +    const NetdevVhostUserOptions *vhost_user_opts = opts;
>>       CharDriverState *chr;
>>
>> -    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
>> -    vhost_user_opts = opts->vhost_user;
>> -
>
> Why drop the assertion?
>
Because otherwise you would have to make a version that gets a Netdev 
and an other that gets Netlegacy, because the common NetClientOptions is 
gone. Unless, of course, I'm overlooking something.

(I've actually tried to simply pass the corect type as arguments, like 
net_init_chost_user(const NetdevVhostUserOptions *opts, ...; but then 
net_client_init_fun in net.c becomes problematic. Maybe replacing it 
with a giant swicth-case would be better?)

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

end of thread, other threads:[~2015-06-19 14:57 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-18 16:43 [Qemu-devel] [PATCH v3 0/8] -audiodev option Kővágó, Zoltán
2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 1/8] qapi: support implicit structs in OptsVisitor Kővágó, Zoltán
2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 2/8] qapi: convert NumaOptions into a flat union Kővágó, Zoltán
2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 3/8] qapi: change Netdev and NetLegacy " Kővágó, Zoltán
2015-06-19 14:06   ` Stefan Hajnoczi
2015-06-19 14:56     ` Kővágó Zoltán
2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 4/8] qapi: qapi for audio backends Kővágó, Zoltán
2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 5/8] qapi: support nested structs in OptsVisitor Kővágó, Zoltán
2015-06-18 17:15   ` Laszlo Ersek
2015-06-18 17:35     ` Kővágó Zoltán
2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 6/8] opts: produce valid command line in qemu_opts_print Kővágó, Zoltán
2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 7/8] audio: use qapi AudioFormat instead of audfmt_e Kővágó, Zoltán
2015-06-18 16:43 ` [Qemu-devel] [PATCH v3 8/8] audio: -audiodev command line option Kővágó, Zoltán

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.