VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostOSSAudio.cpp@ 58923

Last change on this file since 58923 was 58378, checked in by vboxsync, 9 years ago

Audio: Renaming / cleanups:

  • Got rid of custom shifting and use AUDIOMIXBUF_S2B / AUDIOMIXBUF_B2S instead.
  • Renamed drvAudioStreamCfgToProps() to DrvAudioStreamCfgToProps() as this is a public function for the host backends.
  • Replaced drvAudioHstOutSamplesLive() with AudioMixBufAvail().
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.2 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 58378 2015-10-22 12:46:32Z vboxsync $ */
2/** @file
3 * OSS (Open Sound System) host audio backend.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
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#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
19#include <VBox/log.h>
20#include "DrvAudio.h"
21#include "AudioMixBuffer.h"
22
23#include "VBoxDD.h"
24
25#include <errno.h>
26#include <fcntl.h>
27#include <sys/ioctl.h>
28#include <sys/mman.h>
29#include <sys/soundcard.h>
30#include <unistd.h>
31
32#include <iprt/alloc.h>
33#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
34#include <VBox/vmm/pdmaudioifs.h>
35
36/**
37 * OSS host audio driver instance data.
38 * @implements PDMIAUDIOCONNECTOR
39 */
40typedef struct DRVHOSTOSSAUDIO
41{
42 /** Pointer to the driver instance structure. */
43 PPDMDRVINS pDrvIns;
44 /** Pointer to host audio interface. */
45 PDMIHOSTAUDIO IHostAudio;
46 /** Error count for not flooding the release log.
47 * UINT32_MAX for unlimited logging. */
48 uint32_t cLogErrors;
49} DRVHOSTOSSAUDIO, *PDRVHOSTOSSAUDIO;
50
51typedef struct OSSAUDIOSTREAMCFG
52{
53 PDMAUDIOFMT enmFormat;
54 PDMAUDIOENDIANNESS enmENDIANNESS;
55 uint16_t uFreq;
56 uint8_t cChannels;
57 uint16_t cFragments;
58 uint32_t cbFragmentSize;
59} OSSAUDIOSTREAMCFG, *POSSAUDIOSTREAMCFG;
60
61typedef struct OSSAUDIOSTREAMIN
62{
63 /** Note: Always must come first! */
64 PDMAUDIOHSTSTRMIN pStreamIn;
65 int hFile;
66 int cFragments;
67 int cbFragmentSize;
68 /** Own PCM buffer. */
69 void *pvPCMBuf;
70 /** Size (in bytes) of own PCM buffer. */
71 size_t cbPCMBuf;
72 int old_optr;
73} OSSAUDIOSTREAMIN, *POSSAUDIOSTREAMIN;
74
75typedef struct OSSAUDIOSTREAMOUT
76{
77 /** Note: Always must come first! */
78 PDMAUDIOHSTSTRMOUT pStreamOut;
79 int hFile;
80 int cFragments;
81 int cbFragmentSize;
82#ifndef RT_OS_L4
83 /** Whether we use a memory mapped file instead of our
84 * own allocated PCM buffer below. */
85 bool fMemMapped;
86#endif
87 /** Own PCM buffer in case memory mapping is unavailable. */
88 void *pvPCMBuf;
89 /** Size (in bytes) of own PCM buffer. */
90 size_t cbPCMBuf;
91 int old_optr;
92} OSSAUDIOSTREAMOUT, *POSSAUDIOSTREAMOUT;
93
94typedef struct OSSAUDIOCFG
95{
96#ifndef RT_OS_L4
97 bool try_mmap;
98#endif
99 int nfrags;
100 int fragsize;
101 const char *devpath_out;
102 const char *devpath_in;
103 int debug;
104} OSSAUDIOCFG, *POSSAUDIOCFG;
105
106static OSSAUDIOCFG s_OSSConf =
107{
108#ifndef RT_OS_L4
109 false,
110#endif
111 4,
112 4096,
113 "/dev/dsp",
114 "/dev/dsp",
115 0
116};
117
118
119/* http://www.df.lth.se/~john_e/gems/gem002d.html */
120static uint32_t popcount(uint32_t u)
121{
122 u = ((u&0x55555555) + ((u>>1)&0x55555555));
123 u = ((u&0x33333333) + ((u>>2)&0x33333333));
124 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
125 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
126 u = ( u&0x0000ffff) + (u>>16);
127 return u;
128}
129
130static uint32_t lsbindex(uint32_t u)
131{
132 return popcount ((u&-u)-1);
133}
134
135static int drvHostOSSAudioFmtToOSS(PDMAUDIOFMT fmt)
136{
137 switch (fmt)
138 {
139 case AUD_FMT_S8:
140 return AFMT_S8;
141
142 case AUD_FMT_U8:
143 return AFMT_U8;
144
145 case AUD_FMT_S16:
146 return AFMT_S16_LE;
147
148 case AUD_FMT_U16:
149 return AFMT_U16_LE;
150
151 default:
152 break;
153 }
154
155 AssertMsgFailed(("Format %ld not supported\n", fmt));
156 return AFMT_U8;
157}
158
159static int drvHostOSSAudioOSSToFmt(int fmt,
160 PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pENDIANNESS)
161{
162 switch (fmt)
163 {
164 case AFMT_S8:
165 *pFmt = AUD_FMT_S8;
166 if (pENDIANNESS)
167 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
168 break;
169
170 case AFMT_U8:
171 *pFmt = AUD_FMT_U8;
172 if (pENDIANNESS)
173 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
174 break;
175
176 case AFMT_S16_LE:
177 *pFmt = AUD_FMT_S16;
178 if (pENDIANNESS)
179 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
180 break;
181
182 case AFMT_U16_LE:
183 *pFmt = AUD_FMT_U16;
184 if (pENDIANNESS)
185 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
186 break;
187
188 case AFMT_S16_BE:
189 *pFmt = AUD_FMT_S16;
190 if (pENDIANNESS)
191 *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
192 break;
193
194 case AFMT_U16_BE:
195 *pFmt = AUD_FMT_U16;
196 if (pENDIANNESS)
197 *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
198 break;
199
200 default:
201 AssertMsgFailed(("Format %ld not supported\n", fmt));
202 return VERR_NOT_SUPPORTED;
203 }
204
205 return VINF_SUCCESS;
206}
207
208static int drvHostOSSAudioClose(int *phFile)
209{
210 if (!phFile || !*phFile)
211 return VINF_SUCCESS;
212
213 int rc;
214 if (close(*phFile))
215 {
216 LogRel(("OSS: Closing descriptor failed: %s\n",
217 strerror(errno)));
218 rc = VERR_GENERAL_FAILURE; /** @todo */
219 }
220 else
221 {
222 *phFile = -1;
223 rc = VINF_SUCCESS;
224 }
225
226 return rc;
227}
228
229static int drvHostOSSAudioOpen(bool fIn,
230 POSSAUDIOSTREAMCFG pReq, POSSAUDIOSTREAMCFG pObt,
231 int *phFile)
232{
233 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
234 AssertPtrReturn(pObt, VERR_INVALID_POINTER);
235 AssertPtrReturn(phFile, VERR_INVALID_POINTER);
236
237 int rc;
238 int hFile;
239
240 do
241 {
242 const char *pszDev = fIn ? s_OSSConf.devpath_in : s_OSSConf.devpath_out;
243 if (!pszDev)
244 {
245 LogRel(("OSS: Invalid or no %s device name set\n",
246 fIn ? "input" : "output"));
247 rc = VERR_INVALID_PARAMETER;
248 break;
249 }
250
251 hFile = open(pszDev, (fIn ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
252 if (hFile == -1)
253 {
254 LogRel(("OSS: Failed to open %s: %s(%d)\n", pszDev, strerror(errno), errno));
255 rc = RTErrConvertFromErrno(errno);
256 break;
257 }
258
259 int iFormat = drvHostOSSAudioFmtToOSS(pReq->enmFormat);
260 if (ioctl(hFile, SNDCTL_DSP_SAMPLESIZE, &iFormat))
261 {
262 LogRel(("OSS: Failed to set audio format to %ld errno=%s(%d)\n", iFormat, strerror(errno), errno));
263 rc = RTErrConvertFromErrno(errno);
264 break;
265 }
266
267 int cChannels = pReq->cChannels;
268 if (ioctl(hFile, SNDCTL_DSP_CHANNELS, &cChannels))
269 {
270 LogRel(("OSS: Failed to set number of audio channels (%d): %s(%d)\n", pReq->cChannels, strerror(errno), errno));
271 rc = RTErrConvertFromErrno(errno);
272 break;
273 }
274
275 int freq = pReq->uFreq;
276 if (ioctl(hFile, SNDCTL_DSP_SPEED, &freq))
277 {
278 LogRel(("OSS: Failed to set audio frequency (%dHZ): %s(%d)\n", pReq->uFreq, strerror(errno), errno));
279 rc = RTErrConvertFromErrno(errno);
280 break;
281 }
282
283 /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
284#if !(defined(VBOX) && defined(RT_OS_SOLARIS))
285 if (ioctl(hFile, SNDCTL_DSP_NONBLOCK))
286 {
287 LogRel(("OSS: Failed to set non-blocking mode: %s(%d)\n", strerror(errno), errno));
288 rc = RTErrConvertFromErrno(errno);
289 break;
290 }
291#endif
292 int mmmmssss = (pReq->cFragments << 16) | lsbindex(pReq->cbFragmentSize);
293 if (ioctl(hFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss))
294 {
295 LogRel(("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s(%d)\n",
296 pReq->cFragments, pReq->cbFragmentSize, strerror(errno), errno));
297 rc = RTErrConvertFromErrno(errno);
298 break;
299 }
300
301 audio_buf_info abinfo;
302 if (ioctl(hFile, fIn ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo))
303 {
304 LogRel(("OSS: Failed to retrieve buffer length: %s(%d)\n", strerror(errno), errno));
305 rc = RTErrConvertFromErrno(errno);
306 break;
307 }
308
309 rc = drvHostOSSAudioOSSToFmt(iFormat, &pObt->enmFormat, &pObt->enmENDIANNESS);
310 if (RT_SUCCESS(rc))
311 {
312 pObt->cChannels = cChannels;
313 pObt->uFreq = freq;
314 pObt->cFragments = abinfo.fragstotal;
315 pObt->cbFragmentSize = abinfo.fragsize;
316
317 *phFile = hFile;
318 }
319 }
320 while (0);
321
322 if (RT_FAILURE(rc))
323 drvHostOSSAudioClose(&hFile);
324
325 LogFlowFuncLeaveRC(rc);
326 return rc;
327}
328
329static DECLCALLBACK(int) drvHostOSSAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
330 PDMAUDIOSTREAMCMD enmStreamCmd)
331{
332 NOREF(pInterface);
333 NOREF(pHstStrmIn);
334 NOREF(enmStreamCmd);
335
336 /** @todo Nothing to do here right now!? */
337
338 return VINF_SUCCESS;
339}
340
341static DECLCALLBACK(int) drvHostOSSAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
342 PDMAUDIOSTREAMCMD enmStreamCmd)
343{
344 NOREF(pInterface);
345 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
346
347 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
348
349#ifdef RT_OS_L4
350 return VINF_SUCCESS;
351#else
352 if (!pThisStrmOut->fMemMapped)
353 return VINF_SUCCESS;
354#endif
355
356 int rc = VINF_SUCCESS;
357 int mask;
358 switch (enmStreamCmd)
359 {
360 case PDMAUDIOSTREAMCMD_ENABLE:
361 {
362 DrvAudioClearBuf(&pHstStrmOut->Props,
363 pThisStrmOut->pvPCMBuf, pThisStrmOut->cbPCMBuf, AudioMixBufSize(&pHstStrmOut->MixBuf));
364
365 mask = PCM_ENABLE_OUTPUT;
366 if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
367 {
368 LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
369 rc = RTErrConvertFromErrno(errno);
370 }
371
372 break;
373 }
374
375 case PDMAUDIOSTREAMCMD_DISABLE:
376 {
377 mask = 0;
378 if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
379 {
380 LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
381 rc = RTErrConvertFromErrno(errno);
382 }
383
384 break;
385 }
386
387 default:
388 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
389 rc = VERR_INVALID_PARAMETER;
390 break;
391 }
392
393 LogFlowFuncLeaveRC(rc);
394 return rc;
395}
396
397static DECLCALLBACK(int) drvHostOSSAudioInit(PPDMIHOSTAUDIO pInterface)
398{
399 NOREF(pInterface);
400
401 LogFlowFuncEnter();
402
403 return VINF_SUCCESS;
404}
405
406static DECLCALLBACK(int) drvHostOSSAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
407 uint32_t *pcSamplesCaptured)
408{
409 NOREF(pInterface);
410 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
411
412 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
413
414 int rc = VINF_SUCCESS;
415 size_t cbToRead = RT_MIN(pThisStrmIn->cbPCMBuf,
416 AudioMixBufFreeBytes(&pHstStrmIn->MixBuf));
417
418 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
419
420 uint32_t cWrittenTotal = 0;
421 uint32_t cbTemp;
422 ssize_t cbRead;
423 size_t offWrite = 0;
424
425 while (cbToRead)
426 {
427 cbTemp = RT_MIN(cbToRead, pThisStrmIn->cbPCMBuf);
428 AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
429 cbRead = read(pThisStrmIn->hFile, (uint8_t *)pThisStrmIn->pvPCMBuf + offWrite, cbTemp);
430
431 LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n",
432 cbRead, cbTemp, cbToRead));
433
434 if (cbRead < 0)
435 {
436 switch (errno)
437 {
438 case 0:
439 {
440 LogFunc(("Failed to read %z frames\n", cbRead));
441 rc = VERR_ACCESS_DENIED;
442 break;
443 }
444
445 case EINTR:
446 case EAGAIN:
447 rc = VERR_NO_DATA;
448 break;
449
450 default:
451 LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n",
452 cbTemp, rc));
453 rc = VERR_GENERAL_FAILURE; /** @todo */
454 break;
455 }
456
457 if (RT_FAILURE(rc))
458 break;
459 }
460 else if (cbRead)
461 {
462 uint32_t cWritten;
463 rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
464 pThisStrmIn->pvPCMBuf, cbRead,
465 &cWritten);
466 if (RT_FAILURE(rc))
467 break;
468
469 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
470
471 Assert(cbToRead >= cbWritten);
472 cbToRead -= cbWritten;
473 offWrite += cbWritten;
474 cWrittenTotal += cWritten;
475 }
476 else /* No more data, try next round. */
477 break;
478 }
479
480 if (rc == VERR_NO_DATA)
481 rc = VINF_SUCCESS;
482
483 if (RT_SUCCESS(rc))
484 {
485 uint32_t cProcessed = 0;
486 if (cWrittenTotal)
487 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
488 &cProcessed);
489
490 if (pcSamplesCaptured)
491 *pcSamplesCaptured = cWrittenTotal;
492
493 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
494 cWrittenTotal, cProcessed, rc));
495 }
496
497 LogFlowFuncLeaveRC(rc);
498 return rc;
499}
500
501static DECLCALLBACK(int) drvHostOSSAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
502{
503 NOREF(pInterface);
504 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
505
506 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
507
508 LogFlowFuncEnter();
509
510 if (pThisStrmIn->pvPCMBuf)
511 {
512 Assert(pThisStrmIn->cbPCMBuf);
513
514 RTMemFree(pThisStrmIn->pvPCMBuf);
515 pThisStrmIn->pvPCMBuf = NULL;
516 }
517
518 pThisStrmIn->cbPCMBuf = 0;
519
520 return VINF_SUCCESS;
521}
522
523static DECLCALLBACK(int) drvHostOSSAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
524{
525 NOREF(pInterface);
526 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
527
528 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
529
530 LogFlowFuncEnter();
531
532#ifndef RT_OS_L4
533 if (!pThisStrmOut->fMemMapped)
534 {
535 if (pThisStrmOut->pvPCMBuf)
536 {
537 Assert(pThisStrmOut->cbPCMBuf);
538
539 RTMemFree(pThisStrmOut->pvPCMBuf);
540 pThisStrmOut->pvPCMBuf = NULL;
541 }
542
543 pThisStrmOut->cbPCMBuf = 0;
544 }
545#endif
546
547 return VINF_SUCCESS;
548}
549
550static DECLCALLBACK(int) drvHostOSSAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
551{
552 NOREF(pInterface);
553
554 pCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT);
555 pCfg->cbStreamIn = sizeof(OSSAUDIOSTREAMIN);
556 pCfg->cMaxHstStrmsOut = INT_MAX;
557 pCfg->cMaxHstStrmsIn = INT_MAX;
558
559 return VINF_SUCCESS;
560}
561
562static DECLCALLBACK(int) drvHostOSSAudioInitIn(PPDMIHOSTAUDIO pInterface,
563 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
564 PDMAUDIORECSOURCE enmRecSource,
565 uint32_t *pcSamples)
566{
567 NOREF(pInterface);
568 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
569 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
570
571 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
572
573 int rc;
574 int hFile = -1;
575
576 do
577 {
578 uint32_t cSamples;
579
580 OSSAUDIOSTREAMCFG reqStream, obtStream;
581 reqStream.enmFormat = pCfg->enmFormat;
582 reqStream.uFreq = pCfg->uHz;
583 reqStream.cChannels = pCfg->cChannels;
584 reqStream.cFragments = s_OSSConf.nfrags;
585 reqStream.cbFragmentSize = s_OSSConf.fragsize;
586
587 rc = drvHostOSSAudioOpen(true /* fIn */,
588 &reqStream, &obtStream, &hFile);
589 if (RT_SUCCESS(rc))
590 {
591 if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmIn->Props.uAlign)
592 LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
593 obtStream.cFragments * obtStream.cbFragmentSize,
594 pHstStrmIn->Props.uAlign + 1));
595
596 pThisStrmIn->hFile = hFile;
597
598 PDMAUDIOSTREAMCFG streamCfg;
599 streamCfg.enmFormat = obtStream.enmFormat;
600 streamCfg.uHz = obtStream.uFreq;
601 streamCfg.cChannels = pCfg->cChannels;
602 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
603
604 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
605 if (RT_SUCCESS(rc))
606 {
607 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
608 >> pHstStrmIn->Props.cShift;
609 if (!cSamples)
610 rc = VERR_INVALID_PARAMETER;
611 }
612 }
613
614 if (RT_SUCCESS(rc))
615 {
616 size_t cbSample = (1 << pHstStrmIn->Props.cShift);
617 size_t cbBuf = cSamples * cbSample;
618 pThisStrmIn->pvPCMBuf = RTMemAlloc(cbBuf);
619 if (!pThisStrmIn->pvPCMBuf)
620 {
621 LogRel(("OSS: Failed allocating ADC buffer with %RU32 samples (%zu bytes per sample)\n", cSamples, cbSample));
622 rc = VERR_NO_MEMORY;
623 }
624
625 pThisStrmIn->cbPCMBuf = cbBuf;
626
627 if (pcSamples)
628 *pcSamples = cSamples;
629 }
630
631 } while (0);
632
633 if (RT_FAILURE(rc))
634 drvHostOSSAudioClose(&hFile);
635
636 LogFlowFuncLeaveRC(rc);
637 return rc;
638}
639
640static DECLCALLBACK(int) drvHostOSSAudioInitOut(PPDMIHOSTAUDIO pInterface,
641 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
642 uint32_t *pcSamples)
643{
644 NOREF(pInterface);
645 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
646 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
647
648 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
649
650 int rc;
651 int hFile = -1;
652
653 do
654 {
655 uint32_t cSamples;
656
657 OSSAUDIOSTREAMCFG reqStream, obtStream;
658 reqStream.enmFormat = pCfg->enmFormat;
659 reqStream.uFreq = pCfg->uHz;
660 reqStream.cChannels = pCfg->cChannels;
661 reqStream.cFragments = s_OSSConf.nfrags;
662 reqStream.cbFragmentSize = s_OSSConf.fragsize;
663
664 rc = drvHostOSSAudioOpen(false /* fIn */,
665 &reqStream, &obtStream, &hFile);
666 if (RT_SUCCESS(rc))
667 {
668 if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmOut->Props.uAlign)
669 LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
670 obtStream.cFragments * obtStream.cbFragmentSize,
671 pHstStrmOut->Props.uAlign + 1));
672
673 pThisStrmOut->hFile = hFile;
674
675 PDMAUDIOSTREAMCFG streamCfg;
676 streamCfg.enmFormat = obtStream.enmFormat;
677 streamCfg.uHz = obtStream.uFreq;
678 streamCfg.cChannels = pCfg->cChannels;
679 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
680
681 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
682 if (RT_SUCCESS(rc))
683 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
684 >> pHstStrmOut->Props.cShift;
685 }
686
687 if (RT_SUCCESS(rc))
688 {
689#ifndef RT_OS_L4
690 pThisStrmOut->fMemMapped = false;
691 if (s_OSSConf.try_mmap)
692 {
693 pThisStrmOut->pvPCMBuf = mmap(0, cSamples << pHstStrmOut->Props.cShift,
694 PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
695 if (pThisStrmOut->pvPCMBuf == MAP_FAILED)
696 {
697 LogRel(("OSS: Failed to memory map %zu bytes of DAC output file: %s\n",
698 cSamples << pHstStrmOut->Props.cShift, strerror(errno)));
699 rc = RTErrConvertFromErrno(errno);
700 break;
701 }
702 else
703 {
704 int mask = 0;
705 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
706 {
707 LogRel(("OSS: Failed to retrieve initial trigger mask: %s\n",
708 strerror(errno)));
709 rc = RTErrConvertFromErrno(errno);
710 /* Note: No break here, need to unmap file first! */
711 }
712 else
713 {
714 mask = PCM_ENABLE_OUTPUT;
715 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
716 {
717 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n",
718 strerror(errno)));
719 rc = RTErrConvertFromErrno(errno);
720 /* Note: No break here, need to unmap file first! */
721 }
722 else
723 pThisStrmOut->fMemMapped = true;
724 }
725
726 if (!pThisStrmOut->fMemMapped)
727 {
728 int rc2 = munmap(pThisStrmOut->pvPCMBuf,
729 cSamples << pHstStrmOut->Props.cShift);
730 if (rc2)
731 LogRel(("OSS: Failed to unmap DAC output file: %s\n", strerror(errno)));
732 break;
733 }
734 }
735 }
736#endif /* !RT_OS_L4 */
737
738 /* Memory mapping failed above? Try allocating an own buffer. */
739#ifndef RT_OS_L4
740 if (!pThisStrmOut->fMemMapped)
741 {
742#endif
743 size_t cbSample = (1 << pHstStrmOut->Props.cShift);
744 size_t cbPCMBuf = cSamples * cbSample;
745
746 LogFlowFunc(("cSamples=%RU32\n", cSamples));
747
748 pThisStrmOut->pvPCMBuf = RTMemAlloc(cbPCMBuf);
749 if (!pThisStrmOut->pvPCMBuf)
750 {
751 LogRel(("OSS: Failed allocating DAC buffer with %RU32 samples (%zu bytes per sample)\n", cSamples, cbSample));
752 rc = VERR_NO_MEMORY;
753 break;
754 }
755
756 pThisStrmOut->cbPCMBuf = cbPCMBuf;
757#ifndef RT_OS_L4
758 }
759#endif
760 if (pcSamples)
761 *pcSamples = cSamples;
762 }
763
764 } while (0);
765
766 if (RT_FAILURE(rc))
767 drvHostOSSAudioClose(&hFile);
768
769 LogFlowFuncLeaveRC(rc);
770 return rc;
771}
772
773static DECLCALLBACK(bool) drvHostOSSAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
774{
775 NOREF(pInterface);
776 NOREF(enmDir);
777 return true; /* Always all enabled. */
778}
779
780static DECLCALLBACK(int) drvHostOSSAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
781 uint32_t *pcSamplesPlayed)
782{
783 NOREF(pInterface);
784 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
785
786 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
787
788 int rc = VINF_SUCCESS;
789 uint32_t cbReadTotal = 0;
790 count_info cntinfo;
791
792 do
793 {
794 size_t cbBuf = AudioMixBufSizeBytes(&pHstStrmOut->MixBuf);
795
796 uint32_t cLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
797 uint32_t cToRead;
798
799#ifndef RT_OS_L4
800 if (pThisStrmOut->fMemMapped)
801 {
802 /* Get current playback pointer. */
803 int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
804 if (!rc2)
805 {
806 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
807 strerror(errno)));
808 rc = RTErrConvertFromErrno(errno);
809 break;
810 }
811
812 /* Nothing to play? */
813 if (cntinfo.ptr == pThisStrmOut->old_optr)
814 break;
815
816 int cbData;
817 if (cntinfo.ptr > pThisStrmOut->old_optr)
818 cbData = cntinfo.ptr - pThisStrmOut->old_optr;
819 else
820 cbData = cbBuf + cntinfo.ptr - pThisStrmOut->old_optr;
821 Assert(cbData);
822
823 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbData),
824 cLive);
825 }
826 else
827 {
828#endif
829 audio_buf_info abinfo;
830 int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
831 if (rc2 < 0)
832 {
833 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n",
834 strerror(errno)));
835 rc = RTErrConvertFromErrno(errno);
836 break;
837 }
838
839 if ((size_t)abinfo.bytes > cbBuf)
840 {
841 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
842 abinfo.bytes, cbBuf));
843 abinfo.bytes = cbBuf;
844 /* Keep going. */
845 }
846
847 if (abinfo.bytes < 0)
848 {
849 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
850 abinfo.bytes, cbBuf));
851 rc = VERR_INVALID_PARAMETER;
852 break;
853 }
854
855 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, abinfo.bytes),
856 cLive);
857 if (!cToRead)
858 break;
859#ifndef RT_OS_L4
860 }
861#endif
862 size_t cbToRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cToRead);
863 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
864
865 uint32_t cRead, cbRead;
866 while (cbToRead)
867 {
868 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf,
869 pThisStrmOut->pvPCMBuf, cbToRead, &cRead);
870 if (RT_FAILURE(rc))
871 break;
872
873 cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
874 ssize_t cbWritten = write(pThisStrmOut->hFile, pThisStrmOut->pvPCMBuf,
875 cbRead);
876 if (cbWritten == -1)
877 {
878 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
879 rc = RTErrConvertFromErrno(errno);
880 break;
881 }
882
883 Assert(cbToRead >= cbRead);
884 cbToRead -= cbRead;
885 cbReadTotal += cbRead;
886 }
887
888#ifndef RT_OS_L4
889 /* Update read pointer. */
890 if (pThisStrmOut->fMemMapped)
891 pThisStrmOut->old_optr = cntinfo.ptr;
892#endif
893
894 } while(0);
895
896 if (RT_SUCCESS(rc))
897 {
898 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
899 if (cReadTotal)
900 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
901
902 if (pcSamplesPlayed)
903 *pcSamplesPlayed = cReadTotal;
904
905 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
906 cReadTotal, cbReadTotal, rc));
907 }
908
909 LogFlowFuncLeaveRC(rc);
910 return rc;
911}
912
913static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
914{
915 NOREF(pInterface);
916}
917
918/**
919 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
920 */
921static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
922{
923 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
924 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
925 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
926 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
927
928 return NULL;
929}
930
931/**
932 * Constructs an OSS audio driver instance.
933 *
934 * @copydoc FNPDMDRVCONSTRUCT
935 */
936static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
937{
938 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
939 LogRel(("Audio: Initializing OSS driver\n"));
940
941 /*
942 * Init the static parts.
943 */
944 pThis->pDrvIns = pDrvIns;
945 /* IBase */
946 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
947 /* IHostAudio */
948 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
949
950 return VINF_SUCCESS;
951}
952
953/**
954 * Char driver registration record.
955 */
956const PDMDRVREG g_DrvHostOSSAudio =
957{
958 /* u32Version */
959 PDM_DRVREG_VERSION,
960 /* szName */
961 "OSSAudio",
962 /* szRCMod */
963 "",
964 /* szR0Mod */
965 "",
966 /* pszDescription */
967 "OSS audio host driver",
968 /* fFlags */
969 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
970 /* fClass. */
971 PDM_DRVREG_CLASS_AUDIO,
972 /* cMaxInstances */
973 ~0U,
974 /* cbInstance */
975 sizeof(DRVHOSTOSSAUDIO),
976 /* pfnConstruct */
977 drvHostOSSAudioConstruct,
978 /* pfnDestruct */
979 NULL,
980 /* pfnRelocate */
981 NULL,
982 /* pfnIOCtl */
983 NULL,
984 /* pfnPowerOn */
985 NULL,
986 /* pfnReset */
987 NULL,
988 /* pfnSuspend */
989 NULL,
990 /* pfnResume */
991 NULL,
992 /* pfnAttach */
993 NULL,
994 /* pfnDetach */
995 NULL,
996 /* pfnPowerOff */
997 NULL,
998 /* pfnSoftReset */
999 NULL,
1000 /* u32EndVersion */
1001 PDM_DRVREG_VERSION
1002};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette