VirtualBox

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

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

Devices: Log & Assertion formatting fixes.

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