
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 :|