VirtualBox

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

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

Audio: Decoupled backend sinks and sources from the maximum of concurrent streams a backend can handle. Also, added some more enumeration code to the ALSA, PulseAudio and OSS backends, which later also can be used for configuration change callbacks. Some function renaming.

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