[PATCH v3 0/6] bhyve: implement sound device support

Changes since v2: - Add 'ich7' sound device model, - Convert audio id from string to integer, - Use 'union' to store audio backend specific configuration, - Document changes in formatdomain, - Don't use hash for sound<->audio mapping lookups. Roman Bogorodskiy (6): conf: add 'ich7' sound model bhyve: implement sound device support conf: allow to map sound device to host device bhyve: allow to specify host sound device tests: schema: test bhyvexml2xmloutdata schemas docs: formatdomain: document <audio> element docs/formatdomain.rst | 49 +++++ docs/schemas/domaincommon.rng | 37 ++++ src/bhyve/bhyve_capabilities.c | 14 ++ src/bhyve/bhyve_capabilities.h | 1 + src/bhyve/bhyve_command.c | 53 +++++ src/bhyve/bhyve_device.c | 9 + src/conf/domain_capabilities.c | 4 + src/conf/domain_conf.c | 190 +++++++++++++++++- src/conf/domain_conf.h | 33 +++ src/conf/virconftypes.h | 3 + src/libvirt_private.syms | 3 + src/qemu/qemu_command.c | 2 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain_address.c | 3 + src/qemu/qemu_driver.c | 5 + src/qemu/qemu_hotplug.c | 3 + src/qemu/qemu_validate.c | 2 + .../bhyvexml2argv-sound.args | 10 + .../bhyvexml2argv-sound.ldargs | 3 + .../bhyvexml2argvdata/bhyvexml2argv-sound.xml | 30 +++ tests/bhyvexml2argvtest.c | 6 +- .../bhyvexml2xmlout-sound.xml | 41 ++++ tests/bhyvexml2xmltest.c | 1 + tests/virschematest.c | 3 +- 24 files changed, 502 insertions(+), 4 deletions(-) create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml -- 2.27.0

