[PATCH v2 0/4] bhyve: implement sound device support

Changes from v1: Main change is the addition of the "<audio>" element that allows to map the "<sound>" device to the host audio backend. Would appreciate initial feedback on this one, and then I'll proceed with adding more validation. Roman Bogorodskiy (4): 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/schemas/domaincommon.rng | 36 ++++ src/bhyve/bhyve_capabilities.c | 14 ++ src/bhyve/bhyve_capabilities.h | 1 + src/bhyve/bhyve_command.c | 64 +++++++ src/bhyve/bhyve_device.c | 9 + src/conf/domain_capabilities.c | 4 + src/conf/domain_conf.c | 156 +++++++++++++++++- src/conf/domain_conf.h | 24 +++ 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 + .../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 +- 23 files changed, 416 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

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. One more simplification is to stick to "ich6" model, however the device shows as "ich7" in a guest. 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 22d0b24ec4..64af0b3598 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_ICH6) { + 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..dd4f0491a2 --- /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='ich6'/> + </devices> +</domain> diff --git a/tests/bhyvexml2argvtest.c b/tests/bhyvexml2argvtest.c index 9e7eb218b8..179a73bb42 100644 --- a/tests/bhyvexml2argvtest.c +++ b/tests/bhyvexml2argvtest.c @@ -175,7 +175,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"); @@ -210,6 +210,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"); @@ -249,6 +250,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..f5da7c23c8 --- /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='ich6'> + <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 a0c20a14c1..9e40e1ca2e 100644 --- a/tests/bhyvexml2xmltest.c +++ b/tests/bhyvexml2xmltest.c @@ -110,6 +110,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 Tue, Jul 28, 2020 at 06:58:40PM +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.
One more simplification is to stick to "ich6" model, however the device shows as "ich7" in a guest.
If bhyve is emulating an ICH7 for sound, then we should add ICH7 to the virDomainSoundModel enum, rather than (re)using the wrong ICH6 model.
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
The actual impl looks fine, aside from the ICH6 vs ICH7 thing. 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='ich6'> <audio id='audio0'/> </sound> <audio id='audio' 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 | 156 ++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 24 +++++ 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, 236 insertions(+), 2 deletions(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a810f569c6..b0a5e08ba0 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4366,12 +4366,47 @@ <optional> <ref name="address"/> </optional> + <optional> + <element name="audio"> + <attribute name="id"> + <data type="string"/> + </attribute> + </element> + </optional> <zeroOrMore> <ref name="codec"/> </zeroOrMore> </interleave> </element> </define> + <define name="audio"> + <element name="audio"> + <attribute name="id"> + <data type="string"/> + </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"> @@ -5286,6 +5321,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 837b004334..165a792cdf 100644 --- a/src/conf/domain_capabilities.c +++ b/src/conf/domain_capabilities.c @@ -692,6 +692,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 7ecd2818b9..50a5c3387d 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, @@ -728,6 +729,11 @@ VIR_ENUM_IMPL(virDomainSoundModel, "usb", ); +VIR_ENUM_IMPL(virDomainAudioType, + VIR_DOMAIN_AUDIO_TYPE_LAST, + "oss", +); + VIR_ENUM_IMPL(virDomainKeyWrapCipherName, VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_LAST, "aes", @@ -2850,6 +2856,21 @@ void virDomainSoundDefFree(virDomainSoundDefPtr def) virDomainSoundCodecDefFree(def->codecs[i]); VIR_FREE(def->codecs); + VIR_FREE(def->audioId); + + VIR_FREE(def); +} + +void virDomainAudioDefFree(virDomainAudioDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->id); + + VIR_FREE(def->inputDev); + VIR_FREE(def->outputDev); + VIR_FREE(def); } @@ -3206,6 +3227,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; @@ -3466,6 +3490,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); @@ -4054,6 +4082,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; @@ -4148,6 +4177,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; @@ -4414,6 +4446,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 @@ -5406,6 +5439,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; @@ -6795,6 +6829,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: @@ -14984,10 +15020,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"); @@ -15024,6 +15060,10 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, } } + audioNode = virXPathNode("./audio", ctxt); + if (audioNode) + def->audioId = virXMLPropString(audioNode, "id"); + if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error; @@ -15075,6 +15115,51 @@ 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 *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; + } + + def->id = virXMLPropString(node, "id"); + + 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->inputDev = virXMLPropString(inputDevNode, "dev"); + def->outputDev = virXMLPropString(outputDevNode, "dev"); + } + + return def; + + error: + virDomainAudioDefFree(def); + return NULL; +} static virDomainWatchdogDefPtr virDomainWatchdogDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, @@ -17014,6 +17099,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))) @@ -21910,6 +21999,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; @@ -22424,7 +22529,6 @@ virDomainDefParse(const char *xmlStr, virDomainDefPtr def = NULL; int keepBlanksDefault = xmlKeepBlanksDefault(0); xmlNodePtr root; - if (!(xml = virXMLParse(filename, xmlStr, _("(domain_definition)")))) goto cleanup; @@ -24523,6 +24627,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 @@ -27284,6 +27389,9 @@ virDomainSoundDefFormat(virBufferPtr buf, for (i = 0; i < def->ncodecs; i++) virDomainSoundCodecDefFormat(&childBuf, def->codecs[i]); + if (def->audioId) + virBufferAsprintf(&childBuf, "<audio id='%s'/>\n", def->audioId); + if (virDomainDeviceInfoFormat(&childBuf, &def->info, flags) < 0) return -1; @@ -27300,6 +27408,42 @@ 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='%s' type='%s'", def->id, type); + + switch (def->type) { + case VIR_DOMAIN_AUDIO_TYPE_OSS: + if (def->inputDev) + virBufferAsprintf(&childBuf, "<input dev='%s'/>\n", def->inputDev); + if (def->outputDev) + virBufferAsprintf(&childBuf, "<output dev='%s'/>\n", def->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, @@ -29975,6 +30119,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; @@ -31147,6 +31296,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 241149af24..fefd428ccd 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; }; @@ -1415,6 +1417,23 @@ struct _virDomainSoundDef { size_t ncodecs; virDomainSoundCodecDefPtr *codecs; + + char *audioId; +}; + +typedef enum { + VIR_DOMAIN_AUDIO_TYPE_OSS, + + VIR_DOMAIN_AUDIO_TYPE_LAST +} virDomainAudioType; + +struct _virDomainAudioDef { + int type; + + char *id; + + /* OSS specific configuration */ + char *inputDev, *outputDev; }; typedef enum { @@ -2591,6 +2610,9 @@ struct _virDomainDef { size_t nsounds; virDomainSoundDefPtr *sounds; + size_t naudios; + virDomainAudioDefPtr *audios; + size_t nvideos; virDomainVideoDefPtr *videos; @@ -3021,6 +3043,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); @@ -3580,6 +3603,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 73b72c9e10..98e041bd53 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 839c93bfb4..f5c31c8a9f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -522,6 +522,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 7530edac53..d92bf71c8b 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -5255,6 +5255,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 058cbda2a2..97d21aaf8c 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: @@ -1046,6 +1047,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 d185666ed8..4d09391062 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7953,6 +7953,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"), @@ -8087,6 +8088,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"), @@ -8306,6 +8308,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"), @@ -8508,6 +8511,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"), @@ -8615,6 +8619,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 26912334d2..008533bd8c 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 488f258d00..97c227e034 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4144,6 +4144,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 Tue, Jul 28, 2020 at 06:58:41PM +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='ich6'> <audio id='audio0'/> </sound> <audio id='audio' 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 ++++++++
Also docs/formatdomain.html.in needs an update
src/conf/domain_capabilities.c | 4 + src/conf/domain_conf.c | 156 ++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 24 +++++ 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, 236 insertions(+), 2 deletions(-)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 241149af24..fefd428ccd 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; };
@@ -1415,6 +1417,23 @@ struct _virDomainSoundDef {
size_t ncodecs; virDomainSoundCodecDefPtr *codecs; + + char *audioId; +}; + +typedef enum { + VIR_DOMAIN_AUDIO_TYPE_OSS, + + VIR_DOMAIN_AUDIO_TYPE_LAST +} virDomainAudioType; + +struct _virDomainAudioDef { + int type; + + char *id; + + /* OSS specific configuration */ + char *inputDev, *outputDev;
Since we're expecting multiple backends, lets go straight for a union here. union { struct { char *inputDev, *outputDev; } oss; } backend; 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 :|

On Tue, Jul 28, 2020 at 06:58:41PM +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='ich6'> <audio id='audio0'/> </sound> <audio id='audio' type='oss'> <input dev='/dev/dsp0'/> <output dev='/dev/dsp0'/> </audio>
Thinking again about this design, I wonder if we should simplify a little by using an integer index value instead of string ID ? <sound model='ich6' audio='1'/> <audio index='1' type='oss'> <input dev='/dev/dsp0'/> <output dev='/dev/dsp0'/> </audio> my original suggestion of an ID string was biased from my view of QEMU's underlying impl that uses string IDs, but the string ID is not really useful to other hypervisors, and even in QEMU an integer index is sufficient at libvirt level, as we can deterministically turn it into a ID string. The use of an integer index is more aligned with what we do for <controllers> for example. I'm also considering that if we add support for multi-seat we might just use an integer ID there to enumerate seat numbers. 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 | 37 +++++++++++++++++-- .../bhyvexml2argv-sound.args | 2 +- .../bhyvexml2argvdata/bhyvexml2argv-sound.xml | 8 +++- .../bhyvexml2xmlout-sound.xml | 5 +++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 64af0b3598..9c48c31066 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -478,9 +478,13 @@ bhyveBuildGraphicsArgStr(const virDomainDef *def, static int bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, virDomainSoundDefPtr sound, + virHashTablePtr audios, bhyveConnPtr driver, virCommandPtr cmd) { + g_auto(virBuffer) params = VIR_BUFFER_INITIALIZER; + virDomainAudioDefPtr audio; + 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 +501,25 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, } virCommandAddArg(cmd, "-s"); - virCommandAddArgFormat(cmd, "%d:%d,hda,play=/dev/dsp0", + + if (audios) { + audio = virHashLookup(audios, sound->audioId); + + if (audio) { + if (audio->type == VIR_DOMAIN_AUDIO_TYPE_OSS) { + if (audio->inputDev) + virBufferAsprintf(¶ms, ",play=%s", audio->inputDev); + + if (audio->outputDev) + virBufferAsprintf(¶ms, ",rec=%s", audio->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; } @@ -519,6 +539,7 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, size_t i; unsigned nusbcontrollers = 0; unsigned nvcpus = virDomainDefGetVcpus(def); + virHashTablePtr audios = NULL; /* CPUs */ virCommandAddArg(cmd, "-c"); @@ -647,11 +668,20 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, } } + if (def->naudios > 0) { + audios = virHashCreate(def->naudios, NULL); + + for (i = 0; i < def->naudios; i++) + virHashAddEntry(audios, def->audios[i]->id, def->audios[i]); + } + for (i = 0; i < def->nsounds; i++) { - if (bhyveBuildSoundArgStr(def, def->sounds[i], driver, cmd) < 0) + if (bhyveBuildSoundArgStr(def, def->sounds[i], audios, driver, cmd) < 0) goto error; } + virHashFree(audios); + if (bhyveDomainDefNeedsISAController(def)) bhyveBuildLPCArgStr(def, cmd); @@ -675,6 +705,7 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, return cmd; error: + virHashFree(audios); virCommandFree(cmd); return NULL; } 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 dd4f0491a2..f622208b79 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='ich6'/> + <sound model='ich6'> + <audio id="audio0"/> + </sound> + <audio type="oss" id="audio0"> + <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 f5da7c23c8..529629f165 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='ich6'> + <audio id='audio0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </sound> + <audio id='audio0' type='oss'> + <input dev='/dev/dsp0'/> + <output dev='/dev/dsp0'/> + </audio> </devices> </domain> -- 2.27.0

On Tue, Jul 28, 2020 at 06:58:42PM +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 | 37 +++++++++++++++++-- .../bhyvexml2argv-sound.args | 2 +- .../bhyvexml2argvdata/bhyvexml2argv-sound.xml | 8 +++- .../bhyvexml2xmlout-sound.xml | 5 +++ 4 files changed, 47 insertions(+), 5 deletions(-)
diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 64af0b3598..9c48c31066 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -478,9 +478,13 @@ bhyveBuildGraphicsArgStr(const virDomainDef *def, static int bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, virDomainSoundDefPtr sound, + virHashTablePtr audios, bhyveConnPtr driver, virCommandPtr cmd) { + g_auto(virBuffer) params = VIR_BUFFER_INITIALIZER; + virDomainAudioDefPtr audio; + 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 +501,25 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, }
virCommandAddArg(cmd, "-s"); - virCommandAddArgFormat(cmd, "%d:%d,hda,play=/dev/dsp0", + + if (audios) { + audio = virHashLookup(audios, sound->audioId);
Need to take into acccount case where sound->audioId is NULL
+ + if (audio) { + if (audio->type == VIR_DOMAIN_AUDIO_TYPE_OSS) { + if (audio->inputDev) + virBufferAsprintf(¶ms, ",play=%s", audio->inputDev); + + if (audio->outputDev) + virBufferAsprintf(¶ms, ",rec=%s", audio->outputDev); + } + }
else { ....report bad audio ID value... }
+ }
else { ....report bad audio ID value... } Perhaps we should have a helper in domain_conf.h virDomainAudioDefPtr virDomainDefFindAudioForSound(virDomainSoundDefPtr sound); that hides all the logic. I think the number of audio backends is going to be small enough that we can avoid the hash table optimization and just iterate over them each time.
+ 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; }
@@ -519,6 +539,7 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, size_t i; unsigned nusbcontrollers = 0; unsigned nvcpus = virDomainDefGetVcpus(def); + virHashTablePtr audios = NULL;
/* CPUs */ virCommandAddArg(cmd, "-c"); @@ -647,11 +668,20 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, } }
+ if (def->naudios > 0) { + audios = virHashCreate(def->naudios, NULL); + + for (i = 0; i < def->naudios; i++) + virHashAddEntry(audios, def->audios[i]->id, def->audios[i]); + } + for (i = 0; i < def->nsounds; i++) { - if (bhyveBuildSoundArgStr(def, def->sounds[i], driver, cmd) < 0) + if (bhyveBuildSoundArgStr(def, def->sounds[i], audios, driver, cmd) < 0) goto error; }
+ virHashFree(audios); + if (bhyveDomainDefNeedsISAController(def)) bhyveBuildLPCArgStr(def, cmd);
@@ -675,6 +705,7 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def, return cmd;
error: + virHashFree(audios); virCommandFree(cmd); return NULL; }
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 :|

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 Tue, Jul 28, 2020 at 06:58:43PM +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 :|
participants (2)
-
Daniel P. Berrangé
-
Roman Bogorodskiy