VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio_old/DrvHostOSSAudio.cpp@ 61413

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

Audio: Use the old audio code for now when doing a release. Set VBOX_WITH_AUDIO_STABLE to an empty value to enable the new audio code (default for non-release builds for now).

  • 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 61413 2016-06-02 13:24:16Z 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 case PDMAUDIOSTREAMCMD_RESUME:
362 {
363 DrvAudioClearBuf(&pHstStrmOut->Props,
364 pThisStrmOut->pvPCMBuf, pThisStrmOut->cbPCMBuf, AudioMixBufSize(&pHstStrmOut->MixBuf));
365
366 mask = PCM_ENABLE_OUTPUT;
367 if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
368 {
369 LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
370 rc = RTErrConvertFromErrno(errno);
371 }
372
373 break;
374 }
375
376 case PDMAUDIOSTREAMCMD_DISABLE:
377 case PDMAUDIOSTREAMCMD_PAUSE:
378 {
379 mask = 0;
380 if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
381 {
382 LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
383 rc = RTErrConvertFromErrno(errno);
384 }
385
386 break;
387 }
388
389 default:
390 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
391 rc = VERR_INVALID_PARAMETER;
392 break;
393 }
394
395 LogFlowFuncLeaveRC(rc);
396 return rc;
397}
398
399static DECLCALLBACK(int) drvHostOSSAudioInit(PPDMIHOSTAUDIO pInterface)
400{
401 NOREF(pInterface);
402
403 LogFlowFuncEnter();
404
405 return VINF_SUCCESS;
406}
407
408static DECLCALLBACK(int) drvHostOSSAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
409 uint32_t *pcSamplesCaptured)
410{
411 NOREF(pInterface);
412 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
413
414 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
415
416 int rc = VINF_SUCCESS;
417 size_t cbToRead = RT_MIN(pThisStrmIn->cbPCMBuf,
418 AudioMixBufFreeBytes(&pHstStrmIn->MixBuf));
419
420 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
421
422 uint32_t cWrittenTotal = 0;
423 uint32_t cbTemp;
424 ssize_t cbRead;
425 size_t offWrite = 0;
426
427 while (cbToRead)
428 {
429 cbTemp = RT_MIN(cbToRead, pThisStrmIn->cbPCMBuf);
430 AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
431 cbRead = read(pThisStrmIn->hFile, (uint8_t *)pThisStrmIn->pvPCMBuf + offWrite, cbTemp);
432
433 LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n",
434 cbRead, cbTemp, cbToRead));
435
436 if (cbRead < 0)
437 {
438 switch (errno)
439 {
440 case 0:
441 {
442 LogFunc(("Failed to read %z frames\n", cbRead));
443 rc = VERR_ACCESS_DENIED;
444 break;
445 }
446
447 case EINTR:
448 case EAGAIN:
449 rc = VERR_NO_DATA;
450 break;
451
452 default:
453 LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n",
454 cbTemp, rc));
455 rc = VERR_GENERAL_FAILURE; /** @todo */
456 break;
457 }
458
459 if (RT_FAILURE(rc))
460 break;
461 }
462 else if (cbRead)
463 {
464 uint32_t cWritten;
465 rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
466 pThisStrmIn->pvPCMBuf, cbRead,
467 &cWritten);
468 if (RT_FAILURE(rc))
469 break;
470
471 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
472
473 Assert(cbToRead >= cbWritten);
474 cbToRead -= cbWritten;
475 offWrite += cbWritten;
476 cWrittenTotal += cWritten;
477 }
478 else /* No more data, try next round. */
479 break;
480 }
481
482 if (rc == VERR_NO_DATA)
483 rc = VINF_SUCCESS;
484
485 if (RT_SUCCESS(rc))
486 {
487 uint32_t cProcessed = 0;
488 if (cWrittenTotal)
489 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
490 &cProcessed);
491
492 if (pcSamplesCaptured)
493 *pcSamplesCaptured = cWrittenTotal;
494
495 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
496 cWrittenTotal, cProcessed, rc));
497 }
498
499 LogFlowFuncLeaveRC(rc);
500 return rc;
501}
502
503static DECLCALLBACK(int) drvHostOSSAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
504{
505 NOREF(pInterface);
506 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
507
508 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
509
510 LogFlowFuncEnter();
511
512 if (pThisStrmIn->pvPCMBuf)
513 {
514 Assert(pThisStrmIn->cbPCMBuf);
515
516 RTMemFree(pThisStrmIn->pvPCMBuf);
517 pThisStrmIn->pvPCMBuf = NULL;
518 }
519
520 pThisStrmIn->cbPCMBuf = 0;
521
522 return VINF_SUCCESS;
523}
524
525static DECLCALLBACK(int) drvHostOSSAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
526{
527 NOREF(pInterface);
528 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
529
530 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
531
532 LogFlowFuncEnter();
533
534#ifndef RT_OS_L4
535 if (!pThisStrmOut->fMemMapped)
536 {
537 if (pThisStrmOut->pvPCMBuf)
538 {
539 Assert(pThisStrmOut->cbPCMBuf);
540
541 RTMemFree(pThisStrmOut->pvPCMBuf);
542 pThisStrmOut->pvPCMBuf = NULL;
543 }
544
545 pThisStrmOut->cbPCMBuf = 0;
546 }
547#endif
548
549 return VINF_SUCCESS;
550}
551
552static DECLCALLBACK(int) drvHostOSSAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
553{
554 NOREF(pInterface);
555
556 pCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT);
557 pCfg->cbStreamIn = sizeof(OSSAUDIOSTREAMIN);
558 pCfg->cMaxHstStrmsOut = INT_MAX;
559 pCfg->cMaxHstStrmsIn = INT_MAX;
560
561 return VINF_SUCCESS;
562}
563
564static DECLCALLBACK(int) drvHostOSSAudioInitIn(PPDMIHOSTAUDIO pInterface,
565 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
566 PDMAUDIORECSOURCE enmRecSource,
567 uint32_t *pcSamples)
568{
569 NOREF(pInterface);
570 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
571 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
572
573 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
574
575 int rc;
576 int hFile = -1;
577
578 do
579 {
580 uint32_t cSamples;
581
582 OSSAUDIOSTREAMCFG reqStream, obtStream;
583 reqStream.enmFormat = pCfg->enmFormat;
584 reqStream.uFreq = pCfg->uHz;
585 reqStream.cChannels = pCfg->cChannels;
586 reqStream.cFragments = s_OSSConf.nfrags;
587 reqStream.cbFragmentSize = s_OSSConf.fragsize;
588
589 rc = drvHostOSSAudioOpen(true /* fIn */,
590 &reqStream, &obtStream, &hFile);
591 if (RT_SUCCESS(rc))
592 {
593 if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmIn->Props.uAlign)
594 LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
595 obtStream.cFragments * obtStream.cbFragmentSize,
596 pHstStrmIn->Props.uAlign + 1));
597
598 pThisStrmIn->hFile = hFile;
599
600 PDMAUDIOSTREAMCFG streamCfg;
601 streamCfg.enmFormat = obtStream.enmFormat;
602 streamCfg.uHz = obtStream.uFreq;
603 streamCfg.cChannels = pCfg->cChannels;
604 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
605
606 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
607 if (RT_SUCCESS(rc))
608 {
609 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
610 >> pHstStrmIn->Props.cShift;
611 if (!cSamples)
612 rc = VERR_INVALID_PARAMETER;
613 }
614 }
615
616 if (RT_SUCCESS(rc))
617 {
618 size_t cbSample = (1 << pHstStrmIn->Props.cShift);
619 size_t cbBuf = cSamples * cbSample;
620 pThisStrmIn->pvPCMBuf = RTMemAlloc(cbBuf);
621 if (!pThisStrmIn->pvPCMBuf)
622 {
623 LogRel(("OSS: Failed allocating ADC buffer with %RU32 samples (%zu bytes per sample)\n", cSamples, cbSample));
624 rc = VERR_NO_MEMORY;
625 }
626
627 pThisStrmIn->cbPCMBuf = cbBuf;
628
629 if (pcSamples)
630 *pcSamples = cSamples;
631 }
632
633 } while (0);
634
635 if (RT_FAILURE(rc))
636 drvHostOSSAudioClose(&hFile);
637
638 LogFlowFuncLeaveRC(rc);
639 return rc;
640}
641
642static DECLCALLBACK(int) drvHostOSSAudioInitOut(PPDMIHOSTAUDIO pInterface,
643 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
644 uint32_t *pcSamples)
645{
646 NOREF(pInterface);
647 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
648 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
649
650 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
651
652 int rc;
653 int hFile = -1;
654
655 do
656 {
657 uint32_t cSamples;
658
659 OSSAUDIOSTREAMCFG reqStream, obtStream;
660 reqStream.enmFormat = pCfg->enmFormat;
661 reqStream.uFreq = pCfg->uHz;
662 reqStream.cChannels = pCfg->cChannels;
663 reqStream.cFragments = s_OSSConf.nfrags;
664 reqStream.cbFragmentSize = s_OSSConf.fragsize;
665
666 rc = drvHostOSSAudioOpen(false /* fIn */,
667 &reqStream, &obtStream, &hFile);
668 if (RT_SUCCESS(rc))
669 {
670 if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmOut->Props.uAlign)
671 LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
672 obtStream.cFragments * obtStream.cbFragmentSize,
673 pHstStrmOut->Props.uAlign + 1));
674
675 pThisStrmOut->hFile = hFile;
676
677 PDMAUDIOSTREAMCFG streamCfg;
678 streamCfg.enmFormat = obtStream.enmFormat;
679 streamCfg.uHz = obtStream.uFreq;
680 streamCfg.cChannels = pCfg->cChannels;
681 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
682
683 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
684 if (RT_SUCCESS(rc))
685 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
686 >> pHstStrmOut->Props.cShift;
687 }
688
689 if (RT_SUCCESS(rc))
690 {
691#ifndef RT_OS_L4
692 pThisStrmOut->fMemMapped = false;
693 if (s_OSSConf.try_mmap)
694 {
695 pThisStrmOut->pvPCMBuf = mmap(0, cSamples << pHstStrmOut->Props.cShift,
696 PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
697 if (pThisStrmOut->pvPCMBuf == MAP_FAILED)
698 {
699 LogRel(("OSS: Failed to memory map %zu bytes of DAC output file: %s\n",
700 cSamples << pHstStrmOut->Props.cShift, strerror(errno)));
701 rc = RTErrConvertFromErrno(errno);
702 break;
703 }
704 else
705 {
706 int mask = 0;
707 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
708 {
709 LogRel(("OSS: Failed to retrieve initial trigger mask: %s\n",
710 strerror(errno)));
711 rc = RTErrConvertFromErrno(errno);
712 /* Note: No break here, need to unmap file first! */
713 }
714 else
715 {
716 mask = PCM_ENABLE_OUTPUT;
717 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
718 {
719 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n",
720 strerror(errno)));
721 rc = RTErrConvertFromErrno(errno);
722 /* Note: No break here, need to unmap file first! */
723 }
724 else
725 pThisStrmOut->fMemMapped = true;
726 }
727
728 if (!pThisStrmOut->fMemMapped)
729 {
730 int rc2 = munmap(pThisStrmOut->pvPCMBuf,
731 cSamples << pHstStrmOut->Props.cShift);
732 if (rc2)
733 LogRel(("OSS: Failed to unmap DAC output file: %s\n", strerror(errno)));
734 break;
735 }
736 }
737 }
738#endif /* !RT_OS_L4 */
739
740 /* Memory mapping failed above? Try allocating an own buffer. */
741#ifndef RT_OS_L4
742 if (!pThisStrmOut->fMemMapped)
743 {
744#endif
745 size_t cbSample = (1 << pHstStrmOut->Props.cShift);
746 size_t cbPCMBuf = cSamples * cbSample;
747
748 LogFlowFunc(("cSamples=%RU32\n", cSamples));
749
750 pThisStrmOut->pvPCMBuf = RTMemAlloc(cbPCMBuf);
751 if (!pThisStrmOut->pvPCMBuf)
752 {
753 LogRel(("OSS: Failed allocating DAC buffer with %RU32 samples (%zu bytes per sample)\n", cSamples, cbSample));
754 rc = VERR_NO_MEMORY;
755 break;
756 }
757
758 pThisStrmOut->cbPCMBuf = cbPCMBuf;
759#ifndef RT_OS_L4
760 }
761#endif
762 if (pcSamples)
763 *pcSamples = cSamples;
764 }
765
766 } while (0);
767
768 if (RT_FAILURE(rc))
769 drvHostOSSAudioClose(&hFile);
770
771 LogFlowFuncLeaveRC(rc);
772 return rc;
773}
774
775static DECLCALLBACK(bool) drvHostOSSAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
776{
777 NOREF(pInterface);
778 NOREF(enmDir);
779 return true; /* Always all enabled. */
780}
781
782static DECLCALLBACK(int) drvHostOSSAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
783 uint32_t *pcSamplesPlayed)
784{
785 NOREF(pInterface);
786 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
787
788 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
789
790 int rc = VINF_SUCCESS;
791 uint32_t cbReadTotal = 0;
792 count_info cntinfo;
793
794 do
795 {
796 size_t cbBuf = AudioMixBufSizeBytes(&pHstStrmOut->MixBuf);
797
798 uint32_t cLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
799 uint32_t cToRead;
800
801#ifndef RT_OS_L4
802 if (pThisStrmOut->fMemMapped)
803 {
804 /* Get current playback pointer. */
805 int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
806 if (!rc2)
807 {
808 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
809 strerror(errno)));
810 rc = RTErrConvertFromErrno(errno);
811 break;
812 }
813
814 /* Nothing to play? */
815 if (cntinfo.ptr == pThisStrmOut->old_optr)
816 break;
817
818 int cbData;
819 if (cntinfo.ptr > pThisStrmOut->old_optr)
820 cbData = cntinfo.ptr - pThisStrmOut->old_optr;
821 else
822 cbData = cbBuf + cntinfo.ptr - pThisStrmOut->old_optr;
823 Assert(cbData);
824
825 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbData),
826 cLive);
827 }
828 else
829 {
830#endif
831 audio_buf_info abinfo;
832 int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
833 if (rc2 < 0)
834 {
835 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n",
836 strerror(errno)));
837 rc = RTErrConvertFromErrno(errno);
838 break;
839 }
840
841 if ((size_t)abinfo.bytes > cbBuf)
842 {
843 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
844 abinfo.bytes, cbBuf));
845 abinfo.bytes = cbBuf;
846 /* Keep going. */
847 }
848
849 if (abinfo.bytes < 0)
850 {
851 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
852 abinfo.bytes, cbBuf));
853 rc = VERR_INVALID_PARAMETER;
854 break;
855 }
856
857 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, abinfo.bytes),
858 cLive);
859 if (!cToRead)
860 break;
861#ifndef RT_OS_L4
862 }
863#endif
864 size_t cbToRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cToRead);
865 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
866
867 uint32_t cRead, cbRead;
868 while (cbToRead)
869 {
870 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf,
871 pThisStrmOut->pvPCMBuf, cbToRead, &cRead);
872 if (RT_FAILURE(rc))
873 break;
874
875 cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
876 ssize_t cbWritten = write(pThisStrmOut->hFile, pThisStrmOut->pvPCMBuf,
877 cbRead);
878 if (cbWritten == -1)
879 {
880 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
881 rc = RTErrConvertFromErrno(errno);
882 break;
883 }
884
885 Assert(cbToRead >= cbRead);
886 cbToRead -= cbRead;
887 cbReadTotal += cbRead;
888 }
889
890#ifndef RT_OS_L4
891 /* Update read pointer. */
892 if (pThisStrmOut->fMemMapped)
893 pThisStrmOut->old_optr = cntinfo.ptr;
894#endif
895
896 } while(0);
897
898 if (RT_SUCCESS(rc))
899 {
900 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
901 if (cReadTotal)
902 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
903
904 if (pcSamplesPlayed)
905 *pcSamplesPlayed = cReadTotal;
906
907 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
908 cReadTotal, cbReadTotal, rc));
909 }
910
911 LogFlowFuncLeaveRC(rc);
912 return rc;
913}
914
915static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
916{
917 NOREF(pInterface);
918}
919
920/**
921 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
922 */
923static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
924{
925 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
926 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
927 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
928 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
929
930 return NULL;
931}
932
933/**
934 * Constructs an OSS audio driver instance.
935 *
936 * @copydoc FNPDMDRVCONSTRUCT
937 */
938static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
939{
940 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
941 LogRel(("Audio: Initializing OSS driver\n"));
942
943 /*
944 * Init the static parts.
945 */
946 pThis->pDrvIns = pDrvIns;
947 /* IBase */
948 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
949 /* IHostAudio */
950 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
951
952 return VINF_SUCCESS;
953}
954
955/**
956 * Char driver registration record.
957 */
958const PDMDRVREG g_DrvHostOSSAudio =
959{
960 /* u32Version */
961 PDM_DRVREG_VERSION,
962 /* szName */
963 "OSSAudio",
964 /* szRCMod */
965 "",
966 /* szR0Mod */
967 "",
968 /* pszDescription */
969 "OSS audio host driver",
970 /* fFlags */
971 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
972 /* fClass. */
973 PDM_DRVREG_CLASS_AUDIO,
974 /* cMaxInstances */
975 ~0U,
976 /* cbInstance */
977 sizeof(DRVHOSTOSSAUDIO),
978 /* pfnConstruct */
979 drvHostOSSAudioConstruct,
980 /* pfnDestruct */
981 NULL,
982 /* pfnRelocate */
983 NULL,
984 /* pfnIOCtl */
985 NULL,
986 /* pfnPowerOn */
987 NULL,
988 /* pfnReset */
989 NULL,
990 /* pfnSuspend */
991 NULL,
992 /* pfnResume */
993 NULL,
994 /* pfnAttach */
995 NULL,
996 /* pfnDetach */
997 NULL,
998 /* pfnPowerOff */
999 NULL,
1000 /* pfnSoftReset */
1001 NULL,
1002 /* u32EndVersion */
1003 PDM_DRVREG_VERSION
1004};
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