VirtualBox

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

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

scm automatic cleanups.

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