summaryrefslogtreecommitdiffstats
path: root/include/VBox/vmm/pdmaudiohostenuminline.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/VBox/vmm/pdmaudiohostenuminline.h')
-rw-r--r--include/VBox/vmm/pdmaudiohostenuminline.h463
1 files changed, 463 insertions, 0 deletions
diff --git a/include/VBox/vmm/pdmaudiohostenuminline.h b/include/VBox/vmm/pdmaudiohostenuminline.h
new file mode 100644
index 00000000..5c812eee
--- /dev/null
+++ b/include/VBox/vmm/pdmaudiohostenuminline.h
@@ -0,0 +1,463 @@
+/* $Id: pdmaudiohostenuminline.h $ */
+/** @file
+ * PDM - Audio Helpers for host audio device enumeration, Inlined Code. (DEV,++)
+ *
+ * This is all inlined because it's too tedious to create a couple libraries to
+ * contain it all (same bad excuse as for intnetinline.h & pdmnetinline.h).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_vmm_pdmaudiohostenuminline_h
+#define VBOX_INCLUDED_vmm_pdmaudiohostenuminline_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/vmm/pdmaudioifs.h>
+#include <VBox/vmm/pdmaudioinline.h>
+
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+
+/** @defgroup grp_pdm_audio_host_enum_inline The PDM Host Audio Enumeration Helper APIs
+ * @ingroup grp_pdm
+ * @{
+ */
+
+
+/**
+ * Allocates a host audio device for an enumeration result.
+ *
+ * @returns Newly allocated audio device, or NULL on failure.
+ * @param cb The total device structure size. This must be at least the
+ * size of PDMAUDIOHOSTDEV. The idea is that the caller extends
+ * the PDMAUDIOHOSTDEV structure and appends additional data
+ * after it in its private structure.
+ * @param cbName The number of bytes to allocate for the name field
+ * (including the terminator). Pass zero if RTStrAlloc and
+ * friends will be used.
+ * @param cbId The number of bytes to allocate for the ID field. Pass
+ * zero if RTStrAlloc and friends will be used.
+ */
+DECLINLINE(PPDMAUDIOHOSTDEV) PDMAudioHostDevAlloc(size_t cb, size_t cbName, size_t cbId)
+{
+ AssertReturn(cb >= sizeof(PDMAUDIOHOSTDEV), NULL);
+ AssertReturn(cb < _4M, NULL);
+ AssertReturn(cbName < _4K, NULL);
+ AssertReturn(cbId < _16K, NULL);
+
+ PPDMAUDIOHOSTDEV pDev = (PPDMAUDIOHOSTDEV)RTMemAllocZ(RT_ALIGN_Z(cb + cbName + cbId, 64));
+ if (pDev)
+ {
+ pDev->uMagic = PDMAUDIOHOSTDEV_MAGIC;
+ pDev->cbSelf = (uint32_t)cb;
+ RTListInit(&pDev->ListEntry);
+ if (cbName)
+ pDev->pszName = (char *)pDev + cb;
+ if (cbId)
+ pDev->pszId = (char *)pDev + cb + cbName;
+ }
+ return pDev;
+}
+
+/**
+ * Frees a host audio device allocated by PDMAudioHostDevAlloc.
+ *
+ * @param pDev The device to free. NULL is ignored.
+ */
+DECLINLINE(void) PDMAudioHostDevFree(PPDMAUDIOHOSTDEV pDev)
+{
+ if (pDev)
+ {
+ Assert(pDev->uMagic == PDMAUDIOHOSTDEV_MAGIC);
+ pDev->uMagic = ~PDMAUDIOHOSTDEV_MAGIC;
+ pDev->cbSelf = 0;
+
+ if (pDev->fFlags & PDMAUDIOHOSTDEV_F_NAME_ALLOC)
+ {
+ RTStrFree(pDev->pszName);
+ pDev->pszName = NULL;
+ }
+
+ if (pDev->fFlags & PDMAUDIOHOSTDEV_F_ID_ALLOC)
+ {
+ RTStrFree(pDev->pszId);
+ pDev->pszId = NULL;
+ }
+
+ RTMemFree(pDev);
+ }
+}
+
+/**
+ * Duplicates a host audio device enumeration entry.
+ *
+ * @returns Duplicated audio device entry on success, or NULL on failure.
+ * @param pDev The audio device enum entry to duplicate.
+ * @param fOnlyCoreData
+ */
+DECLINLINE(PPDMAUDIOHOSTDEV) PDMAudioHostDevDup(PCPDMAUDIOHOSTDEV pDev, bool fOnlyCoreData)
+{
+ AssertPtrReturn(pDev, NULL);
+ Assert(pDev->uMagic == PDMAUDIOHOSTDEV_MAGIC);
+ Assert(fOnlyCoreData || !(pDev->fFlags & PDMAUDIOHOSTDEV_F_NO_DUP));
+
+ uint32_t cbToDup = fOnlyCoreData ? sizeof(PDMAUDIOHOSTDEV) : pDev->cbSelf;
+ AssertReturn(cbToDup >= sizeof(*pDev), NULL);
+
+ PPDMAUDIOHOSTDEV pDevDup = PDMAudioHostDevAlloc(cbToDup, 0, 0);
+ if (pDevDup)
+ {
+ memcpy(pDevDup, pDev, cbToDup);
+ RTListInit(&pDevDup->ListEntry);
+ pDevDup->cbSelf = cbToDup;
+
+ if (pDev->pszName)
+ {
+ uintptr_t off;
+ if ( (pDevDup->fFlags & PDMAUDIOHOSTDEV_F_NAME_ALLOC)
+ || (off = (uintptr_t)pDev->pszName - (uintptr_t)pDev) >= pDevDup->cbSelf)
+ {
+ pDevDup->fFlags |= PDMAUDIOHOSTDEV_F_NAME_ALLOC;
+ pDevDup->pszName = RTStrDup(pDev->pszName);
+ AssertReturnStmt(pDevDup->pszName, PDMAudioHostDevFree(pDevDup), NULL);
+ }
+ else
+ pDevDup->pszName = (char *)pDevDup + off;
+ }
+
+ if (pDev->pszId)
+ {
+ uintptr_t off;
+ if ( (pDevDup->fFlags & PDMAUDIOHOSTDEV_F_ID_ALLOC)
+ || (off = (uintptr_t)pDev->pszId - (uintptr_t)pDev) >= pDevDup->cbSelf)
+ {
+ pDevDup->fFlags |= PDMAUDIOHOSTDEV_F_ID_ALLOC;
+ pDevDup->pszId = RTStrDup(pDev->pszId);
+ AssertReturnStmt(pDevDup->pszId, PDMAudioHostDevFree(pDevDup), NULL);
+ }
+ else
+ pDevDup->pszId = (char *)pDevDup + off;
+ }
+ }
+
+ return pDevDup;
+}
+
+/**
+ * Initializes a host audio device enumeration.
+ *
+ * @param pDevEnm The enumeration to initialize.
+ */
+DECLINLINE(void) PDMAudioHostEnumInit(PPDMAUDIOHOSTENUM pDevEnm)
+{
+ AssertPtr(pDevEnm);
+
+ pDevEnm->uMagic = PDMAUDIOHOSTENUM_MAGIC;
+ pDevEnm->cDevices = 0;
+ RTListInit(&pDevEnm->LstDevices);
+}
+
+/**
+ * Deletes the host audio device enumeration and frees all device entries
+ * associated with it.
+ *
+ * The user must call PDMAudioHostEnumInit again to use it again.
+ *
+ * @param pDevEnm The host audio device enumeration to delete.
+ */
+DECLINLINE(void) PDMAudioHostEnumDelete(PPDMAUDIOHOSTENUM pDevEnm)
+{
+ if (pDevEnm)
+ {
+ AssertPtr(pDevEnm);
+ AssertReturnVoid(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC);
+
+ PPDMAUDIOHOSTDEV pDev, pDevNext;
+ RTListForEachSafe(&pDevEnm->LstDevices, pDev, pDevNext, PDMAUDIOHOSTDEV, ListEntry)
+ {
+ RTListNodeRemove(&pDev->ListEntry);
+
+ PDMAudioHostDevFree(pDev);
+
+ pDevEnm->cDevices--;
+ }
+
+ /* Sanity. */
+ Assert(RTListIsEmpty(&pDevEnm->LstDevices));
+ Assert(pDevEnm->cDevices == 0);
+
+ pDevEnm->uMagic = ~PDMAUDIOHOSTENUM_MAGIC;
+ }
+}
+
+/**
+ * Adds an audio device to a device enumeration.
+ *
+ * @param pDevEnm Device enumeration to add device to.
+ * @param pDev Device to add. The pointer will be owned by the device enumeration then.
+ */
+DECLINLINE(void) PDMAudioHostEnumAppend(PPDMAUDIOHOSTENUM pDevEnm, PPDMAUDIOHOSTDEV pDev)
+{
+ AssertPtr(pDevEnm);
+ AssertPtr(pDev);
+ Assert(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC);
+
+ RTListAppend(&pDevEnm->LstDevices, &pDev->ListEntry);
+ pDevEnm->cDevices++;
+}
+
+/**
+ * Appends copies of matching host device entries from one to another enumeration.
+ *
+ * @returns VBox status code.
+ * @param pDstDevEnm The target to append copies of matching device to.
+ * @param pSrcDevEnm The source to copy matching devices from.
+ * @param enmUsage The usage to match for copying.
+ * Use PDMAUDIODIR_INVALID to match all entries.
+ * @param fOnlyCoreData Set this to only copy the PDMAUDIOHOSTDEV part.
+ * Careful with passing @c false here as not all
+ * backends have data that can be copied.
+ */
+DECLINLINE(int) PDMAudioHostEnumCopy(PPDMAUDIOHOSTENUM pDstDevEnm, PCPDMAUDIOHOSTENUM pSrcDevEnm,
+ PDMAUDIODIR enmUsage, bool fOnlyCoreData)
+{
+ AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
+ AssertReturn(pDstDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, VERR_WRONG_ORDER);
+
+ AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
+ AssertReturn(pSrcDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, VERR_WRONG_ORDER);
+
+ PPDMAUDIOHOSTDEV pSrcDev;
+ RTListForEach(&pSrcDevEnm->LstDevices, pSrcDev, PDMAUDIOHOSTDEV, ListEntry)
+ {
+ if ( enmUsage == pSrcDev->enmUsage
+ || enmUsage == PDMAUDIODIR_INVALID /*all*/)
+ {
+ PPDMAUDIOHOSTDEV pDstDev = PDMAudioHostDevDup(pSrcDev, fOnlyCoreData);
+ AssertReturn(pDstDev, VERR_NO_MEMORY);
+
+ PDMAudioHostEnumAppend(pDstDevEnm, pDstDev);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Moves all the device entries from one enumeration to another, destroying the
+ * former.
+ *
+ * @returns VBox status code.
+ * @param pDstDevEnm The target to put move @a pSrcDevEnm to. This
+ * does not need to be initialized, but if it is it
+ * must not have any device entries.
+ * @param pSrcDevEnm The source to move from. This will be empty
+ * upon successful return.
+ */
+DECLINLINE(int) PDMAudioHostEnumMove(PPDMAUDIOHOSTENUM pDstDevEnm, PPDMAUDIOHOSTENUM pSrcDevEnm)
+{
+ AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
+ AssertReturn(pDstDevEnm->uMagic != PDMAUDIOHOSTENUM_MAGIC || pDstDevEnm->cDevices == 0, VERR_WRONG_ORDER);
+
+ AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
+ AssertReturn(pSrcDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, VERR_WRONG_ORDER);
+
+ pDstDevEnm->uMagic = PDMAUDIOHOSTENUM_MAGIC;
+ RTListInit(&pDstDevEnm->LstDevices);
+ pDstDevEnm->cDevices = pSrcDevEnm->cDevices;
+ if (pSrcDevEnm->cDevices)
+ {
+ PPDMAUDIOHOSTDEV pCur;
+ while ((pCur = RTListRemoveFirst(&pSrcDevEnm->LstDevices, PDMAUDIOHOSTDEV, ListEntry)) != NULL)
+ RTListAppend(&pDstDevEnm->LstDevices, &pCur->ListEntry);
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Get the default device with the given usage.
+ *
+ * This assumes that only one default device per usage is set, if there should
+ * be more than one, the first one is returned.
+ *
+ * @returns Default device if found, or NULL if not.
+ * @param pDevEnm Device enumeration to get default device for.
+ * @param enmUsage Usage to get default device for.
+ * Pass PDMAUDIODIR_INVALID to get the first device with
+ * either PDMAUDIOHOSTDEV_F_DEFAULT_OUT or
+ * PDMAUDIOHOSTDEV_F_DEFAULT_IN set.
+ */
+DECLINLINE(PPDMAUDIOHOSTDEV) PDMAudioHostEnumGetDefault(PCPDMAUDIOHOSTENUM pDevEnm, PDMAUDIODIR enmUsage)
+{
+ AssertPtrReturn(pDevEnm, NULL);
+ AssertReturn(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, NULL);
+
+ Assert(enmUsage == PDMAUDIODIR_IN || enmUsage == PDMAUDIODIR_OUT || enmUsage == PDMAUDIODIR_INVALID);
+ uint32_t const fFlags = enmUsage == PDMAUDIODIR_IN ? PDMAUDIOHOSTDEV_F_DEFAULT_IN
+ : enmUsage == PDMAUDIODIR_OUT ? PDMAUDIOHOSTDEV_F_DEFAULT_OUT
+ : enmUsage == PDMAUDIODIR_INVALID ? PDMAUDIOHOSTDEV_F_DEFAULT_IN | PDMAUDIOHOSTDEV_F_DEFAULT_OUT
+ : 0;
+
+ PPDMAUDIOHOSTDEV pDev;
+ RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
+ {
+ if (pDev->fFlags & fFlags)
+ {
+ Assert(pDev->enmUsage == enmUsage || pDev->enmUsage == PDMAUDIODIR_DUPLEX || enmUsage == PDMAUDIODIR_INVALID);
+ return pDev;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Get the number of device with the given usage.
+ *
+ * @returns Number of matching devices.
+ * @param pDevEnm Device enumeration to get default device for.
+ * @param enmUsage Usage to count devices for.
+ * Pass PDMAUDIODIR_INVALID to get the total number of devices.
+ */
+DECLINLINE(uint32_t) PDMAudioHostEnumCountMatching(PCPDMAUDIOHOSTENUM pDevEnm, PDMAUDIODIR enmUsage)
+{
+ AssertPtrReturn(pDevEnm, 0);
+ AssertReturn(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, 0);
+
+ if (enmUsage == PDMAUDIODIR_INVALID)
+ return pDevEnm->cDevices;
+
+ uint32_t cDevs = 0;
+ PPDMAUDIOHOSTDEV pDev;
+ RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
+ {
+ if (enmUsage == pDev->enmUsage)
+ cDevs++;
+ }
+
+ return cDevs;
+}
+
+/** The max string length for all PDMAUDIOHOSTDEV_F_XXX.
+ * @sa PDMAudioHostDevFlagsToString */
+#define PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN sizeof("DEFAULT_OUT DEFAULT_IN HOTPLUG BUGGY IGNORE LOCKED DEAD NAME_ALLOC ID_ALLOC NO_DUP ")
+
+/**
+ * Converts an audio device flags to a string.
+ *
+ * @returns
+ * @param pszDst Destination buffer with a size of at least
+ * PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN bytes (including
+ * the string terminator).
+ * @param fFlags Audio flags (PDMAUDIOHOSTDEV_F_XXX) to convert.
+ */
+DECLINLINE(const char *) PDMAudioHostDevFlagsToString(char pszDst[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN], uint32_t fFlags)
+{
+ static const struct { const char *pszMnemonic; uint32_t cchMnemonic; uint32_t fFlag; } s_aFlags[] =
+ {
+ { RT_STR_TUPLE("DEFAULT_OUT "), PDMAUDIOHOSTDEV_F_DEFAULT_OUT },
+ { RT_STR_TUPLE("DEFAULT_IN "), PDMAUDIOHOSTDEV_F_DEFAULT_IN },
+ { RT_STR_TUPLE("HOTPLUG "), PDMAUDIOHOSTDEV_F_HOTPLUG },
+ { RT_STR_TUPLE("BUGGY "), PDMAUDIOHOSTDEV_F_BUGGY },
+ { RT_STR_TUPLE("IGNORE "), PDMAUDIOHOSTDEV_F_IGNORE },
+ { RT_STR_TUPLE("LOCKED "), PDMAUDIOHOSTDEV_F_LOCKED },
+ { RT_STR_TUPLE("DEAD "), PDMAUDIOHOSTDEV_F_DEAD },
+ { RT_STR_TUPLE("NAME_ALLOC "), PDMAUDIOHOSTDEV_F_NAME_ALLOC },
+ { RT_STR_TUPLE("ID_ALLOC "), PDMAUDIOHOSTDEV_F_ID_ALLOC },
+ { RT_STR_TUPLE("NO_DUP "), PDMAUDIOHOSTDEV_F_NO_DUP },
+ };
+ size_t offDst = 0;
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aFlags); i++)
+ if (fFlags & s_aFlags[i].fFlag)
+ {
+ fFlags &= ~s_aFlags[i].fFlag;
+ memcpy(&pszDst[offDst], s_aFlags[i].pszMnemonic, s_aFlags[i].cchMnemonic);
+ offDst += s_aFlags[i].cchMnemonic;
+ }
+ Assert(fFlags == 0);
+ Assert(offDst < PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN);
+
+ if (offDst)
+ pszDst[offDst - 1] = '\0';
+ else
+ memcpy(pszDst, "NONE", sizeof("NONE"));
+ return pszDst;
+}
+
+/**
+ * Logs an audio device enumeration.
+ *
+ * @param pDevEnm Device enumeration to log.
+ * @param pszDesc Logging description (prefix).
+ */
+DECLINLINE(void) PDMAudioHostEnumLog(PCPDMAUDIOHOSTENUM pDevEnm, const char *pszDesc)
+{
+#ifdef LOG_ENABLED
+ AssertPtrReturnVoid(pDevEnm);
+ AssertPtrReturnVoid(pszDesc);
+ AssertReturnVoid(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC);
+
+ if (LogIsEnabled())
+ {
+ LogFunc(("%s: %RU32 devices\n", pszDesc, pDevEnm->cDevices));
+
+ PPDMAUDIOHOSTDEV pDev;
+ RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
+ {
+ char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
+ LogFunc(("Device '%s':\n", pDev->pszName));
+ LogFunc((" ID = %s\n", pDev->pszId ? pDev->pszId : "<none>"));
+ LogFunc((" Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage)));
+ LogFunc((" Flags = %s\n", PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags)));
+ LogFunc((" Input channels = %RU8\n", pDev->cMaxInputChannels));
+ LogFunc((" Output channels = %RU8\n", pDev->cMaxOutputChannels));
+ LogFunc((" cbExtra = %RU32 bytes\n", pDev->cbSelf - sizeof(PDMAUDIOHOSTDEV)));
+ }
+ }
+#else
+ RT_NOREF(pDevEnm, pszDesc);
+#endif
+}
+
+/** @} */
+
+#endif /* !VBOX_INCLUDED_vmm_pdmaudiohostenuminline_h */