Add 'ich7' sound model. This is a preparation for sound support in bhyve, as 'ich7' is the only model it supports. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/schemas/domaincommon.rng | 1 + src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 1 + src/qemu/qemu_command.c | 1 + src/qemu/qemu_domain_address.c | 1 + src/qemu/qemu_validate.c | 1 + 6 files changed, 6 insertions(+) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 0d0dcbc5ce..fb9638f3f6 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4360,6 +4360,7 @@ <value>pcspk</value> <value>ac97</value> <value>ich6</value> + <value>ich7</value> <value>ich9</value> <value>usb</value> </choice> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ef67efa1da..f9cdc4efa9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -726,6 +726,7 @@ VIR_ENUM_IMPL(virDomainSoundModel, "ich6", "ich9", "usb", + "ich7", ); VIR_ENUM_IMPL(virDomainKeyWrapCipherName, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 011bf66cb4..411b9b99e4 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1401,6 +1401,7 @@ typedef enum { VIR_DOMAIN_SOUND_MODEL_ICH6, VIR_DOMAIN_SOUND_MODEL_ICH9, VIR_DOMAIN_SOUND_MODEL_USB, + VIR_DOMAIN_SOUND_MODEL_ICH7, VIR_DOMAIN_SOUND_MODEL_LAST } virDomainSoundModel; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 01812cd39b..ec3d4c8d99 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4026,6 +4026,7 @@ qemuBuildSoundDevStr(const virDomainDef *def, model = "sb16"; break; case VIR_DOMAIN_SOUND_MODEL_PCSPK: /* pc-speaker is handled separately */ + case VIR_DOMAIN_SOUND_MODEL_ICH7: case VIR_DOMAIN_SOUND_MODEL_LAST: return NULL; } diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 058cbda2a2..d25fb653d3 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -759,6 +759,7 @@ qemuDomainDeviceCalculatePCIConnectFlags(virDomainDeviceDefPtr dev, case VIR_DOMAIN_SOUND_MODEL_SB16: case VIR_DOMAIN_SOUND_MODEL_PCSPK: case VIR_DOMAIN_SOUND_MODEL_USB: + case VIR_DOMAIN_SOUND_MODEL_ICH7: case VIR_DOMAIN_SOUND_MODEL_LAST: return 0; } diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 488f258d00..4cd377c8bc 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -3588,6 +3588,7 @@ qemuValidateDomainDeviceDefSound(virDomainSoundDefPtr sound, case VIR_DOMAIN_SOUND_MODEL_SB16: case VIR_DOMAIN_SOUND_MODEL_PCSPK: break; + case VIR_DOMAIN_SOUND_MODEL_ICH7: case VIR_DOMAIN_SOUND_MODEL_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("sound card model '%s' is not supported by qemu"), -- 2.27.0

On Fri, Aug 07, 2020 at 07:09:30PM +0400, Roman Bogorodskiy wrote:
Add 'ich7' sound model. This is a preparation for sound support in bhyve, as 'ich7' is the only model it supports.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/schemas/domaincommon.rng | 1 + src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 1 + src/qemu/qemu_command.c | 1 + src/qemu/qemu_domain_address.c | 1 + src/qemu/qemu_validate.c | 1 + 6 files changed, 6 insertions(+)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

bhyve supports intel hda sound devices that could be specified on the command like using "-1:0,hda,play=$play_dev,rec=$rec_dev", where "1:0" is a PCI address, and "$play_dev" and "$rec_dev" point to the playback and recording device on the host respectively. Currently, schema of the 'sound' element doesn't allow specifying neither playback nor recording devices, so for now hardcode /dev/dsp0, which is the first audio device on the host. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_capabilities.c | 14 ++++++++ src/bhyve/bhyve_capabilities.h | 1 + src/bhyve/bhyve_command.c | 33 +++++++++++++++++ src/bhyve/bhyve_device.c | 9 +++++ .../bhyvexml2argv-sound.args | 10 ++++++ .../bhyvexml2argv-sound.ldargs | 3 ++ .../bhyvexml2argvdata/bhyvexml2argv-sound.xml | 24 +++++++++++++ tests/bhyvexml2argvtest.c | 6 +++- .../bhyvexml2xmlout-sound.xml | 36 +++++++++++++++++++ tests/bhyvexml2xmltest.c | 1 + 10 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml diff --git a/src/bhyve/bhyve_capabilities.c b/src/bhyve/bhyve_capabilities.c index fb8829d571..36f3985335 100644 --- a/src/bhyve/bhyve_capabilities.c +++ b/src/bhyve/bhyve_capabilities.c @@ -323,6 +323,17 @@ bhyveProbeCapsXHCIController(unsigned int *caps, char *binary) } +static int +bhyveProbeCapsSoundHda(unsigned int *caps, char *binary) +{ + return bhyveProbeCapsDeviceHelper(caps, binary, + "-s", + "0,hda", + "pci slot 0:0: unknown device \"hda\"", + BHYVE_CAP_SOUND_HDA); +} + + int virBhyveProbeCaps(unsigned int *caps) { @@ -351,6 +362,9 @@ virBhyveProbeCaps(unsigned int *caps) if ((ret = bhyveProbeCapsXHCIController(caps, binary))) goto out; + if ((ret = bhyveProbeCapsSoundHda(caps, binary))) + goto out; + out: VIR_FREE(binary); return ret; diff --git a/src/bhyve/bhyve_capabilities.h b/src/bhyve/bhyve_capabilities.h index 12926cf423..1ac9ff4283 100644 --- a/src/bhyve/bhyve_capabilities.h +++ b/src/bhyve/bhyve_capabilities.h @@ -49,6 +49,7 @@ typedef enum { BHYVE_CAP_FBUF = 1 << 4, BHYVE_CAP_XHCI = 1 << 5, BHYVE_CAP_CPUTOPOLOGY = 1 << 6, + BHYVE_CAP_SOUND_HDA = 1 << 7, } virBhyveCapsFlags; int virBhyveProbeGrubCaps(virBhyveGrubCapsFlags *caps); diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 86e6640359..1f42f71347 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -475,6 +475,34 @@ bhyveBuildGraphicsArgStr(const virDomainDef *def, } +static int +bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, + virDomainSoundDefPtr sound, + bhyveConnPtr driver, + virCommandPtr cmd) +{ + if (!(bhyveDriverGetBhyveCaps(driver) & BHYVE_CAP_SOUND_HDA)) { + /* Currently, bhyve only supports "hda" sound devices, so + if it's not supported, sound devices are not supported at all */ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Sound devices emulation is not supported " + "by given bhyve binary")); + return -1; + } + + if (sound->model != VIR_DOMAIN_SOUND_MODEL_ICH7) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Sound device model is not supported")); + return -1; + } + + virCommandAddArg(cmd, "-s"); + virCommandAddArgFormat(cmd, "%d:%d,hda,play=/dev/dsp0", + sound->info.addr.pci.slot, + sound->info.addr.pci.function); + return 0; +} + virCommandPtr virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, bool dryRun) @@ -619,6 +647,11 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, } } + for (i = 0; i < def->nsounds; i++) { + if (bhyveBuildSoundArgStr(def, def->sounds[i], driver, cmd) < 0) + goto error; + } + if (bhyveDomainDefNeedsISAController(def)) bhyveBuildLPCArgStr(def, cmd); diff --git a/src/bhyve/bhyve_device.c b/src/bhyve/bhyve_device.c index fc52280361..3c8ff587e0 100644 --- a/src/bhyve/bhyve_device.c +++ b/src/bhyve/bhyve_device.c @@ -154,6 +154,15 @@ bhyveAssignDevicePCISlots(virDomainDefPtr def, return -1; } + for (i = 0; i < def->nsounds; i++) { + if (!virDeviceInfoPCIAddressIsWanted(&def->sounds[i]->info)) + continue; + if (virDomainPCIAddressReserveNextAddr(addrs, + &def->sounds[i]->info, + VIR_PCI_CONNECT_TYPE_PCI_DEVICE, + -1) < 0) + return -1; + } return 0; } diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-sound.args b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.args new file mode 100644 index 0000000000..c242708ff1 --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.args @@ -0,0 +1,10 @@ +/usr/sbin/bhyve \ +-c 1 \ +-m 214 \ +-u \ +-H \ +-P \ +-s 0:0,hostbridge \ +-s 2:0,ahci,hd:/tmp/freebsd.img \ +-s 3:0,virtio-net,faketapdev,mac=52:54:00:b9:94:02 \ +-s 4:0,hda,play=/dev/dsp0 bhyve diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-sound.ldargs b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.ldargs new file mode 100644 index 0000000000..32538b558e --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.ldargs @@ -0,0 +1,3 @@ +/usr/sbin/bhyveload \ +-m 214 \ +-d /tmp/freebsd.img bhyve diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml new file mode 100644 index 0000000000..8e799301fb --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml @@ -0,0 +1,24 @@ +<domain type='bhyve'> + <name>bhyve</name> + <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid> + <memory>219136</memory> + <vcpu>1</vcpu> + <os> + <type>hvm</type> + </os> + <devices> + <disk type='file'> + <driver name='file' type='raw'/> + <source file='/tmp/freebsd.img'/> + <target dev='hda' bus='sata'/> + <address type='drive' controller='0' bus='0' target='2' unit='0'/> + </disk> + <interface type='bridge'> + <mac address='52:54:00:b9:94:02'/> + <model type='virtio'/> + <source bridge="virbr0"/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + <sound model='ich7'/> + </devices> +</domain> diff --git a/tests/bhyvexml2argvtest.c b/tests/bhyvexml2argvtest.c index 7ce4dc6e58..0a05d6d892 100644 --- a/tests/bhyvexml2argvtest.c +++ b/tests/bhyvexml2argvtest.c @@ -166,7 +166,7 @@ mymain(void) driver.bhyvecaps = BHYVE_CAP_RTC_UTC | BHYVE_CAP_AHCI32SLOT | \ BHYVE_CAP_NET_E1000 | BHYVE_CAP_LPC_BOOTROM | \ BHYVE_CAP_FBUF | BHYVE_CAP_XHCI | \ - BHYVE_CAP_CPUTOPOLOGY; + BHYVE_CAP_CPUTOPOLOGY | BHYVE_CAP_SOUND_HDA; DO_TEST("base"); DO_TEST("wired"); @@ -201,6 +201,7 @@ mymain(void) DO_TEST_FAILURE("cputopology-nvcpu-mismatch"); DO_TEST("commandline"); DO_TEST("msrs"); + DO_TEST("sound"); /* Address allocation tests */ DO_TEST("addr-single-sata-disk"); @@ -240,6 +241,9 @@ mymain(void) driver.bhyvecaps &= ~BHYVE_CAP_CPUTOPOLOGY; DO_TEST_FAILURE("cputopology"); + driver.bhyvecaps &= ~BHYVE_CAP_SOUND_HDA; + DO_TEST_FAILURE("sound"); + virObjectUnref(driver.caps); virObjectUnref(driver.xmlopt); virPortAllocatorRangeFree(driver.remotePorts); diff --git a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml new file mode 100644 index 0000000000..a64c5da27a --- /dev/null +++ b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml @@ -0,0 +1,36 @@ +<domain type='bhyve'> + <name>bhyve</name> + <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <disk type='file' device='disk'> + <driver name='file' type='raw'/> + <source file='/tmp/freebsd.img'/> + <target dev='hda' bus='sata'/> + <address type='drive' controller='0' bus='0' target='2' unit='0'/> + </disk> + <controller type='pci' index='0' model='pci-root'/> + <controller type='sata' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </controller> + <interface type='bridge'> + <mac address='52:54:00:b9:94:02'/> + <source bridge='virbr0'/> + <model type='virtio'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + <sound model='ich7'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </sound> + </devices> +</domain> diff --git a/tests/bhyvexml2xmltest.c b/tests/bhyvexml2xmltest.c index 2ac102b8e3..f9af1a364d 100644 --- a/tests/bhyvexml2xmltest.c +++ b/tests/bhyvexml2xmltest.c @@ -108,6 +108,7 @@ mymain(void) DO_TEST_DIFFERENT("vnc-autoport"); DO_TEST_DIFFERENT("commandline"); DO_TEST_DIFFERENT("msrs"); + DO_TEST_DIFFERENT("sound"); /* Address allocation tests */ DO_TEST_DIFFERENT("addr-single-sata-disk"); -- 2.27.0

