[PATCH 1/4] bhyve: implement sound device support

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

Extend <sound> device element to accept "soundDevice" sub-element which allows to map guest sound device to host sound device. Example <sound model='ich6'> <soundDevice playback='/dev/dsp0' recording='/dev/dsp0'/> </sound> The "playback" attribute points to the playback device, and "recording" attribute points to the recording device. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/schemas/domaincommon.rng | 15 +++++++++++++++ src/conf/domain_conf.c | 24 ++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ 3 files changed, 42 insertions(+) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a810f569c6..b11b3f2af6 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4346,6 +4346,18 @@ </attribute> </element> </define> + <define name="soundDevice"> + <element name="soundDevice"> + <attribute name="playback"> + <text/> + </attribute> + <optional> + <attribute name="recording"> + <text/> + </attribute> + </optional> + </element> + </define> <define name="sound"> <element name="sound"> <attribute name="model"> @@ -4366,6 +4378,9 @@ <optional> <ref name="address"/> </optional> + <optional> + <ref name="soundDevice"/> + </optional> <zeroOrMore> <ref name="codec"/> </zeroOrMore> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7ecd2818b9..b678a2319d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2850,6 +2850,9 @@ void virDomainSoundDefFree(virDomainSoundDefPtr def) virDomainSoundCodecDefFree(def->codecs[i]); VIR_FREE(def->codecs); + VIR_FREE(def->playback); + VIR_FREE(def->recording); + VIR_FREE(def); } @@ -14984,6 +14987,8 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, virDomainSoundDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *model = NULL; + char *recording = NULL; + xmlNodePtr soundDeviceNode; if (VIR_ALLOC(def) < 0) return NULL; @@ -15024,6 +15029,14 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, } } + soundDeviceNode = virXPathNode("./soundDevice", ctxt); + if (soundDeviceNode) { + def->playback = virXMLPropString(soundDeviceNode, "playback"); + recording = virXMLPropString(soundDeviceNode, "recording"); + if (recording) + def->recording = recording; + } + if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error; @@ -15056,6 +15069,9 @@ virDomainSoundDefEquals(const virDomainSoundDef *a, !virDomainDeviceInfoAddressIsEqual(&a->info, &b->info)) return false; + if ((a->playback != b->playback) || (a->recording != b->recording)) + return false; + return true; } @@ -27284,6 +27300,14 @@ virDomainSoundDefFormat(virBufferPtr buf, for (i = 0; i < def->ncodecs; i++) virDomainSoundCodecDefFormat(&childBuf, def->codecs[i]); + if (def->playback) { + virBufferAsprintf(&childBuf, "<soundDevice playback='%s'", def->playback); + if (def->recording) + virBufferAsprintf(&childBuf, " recording='%s'/>\n", def->recording); + else + virBufferAddLit(&childBuf, "/>\n"); + } + if (virDomainDeviceInfoFormat(&childBuf, &def->info, flags) < 0) return -1; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 241149af24..b501f48442 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1415,6 +1415,9 @@ struct _virDomainSoundDef { size_t ncodecs; virDomainSoundCodecDefPtr *codecs; + + char *playback; + char *recording; }; typedef enum { -- 2.27.0

On Sat, Jul 18, 2020 at 04:31:16PM +0400, Roman Bogorodskiy wrote:
Extend <sound> device element to accept "soundDevice" sub-element which allows to map guest sound device to host sound device.
Example
<sound model='ich6'> <soundDevice playback='/dev/dsp0' recording='/dev/dsp0'/> </sound>
IIUC, FreeBSD's audio subsystem is the classic OSS API ?
The "playback" attribute points to the playback device, and "recording" attribute points to the recording device.
I'm thinking about how we'll have to deal with QEMU's sound backend options, and alignment with BHyve / FreeBSD. In QEMU there are multiple backends, OSS, ALSA, PulseAudio, SPICE, VNC, and many more. The backends have many properties, and many of the properties can be set separately for input and output. IIUC, QEMU can expose multiple sound devices to the guest too. I think this means that we can have a M:N relationship between a sound device, and an audio backend, not just 1:1. Assuming I'm right about the M:N relationship, I assume that of multiple cards all do playback concurrently, something will have todo mixing of the streams ? How will that work with audio capture, is only one card allowed to capture at any time ? I'm copying Gerd to confirm the above... Anyway, if we have M:N relation, then we'll need separate configuration elements. So I think we'd need to allow something like this: <sound model='ich6'> <audio id="audio0"/> </sound> <sound model='ich6'> <audio id="audio1"/> </sound> <sound model='ich6'> <audio id="audio1"/> </sound> <audio type="oss" id="audio0"> <input dev='/dev/dsp0'/> <output dev='/dev/dsp0'/> </audio> <audio type="spice" id="audio1"/> Here we have one sound device connected to OSS, and two sound devices connected to SPICE.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/schemas/domaincommon.rng | 15 +++++++++++++++ src/conf/domain_conf.c | 24 ++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ 3 files changed, 42 insertions(+)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a810f569c6..b11b3f2af6 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4346,6 +4346,18 @@ </attribute> </element> </define> + <define name="soundDevice"> + <element name="soundDevice"> + <attribute name="playback"> + <text/> + </attribute> + <optional> + <attribute name="recording"> + <text/> + </attribute> + </optional> + </element> + </define> <define name="sound"> <element name="sound"> <attribute name="model"> @@ -4366,6 +4378,9 @@ <optional> <ref name="address"/> </optional> + <optional> + <ref name="soundDevice"/> + </optional> <zeroOrMore> <ref name="codec"/> </zeroOrMore> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7ecd2818b9..b678a2319d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2850,6 +2850,9 @@ void virDomainSoundDefFree(virDomainSoundDefPtr def) virDomainSoundCodecDefFree(def->codecs[i]); VIR_FREE(def->codecs);
+ VIR_FREE(def->playback); + VIR_FREE(def->recording); + VIR_FREE(def); }
@@ -14984,6 +14987,8 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, virDomainSoundDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *model = NULL; + char *recording = NULL; + xmlNodePtr soundDeviceNode;
if (VIR_ALLOC(def) < 0) return NULL; @@ -15024,6 +15029,14 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, } }
+ soundDeviceNode = virXPathNode("./soundDevice", ctxt); + if (soundDeviceNode) { + def->playback = virXMLPropString(soundDeviceNode, "playback"); + recording = virXMLPropString(soundDeviceNode, "recording"); + if (recording) + def->recording = recording; + } + if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error;
@@ -15056,6 +15069,9 @@ virDomainSoundDefEquals(const virDomainSoundDef *a, !virDomainDeviceInfoAddressIsEqual(&a->info, &b->info)) return false;
+ if ((a->playback != b->playback) || (a->recording != b->recording)) + return false; + return true; }
@@ -27284,6 +27300,14 @@ virDomainSoundDefFormat(virBufferPtr buf, for (i = 0; i < def->ncodecs; i++) virDomainSoundCodecDefFormat(&childBuf, def->codecs[i]);
+ if (def->playback) { + virBufferAsprintf(&childBuf, "<soundDevice playback='%s'", def->playback); + if (def->recording) + virBufferAsprintf(&childBuf, " recording='%s'/>\n", def->recording); + else + virBufferAddLit(&childBuf, "/>\n"); + } + if (virDomainDeviceInfoFormat(&childBuf, &def->info, flags) < 0) return -1;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 241149af24..b501f48442 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1415,6 +1415,9 @@ struct _virDomainSoundDef {
size_t ncodecs; virDomainSoundCodecDefPtr *codecs; + + char *playback; + char *recording; };
typedef enum { -- 2.27.0
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 :|

Hi,
IIUC, QEMU can expose multiple sound devices to the guest too.
I think this means that we can have a M:N relationship between a sound device, and an audio backend, not just 1:1.
It's 1:N. Sound devices have a single backend, but a backends can service multiple sound devices.
Assuming I'm right about the M:N relationship, I assume that of multiple cards all do playback concurrently, something will have todo mixing of the streams ?
In general it is a good idea to go with 1:1 if possible. With pulseaudio this works fine. You'll have two streams to pulseaudio then, pulseaudio does the mixing, and you'll see both streams in mixer apps. OSS devices tend to not like being opened multiple times, so you have to go with 1:N if you want multiple sound devices. qemu mixes the playback streams then. Not fully sure what happens with recording, probably all sound devices see the very same stream. take care, Gerd

On Wed, Jul 22, 2020 at 08:55:02AM +0200, Gerd Hoffmann wrote:
Hi,
IIUC, QEMU can expose multiple sound devices to the guest too.
I think this means that we can have a M:N relationship between a sound device, and an audio backend, not just 1:1.
It's 1:N. Sound devices have a single backend, but a backends can service multiple sound devices.
Sigh yes, of course, wasn't thinking right.
Assuming I'm right about the M:N relationship, I assume that of multiple cards all do playback concurrently, something will have todo mixing of the streams ?
In general it is a good idea to go with 1:1 if possible. With pulseaudio this works fine. You'll have two streams to pulseaudio then, pulseaudio does the mixing, and you'll see both streams in mixer apps.
OSS devices tend to not like being opened multiple times, so you have to go with 1:N if you want multiple sound devices. qemu mixes the playback streams then. Not fully sure what happens with recording, probably all sound devices see the very same stream.
Ok, so it sounds like we need the XML schema I illustrated. Even though we'll recommend 1:1 for general use, we'll want the option to supoort 1:N setups. 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 Sat, Jul 18, 2020 at 04:31:16PM +0400, Roman Bogorodskiy wrote:
Extend <sound> device element to accept "soundDevice" sub-element which allows to map guest sound device to host sound device.
Example
<sound model='ich6'> <soundDevice playback='/dev/dsp0' recording='/dev/dsp0'/> </sound>
IIUC, FreeBSD's audio subsystem is the classic OSS API ?
The sound(4) manpage[1] claims it supports most of the OSS ioctls().
The "playback" attribute points to the playback device, and "recording" attribute points to the recording device.
I'm thinking about how we'll have to deal with QEMU's sound backend options, and alignment with BHyve / FreeBSD.
In QEMU there are multiple backends, OSS, ALSA, PulseAudio, SPICE, VNC, and many more. The backends have many properties, and many of the properties can be set separately for input and output.
IIUC, QEMU can expose multiple sound devices to the guest too.
I think this means that we can have a M:N relationship between a sound device, and an audio backend, not just 1:1.
Assuming I'm right about the M:N relationship, I assume that of multiple cards all do playback concurrently, something will have todo mixing of the streams ? How will that work with audio capture, is only one card allowed to capture at any time ?
In case of FreeBSD, the sound driver does mixing on its own, i.e. you can boot multiple guests pointed to /dev/dsp0 and audio streams from these guests will be played properly. I didn't test capturing though.
I'm copying Gerd to confirm the above...
Anyway, if we have M:N relation, then we'll need separate configuration elements.
So I think we'd need to allow something like this:
<sound model='ich6'> <audio id="audio0"/> </sound>
<sound model='ich6'> <audio id="audio1"/> </sound>
<sound model='ich6'> <audio id="audio1"/> </sound>
<audio type="oss" id="audio0"> <input dev='/dev/dsp0'/> <output dev='/dev/dsp0'/> </audio>
<audio type="spice" id="audio1"/>
Here we have one sound device connected to OSS, and two sound devices connected to SPICE.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- docs/schemas/domaincommon.rng | 15 +++++++++++++++ src/conf/domain_conf.c | 24 ++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ 3 files changed, 42 insertions(+)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a810f569c6..b11b3f2af6 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4346,6 +4346,18 @@ </attribute> </element> </define> + <define name="soundDevice"> + <element name="soundDevice"> + <attribute name="playback"> + <text/> + </attribute> + <optional> + <attribute name="recording"> + <text/> + </attribute> + </optional> + </element> + </define> <define name="sound"> <element name="sound"> <attribute name="model"> @@ -4366,6 +4378,9 @@ <optional> <ref name="address"/> </optional> + <optional> + <ref name="soundDevice"/> + </optional> <zeroOrMore> <ref name="codec"/> </zeroOrMore> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7ecd2818b9..b678a2319d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2850,6 +2850,9 @@ void virDomainSoundDefFree(virDomainSoundDefPtr def) virDomainSoundCodecDefFree(def->codecs[i]); VIR_FREE(def->codecs);
+ VIR_FREE(def->playback); + VIR_FREE(def->recording); + VIR_FREE(def); }
@@ -14984,6 +14987,8 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, virDomainSoundDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *model = NULL; + char *recording = NULL; + xmlNodePtr soundDeviceNode;
if (VIR_ALLOC(def) < 0) return NULL; @@ -15024,6 +15029,14 @@ virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, } }
+ soundDeviceNode = virXPathNode("./soundDevice", ctxt); + if (soundDeviceNode) { + def->playback = virXMLPropString(soundDeviceNode, "playback"); + recording = virXMLPropString(soundDeviceNode, "recording"); + if (recording) + def->recording = recording; + } + if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error;
@@ -15056,6 +15069,9 @@ virDomainSoundDefEquals(const virDomainSoundDef *a, !virDomainDeviceInfoAddressIsEqual(&a->info, &b->info)) return false;
+ if ((a->playback != b->playback) || (a->recording != b->recording)) + return false; + return true; }
@@ -27284,6 +27300,14 @@ virDomainSoundDefFormat(virBufferPtr buf, for (i = 0; i < def->ncodecs; i++) virDomainSoundCodecDefFormat(&childBuf, def->codecs[i]);
+ if (def->playback) { + virBufferAsprintf(&childBuf, "<soundDevice playback='%s'", def->playback); + if (def->recording) + virBufferAsprintf(&childBuf, " recording='%s'/>\n", def->recording); + else + virBufferAddLit(&childBuf, "/>\n"); + } + if (virDomainDeviceInfoFormat(&childBuf, &def->info, flags) < 0) return -1;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 241149af24..b501f48442 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1415,6 +1415,9 @@ struct _virDomainSoundDef {
size_t ncodecs; virDomainSoundCodecDefPtr *codecs; + + char *playback; + char *recording; };
typedef enum { -- 2.27.0
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

Allow to map sound playback and recording devices to host devices using the: <soundDevice playback='/dev/playback_dev' recording='recording_dev'/> element under '<sound>'. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_command.c | 13 +++++++++++-- tests/bhyvexml2argvdata/bhyvexml2argv-sound.args | 2 +- tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml | 4 +++- tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 64af0b3598..62109f2db3 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -481,6 +481,8 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, 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 +499,16 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, } virCommandAddArg(cmd, "-s"); - virCommandAddArgFormat(cmd, "%d:%d,hda,play=/dev/dsp0", + + if (sound->playback) + virBufferAsprintf(¶ms, ",play=%s", sound->playback); + if (sound->recording) + virBufferAsprintf(¶ms, ",rec=%s", sound->recording); + + 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; } 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..db6ebc51e3 100644 --- a/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-sound.xml @@ -19,6 +19,8 @@ <source bridge="virbr0"/> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> - <sound model='ich6'/> + <sound model='ich6'> + <soundDevice playback="/dev/dsp0" recording="/dev/dsp0"/> + </sound> </devices> </domain> diff --git a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml index f5da7c23c8..9c0007c827 100644 --- a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml +++ b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-sound.xml @@ -30,6 +30,7 @@ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> <sound model='ich6'> + <soundDevice playback='/dev/dsp0' recording='/dev/dsp0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </sound> </devices> -- 2.27.0

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
participants (3)
-
Daniel P. Berrangé
-
Gerd Hoffmann
-
Roman Bogorodskiy