[70533] | 1 | /* $Id: AudioDriver.cpp 93444 2022-01-26 18:01:15Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VirtualBox audio base class for Main audio drivers.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[93115] | 7 | * Copyright (C) 2018-2022 Oracle Corporation
|
---|
[70533] | 8 | *
|
---|
| 9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
| 10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
| 11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
| 12 | * General Public License (GPL) as published by the Free Software
|
---|
| 13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
| 14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
| 15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
| 16 | */
|
---|
| 17 |
|
---|
| 18 |
|
---|
| 19 | /*********************************************************************************************************************************
|
---|
| 20 | * Header Files *
|
---|
| 21 | *********************************************************************************************************************************/
|
---|
| 22 | #define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
|
---|
| 23 | #include "LoggingNew.h"
|
---|
| 24 |
|
---|
| 25 | #include <VBox/log.h>
|
---|
| 26 | #include <VBox/vmm/cfgm.h>
|
---|
| 27 | #include <VBox/vmm/pdmaudioifs.h>
|
---|
| 28 | #include <VBox/vmm/pdmapi.h>
|
---|
| 29 | #include <VBox/vmm/pdmdrv.h>
|
---|
[93444] | 30 | #include <VBox/vmm/vmmr3vtable.h>
|
---|
[70533] | 31 |
|
---|
| 32 | #include "AudioDriver.h"
|
---|
| 33 | #include "ConsoleImpl.h"
|
---|
| 34 |
|
---|
| 35 | AudioDriver::AudioDriver(Console *pConsole)
|
---|
| 36 | : mpConsole(pConsole)
|
---|
| 37 | , mfAttached(false)
|
---|
| 38 | {
|
---|
| 39 | }
|
---|
| 40 |
|
---|
[81674] | 41 |
|
---|
[70533] | 42 | AudioDriver::~AudioDriver(void)
|
---|
| 43 | {
|
---|
| 44 | }
|
---|
| 45 |
|
---|
| 46 |
|
---|
[81674] | 47 | AudioDriver &AudioDriver::operator=(AudioDriver const &a_rThat) RT_NOEXCEPT
|
---|
| 48 | {
|
---|
| 49 | mpConsole = a_rThat.mpConsole;
|
---|
| 50 | mCfg = a_rThat.mCfg;
|
---|
| 51 | mfAttached = a_rThat.mfAttached;
|
---|
| 52 |
|
---|
| 53 | return *this;
|
---|
| 54 | }
|
---|
| 55 |
|
---|
| 56 |
|
---|
[70564] | 57 | /**
|
---|
| 58 | * Initializes the audio driver with a certain (device) configuration.
|
---|
[70579] | 59 | *
|
---|
[70564] | 60 | * @returns VBox status code.
|
---|
[70579] | 61 | * @param pCfg Audio driver configuration to use.
|
---|
[70564] | 62 | */
|
---|
[70579] | 63 | int AudioDriver::InitializeConfig(AudioDriverCfg *pCfg)
|
---|
[70563] | 64 | {
|
---|
| 65 | AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
|
---|
| 66 |
|
---|
[70644] | 67 | /* Sanity. */
|
---|
| 68 | AssertReturn(pCfg->strDev.isNotEmpty(), VERR_INVALID_PARAMETER);
|
---|
| 69 | AssertReturn(pCfg->uLUN != UINT8_MAX, VERR_INVALID_PARAMETER);
|
---|
| 70 | AssertReturn(pCfg->strName.isNotEmpty(), VERR_INVALID_PARAMETER);
|
---|
| 71 |
|
---|
[70563] | 72 | /* Apply configuration. */
|
---|
| 73 | mCfg = *pCfg;
|
---|
| 74 |
|
---|
| 75 | return VINF_SUCCESS;
|
---|
| 76 | }
|
---|
| 77 |
|
---|
[70579] | 78 |
|
---|
[70533] | 79 | /**
|
---|
[70579] | 80 | * Attaches the driver via EMT, if configured.
|
---|
| 81 | *
|
---|
[88300] | 82 | * @returns VBox status code.
|
---|
[93444] | 83 | * @param pUVM The user mode VM handle for talking to EMT.
|
---|
| 84 | * @param pVMM The VMM ring-3 vtable.
|
---|
| 85 | * @param pAutoLock The callers auto lock instance. Can be NULL if not
|
---|
| 86 | * locked.
|
---|
[70579] | 87 | */
|
---|
[93444] | 88 | int AudioDriver::doAttachDriverViaEmt(PUVM pUVM, PCVMMR3VTABLE pVMM, util::AutoWriteLock *pAutoLock)
|
---|
[70579] | 89 | {
|
---|
| 90 | if (!isConfigured())
|
---|
| 91 | return VINF_SUCCESS;
|
---|
| 92 |
|
---|
| 93 | PVMREQ pReq;
|
---|
[93444] | 94 | int vrc = pVMM->pfnVMR3ReqCallU(pUVM, VMCPUID_ANY, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
|
---|
| 95 | (PFNRT)attachDriverOnEmt, 1, this);
|
---|
[70579] | 96 | if (vrc == VERR_TIMEOUT)
|
---|
| 97 | {
|
---|
| 98 | /* Release the lock before a blocking VMR3* call (EMT might wait for it, @bugref{7648})! */
|
---|
| 99 | if (pAutoLock)
|
---|
| 100 | pAutoLock->release();
|
---|
| 101 |
|
---|
[93444] | 102 | vrc = pVMM->pfnVMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
|
---|
[70579] | 103 |
|
---|
| 104 | if (pAutoLock)
|
---|
| 105 | pAutoLock->acquire();
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | AssertRC(vrc);
|
---|
[93444] | 109 | pVMM->pfnVMR3ReqFree(pReq);
|
---|
[70579] | 110 |
|
---|
| 111 | return vrc;
|
---|
| 112 | }
|
---|
| 113 |
|
---|
| 114 |
|
---|
| 115 | /**
|
---|
[70533] | 116 | * Configures the audio driver (to CFGM) and attaches it to the audio chain.
|
---|
[70547] | 117 | * Does nothing if the audio driver already is attached.
|
---|
[70533] | 118 | *
|
---|
[70564] | 119 | * @returns VBox status code.
|
---|
[70536] | 120 | * @param pThis Audio driver to detach.
|
---|
[70533] | 121 | */
|
---|
| 122 | /* static */
|
---|
[70579] | 123 | DECLCALLBACK(int) AudioDriver::attachDriverOnEmt(AudioDriver *pThis)
|
---|
[70533] | 124 | {
|
---|
| 125 | AssertPtrReturn(pThis, VERR_INVALID_POINTER);
|
---|
| 126 |
|
---|
| 127 | Console::SafeVMPtrQuiet ptrVM(pThis->mpConsole);
|
---|
| 128 | Assert(ptrVM.isOk());
|
---|
| 129 |
|
---|
| 130 | if (pThis->mfAttached) /* Already attached? Bail out. */
|
---|
[70563] | 131 | {
|
---|
| 132 | LogFunc(("%s: Already attached\n", pThis->mCfg.strName.c_str()));
|
---|
[70533] | 133 | return VINF_SUCCESS;
|
---|
[70563] | 134 | }
|
---|
[70533] | 135 |
|
---|
[70563] | 136 | AudioDriverCfg *pCfg = &pThis->mCfg;
|
---|
[70533] | 137 |
|
---|
[70544] | 138 | LogFunc(("strName=%s, strDevice=%s, uInst=%u, uLUN=%u\n",
|
---|
[70644] | 139 | pCfg->strName.c_str(), pCfg->strDev.c_str(), pCfg->uInst, pCfg->uLUN));
|
---|
[70536] | 140 |
|
---|
[70670] | 141 | /* Detach the driver chain from the audio device first. */
|
---|
[93444] | 142 | int rc = ptrVM.vtable()->pfnPDMR3DeviceDetach(ptrVM.rawUVM(), pCfg->strDev.c_str(), pCfg->uInst, pCfg->uLUN, 0 /* fFlags */);
|
---|
[70644] | 143 | if (RT_SUCCESS(rc))
|
---|
[70670] | 144 | {
|
---|
| 145 | rc = pThis->configure(pCfg->uLUN, true /* Attach */);
|
---|
| 146 | if (RT_SUCCESS(rc))
|
---|
[93444] | 147 | rc = ptrVM.vtable()->pfnPDMR3DriverAttach(ptrVM.rawUVM(), pCfg->strDev.c_str(), pCfg->uInst, pCfg->uLUN,
|
---|
| 148 | 0 /* fFlags */, NULL /* ppBase */);
|
---|
[70670] | 149 | }
|
---|
| 150 |
|
---|
[70644] | 151 | if (RT_SUCCESS(rc))
|
---|
[70536] | 152 | {
|
---|
| 153 | pThis->mfAttached = true;
|
---|
[70644] | 154 | LogRel2(("%s: Driver attached (LUN #%u)\n", pCfg->strName.c_str(), pCfg->uLUN));
|
---|
[70533] | 155 | }
|
---|
[70536] | 156 | else
|
---|
[70644] | 157 | LogRel(("%s: Failed to attach audio driver, rc=%Rrc\n", pCfg->strName.c_str(), rc));
|
---|
[70533] | 158 |
|
---|
[70644] | 159 | LogFunc(("Returning %Rrc\n", rc));
|
---|
| 160 | return rc;
|
---|
[70533] | 161 | }
|
---|
| 162 |
|
---|
[70579] | 163 |
|
---|
[70533] | 164 | /**
|
---|
[70579] | 165 | * Detatches the driver via EMT, if configured.
|
---|
| 166 | *
|
---|
[88300] | 167 | * @returns VBox status code.
|
---|
[93444] | 168 | * @param pUVM The user mode VM handle for talking to EMT.
|
---|
| 169 | * @param pVMM The VMM ring-3 vtable.
|
---|
| 170 | * @param pAutoLock The callers auto lock instance. Can be NULL if not
|
---|
| 171 | * locked.
|
---|
[70579] | 172 | */
|
---|
[93444] | 173 | int AudioDriver::doDetachDriverViaEmt(PUVM pUVM, PCVMMR3VTABLE pVMM, util::AutoWriteLock *pAutoLock)
|
---|
[70579] | 174 | {
|
---|
| 175 | if (!isConfigured())
|
---|
| 176 | return VINF_SUCCESS;
|
---|
| 177 |
|
---|
| 178 | PVMREQ pReq;
|
---|
[93444] | 179 | int vrc = pVMM->pfnVMR3ReqCallU(pUVM, VMCPUID_ANY, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
|
---|
| 180 | (PFNRT)detachDriverOnEmt, 1, this);
|
---|
[70579] | 181 | if (vrc == VERR_TIMEOUT)
|
---|
| 182 | {
|
---|
| 183 | /* Release the lock before a blocking VMR3* call (EMT might wait for it, @bugref{7648})! */
|
---|
| 184 | if (pAutoLock)
|
---|
| 185 | pAutoLock->release();
|
---|
| 186 |
|
---|
[93444] | 187 | vrc = pVMM->pfnVMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
|
---|
[70579] | 188 |
|
---|
| 189 | if (pAutoLock)
|
---|
| 190 | pAutoLock->acquire();
|
---|
| 191 | }
|
---|
| 192 |
|
---|
| 193 | AssertRC(vrc);
|
---|
[93444] | 194 | pVMM->pfnVMR3ReqFree(pReq);
|
---|
[70579] | 195 |
|
---|
| 196 | return vrc;
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 |
|
---|
| 200 | /**
|
---|
[70533] | 201 | * Detaches an already attached audio driver from the audio chain.
|
---|
[70547] | 202 | * Does nothing if the audio driver already is detached or not attached.
|
---|
[70533] | 203 | *
|
---|
[70564] | 204 | * @returns VBox status code.
|
---|
[70533] | 205 | * @param pThis Audio driver to detach.
|
---|
| 206 | */
|
---|
| 207 | /* static */
|
---|
[70579] | 208 | DECLCALLBACK(int) AudioDriver::detachDriverOnEmt(AudioDriver *pThis)
|
---|
[70533] | 209 | {
|
---|
| 210 | AssertPtrReturn(pThis, VERR_INVALID_POINTER);
|
---|
| 211 |
|
---|
[70563] | 212 | if (!pThis->mfAttached) /* Not attached? Bail out. */
|
---|
| 213 | {
|
---|
| 214 | LogFunc(("%s: Not attached\n", pThis->mCfg.strName.c_str()));
|
---|
| 215 | return VINF_SUCCESS;
|
---|
| 216 | }
|
---|
| 217 |
|
---|
[70533] | 218 | Console::SafeVMPtrQuiet ptrVM(pThis->mpConsole);
|
---|
| 219 | Assert(ptrVM.isOk());
|
---|
| 220 |
|
---|
| 221 | AudioDriverCfg *pCfg = &pThis->mCfg;
|
---|
| 222 |
|
---|
[70644] | 223 | Assert(pCfg->uLUN != UINT8_MAX);
|
---|
| 224 |
|
---|
[70544] | 225 | LogFunc(("strName=%s, strDevice=%s, uInst=%u, uLUN=%u\n",
|
---|
[70644] | 226 | pCfg->strName.c_str(), pCfg->strDev.c_str(), pCfg->uInst, pCfg->uLUN));
|
---|
[70536] | 227 |
|
---|
[70670] | 228 | /* Destroy the entire driver chain for the specified LUN.
|
---|
| 229 | *
|
---|
| 230 | * Start with the "AUDIO" driver, as this driver serves as the audio connector between
|
---|
| 231 | * the device emulation and the select backend(s). */
|
---|
[93444] | 232 | int rc = ptrVM.vtable()->pfnPDMR3DriverDetach(ptrVM.rawUVM(), pCfg->strDev.c_str(), pCfg->uInst, pCfg->uLUN,
|
---|
| 233 | "AUDIO", 0 /* iOccurrence */, 0 /* fFlags */);
|
---|
[70644] | 234 | if (RT_SUCCESS(rc))
|
---|
[88269] | 235 | rc = pThis->configure(pCfg->uLUN, false /* Detach */);/** @todo r=bird: Illogical and from what I can tell pointless! */
|
---|
[70533] | 236 |
|
---|
[70644] | 237 | if (RT_SUCCESS(rc))
|
---|
[70533] | 238 | {
|
---|
[70563] | 239 | pThis->mfAttached = false;
|
---|
| 240 | LogRel2(("%s: Driver detached\n", pCfg->strName.c_str()));
|
---|
[70533] | 241 | }
|
---|
| 242 | else
|
---|
[70644] | 243 | LogRel(("%s: Failed to detach audio driver, rc=%Rrc\n", pCfg->strName.c_str(), rc));
|
---|
[70533] | 244 |
|
---|
[70644] | 245 | LogFunc(("Returning %Rrc\n", rc));
|
---|
| 246 | return rc;
|
---|
[70533] | 247 | }
|
---|
| 248 |
|
---|
| 249 | /**
|
---|
| 250 | * Configures the audio driver via CFGM.
|
---|
| 251 | *
|
---|
| 252 | * @returns VBox status code.
|
---|
[70563] | 253 | * @param uLUN LUN to attach driver to.
|
---|
[70533] | 254 | * @param fAttach Whether to attach or detach the driver configuration to CFGM.
|
---|
| 255 | *
|
---|
| 256 | * @thread EMT
|
---|
| 257 | */
|
---|
[70563] | 258 | int AudioDriver::configure(unsigned uLUN, bool fAttach)
|
---|
[70533] | 259 | {
|
---|
| 260 | Console::SafeVMPtrQuiet ptrVM(mpConsole);
|
---|
[93444] | 261 | AssertReturn(ptrVM.isOk(), VERR_INVALID_STATE);
|
---|
[70533] | 262 |
|
---|
[93444] | 263 | PCFGMNODE pRoot = ptrVM.vtable()->pfnCFGMR3GetRootU(ptrVM.rawUVM());
|
---|
[70533] | 264 | AssertPtr(pRoot);
|
---|
[93444] | 265 | PCFGMNODE pDev0 = ptrVM.vtable()->pfnCFGMR3GetChildF(pRoot, "Devices/%s/%u/", mCfg.strDev.c_str(), mCfg.uInst);
|
---|
[70533] | 266 |
|
---|
[70583] | 267 | if (!pDev0) /* No audio device configured? Bail out. */
|
---|
[70626] | 268 | {
|
---|
| 269 | LogRel2(("%s: No audio device configured, skipping to attach driver\n", mCfg.strName.c_str()));
|
---|
[70583] | 270 | return VINF_SUCCESS;
|
---|
[70626] | 271 | }
|
---|
[70583] | 272 |
|
---|
[70644] | 273 | int rc = VINF_SUCCESS;
|
---|
| 274 |
|
---|
[93444] | 275 | PCFGMNODE pDevLun = ptrVM.vtable()->pfnCFGMR3GetChildF(pDev0, "LUN#%u/", uLUN);
|
---|
[70533] | 276 |
|
---|
| 277 | if (fAttach)
|
---|
| 278 | {
|
---|
[93444] | 279 | do /* break "loop" */
|
---|
[70536] | 280 | {
|
---|
[70644] | 281 | AssertMsgBreakStmt(pDevLun, ("%s: Device LUN #%u not found\n", mCfg.strName.c_str(), uLUN), rc = VERR_NOT_FOUND);
|
---|
[70533] | 282 |
|
---|
[70644] | 283 | LogRel2(("%s: Configuring audio driver (to LUN #%u)\n", mCfg.strName.c_str(), uLUN));
|
---|
[70533] | 284 |
|
---|
[93444] | 285 | ptrVM.vtable()->pfnCFGMR3RemoveNode(pDevLun); /* Remove LUN completely first. */
|
---|
[70533] | 286 |
|
---|
[70670] | 287 | /* Insert new LUN configuration and build up the new driver chain. */
|
---|
[93444] | 288 | rc = ptrVM.vtable()->pfnCFGMR3InsertNodeF(pDev0, &pDevLun, "LUN#%u/", uLUN); AssertRCBreak(rc);
|
---|
| 289 | rc = ptrVM.vtable()->pfnCFGMR3InsertString(pDevLun, "Driver", "AUDIO"); AssertRCBreak(rc);
|
---|
[70670] | 290 |
|
---|
| 291 | PCFGMNODE pLunCfg;
|
---|
[93444] | 292 | rc = ptrVM.vtable()->pfnCFGMR3InsertNode(pDevLun, "Config", &pLunCfg); AssertRCBreak(rc);
|
---|
[70670] | 293 |
|
---|
[93444] | 294 | rc = ptrVM.vtable()->pfnCFGMR3InsertStringF(pLunCfg, "DriverName", "%s", mCfg.strName.c_str()); AssertRCBreak(rc);
|
---|
| 295 | rc = ptrVM.vtable()->pfnCFGMR3InsertInteger(pLunCfg, "InputEnabled", mCfg.fEnabledIn); AssertRCBreak(rc);
|
---|
| 296 | rc = ptrVM.vtable()->pfnCFGMR3InsertInteger(pLunCfg, "OutputEnabled", mCfg.fEnabledOut); AssertRCBreak(rc);
|
---|
[70533] | 297 |
|
---|
[89580] | 298 | PCFGMNODE pAttachedDriver;
|
---|
[93444] | 299 | rc = ptrVM.vtable()->pfnCFGMR3InsertNode(pDevLun, "AttachedDriver", &pAttachedDriver); AssertRCBreak(rc);
|
---|
| 300 | rc = ptrVM.vtable()->pfnCFGMR3InsertStringF(pAttachedDriver, "Driver", "%s", mCfg.strName.c_str()); AssertRCBreak(rc);
|
---|
[89580] | 301 | PCFGMNODE pAttachedDriverCfg;
|
---|
[93444] | 302 | rc = ptrVM.vtable()->pfnCFGMR3InsertNode(pAttachedDriver, "Config", &pAttachedDriverCfg); AssertRCBreak(rc);
|
---|
[70533] | 303 |
|
---|
[89580] | 304 | /* Call the (virtual) method for driver-specific configuration. */
|
---|
[93444] | 305 | rc = configureDriver(pAttachedDriverCfg, ptrVM.vtable()); AssertRCBreak(rc);
|
---|
[70644] | 306 |
|
---|
| 307 | } while (0);
|
---|
[70533] | 308 | }
|
---|
| 309 | else /* Detach */
|
---|
| 310 | {
|
---|
[70670] | 311 | LogRel2(("%s: Unconfiguring audio driver\n", mCfg.strName.c_str()));
|
---|
| 312 | }
|
---|
[70644] | 313 |
|
---|
[89580] | 314 | if (RT_SUCCESS(rc))
|
---|
| 315 | {
|
---|
[70670] | 316 | #ifdef LOG_ENABLED
|
---|
[89580] | 317 | LogFunc(("%s: fAttach=%RTbool\n", mCfg.strName.c_str(), fAttach));
|
---|
[93444] | 318 | ptrVM.vtable()->pfnCFGMR3Dump(pDevLun);
|
---|
[70670] | 319 | #endif
|
---|
[89580] | 320 | }
|
---|
| 321 | else
|
---|
| 322 | LogRel(("%s: %s audio driver failed with rc=%Rrc\n", mCfg.strName.c_str(), fAttach ? "Configuring" : "Unconfiguring", rc));
|
---|
[70644] | 323 |
|
---|
| 324 | LogFunc(("Returning %Rrc\n", rc));
|
---|
[70533] | 325 | return rc;
|
---|
| 326 | }
|
---|
| 327 |
|
---|