On Fri, Aug 07, 2020 at 07:09:31PM +0400, Roman Bogorodskiy wrote:
bhyve supports intel hda sound devices that could be specified on the command like using "-1:0,hda,play=$play_dev,rec=$rec_dev", where "1:0" is a PCI address, and "$play_dev" and "$rec_dev" point to the playback and recording device on the host respectively. Currently, schema of the 'sound' element doesn't allow specifying neither playback nor recording devices, so for now hardcode /dev/dsp0, which is the first audio device on the host.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_capabilities.c | 14 ++++++++ src/bhyve/bhyve_capabilities.h | 1 + src/bhyve/bhyve_command.c | 33 +++++++++++++++++ src/bhyve/bhyve_device.c | 9 +++++ .../bhyvexml2argv-sound.args | 10 ++++++ .../bhyvexml2argv-sound.ldargs | 3 ++ .../bhyvexml2argvdata/bhyvexml2argv-sound.xml | 24 +++++++++++++ tests/bhyvexml2argvtest.c | 6 +++- .../bhyvexml2xmlout-sound.xml | 36 +++++++++++++++++++ tests/bhyvexml2xmltest.c | 1 + 10 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Introduce a new device element "<audio>" which allows to map guest sound device specified using the "<sound>" element to specific audio backend. Example: <sound model='ich7'> <audio id='1'/> </sound> <audio id='1' type='oss'> <input dev='/dev/dsp0'/> <output dev='/dev/dsp0'/> </audio> This block maps to OSS audio backend on the host using /dev/dsp0 device for both input (recording) and output (playback). OSS is the only backend supported so far. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/schemas/domaincommon.rng | 36 +++++++ src/conf/domain_capabilities.c | 4 + src/conf/domain_conf.c | 176 ++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 28 ++++++ src/conf/virconftypes.h | 3 + src/libvirt_private.syms | 2 + src/qemu/qemu_command.c | 1 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain_address.c | 2 + src/qemu/qemu_driver.c | 5 + src/qemu/qemu_hotplug.c | 3 + src/qemu/qemu_validate.c | 1 + 12 files changed, 260 insertions(+), 2 deletions(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index fb9638f3f6..c933c71035 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4372,12 +4372,47 @@ <optional> <ref name="address"/> </optional> + <optional> + <element name="audio"> + <attribute name="id"> + <ref name="uint8"/> + </attribute> + </element> + </optional> <zeroOrMore> <ref name="codec"/> </zeroOrMore> </interleave> </element> </define> + <define name="audio"> + <element name="audio"> + <attribute name="id"> + <ref name="uint8"/> + </attribute> + <attribute name="type"> + <choice> + <value>oss</value> + </choice> + </attribute> + <interleave> + <optional> + <element name="input"> + <attribute name="dev"> + <ref name="deviceName"/> + </attribute> + </element> + </optional> + <optional> + <element name="output"> + <attribute name="dev"> + <ref name="deviceName"/> + </attribute> + </element> + </optional> + </interleave> + </element> + </define> <define name="watchdog"> <element name="watchdog"> <attribute name="model"> @@ -5303,6 +5338,7 @@ <ref name="interface"/> <ref name="input"/> <ref name="sound"/> + <ref name="audio"/> <ref name="hostdev"/> <ref name="graphic"/> <ref name="video"/> diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c index d61108e125..59ad8937a4 100644 --- a/src/conf/domain_capabilities.c +++ b/src/conf/domain_capabilities.c @@ -688,6 +688,10 @@ virDomainCapsDeviceDefValidate(const virDomainCaps *caps, ret = virDomainCapsDeviceVideoDefValidate(caps, dev->data.video); break; + case VIR_DOMAIN_DEVICE_AUDIO: + /* TODO: add validation */ + break; + case VIR_DOMAIN_DEVICE_DISK: case VIR_DOMAIN_DEVICE_REDIRDEV: case VIR_DOMAIN_DEVICE_NET: diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f9cdc4efa9..165d5d5f7a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -323,6 +323,7 @@ VIR_ENUM_IMPL(virDomainDevice, "memory", "iommu", "vsock", + "audio", ); VIR_ENUM_IMPL(virDomainDiskDevice, @@ -729,6 +730,11 @@ VIR_ENUM_IMPL(virDomainSoundModel, "ich7", ); +VIR_ENUM_IMPL(virDomainAudioType, + VIR_DOMAIN_AUDIO_TYPE_LAST, + "oss", +); + VIR_ENUM_IMPL(virDomainKeyWrapCipherName, VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_LAST, "aes", @@ -2872,6 +2878,24 @@ void virDomainSoundDefFree(virDomainSoundDefPtr def) VIR_FREE(def); } +void virDomainAudioDefFree(virDomainAudioDefPtr def) +{ + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_AUDIO_TYPE_OSS: + VIR_FREE(def->backend.oss.inputDev); + VIR_FREE(def->backend.oss.outputDev); + break; + + case VIR_DOMAIN_AUDIO_TYPE_LAST: + break; + } + + VIR_FREE(def); +} + virDomainSoundDefPtr virDomainSoundDefRemove(virDomainDefPtr def, size_t idx) { @@ -3225,6 +3249,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) case VIR_DOMAIN_DEVICE_VSOCK: virDomainVsockDefFree(def->data.vsock); break; + case VIR_DOMAIN_DEVICE_AUDIO: + virDomainAudioDefFree(def->data.audio); + break; case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -3485,6 +3512,10 @@ void virDomainDefFree(virDomainDefPtr def) virDomainSoundDefFree(def->sounds[i]); VIR_FREE(def->sounds); + for (i = 0; i < def->naudios; i++) + virDomainAudioDefFree(def->audios[i]); + VIR_FREE(def->audios); + for (i = 0; i < def->nvideos; i++) virDomainVideoDefFree(def->videos[i]); VIR_FREE(def->videos); @@ -4073,6 +4104,7 @@ virDomainDeviceGetInfo(virDomainDeviceDefPtr device) case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_GRAPHICS: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -4167,6 +4199,9 @@ virDomainDeviceSetData(virDomainDeviceDefPtr device, case VIR_DOMAIN_DEVICE_LEASE: device->data.lease = devicedata; break; + case VIR_DOMAIN_DEVICE_AUDIO: + device->data.audio = devicedata; + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -4433,6 +4468,7 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def, case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_VSOCK: + case VIR_DOMAIN_DEVICE_AUDIO: break; } #endif @@ -5425,6 +5461,7 @@ virDomainDeviceDefPostParseCommon(virDomainDeviceDefPtr dev, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: ret = 0; break; @@ -6814,6 +6851,8 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_SHMEM: return virDomainShmemDefValidate(dev->data.shmem); + case VIR_DOMAIN_DEVICE_AUDIO: + /* TODO: validate? */ case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: @@ -15016,10 +15055,10 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, virDomainSoundDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *model = NULL; + xmlNodePtr audioNode; if (VIR_ALLOC(def) < 0) return NULL; - ctxt->node = node; model = virXMLPropString(node, "model"); @@ -15056,6 +15095,18 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, } } + audioNode = virXPathNode("./audio", ctxt); + if (audioNode) { + g_autofree char *tmp = NULL; + tmp = virXMLPropString(audioNode, "id"); + if (virStrToLong_ui(tmp, NULL, 10, &def->audioId) < 0 || + def->audioId == 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid audio 'id' value '%s'"), tmp); + goto error; + } + } + if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error; @@ -15107,6 +15158,58 @@ virDomainSoundDefFind(const virDomainDef *def, } +static virDomainAudioDefPtr +virDomainAudioDefParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED, + xmlNodePtr node G_GNUC_UNUSED, + xmlXPathContextPtr ctxt G_GNUC_UNUSED) +{ + virDomainAudioDefPtr def; + VIR_XPATH_NODE_AUTORESTORE(ctxt); + g_autofree char *tmp = NULL; + g_autofree char *type = NULL; + + if (VIR_ALLOC(def) < 0) + return NULL; + ctxt->node = node; + + type = virXMLPropString(node, "type"); + if ((def->type = virDomainAudioTypeTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown audio type '%s'"), type); + goto error; + } + + tmp = virXMLPropString(node, "id"); + if (virStrToLong_ui(tmp, NULL, 10, &def->id) < 0 || + def->id == 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid audio 'id' value '%s'"), tmp); + goto error; + } + + if (def->type == VIR_DOMAIN_AUDIO_TYPE_OSS) { + xmlNodePtr inputDevNode, outputDevNode; + + inputDevNode = virXPathNode("./input", ctxt); + outputDevNode = virXPathNode("./output", ctxt); + + if (!inputDevNode || !outputDevNode) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Audio type OSS requires to have <input> " + "and <output> specified")); + goto error; + } + + def->backend.oss.inputDev = virXMLPropString(inputDevNode, "dev"); + def->backend.oss.outputDev = virXMLPropString(outputDevNode, "dev"); + } + + return def; + + error: + virDomainAudioDefFree(def); + return NULL; +} static virDomainWatchdogDefPtr virDomainWatchdogDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, @@ -17063,6 +17166,10 @@ virDomainDeviceDefParse(const char *xmlStr, ctxt, flags))) return NULL; break; + case VIR_DOMAIN_DEVICE_AUDIO: + if (!(dev->data.audio = virDomainAudioDefParseXML(xmlopt, node, ctxt))) + return NULL; + break; case VIR_DOMAIN_DEVICE_WATCHDOG: if (!(dev->data.watchdog = virDomainWatchdogDefParseXML(xmlopt, node, flags))) @@ -21971,6 +22078,22 @@ virDomainDefParseXML(xmlDocPtr xml, } VIR_FREE(nodes); + /* analysis of the audio devices */ + if ((n = virXPathNodeSet("./devices/audio", ctxt, &nodes)) < 0) + goto error; + if (n && VIR_ALLOC_N(def->audios, n) < 0) + goto error; + for (i = 0; i < n; i++) { + virDomainAudioDefPtr audio = virDomainAudioDefParseXML(xmlopt, + nodes[i], + ctxt); + if (!audio) + goto error; + + def->audios[def->naudios++] = audio; + } + VIR_FREE(nodes); + /* analysis of the video devices */ if ((n = virXPathNodeSet("./devices/video", ctxt, &nodes)) < 0) goto error; @@ -22485,7 +22608,6 @@ virDomainDefParse(const char *xmlStr, virDomainDefPtr def = NULL; int keepBlanksDefault = xmlKeepBlanksDefault(0); xmlNodePtr root; - if (!(xml = virXMLParse(filename, xmlStr, _("(domain_definition)")))) goto cleanup; @@ -24586,6 +24708,7 @@ virDomainDefCheckABIStabilityFlags(virDomainDefPtr src, case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_VSOCK: + case VIR_DOMAIN_DEVICE_AUDIO: break; } #endif @@ -27352,6 +27475,9 @@ virDomainSoundDefFormat(virBufferPtr buf, for (i = 0; i < def->ncodecs; i++) virDomainSoundCodecDefFormat(&childBuf, def->codecs[i]); + if (def->audioId > 0) + virBufferAsprintf(&childBuf, "<audio id='%d'/>\n", def->audioId); + if (virDomainDeviceInfoFormat(&childBuf, &def->info, flags) < 0) return -1; @@ -27368,6 +27494,44 @@ virDomainSoundDefFormat(virBufferPtr buf, } +static int +virDomainAudioDefFormat(virBufferPtr buf, + virDomainAudioDefPtr def) +{ + g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); + const char *type = virDomainAudioTypeTypeToString(def->type); + + if (!type) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected audio type %d"), def->type); + return -1; + } + + virBufferAsprintf(buf, "<audio id='%d' type='%s'", def->id, type); + + switch (def->type) { + case VIR_DOMAIN_AUDIO_TYPE_OSS: + if (def->backend.oss.inputDev) + virBufferAsprintf(&childBuf, "<input dev='%s'/>\n", + def->backend.oss.inputDev); + if (def->backend.oss.outputDev) + virBufferAsprintf(&childBuf, "<output dev='%s'/>\n", + def->backend.oss.outputDev); + break; + } + + if (virBufferUse(&childBuf)) { + virBufferAddLit(buf, ">\n"); + virBufferAddBuffer(buf, &childBuf); + virBufferAddLit(buf, "</audio>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } + + return 0; +} + + static int virDomainMemballoonDefFormat(virBufferPtr buf, virDomainMemballoonDefPtr def, @@ -30047,6 +30211,11 @@ virDomainDefFormatInternalSetRootName(virDomainDefPtr def, return -1; } + for (n = 0; n < def->naudios; n++) { + if (virDomainAudioDefFormat(buf, def->audios[n]) < 0) + return -1; + } + for (n = 0; n < def->nvideos; n++) { if (virDomainVideoDefFormat(buf, def->videos[n], flags) < 0) return -1; @@ -31219,6 +31388,9 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src, case VIR_DOMAIN_DEVICE_VSOCK: rc = virDomainVsockDefFormat(&buf, src->data.vsock); break; + case VIR_DOMAIN_DEVICE_AUDIO: + rc = virDomainAudioDefFormat(&buf, src->data.audio); + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_SMARTCARD: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 411b9b99e4..fdf706a7e0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -85,6 +85,7 @@ typedef enum { VIR_DOMAIN_DEVICE_MEMORY, VIR_DOMAIN_DEVICE_IOMMU, VIR_DOMAIN_DEVICE_VSOCK, + VIR_DOMAIN_DEVICE_AUDIO, VIR_DOMAIN_DEVICE_LAST } virDomainDeviceType; @@ -116,6 +117,7 @@ struct _virDomainDeviceDef { virDomainMemoryDefPtr memory; virDomainIOMMUDefPtr iommu; virDomainVsockDefPtr vsock; + virDomainAudioDefPtr audio; } data; }; @@ -1417,6 +1419,27 @@ struct _virDomainSoundDef { size_t ncodecs; virDomainSoundCodecDefPtr *codecs; + + unsigned int audioId; +}; + +typedef enum { + VIR_DOMAIN_AUDIO_TYPE_OSS, + + VIR_DOMAIN_AUDIO_TYPE_LAST +} virDomainAudioType; + +struct _virDomainAudioDef { + int type; + + unsigned int id; + + union { + struct { + char *inputDev; + char *outputDev; + } oss; + } backend; }; typedef enum { @@ -2602,6 +2625,9 @@ struct _virDomainDef { size_t nsounds; virDomainSoundDefPtr *sounds; + size_t naudios; + virDomainAudioDefPtr *audios; + size_t nvideos; virDomainVideoDefPtr *videos; @@ -3032,6 +3058,7 @@ ssize_t virDomainSoundDefFind(const virDomainDef *def, const virDomainSoundDef *sound); void virDomainSoundDefFree(virDomainSoundDefPtr def); virDomainSoundDefPtr virDomainSoundDefRemove(virDomainDefPtr def, size_t idx); +void virDomainAudioDefFree(virDomainAudioDefPtr def); void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def); void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def); void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def); @@ -3591,6 +3618,7 @@ VIR_ENUM_DECL(virDomainChrTcpProtocol); VIR_ENUM_DECL(virDomainChrSpicevmc); VIR_ENUM_DECL(virDomainSoundCodec); VIR_ENUM_DECL(virDomainSoundModel); +VIR_ENUM_DECL(virDomainAudioType); VIR_ENUM_DECL(virDomainKeyWrapCipherName); VIR_ENUM_DECL(virDomainMemballoonModel); VIR_ENUM_DECL(virDomainSmbiosMode); diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index 1c62cde251..9042a2b34f 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -309,6 +309,9 @@ typedef virDomainSoundCodecDef *virDomainSoundCodecDefPtr; typedef struct _virDomainSoundDef virDomainSoundDef; typedef virDomainSoundDef *virDomainSoundDefPtr; +typedef struct _virDomainAudioDef virDomainAudioDef; +typedef virDomainAudioDef *virDomainAudioDefPtr; + typedef struct _virDomainTPMDef virDomainTPMDef; typedef virDomainTPMDef *virDomainTPMDefPtr; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 01c2e710cd..35bf9f08ef 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -224,6 +224,8 @@ virDiskNameToBusDeviceIndex; virDiskNameToIndex; virDomainActualNetDefFree; virDomainActualNetDefValidate; +virDomainAudioTypeTypeFromString; +virDomainAudioTypeTypeToString; virDomainBlockedReasonTypeFromString; virDomainBlockedReasonTypeToString; virDomainBlockIoTuneInfoCopy; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ec3d4c8d99..40408fdd70 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -523,6 +523,7 @@ qemuBuildVirtioDevStr(virBufferPtr buf, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: default: return 0; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c440c79e1d..9c735e4776 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -5265,6 +5265,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, case VIR_DOMAIN_DEVICE_RNG: case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: ret = 0; break; diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index d25fb653d3..cbf8ffddd7 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -531,6 +531,7 @@ qemuDomainDeviceSupportZPCI(virDomainDeviceDefPtr device) case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_VSOCK: + case VIR_DOMAIN_DEVICE_AUDIO: break; case VIR_DOMAIN_DEVICE_NONE: @@ -1047,6 +1048,7 @@ qemuDomainDeviceCalculatePCIConnectFlags(virDomainDeviceDefPtr dev, case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_GRAPHICS: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: return 0; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0f98243fe4..43d6f6eb5b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7960,6 +7960,7 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("live attach of device '%s' is not supported"), @@ -8094,6 +8095,7 @@ qemuDomainUpdateDeviceLive(virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_VSOCK: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("live update of device '%s' is not supported"), @@ -8313,6 +8315,7 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent attach of device '%s' is not supported"), @@ -8515,6 +8518,7 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent detach of device '%s' is not supported"), @@ -8622,6 +8626,7 @@ qemuDomainUpdateDeviceConfig(virDomainDefPtr vmdef, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_VSOCK: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent update of device '%s' is not supported"), diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 2c6c30ce03..3a780d00af 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4944,6 +4944,7 @@ qemuDomainRemoveAuditDevice(virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: /* libvirt doesn't yet support detaching these devices */ break; @@ -5042,6 +5043,7 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("don't know how to remove a %s device"), @@ -5813,6 +5815,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("live detach of device '%s' is not supported"), diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 4cd377c8bc..fe8a64b933 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4145,6 +4145,7 @@ qemuValidateDomainDeviceDef(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_SHMEM: case VIR_DOMAIN_DEVICE_PANIC: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; -- 2.27.0

On Fri, Aug 07, 2020 at 07:09:32PM +0400, Roman Bogorodskiy wrote:
Introduce a new device element "<audio>" which allows to map guest sound device specified using the "<sound>" element to specific audio backend.
Example:
<sound model='ich7'> <audio id='1'/> </sound> <audio id='1' type='oss'> <input dev='/dev/dsp0'/> <output dev='/dev/dsp0'/> </audio>
This block maps to OSS audio backend on the host using /dev/dsp0 device for both input (recording) and output (playback).
OSS is the only backend supported so far.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/schemas/domaincommon.rng | 36 +++++++ src/conf/domain_capabilities.c | 4 + src/conf/domain_conf.c | 176 ++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 28 ++++++ src/conf/virconftypes.h | 3 + src/libvirt_private.syms | 2 + src/qemu/qemu_command.c | 1 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain_address.c | 2 + src/qemu/qemu_driver.c | 5 + src/qemu/qemu_hotplug.c | 3 + src/qemu/qemu_validate.c | 1 + 12 files changed, 260 insertions(+), 2 deletions(-)
+void virDomainAudioDefFree(virDomainAudioDefPtr def) +{ + if (!def) + return; + + switch (def->type) {
Cast to (virDomainAudioType), so the compiler forces the existance of all possible cases.
+ case VIR_DOMAIN_AUDIO_TYPE_OSS: + VIR_FREE(def->backend.oss.inputDev); + VIR_FREE(def->backend.oss.outputDev); + break; + + case VIR_DOMAIN_AUDIO_TYPE_LAST: + break; + } + + VIR_FREE(def); +} + virDomainSoundDefPtr virDomainSoundDefRemove(virDomainDefPtr def, size_t idx) { @@ -3225,6 +3249,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) case VIR_DOMAIN_DEVICE_VSOCK: virDomainVsockDefFree(def->data.vsock); break; + case VIR_DOMAIN_DEVICE_AUDIO: + virDomainAudioDefFree(def->data.audio); + break; case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -3485,6 +3512,10 @@ void virDomainDefFree(virDomainDefPtr def) virDomainSoundDefFree(def->sounds[i]); VIR_FREE(def->sounds);
+ for (i = 0; i < def->naudios; i++) + virDomainAudioDefFree(def->audios[i]); + VIR_FREE(def->audios); + for (i = 0; i < def->nvideos; i++) virDomainVideoDefFree(def->videos[i]); VIR_FREE(def->videos); @@ -4073,6 +4104,7 @@ virDomainDeviceGetInfo(virDomainDeviceDefPtr device) case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_GRAPHICS: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -4167,6 +4199,9 @@ virDomainDeviceSetData(virDomainDeviceDefPtr device, case VIR_DOMAIN_DEVICE_LEASE: device->data.lease = devicedata; break; + case VIR_DOMAIN_DEVICE_AUDIO: + device->data.audio = devicedata; + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -4433,6 +4468,7 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def, case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_VSOCK: + case VIR_DOMAIN_DEVICE_AUDIO: break; } #endif @@ -5425,6 +5461,7 @@ virDomainDeviceDefPostParseCommon(virDomainDeviceDefPtr dev, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_AUDIO: ret = 0; break;
@@ -6814,6 +6851,8 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_SHMEM: return virDomainShmemDefValidate(dev->data.shmem);
+ case VIR_DOMAIN_DEVICE_AUDIO: + /* TODO: validate? */ case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: @@ -15016,10 +15055,10 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, virDomainSoundDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *model = NULL; + xmlNodePtr audioNode;
if (VIR_ALLOC(def) < 0) return NULL; - ctxt->node = node;
model = virXMLPropString(node, "model"); @@ -15056,6 +15095,18 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, } }
+ audioNode = virXPathNode("./audio", ctxt); + if (audioNode) { + g_autofree char *tmp = NULL; + tmp = virXMLPropString(audioNode, "id"); + if (virStrToLong_ui(tmp, NULL, 10, &def->audioId) < 0 || + def->audioId == 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid audio 'id' value '%s'"), tmp); + goto error; + } + } + if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error;
@@ -15107,6 +15158,58 @@ virDomainSoundDefFind(const virDomainDef *def, }
+static virDomainAudioDefPtr +virDomainAudioDefParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED, + xmlNodePtr node G_GNUC_UNUSED, + xmlXPathContextPtr ctxt G_GNUC_UNUSED) +{ + virDomainAudioDefPtr def; + VIR_XPATH_NODE_AUTORESTORE(ctxt); + g_autofree char *tmp = NULL; + g_autofree char *type = NULL; + + if (VIR_ALLOC(def) < 0) + return NULL; + ctxt->node = node; + + type = virXMLPropString(node, "type"); + if ((def->type = virDomainAudioTypeTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown audio type '%s'"), type); + goto error; + } + + tmp = virXMLPropString(node, "id"); + if (virStrToLong_ui(tmp, NULL, 10, &def->id) < 0 || + def->id == 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid audio 'id' value '%s'"), tmp); + goto error; + } + + if (def->type == VIR_DOMAIN_AUDIO_TYPE_OSS) {
Use a switch here again to force compiler checking of all csaes.
+ xmlNodePtr inputDevNode, outputDevNode; + + inputDevNode = virXPathNode("./input", ctxt); + outputDevNode = virXPathNode("./output", ctxt); + + if (!inputDevNode || !outputDevNode) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Audio type OSS requires to have <input> " + "and <output> specified")); + goto error; + } + + def->backend.oss.inputDev = virXMLPropString(inputDevNode, "dev"); + def->backend.oss.outputDev = virXMLPropString(outputDevNode, "dev"); + }
Looks fine bar the switch() Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Allow to map sound playback and recording devices to host devices using "<audio type='oss'/>" OSS audio backend. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_command.c | 26 ++++++++++++++++--- src/conf/domain_conf.c | 13 ++++++++++ src/conf/domain_conf.h | 4 +++ src/libvirt_private.syms | 1 + .../bhyvexml2argv-sound.args | 2 +- .../bhyvexml2argvdata/bhyvexml2argv-sound.xml | 8 +++++- .../bhyvexml2xmlout-sound.xml | 5 ++++ 7 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 1f42f71347..8e7907b882 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -478,9 +478,12 @@ bhyveBuildGraphicsArgStr(const virDomainDef *def, static int bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, virDomainSoundDefPtr sound, + virDomainAudioDefPtr audio, bhyveConnPtr driver, virCommandPtr cmd) { + g_auto(virBuffer) params = VIR_BUFFER_INITIALIZER; + if (!(bhyveDriverGetBhyveCaps(driver) & BHYVE_CAP_SOUND_HDA)) { /* Currently, bhyve only supports "hda" sound devices, so if it's not supported, sound devices are not supported at all */ @@ -497,9 +500,24 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, } virCommandAddArg(cmd, "-s"); - virCommandAddArgFormat(cmd, "%d:%d,hda,play=/dev/dsp0", + + if (audio) { + if (audio->type == VIR_DOMAIN_AUDIO_TYPE_OSS) { + if (audio->backend.oss.inputDev) + virBufferAsprintf(¶ms, ",play=%s", + audio->backend.oss.inputDev); + + if (audio->backend.oss.outputDev) + virBufferAsprintf(¶ms, ",rec=%s", + audio->backend.oss.outputDev); + } + } + + virCommandAddArgFormat(cmd, "%d:%d,hda%s", sound->info.addr.pci.slot, - sound->info.addr.pci.function); + sound->info.addr.pci.function, + virBufferCurrentContent(¶ms)); + return 0; } @@ -648,7 +666,9 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, } for (i = 0; i < def->nsounds; i++) { - if (bhyveBuildSoundArgStr(def, def->sounds[i], driver, cmd) < 0) + if (bhyveBuildSoundArgStr(def, def->sounds[i], + virDomainDefFindAudioForSound(def, def->sounds[i]), + driver, cmd) < 0) goto error; } diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 165d5d5f7a..28ff4d3994 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -31499,6 +31499,19 @@ virDomainDefFindDevice(virDomainDefPtr def, } +virDomainAudioDefPtr +virDomainDefFindAudioForSound(virDomainDefPtr def, + virDomainSoundDefPtr sound) +{ + size_t i; + for (i = 0; i < def->naudios; i++) + if (def->audios[i]->id == sound->audioId) + return def->audios[i]; + + return NULL; +} + + char * virDomainObjGetMetadata(virDomainObjPtr vm, int type, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fdf706a7e0..d9d7b094ac 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3712,6 +3712,10 @@ int virDomainDefFindDevice(virDomainDefPtr def, virDomainDeviceDefPtr dev, bool reportError); +virDomainAudioDefPtr +virDomainDefFindAudioForSound(virDomainDefPtr def, + virDomainSoundDefPtr sound); + const char *virDomainChrSourceDefGetPath(virDomainChrSourceDefPtr chr); void virDomainChrSourceDefClear(virDomainChrSourceDefPtr def); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 35bf9f08ef..f950a68179 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -298,6 +298,7 @@ virDomainDefCheckABIStability; virDomainDefCheckABIStabilityFlags; virDomainDefCompatibleDevice; virDomainDefCopy; +virDomainDefFindAudioForSound; virDomainDefFindDevice; virDomainDefFormat; virDomainDefFormatConvertXMLFlags; diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-sound.args b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.args index c242708ff1..05ff4965dd 100644 --- a/tests/bhyvexml2argvdata/bhyvexml2argv-sound.args +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.args @@ -7,4 +7,4 @@ -s 0:0,hostbridge \ -s 2:0,ahci,hd:/tmp/freebsd.img \ -s 3:0,virtio-net,faketapdev,mac=52:54:00:b9:94:02 \ --s 4:0,hda,play=/dev/dsp0 bhyve +-s 4:0,hda,play=/dev/dsp0,rec=/dev/dsp0 bhyve diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml index 8e799301fb..831e8670e7 100644 --- a/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml @@ -19,6 +19,12 @@ <source bridge="virbr0"/> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> - <sound model='ich7'/> + <sound model='ich7'> + <audio id="1"/> + </sound> + <audio type="oss" id="1"> + <input dev="/dev/dsp0"/> + <output dev="/dev/dsp0"/> + </audio> </devices> </domain> diff --git a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml index a64c5da27a..c23892eec9 100644 --- a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml +++ b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml @@ -30,7 +30,12 @@ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> <sound model='ich7'> + <audio id='1'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </sound> + <audio id='1' type='oss'> + <input dev='/dev/dsp0'/> + <output dev='/dev/dsp0'/> + </audio> </devices> </domain> -- 2.27.0

On Fri, Aug 07, 2020 at 07:09:33PM +0400, Roman Bogorodskiy wrote:
Allow to map sound playback and recording devices to host devices using "<audio type='oss'/>" OSS audio backend.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_command.c | 26 ++++++++++++++++--- src/conf/domain_conf.c | 13 ++++++++++ src/conf/domain_conf.h | 4 +++ src/libvirt_private.syms | 1 + .../bhyvexml2argv-sound.args | 2 +- .../bhyvexml2argvdata/bhyvexml2argv-sound.xml | 8 +++++- .../bhyvexml2xmlout-sound.xml | 5 ++++ 7 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 1f42f71347..8e7907b882 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -478,9 +478,12 @@ bhyveBuildGraphicsArgStr(const virDomainDef *def, static int bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, virDomainSoundDefPtr sound, + virDomainAudioDefPtr audio, bhyveConnPtr driver, virCommandPtr cmd) { + g_auto(virBuffer) params = VIR_BUFFER_INITIALIZER; + if (!(bhyveDriverGetBhyveCaps(driver) & BHYVE_CAP_SOUND_HDA)) { /* Currently, bhyve only supports "hda" sound devices, so if it's not supported, sound devices are not supported at all */ @@ -497,9 +500,24 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, }
virCommandAddArg(cmd, "-s"); - virCommandAddArgFormat(cmd, "%d:%d,hda,play=/dev/dsp0", + + if (audio) { + if (audio->type == VIR_DOMAIN_AUDIO_TYPE_OSS) {
Should use a switch() here and report enum range errors in the LAST/default case. This will force us to fix bhyve when we add more audio types later to do proper error reporting.
+ if (audio->backend.oss.inputDev) + virBufferAsprintf(¶ms, ",play=%s", + audio->backend.oss.inputDev); + + if (audio->backend.oss.outputDev) + virBufferAsprintf(¶ms, ",rec=%s", + audio->backend.oss.outputDev); + } + } + + virCommandAddArgFormat(cmd, "%d:%d,hda%s", sound->info.addr.pci.slot, - sound->info.addr.pci.function); + sound->info.addr.pci.function, + virBufferCurrentContent(¶ms)); +
What happens if you dont give an any play arg - does it just assume the default /dev/dsp0 ? Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Daniel P. Berrangé wrote:
On Fri, Aug 07, 2020 at 07:09:33PM +0400, Roman Bogorodskiy wrote:
Allow to map sound playback and recording devices to host devices using "<audio type='oss'/>" OSS audio backend.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_command.c | 26 ++++++++++++++++--- src/conf/domain_conf.c | 13 ++++++++++ src/conf/domain_conf.h | 4 +++ src/libvirt_private.syms | 1 + .../bhyvexml2argv-sound.args | 2 +- .../bhyvexml2argvdata/bhyvexml2argv-sound.xml | 8 +++++- .../bhyvexml2xmlout-sound.xml | 5 ++++ 7 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 1f42f71347..8e7907b882 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -478,9 +478,12 @@ bhyveBuildGraphicsArgStr(const virDomainDef *def, static int bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, virDomainSoundDefPtr sound, + virDomainAudioDefPtr audio, bhyveConnPtr driver, virCommandPtr cmd) { + g_auto(virBuffer) params = VIR_BUFFER_INITIALIZER; + if (!(bhyveDriverGetBhyveCaps(driver) & BHYVE_CAP_SOUND_HDA)) { /* Currently, bhyve only supports "hda" sound devices, so if it's not supported, sound devices are not supported at all */ @@ -497,9 +500,24 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, }
virCommandAddArg(cmd, "-s"); - virCommandAddArgFormat(cmd, "%d:%d,hda,play=/dev/dsp0", + + if (audio) { + if (audio->type == VIR_DOMAIN_AUDIO_TYPE_OSS) {
Should use a switch() here and report enum range errors in the LAST/default case.
This will force us to fix bhyve when we add more audio types later to do proper error reporting.
+ if (audio->backend.oss.inputDev) + virBufferAsprintf(¶ms, ",play=%s", + audio->backend.oss.inputDev); + + if (audio->backend.oss.outputDev) + virBufferAsprintf(¶ms, ",rec=%s", + audio->backend.oss.outputDev); + } + } + + virCommandAddArgFormat(cmd, "%d:%d,hda%s", sound->info.addr.pci.slot, - sound->info.addr.pci.function); + sound->info.addr.pci.function, + virBufferCurrentContent(¶ms)); +
What happens if you dont give an any play arg - does it just assume the default /dev/dsp0 ?
It doesn't assume these defaults. When started this way, guest will have a sound device, but playback (and likely recording) will not work because the sound device will fail to open. I'm not sure what's the purpose of things configured this way.
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Roman Bogorodskiy

Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- tests/virschematest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/virschematest.c b/tests/virschematest.c index 8720031375..17eb2a4b34 100644 --- a/tests/virschematest.c +++ b/tests/virschematest.c @@ -201,7 +201,8 @@ mymain(void) DO_TEST_DIR("domain.rng", "domainschemadata", "qemuxml2argvdata", "xmconfigdata", "qemuxml2xmloutdata", "lxcxml2xmldata", - "lxcxml2xmloutdata", "bhyvexml2argvdata", "genericxml2xmlindata", + "lxcxml2xmloutdata", "bhyvexml2argvdata", + "bhyvexml2xmloutdata", "genericxml2xmlindata", "genericxml2xmloutdata", "xlconfigdata", "libxlxml2domconfigdata", "qemuhotplugtestdomains"); DO_TEST_DIR("domaincaps.rng", "domaincapsdata"); -- 2.27.0

On Fri, Aug 07, 2020 at 07:09:34PM +0400, Roman Bogorodskiy wrote:
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- tests/virschematest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Daniel P. Berrangé wrote:
On Fri, Aug 07, 2020 at 07:09:34PM +0400, Roman Bogorodskiy wrote:
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- tests/virschematest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Will merging this patch alone and sending the rest of the patches updated as v4 be the right way to proceed with this series? Roman Bogorodskiy

On Fri, Aug 21, 2020 at 05:00:01PM +0400, Roman Bogorodskiy wrote:
Daniel P. Berrangé wrote:
On Fri, Aug 07, 2020 at 07:09:34PM +0400, Roman Bogorodskiy wrote:
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- tests/virschematest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Will merging this patch alone and sending the rest of the patches updated as v4 be the right way to proceed with this series?
This patch is self-contained IIUC, so merging it on its own is fine Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Document the new <audio> element which allows to specify host audio backend for a guest <sound> device, and update the <sound> element description with the new <audio> sub-element which specified the other end of the mapping. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/formatdomain.rst | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 218f0c1718..fc1aedc2ec 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -6572,6 +6572,55 @@ Valid values are: Each ``sound`` element has an optional sub-element ``<address>`` which can tie the device to a particular PCI slot, `documented above <#elementsAddress>`__. +:since:`Since 6.7.0`, a sound device could be optionally mapped to the specific +host audio backend using the ``<audio>`` sub-element: + +:: + + ... + <devices> + <sound model='ich7'> + <audio id='1'> + </sound> + </devices> + ... + +Where ``1`` is an id of the `audio device <#elementsAudio>`__. +This is supported for bhyve only. + +:anchor:`<a id="elementsAudio"/>` + +Audio devices +~~~~~~~~~~~~~ + +A virtual audio device corresponds to a host audio backend that is mapped +to the guest sound device. :since:`Since 6.7.0, bhyve only` + +``type`` + The required ``type`` attribute specifies audio backend type. + Currently, the only supported value is 'oss'. + +``id`` + Integer id of the audio device. Must be greater than 0. + +The 'oss' audio type supports additional configuration: + +:: + + ... + <devices> + <audio type='oss' id='1'> + <input dev='/dev/dsp0'/> + <output dev='/dev/dsp0'/> + </audio> + </devices> + +``input`` + Input device. The required ``dev`` attribute specifies device path. + +``output`` + Output device. The required ``dev`` attribute specifies device path. + :anchor:`<a id="elementsWatchdog"/>` Watchdog device -- 2.27.0

On Fri, Aug 07, 2020 at 07:09:35PM +0400, Roman Bogorodskiy wrote:
Document the new <audio> element which allows to specify host audio backend for a guest <sound> device, and update the <sound> element description with the new <audio> sub-element which specified the other end of the mapping.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/formatdomain.rst | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
participants (2)
-
Daniel P. Berrangé
-
Roman Bogorodskiy