VirtualBox

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

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

DrvHostOSSAudio: typo

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