VirtualBox

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

Last change on this file since 56622 was 56622, checked in by vboxsync, 10 years ago

don't include vl_vbox.h anymore

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

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