VirtualBox

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

Last change on this file since 60404 was 59992, checked in by vboxsync, 8 years ago

Build fix for Solaris.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.5 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 59992 2016-03-11 12:59:22Z 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. This is handy for e.g. Solaris. */
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", ossInfo.numaudios));
612 LogRel2(("OSS: Number of mixers: %d\n", ossInfo.nummixers));
613
614 int cDev = ossInfo.nummixers;
615 if (!cDev)
616 cDev = ossInfo.numaudios;
617
618 pCfg->cSources = cDev;
619 pCfg->cSinks = cDev;
620
621 pCfg->cMaxStreamsIn = UINT32_MAX;
622 pCfg->cMaxStreamsOut = UINT32_MAX;
623 }
624 else
625 {
626#endif
627 /* Since we cannot query anything, assume that we have at least
628 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */
629 pCfg->cSources = 1;
630 pCfg->cSinks = 1;
631
632 pCfg->cMaxStreamsIn = UINT32_MAX;
633 pCfg->cMaxStreamsOut = UINT32_MAX;
634#ifdef VBOX_WITH_OSS_SYSINFO
635 }
636#endif
637 }
638 else
639 LogRel(("OSS: Unable to determine installed version: %s (%d)\n", strerror(err), err));
640 }
641 else
642 LogRel(("OSS: No devices found, audio is not available\n"));
643
644 return VINF_SUCCESS;
645}
646
647static DECLCALLBACK(int) drvHostOSSAudioInitIn(PPDMIHOSTAUDIO pInterface,
648 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
649 PDMAUDIORECSOURCE enmRecSource,
650 uint32_t *pcSamples)
651{
652 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
653 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
654 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
655
656 PDRVHOSTOSSAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTOSSAUDIO(pInterface);
657 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pHstStrmIn;
658
659 int rc;
660 int hFile = -1;
661
662 do
663 {
664 uint32_t cSamples;
665
666 OSSAUDIOSTREAMCFG reqStream, obtStream;
667 reqStream.enmFormat = pCfg->enmFormat;
668 reqStream.uFreq = pCfg->uHz;
669 reqStream.cChannels = pCfg->cChannels;
670 reqStream.cFragments = s_OSSConf.nfrags;
671 reqStream.cbFragmentSize = s_OSSConf.fragsize;
672
673 rc = ossStreamOpen(s_OSSConf.devpath_in, O_RDONLY | O_NONBLOCK, &reqStream, &obtStream, &hFile);
674 if (RT_SUCCESS(rc))
675 {
676 if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmIn->Props.uAlign)
677 LogRel(("OSS: Warning: Misaligned capturing buffer: Size = %zu, Alignment = %u\n",
678 obtStream.cFragments * obtStream.cbFragmentSize,
679 pHstStrmIn->Props.uAlign + 1));
680
681 PDMAUDIOSTREAMCFG streamCfg;
682 streamCfg.enmFormat = obtStream.enmFormat;
683 streamCfg.uHz = obtStream.uFreq;
684 streamCfg.cChannels = pCfg->cChannels;
685 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
686
687 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
688 if (RT_SUCCESS(rc))
689 {
690 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
691 >> pHstStrmIn->Props.cShift;
692 if (!cSamples)
693 rc = VERR_INVALID_PARAMETER;
694 }
695 }
696
697 if (RT_SUCCESS(rc))
698 {
699 size_t cbSample = (1 << pHstStrmIn->Props.cShift);
700
701 size_t cbBuf = cSamples * cbSample;
702 void *pvBuf = RTMemAlloc(cbBuf);
703 if (!pvBuf)
704 {
705 LogRel(("OSS: Failed allocating capturing buffer with %RU32 samples (%zu bytes per sample)\n",
706 cSamples, cbSample));
707 rc = VERR_NO_MEMORY;
708 break;
709 }
710
711 pStrm->hFile = hFile;
712 pStrm->pvBuf = pvBuf;
713 pStrm->cbBuf = cbBuf;
714
715 if (pcSamples)
716 *pcSamples = cSamples;
717 }
718
719 } while (0);
720
721 if (RT_FAILURE(rc))
722 ossStreamClose(&hFile);
723
724 LogFlowFuncLeaveRC(rc);
725 return rc;
726}
727
728static DECLCALLBACK(int) drvHostOSSAudioInitOut(PPDMIHOSTAUDIO pInterface,
729 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
730 uint32_t *pcSamples)
731{
732 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
733 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
734 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
735
736 PDRVHOSTOSSAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTOSSAUDIO(pInterface);
737 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pHstStrmOut;
738
739 int rc;
740 int hFile = -1;
741
742 do
743 {
744 uint32_t cSamples;
745
746 OSSAUDIOSTREAMCFG reqStream, obtStream;
747 reqStream.enmFormat = pCfg->enmFormat;
748 reqStream.uFreq = pCfg->uHz;
749 reqStream.cChannels = pCfg->cChannels;
750 reqStream.cFragments = s_OSSConf.nfrags;
751 reqStream.cbFragmentSize = s_OSSConf.fragsize;
752
753 rc = ossStreamOpen(s_OSSConf.devpath_out, O_WRONLY | O_NONBLOCK, &reqStream, &obtStream, &hFile);
754 if (RT_SUCCESS(rc))
755 {
756 if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmOut->Props.uAlign)
757 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
758 obtStream.cFragments * obtStream.cbFragmentSize,
759 pHstStrmOut->Props.uAlign + 1));
760
761 PDMAUDIOSTREAMCFG streamCfg;
762 streamCfg.enmFormat = obtStream.enmFormat;
763 streamCfg.uHz = obtStream.uFreq;
764 streamCfg.cChannels = pCfg->cChannels;
765 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
766
767 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
768 if (RT_SUCCESS(rc))
769 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
770 >> pHstStrmOut->Props.cShift;
771 }
772
773 if (RT_SUCCESS(rc))
774 {
775 pStrm->fMemMapped = false;
776
777 size_t cbSamples = cSamples << pHstStrmOut->Props.cShift;
778 Assert(cbSamples);
779
780#ifndef RT_OS_L4
781 if (s_OSSConf.try_mmap)
782 {
783 pStrm->pvBuf = mmap(0, cbSamples, PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
784 if (pStrm->pvBuf == MAP_FAILED)
785 {
786 LogRel(("OSS: Failed to memory map %zu bytes of playback buffer: %s\n", cbSamples, strerror(errno)));
787 rc = RTErrConvertFromErrno(errno);
788 break;
789 }
790 else
791 {
792 int mask = 0;
793 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
794 {
795 LogRel(("OSS: Failed to retrieve initial trigger mask for playback buffer: %s\n", strerror(errno)));
796 rc = RTErrConvertFromErrno(errno);
797 /* Note: No break here, need to unmap file first! */
798 }
799 else
800 {
801 mask = PCM_ENABLE_OUTPUT;
802 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
803 {
804 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n", strerror(errno)));
805 rc = RTErrConvertFromErrno(errno);
806 /* Note: No break here, need to unmap file first! */
807 }
808 else
809 pStrm->fMemMapped = true;
810 }
811
812 if (RT_FAILURE(rc))
813 {
814 int rc2 = munmap(pStrm->pvBuf, cbSamples);
815 if (rc2)
816 LogRel(("OSS: Failed to memory unmap playback buffer: %s\n", strerror(errno)));
817 break;
818 }
819 }
820 }
821#endif /* !RT_OS_L4 */
822
823 /* Memory mapping failed above? Try allocating an own buffer. */
824#ifndef RT_OS_L4
825 if (!pStrm->fMemMapped)
826 {
827#endif
828 void *pvBuf = RTMemAlloc(cbSamples);
829 if (!pvBuf)
830 {
831 LogRel(("OSS: Failed allocating playback buffer with %RU32 samples (%zu bytes)\n", cSamples, cbSamples));
832 rc = VERR_NO_MEMORY;
833 break;
834 }
835
836 pStrm->hFile = hFile;
837 pStrm->pvBuf = pvBuf;
838 pStrm->cbBuf = cbSamples;
839#ifndef RT_OS_L4
840 }
841#endif
842 if (pcSamples)
843 *pcSamples = cSamples;
844 }
845
846 } while (0);
847
848 if (RT_FAILURE(rc))
849 ossStreamClose(&hFile);
850
851 LogFlowFuncLeaveRC(rc);
852 return rc;
853}
854
855static DECLCALLBACK(bool) drvHostOSSAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
856{
857 NOREF(pInterface);
858 NOREF(enmDir);
859 return true; /* Always all enabled. */
860}
861
862static DECLCALLBACK(int) drvHostOSSAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
863 uint32_t *pcSamplesPlayed)
864{
865 NOREF(pInterface);
866 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
867
868 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pHstStrmOut;
869
870 int rc = VINF_SUCCESS;
871 uint32_t cbReadTotal = 0;
872 count_info cntinfo;
873
874 do
875 {
876 size_t cbBuf = AudioMixBufSizeBytes(&pHstStrmOut->MixBuf);
877
878 uint32_t cLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
879 uint32_t cToRead;
880
881#ifndef RT_OS_L4
882 if (pStrm->fMemMapped)
883 {
884 /* Get current playback pointer. */
885 int rc2 = ioctl(pStrm->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
886 if (!rc2)
887 {
888 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
889 strerror(errno)));
890 rc = RTErrConvertFromErrno(errno);
891 break;
892 }
893
894 /* Nothing to play? */
895 if (cntinfo.ptr == pStrm->old_optr)
896 break;
897
898 int cbData;
899 if (cntinfo.ptr > pStrm->old_optr)
900 cbData = cntinfo.ptr - pStrm->old_optr;
901 else
902 cbData = cbBuf + cntinfo.ptr - pStrm->old_optr;
903 Assert(cbData);
904
905 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbData),
906 cLive);
907 }
908 else
909 {
910#endif
911 audio_buf_info abinfo;
912 int rc2 = ioctl(pStrm->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
913 if (rc2 < 0)
914 {
915 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n", strerror(errno)));
916 rc = RTErrConvertFromErrno(errno);
917 break;
918 }
919
920 if ((size_t)abinfo.bytes > cbBuf)
921 {
922 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%zu\n", abinfo.bytes, cbBuf));
923 abinfo.bytes = cbBuf;
924 /* Keep going. */
925 }
926
927 if (abinfo.bytes < 0)
928 {
929 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%zu\n", abinfo.bytes, cbBuf));
930 rc = VERR_INVALID_PARAMETER;
931 break;
932 }
933
934 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, abinfo.bytes), cLive);
935 if (!cToRead)
936 break;
937#ifndef RT_OS_L4
938 }
939#endif
940 size_t cbToRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cToRead);
941 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
942
943 uint32_t cRead, cbRead;
944 while (cbToRead)
945 {
946 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pStrm->pvBuf, cbToRead, &cRead);
947 if (RT_FAILURE(rc))
948 break;
949
950 cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
951 ssize_t cbWritten = write(pStrm->hFile, pStrm->pvBuf, cbRead);
952 if (cbWritten == -1)
953 {
954 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
955 rc = RTErrConvertFromErrno(errno);
956 break;
957 }
958
959 Assert(cbToRead >= cbRead);
960 cbToRead -= cbRead;
961 cbReadTotal += cbRead;
962 }
963
964#ifndef RT_OS_L4
965 /* Update read pointer. */
966 if (pStrm->fMemMapped)
967 pStrm->old_optr = cntinfo.ptr;
968#endif
969
970 } while(0);
971
972 if (RT_SUCCESS(rc))
973 {
974 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
975 if (cReadTotal)
976 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
977
978 if (pcSamplesPlayed)
979 *pcSamplesPlayed = cReadTotal;
980
981 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n", cReadTotal, cbReadTotal, rc));
982 }
983
984 LogFlowFuncLeaveRC(rc);
985 return rc;
986}
987
988static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
989{
990 NOREF(pInterface);
991}
992
993/**
994 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
995 */
996static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
997{
998 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
999 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1000 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1001 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1002
1003 return NULL;
1004}
1005
1006/**
1007 * Constructs an OSS audio driver instance.
1008 *
1009 * @copydoc FNPDMDRVCONSTRUCT
1010 */
1011static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1012{
1013 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1014 LogRel(("Audio: Initializing OSS driver\n"));
1015
1016 /*
1017 * Init the static parts.
1018 */
1019 pThis->pDrvIns = pDrvIns;
1020 /* IBase */
1021 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
1022 /* IHostAudio */
1023 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
1024
1025 return VINF_SUCCESS;
1026}
1027
1028/**
1029 * Char driver registration record.
1030 */
1031const PDMDRVREG g_DrvHostOSSAudio =
1032{
1033 /* u32Version */
1034 PDM_DRVREG_VERSION,
1035 /* szName */
1036 "OSSAudio",
1037 /* szRCMod */
1038 "",
1039 /* szR0Mod */
1040 "",
1041 /* pszDescription */
1042 "OSS audio host driver",
1043 /* fFlags */
1044 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1045 /* fClass. */
1046 PDM_DRVREG_CLASS_AUDIO,
1047 /* cMaxInstances */
1048 ~0U,
1049 /* cbInstance */
1050 sizeof(DRVHOSTOSSAUDIO),
1051 /* pfnConstruct */
1052 drvHostOSSAudioConstruct,
1053 /* pfnDestruct */
1054 NULL,
1055 /* pfnRelocate */
1056 NULL,
1057 /* pfnIOCtl */
1058 NULL,
1059 /* pfnPowerOn */
1060 NULL,
1061 /* pfnReset */
1062 NULL,
1063 /* pfnSuspend */
1064 NULL,
1065 /* pfnResume */
1066 NULL,
1067 /* pfnAttach */
1068 NULL,
1069 /* pfnDetach */
1070 NULL,
1071 /* pfnPowerOff */
1072 NULL,
1073 /* pfnSoftReset */
1074 NULL,
1075 /* u32EndVersion */
1076 PDM_DRVREG_VERSION
1077};
1078
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use