DrvHostOSSAudio.cpp revision 1705f7565ed8533058b8541d72d6c5d4453de00f
/* $Id */
/** @file
* OSS (Open Sound System) host audio backend.
*/
/*
* Copyright (C) 2014-2015 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
* --------------------------------------------------------------------
*/
#include "DrvAudio.h"
#include "AudioMixBuffer.h"
#include "VBoxDD.h"
#include "vl_vbox.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <unistd.h>
#ifdef LOG_GROUP
#endif
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
/**
* OSS host audio driver instance data.
* @implements PDMIAUDIOCONNECTOR
*/
typedef struct DRVHOSTOSSAUDIO
{
/** Pointer to the driver instance structure. */
/** Pointer to host audio interface. */
/** Error count for not flooding the release log.
* UINT32_MAX for unlimited logging. */
typedef struct OSSAUDIOSTREAMCFG
{
typedef struct OSSAUDIOSTREAMIN
{
/** Note: Always must come first! */
int hFile;
int cFragments;
int cbFragmentSize;
void *pvBuf;
int old_optr;
typedef struct OSSAUDIOSTREAMOUT
{
/** Note: Always must come first! */
int hFile;
int cFragments;
int cbFragmentSize;
#ifndef RT_OS_L4
bool fMemMapped;
#endif
void *pvPCMBuf;
int old_optr;
typedef struct OSSAUDIOCFG
{
#ifndef RT_OS_L4
bool try_mmap;
#endif
int nfrags;
int fragsize;
const char *devpath_out;
const char *devpath_in;
int debug;
} OSSAUDIOCFG, *POSSAUDIOCFG;
static OSSAUDIOCFG s_OSSConf =
{
#ifndef RT_OS_L4
false,
#endif
4,
4096,
0
};
{
u = ((u&0x55555555) + ((u>>1)&0x55555555));
u = ((u&0x33333333) + ((u>>2)&0x33333333));
u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
u = ( u&0x0000ffff) + (u>>16);
return u;
}
{
return popcount ((u&-u)-1);
}
{
switch (fmt)
{
case AUD_FMT_S8:
return AFMT_S8;
case AUD_FMT_U8:
return AFMT_U8;
case AUD_FMT_S16:
return AFMT_S16_LE;
case AUD_FMT_U16:
return AFMT_U16_LE;
default:
break;
}
return AFMT_U8;
}
static int drvHostOSSAudioOSSToFmt(int fmt,
{
switch (fmt)
{
case AFMT_S8:
*pFmt = AUD_FMT_S8;
if (pEndianess)
break;
case AFMT_U8:
*pFmt = AUD_FMT_U8;
if (pEndianess)
break;
case AFMT_S16_LE:
*pFmt = AUD_FMT_S16;
if (pEndianess)
break;
case AFMT_U16_LE:
*pFmt = AUD_FMT_U16;
if (pEndianess)
break;
case AFMT_S16_BE:
*pFmt = AUD_FMT_S16;
if (pEndianess)
break;
case AFMT_U16_BE:
*pFmt = AUD_FMT_U16;
if (pEndianess)
break;
default:
return VERR_NOT_SUPPORTED;
}
return VINF_SUCCESS;
}
static int drvHostOSSAudioClose(int *phFile)
{
return VINF_SUCCESS;
int rc;
{
LogRel(("OSS: Closing descriptor failed: %s\n",
}
else
{
*phFile = -1;
rc = VINF_SUCCESS;
}
return rc;
}
static int drvHostOSSAudioOpen(bool fIn,
int *phFile)
{
int rc;
int hFile;
do
{
if (!pszDev)
{
LogRel(("OSS: Invalid or no %s device name set\n",
break;
}
if (hFile == -1)
{
break;
}
{
LogRel(("OSS: Failed to set audio format to %ld\n",
break;
}
{
LogRel(("OSS: Failed to set number of audio channels (%d): %s\n",
break;
}
{
LogRel(("OSS: Failed to set audio frequency (%dHZ): %s\n",
break;
}
/* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
#if !(defined(VBOX) && defined(RT_OS_SOLARIS))
{
LogRel(("OSS: Failed to set non-blocking mode: %s\n",
break;
}
#endif
{
LogRel(("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s\n",
break;
}
&abinfo))
{
break;
}
if (RT_SUCCESS(rc))
{
}
}
while (0);
if (RT_FAILURE(rc))
return rc;
}
static DECLCALLBACK(int) drvHostOSSAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
{
/** @todo Nothing to do here right now!? */
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvHostOSSAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
{
#ifdef RT_OS_L4
return VINF_SUCCESS;
#else
if (!pThisStrmOut->fMemMapped)
return VINF_SUCCESS;
#endif
int rc = VINF_SUCCESS;
int mask;
switch (enmStreamCmd)
{
case PDMAUDIOSTREAMCMD_ENABLE:
{
{
}
break;
}
{
mask = 0;
{
}
break;
}
default:
break;
}
return rc;
}
{
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvHostOSSAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
{
int rc = VINF_SUCCESS;
uint32_t cWrittenTotal = 0;
while (cbToRead)
{
LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n",
if (cbRead < 0)
{
switch (errno)
{
case 0:
{
break;
}
case EINTR:
case EAGAIN:
rc = VERR_NO_DATA;
break;
default:
LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n",
break;
}
if (RT_FAILURE(rc))
break;
}
else if (cbRead)
{
&cWritten);
if (RT_FAILURE(rc))
break;
}
else /* No more data, try next round. */
break;
}
if (rc == VERR_NO_DATA)
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
uint32_t cProcessed = 0;
if (cWrittenTotal)
&cProcessed);
if (pcSamplesCaptured)
LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
}
return rc;
}
static DECLCALLBACK(int) drvHostOSSAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
{
if (pThisStrmIn->pvBuf)
{
}
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvHostOSSAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
{
#ifndef RT_OS_L4
if (!pThisStrmOut->fMemMapped)
{
if (pThisStrmOut->pvPCMBuf)
{
}
}
#endif
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvHostOSSAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
{
return VINF_SUCCESS;
}
{
int rc;
int hFile = -1;
do
{
if (RT_SUCCESS(rc))
{
LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
if (RT_SUCCESS(rc))
{
if (!cSamples)
}
}
if (RT_SUCCESS(rc))
{
if (!pThisStrmIn->pvBuf)
{
LogRel(("OSS: Failed allocating ADC buffer with %RU32 samples, each %d bytes\n",
rc = VERR_NO_MEMORY;
}
if (pcSamples)
}
} while (0);
if (RT_FAILURE(rc))
return rc;
}
{
int rc;
int hFile = -1;
do
{
if (RT_SUCCESS(rc))
{
LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
{
#ifndef RT_OS_L4
pThisStrmOut->fMemMapped = false;
{
{
LogRel(("OSS: Failed to memory map %zu bytes of DAC output file: %s\n",
break;
}
else
{
int mask = 0;
{
LogRel(("OSS: Failed to retrieve initial trigger mask: %s\n",
/* Note: No break here, need to unmap file first! */
}
else
{
{
LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n",
/* Note: No break here, need to unmap file first! */
}
else
pThisStrmOut->fMemMapped = true;
}
if (!pThisStrmOut->fMemMapped)
{
if (rc2)
LogRel(("OSS: Failed to unmap DAC output file: %s\n",
break;
}
}
}
#endif /* !RT_OS_L4 */
/* Memory mapping failed above? Try allocating an own buffer. */
#ifndef RT_OS_L4
if (!pThisStrmOut->fMemMapped)
{
#endif
if (!pThisStrmOut->pvPCMBuf)
{
LogRel(("OSS: Failed allocating DAC buffer with %RU32 samples, each %d bytes\n",
rc = VERR_NO_MEMORY;
break;
}
#ifndef RT_OS_L4
}
#endif
if (pcSamples)
}
} while (0);
if (RT_FAILURE(rc))
return rc;
}
{
return true; /* Always all enabled. */
}
static DECLCALLBACK(int) drvHostOSSAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
{
int rc = VINF_SUCCESS;
uint32_t cbReadTotal = 0;
do
{
NULL /* pcStreamsLive */);
#ifndef RT_OS_L4
if (pThisStrmOut->fMemMapped)
{
/* Get current playback pointer. */
if (!rc2)
{
LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
break;
}
/* Nothing to play? */
break;
int cbData;
else
cLive);
}
else
{
#endif
if (rc2 < 0)
{
LogRel(("OSS: Failed to retrieve current playback buffer: %s\n",
break;
}
{
LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
/* Keep going. */
}
{
LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
break;
}
cLive);
if (!cToRead)
break;
#ifndef RT_OS_L4
}
#endif
while (cbToRead)
{
if (RT_FAILURE(rc))
break;
cbRead);
if (cbWritten == -1)
{
break;
}
cbReadTotal += cbRead;
}
#ifndef RT_OS_L4
/* Update read pointer. */
if (pThisStrmOut->fMemMapped)
#endif
} while(0);
if (RT_SUCCESS(rc))
{
if (cReadTotal)
if (pcSamplesPlayed)
LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
}
return rc;
}
{
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/**
* Constructs an OSS audio driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
LogRel(("Audio: Initializing OSS driver\n"));
/*
* Init the static parts.
*/
/* IBase */
/* IHostAudio */
return VINF_SUCCESS;
}
/**
* Char driver registration record.
*/
const PDMDRVREG g_DrvHostOSSAudio =
{
/* u32Version */
/* szName */
"OSSAudio",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"OSS audio host driver",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(DRVHOSTOSSAUDIO),
/* pfnConstruct */
/* pfnDestruct */
NULL,
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
};