DrvHostOSSAudio.cpp revision 8a204879c937e7c55da35682a0e5d2e1df91c856
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync/* $Id */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync/** @file
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * OSS (Open Sound System) host audio backend.
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync/*
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * Copyright (C) 2014-2015 Oracle Corporation
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * available from http://www.virtualbox.org. This file is free software;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * you can redistribute it and/or modify it under the terms of the GNU
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * General Public License (GPL) as published by the Free Software
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * --------------------------------------------------------------------
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include "DrvAudio.h"
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include "AudioMixBuffer.h"
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include "VBoxDD.h"
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include "vl_vbox.h"
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <errno.h>
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <fcntl.h>
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <sys/ioctl.h>
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <sys/mman.h>
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <sys/soundcard.h>
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <unistd.h>
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <iprt/alloc.h>
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#include <VBox/vmm/pdmaudioifs.h>
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#ifdef LOG_GROUP
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync# undef LOG_GROUP
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#endif
377d3eefbc092f9d4220535d896c510d560bbd74vboxsync#define LOG_GROUP LOG_GROUP_DEV_AUDIO
377d3eefbc092f9d4220535d896c510d560bbd74vboxsync#include <VBox/log.h>
377d3eefbc092f9d4220535d896c510d560bbd74vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync/**
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * OSS host audio driver instance data.
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * @implements PDMIAUDIOCONNECTOR
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsynctypedef struct DRVHOSTOSSAUDIO
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync /** Pointer to the driver instance structure. */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PPDMDRVINS pDrvIns;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync /** Pointer to host audio interface. */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PDMIHOSTAUDIO IHostAudio;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync /** Error count for not flooding the release log.
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync * UINT32_MAX for unlimited logging. */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync uint32_t cLogErrors;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync} DRVHOSTOSSAUDIO, *PDRVHOSTOSSAUDIO;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsynctypedef struct OSSAUDIOSTREAMCFG
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PDMAUDIOFMT enmFormat;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PDMAUDIOENDIANESS enmEndianess;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync uint16_t uFreq;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync uint8_t cChannels;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync uint16_t cFragments;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync uint32_t cbFragmentSize;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync} OSSAUDIOSTREAMCFG, *POSSAUDIOSTREAMCFG;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsynctypedef struct OSSAUDIOSTREAMIN
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync /** Note: Always must come first! */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PDMAUDIOHSTSTRMIN pStreamIn;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int hFile;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int cFragments;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int cbFragmentSize;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync void *pvBuf;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync size_t cbBuf;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int old_optr;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync} OSSAUDIOSTREAMIN, *POSSAUDIOSTREAMIN;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsynctypedef struct OSSAUDIOSTREAMOUT
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync /** Note: Always must come first! */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PDMAUDIOHSTSTRMOUT pStreamOut;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int hFile;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int cFragments;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int cbFragmentSize;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#ifndef RT_OS_L4
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync bool fMemMapped;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#endif
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync void *pvPCMBuf;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int old_optr;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync} OSSAUDIOSTREAMOUT, *POSSAUDIOSTREAMOUT;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsynctypedef struct OSSAUDIOCFG
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#ifndef RT_OS_L4
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync bool try_mmap;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#endif
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int nfrags;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int fragsize;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync const char *devpath_out;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync const char *devpath_in;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int debug;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync} OSSAUDIOCFG, *POSSAUDIOCFG;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic OSSAUDIOCFG s_OSSConf =
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#ifndef RT_OS_L4
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync false,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#endif
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync 4,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync 4096,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync "/dev/dsp",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync "/dev/dsp",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync 0
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync};
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync/* http://www.df.lth.se/~john_e/gems/gem002d.html */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic uint32_t popcount(uint32_t u)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync u = ((u&0x55555555) + ((u>>1)&0x55555555));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync u = ((u&0x33333333) + ((u>>2)&0x33333333));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync u = ( u&0x0000ffff) + (u>>16);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return u;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync}
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic uint32_t lsbindex(uint32_t u)
377d3eefbc092f9d4220535d896c510d560bbd74vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return popcount ((u&-u)-1);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync}
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic int drvHostOSSAudioFmtToOSS(PDMAUDIOFMT fmt)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync switch (fmt)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AUD_FMT_S8:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return AFMT_S8;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AUD_FMT_U8:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return AFMT_U8;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AUD_FMT_S16:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return AFMT_S16_LE;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AUD_FMT_U16:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return AFMT_U16_LE;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync default:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync AssertMsgFailed(("Format %ld not supported\n", fmt));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return AFMT_U8;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync}
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic int drvHostOSSAudioOSSToFmt(int fmt,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PDMAUDIOFMT *pFmt, PDMAUDIOENDIANESS *pEndianess)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync switch (fmt)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AFMT_S8:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pFmt = AUD_FMT_S8;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (pEndianess)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pEndianess = PDMAUDIOENDIANESS_LITTLE;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AFMT_U8:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pFmt = AUD_FMT_U8;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (pEndianess)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pEndianess = PDMAUDIOENDIANESS_LITTLE;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AFMT_S16_LE:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pFmt = AUD_FMT_S16;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (pEndianess)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pEndianess = PDMAUDIOENDIANESS_LITTLE;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AFMT_U16_LE:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pFmt = AUD_FMT_U16;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (pEndianess)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pEndianess = PDMAUDIOENDIANESS_LITTLE;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AFMT_S16_BE:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pFmt = AUD_FMT_S16;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (pEndianess)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pEndianess = PDMAUDIOENDIANESS_BIG;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case AFMT_U16_BE:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pFmt = AUD_FMT_U16;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (pEndianess)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *pEndianess = PDMAUDIOENDIANESS_BIG;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync default:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync AssertMsgFailed(("Format %ld not supported\n", fmt));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return VERR_NOT_SUPPORTED;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return VINF_SUCCESS;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync}
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic int drvHostOSSAudioClose(int *phFile)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (!phFile || !*phFile)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return VINF_SUCCESS;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int rc;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (close(*phFile))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Closing descriptor failed: %s\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = VERR_GENERAL_FAILURE; /** @todo */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync else
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *phFile = -1;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = VINF_SUCCESS;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return rc;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync}
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic int drvHostOSSAudioOpen(bool fIn,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync POSSAUDIOSTREAMCFG pReq, POSSAUDIOSTREAMCFG pObt,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int *phFile)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync AssertPtrReturn(pReq, VERR_INVALID_POINTER);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync AssertPtrReturn(pObt, VERR_INVALID_POINTER);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync AssertPtrReturn(phFile, VERR_INVALID_POINTER);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int rc;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int hFile;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync do
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync const char *pszDev = fIn ? s_OSSConf.devpath_in : s_OSSConf.devpath_out;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (!pszDev)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Invalid or no %s device name set\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync fIn ? "input" : "output"));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = VERR_INVALID_PARAMETER;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync hFile = open(pszDev, (fIn ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (hFile == -1)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to open %s: %s\n", pszDev, strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int iFormat = drvHostOSSAudioFmtToOSS(pReq->enmFormat);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (ioctl(hFile, SNDCTL_DSP_SAMPLESIZE, &iFormat))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to set audio format to %ld\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync iFormat, strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int cChannels = pReq->cChannels;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (ioctl(hFile, SNDCTL_DSP_CHANNELS, &cChannels))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to set number of audio channels (%d): %s\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync pReq->cChannels, strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int freq = pReq->uFreq;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (ioctl(hFile, SNDCTL_DSP_SPEED, &freq))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to set audio frequency (%dHZ): %s\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync pReq->uFreq, strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#if !(defined(VBOX) && defined(RT_OS_SOLARIS))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (ioctl(hFile, SNDCTL_DSP_NONBLOCK))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to set non-blocking mode: %s\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#endif
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int mmmmssss = (pReq->cFragments << 16) | lsbindex(pReq->cbFragmentSize);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (ioctl(hFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync pReq->cFragments, pReq->cbFragmentSize, strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync audio_buf_info abinfo;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (ioctl(hFile, fIn ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync &abinfo))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to retrieve buffer length: %s\n", strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = drvHostOSSAudioOSSToFmt(iFormat,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync &pObt->enmFormat, &pObt->enmEndianess);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (RT_SUCCESS(rc))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync pObt->cChannels = cChannels;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync pObt->uFreq = freq;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync pObt->cFragments = abinfo.fragstotal;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync pObt->cbFragmentSize = abinfo.fragsize;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync *phFile = hFile;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync while (0);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (RT_FAILURE(rc))
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync drvHostOSSAudioClose(&hFile);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogFlowFuncLeaveRC(rc);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return rc;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync}
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic DECLCALLBACK(int) drvHostOSSAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PDMAUDIOSTREAMCMD enmStreamCmd)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync NOREF(pInterface);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync NOREF(pHstStrmIn);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync NOREF(enmStreamCmd);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync /** @todo Nothing to do here right now!? */
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return VINF_SUCCESS;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync}
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsyncstatic DECLCALLBACK(int) drvHostOSSAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync PDMAUDIOSTREAMCMD enmStreamCmd)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync{
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync NOREF(pInterface);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#ifdef RT_OS_L4
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return VINF_SUCCESS;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#else
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (!pThisStrmOut->fMemMapped)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync return VINF_SUCCESS;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync#endif
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync int rc, mask;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync switch (enmStreamCmd)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case PDMAUDIOSTREAMCMD_ENABLE:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync audio_pcm_info_clear_buf(&pHstStrmOut->Props,
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync pThisStrmOut->pvPCMBuf, audioMixBufSize(&pHstStrmOut->MixBuf));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync mask = PCM_ENABLE_OUTPUT;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to enable output stream: %s\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync break;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync }
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync case PDMAUDIOSTREAMCMD_DISABLE:
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync mask = 0;
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync {
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync LogRel(("OSS: Failed to disable output stream: %s\n",
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync strerror(errno)));
527eff46676ca64c098dc7d1bf0857408c3a6f82vboxsync rc = RTErrConvertFromErrno(errno);
}
break;
}
default:
AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
rc = VERR_INVALID_PARAMETER;
break;
}
LogFlowFuncLeaveRC(rc);
return rc;
}
static DECLCALLBACK(int) drvHostOSSAudioInit(PPDMIHOSTAUDIO pInterface)
{
NOREF(pInterface);
LogFlowFuncEnter();
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvHostOSSAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
uint32_t *pcSamplesCaptured)
{
NOREF(pInterface);
AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
int rc = VINF_SUCCESS;
size_t cbToRead = RT_MIN(pThisStrmIn->cbBuf,
audioMixBufFreeBytes(&pHstStrmIn->MixBuf));
LogFlowFunc(("cbToRead=%zu\n", cbToRead));
uint32_t cWrittenTotal = 0;
uint32_t cbTemp;
ssize_t cbRead;
size_t offWrite = 0;
while (cbToRead)
{
cbTemp = RT_MIN(cbToRead, pThisStrmIn->cbBuf);
AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
cbRead = read(pThisStrmIn->hFile, pThisStrmIn->pvBuf + offWrite, cbTemp);
LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n",
cbRead, cbTemp, cbToRead));
if (cbRead < 0)
{
switch (errno)
{
case 0:
{
LogFunc(("Failed to read %z frames\n", cbRead));
rc = VERR_ACCESS_DENIED;
break;
}
case EINTR:
case EAGAIN:
rc = VERR_NO_DATA;
break;
default:
LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n",
cbTemp, rc));
rc = VERR_GENERAL_FAILURE; /** @todo */
break;
}
if (RT_FAILURE(rc))
break;
}
else if (cbRead)
{
uint32_t cWritten;
rc = audioMixBufWriteCirc(&pHstStrmIn->MixBuf,
pThisStrmIn->pvBuf, cbRead,
&cWritten);
if (RT_FAILURE(rc))
break;
uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
Assert(cbToRead >= cbWritten);
cbToRead -= cbWritten;
offWrite += cbWritten;
cWrittenTotal += cWritten;
}
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)
rc = audioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
&cProcessed);
if (pcSamplesCaptured)
*pcSamplesCaptured = cWrittenTotal;
LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
cWrittenTotal, cProcessed, rc));
}
LogFlowFuncLeaveRC(rc);
return rc;
}
static DECLCALLBACK(int) drvHostOSSAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
{
NOREF(pInterface);
AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
LogFlowFuncEnter();
if (pThisStrmIn->pvBuf)
{
RTMemFree(pThisStrmIn->pvBuf);
pThisStrmIn->pvBuf = NULL;
}
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvHostOSSAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
{
NOREF(pInterface);
AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
LogFlowFuncEnter();
#ifndef RT_OS_L4
if (!pThisStrmOut->fMemMapped)
{
if (pThisStrmOut->pvPCMBuf)
{
RTMemFree(pThisStrmOut->pvPCMBuf);
pThisStrmOut->pvPCMBuf = NULL;
}
}
#endif
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvHostOSSAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
{
NOREF(pInterface);
pCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT);
pCfg->cbStreamIn = sizeof(OSSAUDIOSTREAMIN);
pCfg->cMaxHstStrmsOut = INT_MAX;
pCfg->cMaxHstStrmsIn = INT_MAX;
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvHostOSSAudioInitIn(PPDMIHOSTAUDIO pInterface,
PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
PDMAUDIORECSOURCE enmRecSource,
uint32_t *pcSamples)
{
NOREF(pInterface);
AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
int rc;
int hFile = -1;
do
{
uint32_t cSamples;
OSSAUDIOSTREAMCFG reqStream, obtStream;
reqStream.enmFormat = pCfg->enmFormat;
reqStream.uFreq = pCfg->uHz;
reqStream.cChannels = pCfg->cChannels;
reqStream.cFragments = s_OSSConf.nfrags;
reqStream.cbFragmentSize = s_OSSConf.fragsize;
rc = drvHostOSSAudioOpen(true /* fIn */,
&reqStream, &obtStream, &hFile);
if (RT_SUCCESS(rc))
{
if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmIn->Props.uAlign)
LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
obtStream.cFragments * obtStream.cbFragmentSize,
pHstStrmIn->Props.uAlign + 1));
pThisStrmIn->hFile = hFile;
PDMAUDIOSTREAMCFG streamCfg;
streamCfg.enmFormat = obtStream.enmFormat;
streamCfg.uHz = obtStream.uFreq;
streamCfg.cChannels = pCfg->cChannels;
streamCfg.enmEndianness = obtStream.enmEndianess;
rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
if (RT_SUCCESS(rc))
{
cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
>> pHstStrmIn->Props.cShift;
if (!cSamples)
rc = VERR_INVALID_PARAMETER;
}
}
if (RT_SUCCESS(rc))
{
size_t cbBuf = cSamples * (1 << pHstStrmIn->Props.cShift);
pThisStrmIn->pvBuf = RTMemAlloc(cbBuf);
if (!pThisStrmIn->pvBuf)
{
LogRel(("OSS: Failed allocating ADC buffer with %RU32 samples, each %d bytes\n",
cSamples, 1 << pHstStrmIn->Props.cShift));
rc = VERR_NO_MEMORY;
}
pThisStrmIn->cbBuf = cbBuf;
if (pcSamples)
*pcSamples = cSamples;
}
} while (0);
if (RT_FAILURE(rc))
drvHostOSSAudioClose(&hFile);
LogFlowFuncLeaveRC(rc);
return rc;
}
static DECLCALLBACK(int) drvHostOSSAudioInitOut(PPDMIHOSTAUDIO pInterface,
PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
uint32_t *pcSamples)
{
NOREF(pInterface);
AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
int rc;
int hFile = -1;
do
{
uint32_t cSamples;
OSSAUDIOSTREAMCFG reqStream, obtStream;
reqStream.enmFormat = pCfg->enmFormat;
reqStream.uFreq = pCfg->uHz;
reqStream.cChannels = pCfg->cChannels;
reqStream.cFragments = s_OSSConf.nfrags;
reqStream.cbFragmentSize = s_OSSConf.fragsize;
rc = drvHostOSSAudioOpen(false /* fIn */,
&reqStream, &obtStream, &hFile);
if (RT_SUCCESS(rc))
{
if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmOut->Props.uAlign)
LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
obtStream.cFragments * obtStream.cbFragmentSize,
pHstStrmOut->Props.uAlign + 1));
pThisStrmOut->hFile = hFile;
PDMAUDIOSTREAMCFG streamCfg;
streamCfg.enmFormat = obtStream.enmFormat;
streamCfg.uHz = obtStream.uFreq;
streamCfg.cChannels = pCfg->cChannels;
streamCfg.enmEndianness = obtStream.enmEndianess;
rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
if (RT_SUCCESS(rc))
cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
>> pHstStrmOut->Props.cShift;
}
if (RT_SUCCESS(rc))
{
#ifndef RT_OS_L4
pThisStrmOut->fMemMapped = false;
if (s_OSSConf.try_mmap)
{
pThisStrmOut->pvPCMBuf = mmap(0, cSamples << pHstStrmOut->Props.cShift,
PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
if (pThisStrmOut->pvPCMBuf == MAP_FAILED)
{
LogRel(("OSS: Failed to memory map %zu bytes of DAC output file: %s\n",
cSamples << pHstStrmOut->Props.cShift, strerror(errno)));
rc = RTErrConvertFromErrno(errno);
break;
}
else
{
int mask = 0;
if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
{
LogRel(("OSS: Failed to retrieve initial trigger mask: %s\n",
strerror(errno)));
rc = RTErrConvertFromErrno(errno);
/* Note: No break here, need to unmap file first! */
}
else
{
mask = PCM_ENABLE_OUTPUT;
if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
{
LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n",
strerror(errno)));
rc = RTErrConvertFromErrno(errno);
/* Note: No break here, need to unmap file first! */
}
else
pThisStrmOut->fMemMapped = true;
}
if (!pThisStrmOut->fMemMapped)
{
int rc2 = munmap(pThisStrmOut->pvPCMBuf,
cSamples << pHstStrmOut->Props.cShift);
if (rc2)
LogRel(("OSS: Failed to unmap DAC output file: %s\n",
strerror(errno)));
break;
}
}
}
#endif /* !RT_OS_L4 */
/* Memory mapping failed above? Try allocating an own buffer. */
#ifndef RT_OS_L4
if (!pThisStrmOut->fMemMapped)
{
#endif
LogFlowFunc(("cSamples=%RU32\n", cSamples));
pThisStrmOut->pvPCMBuf = RTMemAlloc(cSamples * (1 << pHstStrmOut->Props.cShift));
if (!pThisStrmOut->pvPCMBuf)
{
LogRel(("OSS: Failed allocating DAC buffer with %RU32 samples, each %d bytes\n",
cSamples, 1 << pHstStrmOut->Props.cShift));
rc = VERR_NO_MEMORY;
break;
}
#ifndef RT_OS_L4
}
#endif
if (pcSamples)
*pcSamples = cSamples;
}
} while (0);
if (RT_FAILURE(rc))
drvHostOSSAudioClose(&hFile);
LogFlowFuncLeaveRC(rc);
return rc;
}
static DECLCALLBACK(bool) drvHostOSSAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
{
NOREF(pInterface);
NOREF(enmDir);
return true; /* Always all enabled. */
}
static DECLCALLBACK(int) drvHostOSSAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
uint32_t *pcSamplesPlayed)
{
NOREF(pInterface);
AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
int rc;
uint32_t cbReadTotal = 0;
count_info cntinfo;
do
{
size_t cbBuf = audioMixBufSizeBytes(&pHstStrmOut->MixBuf);
uint32_t cLive = drvAudioHstOutSamplesLive(pHstStrmOut,
NULL /* pcStreamsLive */);
uint32_t cToRead;
#ifndef RT_OS_L4
if (pThisStrmOut->fMemMapped)
{
/* Get current playback pointer. */
int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
if (!rc2)
{
LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
strerror(errno)));
rc = RTErrConvertFromErrno(errno);
break;
}
/* Nothing to play? */
if (cntinfo.ptr == pThisStrmOut->old_optr)
break;
int cbData;
if (cntinfo.ptr > pThisStrmOut->old_optr)
cbData = cntinfo.ptr - pThisStrmOut->old_optr;
else
cbData = cbBuf + cntinfo.ptr - pThisStrmOut->old_optr;
Assert(cbData);
cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbData),
cLive);
}
else
{
#endif
audio_buf_info abinfo;
int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
if (rc2 < 0)
{
LogRel(("OSS: Failed to retrieve current playback buffer: %s\n",
strerror(errno)));
rc = RTErrConvertFromErrno(errno);
break;
}
if ((size_t)abinfo.bytes > cbBuf)
{
LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
abinfo.bytes, cbBuf));
abinfo.bytes = cbBuf;
/* Keep going. */
}
if (!abinfo.bytes < 0)
{
LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
abinfo.bytes, cbBuf));
rc = VERR_INVALID_PARAMETER;
break;
}
cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, abinfo.bytes),
cLive);
if (!cToRead)
break;
#ifndef RT_OS_L4
}
#endif
size_t cbToRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cToRead);
LogFlowFunc(("cbToRead=%zu\n", cbToRead));
uint32_t cRead, cbRead;
while (cbToRead)
{
rc = audioMixBufReadCirc(&pHstStrmOut->MixBuf,
pThisStrmOut->pvPCMBuf, cbToRead, &cRead);
if (RT_FAILURE(rc))
break;
cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
ssize_t cbWritten = write(pThisStrmOut->hFile, pThisStrmOut->pvPCMBuf,
cbRead);
if (cbWritten == -1)
{
LogRel(("OSS: Failed writing output data %s\n", strerror(errno)));
rc = RTErrConvertFromErrno(errno);
break;
}
Assert(cbToRead >= cRead);
cbToRead -= cbRead;
cbReadTotal += cbRead;
}
#ifndef RT_OS_L4
/* Update read pointer. */
if (pThisStrmOut->fMemMapped)
pThisStrmOut->old_optr = cntinfo.ptr;
#endif
} while(0);
if (RT_SUCCESS(rc))
{
uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
if (cReadTotal)
audioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
if (pcSamplesPlayed)
*pcSamplesPlayed = cReadTotal;
LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
cReadTotal, cbReadTotal, rc));
}
LogFlowFuncLeaveRC(rc);
return rc;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
return NULL;
}
static DECLCALLBACK(void) drvHostOSSAudioDestruct(PPDMDRVINS pDrvIns)
{
}
/**
* Constructs an OSS audio driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
LogRel(("Audio: Initializing OSS driver\n"));
/*
* Init the static parts.
*/
pThis->pDrvIns = pDrvIns;
/* IBase */
pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
/* IHostAudio */
PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
return VINF_SUCCESS;
}
/**
* Char driver registration record.
*/
const PDMDRVREG g_DrvHostOSSAudio =
{
/* u32Version */
PDM_DRVREG_VERSION,
/* szName */
"OSSAudio",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"OSS audio host driver",
/* fFlags */
PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
/* fClass. */
PDM_DRVREG_CLASS_AUDIO,
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(DRVHOSTOSSAUDIO),
/* pfnConstruct */
drvHostOSSAudioConstruct,
/* pfnDestruct */
drvHostOSSAudioDestruct,
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
PDM_DRVREG_VERSION
};