VirtualBox

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

Last change on this file since 63214 was 63212, checked in by vboxsync, 8 years ago

Devices: warnings (gcc)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.4 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 63212 2016-08-09 14:52:41Z 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#include <errno.h>
19#include <fcntl.h>
20#include <sys/ioctl.h>
21#include <sys/mman.h>
22#include <sys/soundcard.h>
23#include <unistd.h>
24
25#include <iprt/alloc.h>
26#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
27
28#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
29#include <VBox/log.h>
30#include <VBox/vmm/pdmaudioifs.h>
31
32#include "DrvAudio.h"
33#include "AudioMixBuffer.h"
34
35#include "VBoxDD.h"
36
37
38/*********************************************************************************************************************************
39* Defines *
40*********************************************************************************************************************************/
41
42#if ((SOUND_VERSION > 360) && (defined(OSS_SYSINFO)))
43/* OSS > 3.6 has a new syscall available for querying a bit more detailed information
44 * about OSS' audio capabilities. This is handy for e.g. Solaris. */
45# define VBOX_WITH_AUDIO_OSS_SYSINFO 1
46#endif
47
48/** Makes DRVHOSTOSSAUDIO out of PDMIHOSTAUDIO. */
49#define PDMIHOSTAUDIO_2_DRVHOSTOSSAUDIO(pInterface) \
50 ( (PDRVHOSTOSSAUDIO)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTOSSAUDIO, IHostAudio)) )
51
52
53/*********************************************************************************************************************************
54* Structures *
55*********************************************************************************************************************************/
56
57/**
58 * OSS host audio driver instance data.
59 * @implements PDMIAUDIOCONNECTOR
60 */
61typedef struct DRVHOSTOSSAUDIO
62{
63 /** Pointer to the driver instance structure. */
64 PPDMDRVINS pDrvIns;
65 /** Pointer to host audio interface. */
66 PDMIHOSTAUDIO IHostAudio;
67 /** Error count for not flooding the release log.
68 * UINT32_MAX for unlimited logging. */
69 uint32_t cLogErrors;
70} DRVHOSTOSSAUDIO, *PDRVHOSTOSSAUDIO;
71
72typedef struct OSSAUDIOSTREAMCFG
73{
74 PDMAUDIOFMT enmFormat;
75 PDMAUDIOENDIANNESS enmENDIANNESS;
76 uint16_t uFreq;
77 uint8_t cChannels;
78 uint16_t cFragments;
79 uint32_t cbFragmentSize;
80} OSSAUDIOSTREAMCFG, *POSSAUDIOSTREAMCFG;
81
82typedef struct OSSAUDIOSTREAMIN
83{
84 /** Note: Always must come first! */
85 PDMAUDIOSTREAM pStreamIn;
86 int hFile;
87 int cFragments;
88 int cbFragmentSize;
89 /** Own PCM buffer. */
90 void *pvBuf;
91 /** Size (in bytes) of own PCM buffer. */
92 size_t cbBuf;
93 int old_optr;
94} OSSAUDIOSTREAMIN, *POSSAUDIOSTREAMIN;
95
96typedef struct OSSAUDIOSTREAMOUT
97{
98 /** Note: Always must come first! */
99 PDMAUDIOSTREAM pStreamOut;
100 int hFile;
101 int cFragments;
102 int cbFragmentSize;
103#ifndef RT_OS_L4
104 /** Whether we use a memory mapped file instead of our
105 * own allocated PCM buffer below. */
106 bool fMemMapped;
107#endif
108 /** Own PCM buffer in case memory mapping is unavailable. */
109 void *pvBuf;
110 /** Size (in bytes) of own PCM buffer. */
111 size_t cbBuf;
112 int old_optr;
113} OSSAUDIOSTREAMOUT, *POSSAUDIOSTREAMOUT;
114
115typedef struct OSSAUDIOCFG
116{
117#ifndef RT_OS_L4
118 bool try_mmap;
119#endif
120 int nfrags;
121 int fragsize;
122 const char *devpath_out;
123 const char *devpath_in;
124 int debug;
125} OSSAUDIOCFG, *POSSAUDIOCFG;
126
127static OSSAUDIOCFG s_OSSConf =
128{
129#ifndef RT_OS_L4
130 false,
131#endif
132 4,
133 4096,
134 "/dev/dsp",
135 "/dev/dsp",
136 0
137};
138
139
140/* http://www.df.lth.se/~john_e/gems/gem002d.html */
141static uint32_t popcount(uint32_t u)
142{
143 u = ((u&0x55555555) + ((u>>1)&0x55555555));
144 u = ((u&0x33333333) + ((u>>2)&0x33333333));
145 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
146 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
147 u = ( u&0x0000ffff) + (u>>16);
148 return u;
149}
150
151static uint32_t lsbindex(uint32_t u)
152{
153 return popcount ((u&-u)-1);
154}
155
156static int ossAudioFmtToOSS(PDMAUDIOFMT fmt)
157{
158 switch (fmt)
159 {
160 case PDMAUDIOFMT_S8:
161 return AFMT_S8;
162
163 case PDMAUDIOFMT_U8:
164 return AFMT_U8;
165
166 case PDMAUDIOFMT_S16:
167 return AFMT_S16_LE;
168
169 case PDMAUDIOFMT_U16:
170 return AFMT_U16_LE;
171
172 default:
173 break;
174 }
175
176 AssertMsgFailed(("Format %ld not supported\n", fmt));
177 return AFMT_U8;
178}
179
180static int ossOSSToAudioFmt(int fmt, PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pENDIANNESS)
181{
182 switch (fmt)
183 {
184 case AFMT_S8:
185 *pFmt = PDMAUDIOFMT_S8;
186 if (pENDIANNESS)
187 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
188 break;
189
190 case AFMT_U8:
191 *pFmt = PDMAUDIOFMT_U8;
192 if (pENDIANNESS)
193 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
194 break;
195
196 case AFMT_S16_LE:
197 *pFmt = PDMAUDIOFMT_S16;
198 if (pENDIANNESS)
199 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
200 break;
201
202 case AFMT_U16_LE:
203 *pFmt = PDMAUDIOFMT_U16;
204 if (pENDIANNESS)
205 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
206 break;
207
208 case AFMT_S16_BE:
209 *pFmt = PDMAUDIOFMT_S16;
210 if (pENDIANNESS)
211 *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
212 break;
213
214 case AFMT_U16_BE:
215 *pFmt = PDMAUDIOFMT_U16;
216 if (pENDIANNESS)
217 *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
218 break;
219
220 default:
221 AssertMsgFailed(("Format %ld not supported\n", fmt));
222 return VERR_NOT_SUPPORTED;
223 }
224
225 return VINF_SUCCESS;
226}
227
228static int ossStreamClose(int *phFile)
229{
230 if (!phFile || !*phFile)
231 return VINF_SUCCESS;
232
233 int rc;
234 if (close(*phFile))
235 {
236 LogRel(("OSS: Closing stream failed: %s\n", strerror(errno)));
237 rc = VERR_GENERAL_FAILURE; /** @todo */
238 }
239 else
240 {
241 *phFile = -1;
242 rc = VINF_SUCCESS;
243 }
244
245 return rc;
246}
247
248static int ossStreamOpen(const char *pszDev, int fOpen, POSSAUDIOSTREAMCFG pReq, POSSAUDIOSTREAMCFG pObt, int *phFile)
249{
250 AssertPtrReturn(pszDev, VERR_INVALID_POINTER);
251 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
252 AssertPtrReturn(pObt, VERR_INVALID_POINTER);
253 AssertPtrReturn(phFile, VERR_INVALID_POINTER);
254
255 int rc;
256
257 int hFile = -1;
258 do
259 {
260 hFile = open(pszDev, fOpen);
261 if (hFile == -1)
262 {
263 LogRel(("OSS: Failed to open %s: %s (%d)\n", pszDev, strerror(errno), errno));
264 rc = RTErrConvertFromErrno(errno);
265 break;
266 }
267
268 int iFormat = ossAudioFmtToOSS(pReq->enmFormat);
269 if (ioctl(hFile, SNDCTL_DSP_SAMPLESIZE, &iFormat))
270 {
271 LogRel(("OSS: Failed to set audio format to %ld: %s (%d)\n", iFormat, strerror(errno), errno));
272 rc = RTErrConvertFromErrno(errno);
273 break;
274 }
275
276 int cChannels = pReq->cChannels;
277 if (ioctl(hFile, SNDCTL_DSP_CHANNELS, &cChannels))
278 {
279 LogRel(("OSS: Failed to set number of audio channels (%d): %s (%d)\n", pReq->cChannels, strerror(errno), errno));
280 rc = RTErrConvertFromErrno(errno);
281 break;
282 }
283
284 int freq = pReq->uFreq;
285 if (ioctl(hFile, SNDCTL_DSP_SPEED, &freq))
286 {
287 LogRel(("OSS: Failed to set audio frequency (%dHZ): %s (%d)\n", pReq->uFreq, strerror(errno), errno));
288 rc = RTErrConvertFromErrno(errno);
289 break;
290 }
291
292 /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
293#if !(defined(VBOX) && defined(RT_OS_SOLARIS))
294 if (ioctl(hFile, SNDCTL_DSP_NONBLOCK))
295 {
296 LogRel(("OSS: Failed to set non-blocking mode: %s (%d)\n", strerror(errno), errno));
297 rc = RTErrConvertFromErrno(errno);
298 break;
299 }
300#endif
301 int mmmmssss = (pReq->cFragments << 16) | lsbindex(pReq->cbFragmentSize);
302 if (ioctl(hFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss))
303 {
304 LogRel(("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s (%d)\n",
305 pReq->cFragments, pReq->cbFragmentSize, strerror(errno), errno));
306 rc = RTErrConvertFromErrno(errno);
307 break;
308 }
309
310 audio_buf_info abinfo;
311 if (ioctl(hFile, (fOpen & O_RDONLY) ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo))
312 {
313 LogRel(("OSS: Failed to retrieve buffer length: %s (%d)\n", strerror(errno), errno));
314 rc = RTErrConvertFromErrno(errno);
315 break;
316 }
317
318 rc = ossOSSToAudioFmt(iFormat, &pObt->enmFormat, &pObt->enmENDIANNESS);
319 if (RT_SUCCESS(rc))
320 {
321 pObt->cChannels = cChannels;
322 pObt->uFreq = freq;
323 pObt->cFragments = abinfo.fragstotal;
324 pObt->cbFragmentSize = abinfo.fragsize;
325
326 *phFile = hFile;
327 }
328 }
329 while (0);
330
331 if (RT_FAILURE(rc))
332 ossStreamClose(&hFile);
333
334 LogFlowFuncLeaveRC(rc);
335 return rc;
336}
337
338static int ossControlStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
339 PDMAUDIOSTREAMCMD enmStreamCmd)
340{
341 NOREF(pInterface);
342 NOREF(pStream);
343 NOREF(enmStreamCmd);
344
345 /** @todo Nothing to do here right now!? */
346
347 return VINF_SUCCESS;
348}
349
350static int ossControlStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
351 PDMAUDIOSTREAMCMD enmStreamCmd)
352{
353 NOREF(pInterface);
354 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
355
356 POSSAUDIOSTREAMOUT pThisStream = (POSSAUDIOSTREAMOUT)pStream;
357
358#ifdef RT_OS_L4
359 return VINF_SUCCESS;
360#else
361 if (!pThisStream->fMemMapped)
362 return VINF_SUCCESS;
363#endif
364
365 int rc = VINF_SUCCESS;
366 int mask;
367 switch (enmStreamCmd)
368 {
369 case PDMAUDIOSTREAMCMD_ENABLE:
370 case PDMAUDIOSTREAMCMD_RESUME:
371 {
372 DrvAudioHlpClearBuf(&pStream->Props,
373 pThisStream->pvBuf, pThisStream->cbBuf, AudioMixBufSize(&pStream->MixBuf));
374
375 mask = PCM_ENABLE_OUTPUT;
376 if (ioctl(pThisStream->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
377 {
378 LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
379 rc = RTErrConvertFromErrno(errno);
380 }
381
382 break;
383 }
384
385 case PDMAUDIOSTREAMCMD_DISABLE:
386 case PDMAUDIOSTREAMCMD_PAUSE:
387 {
388 mask = 0;
389 if (ioctl(pThisStream->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
390 {
391 LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
392 rc = RTErrConvertFromErrno(errno);
393 }
394
395 break;
396 }
397
398 default:
399 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
400 rc = VERR_INVALID_PARAMETER;
401 break;
402 }
403
404 LogFlowFuncLeaveRC(rc);
405 return rc;
406}
407
408static DECLCALLBACK(int) drvHostOSSAudioInit(PPDMIHOSTAUDIO pInterface)
409{
410 NOREF(pInterface);
411
412 LogFlowFuncEnter();
413
414 return VINF_SUCCESS;
415}
416
417static DECLCALLBACK(int) drvHostOSSAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
418 uint32_t *pcSamplesCaptured)
419{
420 NOREF(pInterface);
421 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
422
423 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
424
425 int rc = VINF_SUCCESS;
426 size_t cbToRead = RT_MIN(pStrm->cbBuf,
427 AudioMixBufFreeBytes(&pStream->MixBuf));
428
429 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
430
431 uint32_t cWrittenTotal = 0;
432 uint32_t cbTemp;
433 ssize_t cbRead;
434 size_t offWrite = 0;
435
436 while (cbToRead)
437 {
438 cbTemp = RT_MIN(cbToRead, pStrm->cbBuf);
439 AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
440 cbRead = read(pStrm->hFile, (uint8_t *)pStrm->pvBuf + offWrite, cbTemp);
441
442 LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n", cbRead, cbTemp, cbToRead));
443
444 if (cbRead < 0)
445 {
446 switch (errno)
447 {
448 case 0:
449 {
450 LogFunc(("Failed to read %z frames\n", cbRead));
451 rc = VERR_ACCESS_DENIED;
452 break;
453 }
454
455 case EINTR:
456 case EAGAIN:
457 rc = VERR_NO_DATA;
458 break;
459
460 default:
461 LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n", cbTemp, rc));
462 rc = VERR_GENERAL_FAILURE; /** @todo Fix this. */
463 break;
464 }
465
466 if (RT_FAILURE(rc))
467 break;
468 }
469 else if (cbRead)
470 {
471 uint32_t cWritten;
472 rc = AudioMixBufWriteCirc(&pStream->MixBuf, pStrm->pvBuf, cbRead, &cWritten);
473 if (RT_FAILURE(rc))
474 break;
475
476 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten);
477
478 Assert(cbToRead >= cbWritten);
479 cbToRead -= cbWritten;
480 offWrite += cbWritten;
481 cWrittenTotal += cWritten;
482 }
483 else /* No more data, try next round. */
484 break;
485 }
486
487 if (rc == VERR_NO_DATA)
488 rc = VINF_SUCCESS;
489
490 if (RT_SUCCESS(rc))
491 {
492 uint32_t cProcessed = 0;
493 if (cWrittenTotal)
494 rc = AudioMixBufMixToParent(&pStream->MixBuf, cWrittenTotal, &cProcessed);
495
496 if (pcSamplesCaptured)
497 *pcSamplesCaptured = cWrittenTotal;
498
499 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
500 cWrittenTotal, cProcessed, rc));
501 }
502
503 LogFlowFuncLeaveRC(rc);
504 return rc;
505}
506
507static int ossDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
508{
509 NOREF(pInterface);
510 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
511
512 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
513
514 LogFlowFuncEnter();
515
516 if (pStrm->pvBuf)
517 {
518 Assert(pStrm->cbBuf);
519
520 RTMemFree(pStrm->pvBuf);
521 pStrm->pvBuf = NULL;
522 }
523
524 pStrm->cbBuf = 0;
525
526 ossStreamClose(&pStrm->hFile);
527
528 return VINF_SUCCESS;
529}
530
531static int ossDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
532{
533 NOREF(pInterface);
534 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
535
536 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
537
538 LogFlowFuncEnter();
539
540#ifndef RT_OS_L4
541 if (pStrm->fMemMapped)
542 {
543 if (pStrm->pvBuf)
544 {
545 Assert(pStrm->cbBuf);
546
547 int rc2 = munmap(pStrm->pvBuf, pStrm->cbBuf);
548 if (rc2 == 0)
549 {
550 pStrm->pvBuf = NULL;
551 pStrm->cbBuf = 0;
552
553 pStrm->fMemMapped = false;
554 }
555 else
556 LogRel(("OSS: Failed to memory unmap playback buffer on close: %s\n", strerror(errno)));
557 }
558 }
559 else
560 {
561#endif
562 if (pStrm->pvBuf)
563 {
564 Assert(pStrm->cbBuf);
565
566 RTMemFree(pStrm->pvBuf);
567 pStrm->pvBuf = NULL;
568 }
569
570 pStrm->cbBuf = 0;
571#ifndef RT_OS_L4
572 }
573#endif
574
575 ossStreamClose(&pStrm->hFile);
576
577 return VINF_SUCCESS;
578}
579
580static DECLCALLBACK(int) drvHostOSSAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
581{
582 NOREF(pInterface);
583
584 pCfg->cbStreamIn = sizeof(OSSAUDIOSTREAMIN);
585 pCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT);
586
587 pCfg->cSources = 0;
588 pCfg->cSinks = 0;
589
590 int hFile = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0);
591 if (hFile == -1)
592 {
593 /* Try opening the mixing device instead. */
594 hFile = open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
595 }
596
597 int ossVer = -1;
598
599#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
600 oss_sysinfo ossInfo;
601 RT_ZERO(ossInfo);
602#endif
603
604 if (hFile != -1)
605 {
606 int err = ioctl(hFile, OSS_GETVERSION, &ossVer);
607 if (err == 0)
608 {
609 LogRel2(("OSS: Using version: %d\n", ossVer));
610#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
611 err = ioctl(hFile, OSS_SYSINFO, &ossInfo);
612 if (err == 0)
613 {
614 LogRel2(("OSS: Number of DSPs: %d\n", ossInfo.numaudios));
615 LogRel2(("OSS: Number of mixers: %d\n", ossInfo.nummixers));
616
617 int cDev = ossInfo.nummixers;
618 if (!cDev)
619 cDev = ossInfo.numaudios;
620
621 pCfg->cSources = cDev;
622 pCfg->cSinks = cDev;
623
624 pCfg->cMaxStreamsIn = UINT32_MAX;
625 pCfg->cMaxStreamsOut = UINT32_MAX;
626 }
627 else
628 {
629#endif
630 /* Since we cannot query anything, assume that we have at least
631 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */
632 pCfg->cSources = 1;
633 pCfg->cSinks = 1;
634
635 pCfg->cMaxStreamsIn = UINT32_MAX;
636 pCfg->cMaxStreamsOut = UINT32_MAX;
637#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
638 }
639#endif
640 }
641 else
642 LogRel(("OSS: Unable to determine installed version: %s (%d)\n", strerror(err), err));
643 }
644 else
645 LogRel(("OSS: No devices found, audio is not available\n"));
646
647 return VINF_SUCCESS;
648}
649
650static int ossCreateStreamIn(PPDMIHOSTAUDIO pInterface,
651 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
652{
653 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
654 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
655 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
656
657 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
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 & pStream->Props.uAlign)
677 LogRel(("OSS: Warning: Misaligned capturing buffer: Size = %zu, Alignment = %u\n",
678 obtStream.cFragments * obtStream.cbFragmentSize,
679 pStream->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 = DrvAudioHlpStreamCfgToProps(&streamCfg, &pStream->Props);
688 if (RT_SUCCESS(rc))
689 {
690 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
691 >> pStream->Props.cShift;
692 if (!cSamples)
693 rc = VERR_INVALID_PARAMETER;
694 }
695 }
696
697 if (RT_SUCCESS(rc))
698 {
699 size_t cbSample = (1 << pStream->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 int ossCreateStreamOut(PPDMIHOSTAUDIO pInterface,
729 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg,
730 uint32_t *pcSamples)
731{
732 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
733 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
734 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
735
736 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
737
738 int rc;
739 int hFile = -1;
740
741 do
742 {
743 uint32_t cSamples;
744
745 OSSAUDIOSTREAMCFG reqStream, obtStream;
746 reqStream.enmFormat = pCfg->enmFormat;
747 reqStream.uFreq = pCfg->uHz;
748 reqStream.cChannels = pCfg->cChannels;
749 reqStream.cFragments = s_OSSConf.nfrags;
750 reqStream.cbFragmentSize = s_OSSConf.fragsize;
751
752 rc = ossStreamOpen(s_OSSConf.devpath_out, O_WRONLY | O_NONBLOCK, &reqStream, &obtStream, &hFile);
753 if (RT_SUCCESS(rc))
754 {
755 if (obtStream.cFragments * obtStream.cbFragmentSize & pStream->Props.uAlign)
756 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
757 obtStream.cFragments * obtStream.cbFragmentSize,
758 pStream->Props.uAlign + 1));
759
760 PDMAUDIOSTREAMCFG streamCfg;
761 streamCfg.enmFormat = obtStream.enmFormat;
762 streamCfg.uHz = obtStream.uFreq;
763 streamCfg.cChannels = pCfg->cChannels;
764 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
765
766 rc = DrvAudioHlpStreamCfgToProps(&streamCfg, &pStream->Props);
767 if (RT_SUCCESS(rc))
768 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
769 >> pStream->Props.cShift;
770 }
771
772 if (RT_SUCCESS(rc))
773 {
774 pStrm->fMemMapped = false;
775
776 size_t cbSamples = cSamples << pStream->Props.cShift;
777 Assert(cbSamples);
778
779#ifndef RT_OS_L4
780 if (s_OSSConf.try_mmap)
781 {
782 pStrm->pvBuf = mmap(0, cbSamples, PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
783 if (pStrm->pvBuf == MAP_FAILED)
784 {
785 LogRel(("OSS: Failed to memory map %zu bytes of playback buffer: %s\n", cbSamples, strerror(errno)));
786 rc = RTErrConvertFromErrno(errno);
787 break;
788 }
789 else
790 {
791 int mask = 0;
792 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
793 {
794 LogRel(("OSS: Failed to retrieve initial trigger mask for playback buffer: %s\n", strerror(errno)));
795 rc = RTErrConvertFromErrno(errno);
796 /* Note: No break here, need to unmap file first! */
797 }
798 else
799 {
800 mask = PCM_ENABLE_OUTPUT;
801 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
802 {
803 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n", strerror(errno)));
804 rc = RTErrConvertFromErrno(errno);
805 /* Note: No break here, need to unmap file first! */
806 }
807 else
808 pStrm->fMemMapped = true;
809 }
810
811 if (RT_FAILURE(rc))
812 {
813 int rc2 = munmap(pStrm->pvBuf, cbSamples);
814 if (rc2)
815 LogRel(("OSS: Failed to memory unmap playback buffer: %s\n", strerror(errno)));
816 break;
817 }
818 }
819 }
820#endif /* !RT_OS_L4 */
821
822 /* Memory mapping failed above? Try allocating an own buffer. */
823#ifndef RT_OS_L4
824 if (!pStrm->fMemMapped)
825 {
826#endif
827 void *pvBuf = RTMemAlloc(cbSamples);
828 if (!pvBuf)
829 {
830 LogRel(("OSS: Failed allocating playback buffer with %RU32 samples (%zu bytes)\n", cSamples, cbSamples));
831 rc = VERR_NO_MEMORY;
832 break;
833 }
834
835 pStrm->hFile = hFile;
836 pStrm->pvBuf = pvBuf;
837 pStrm->cbBuf = cbSamples;
838#ifndef RT_OS_L4
839 }
840#endif
841 if (pcSamples)
842 *pcSamples = cSamples;
843 }
844
845 } while (0);
846
847 if (RT_FAILURE(rc))
848 ossStreamClose(&hFile);
849
850 LogFlowFuncLeaveRC(rc);
851 return rc;
852}
853
854static DECLCALLBACK(int) drvHostOSSAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesPlayed)
855{
856 NOREF(pInterface);
857 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
858
859 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
860
861 int rc = VINF_SUCCESS;
862 uint32_t cbReadTotal = 0;
863 count_info cntinfo;
864
865 do
866 {
867 size_t cbBuf = AudioMixBufSizeBytes(&pStream->MixBuf);
868
869 uint32_t cLive = AudioMixBufLive(&pStream->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(&pStream->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(&pStream->MixBuf, abinfo.bytes), cLive);
926 if (!cToRead)
927 break;
928#ifndef RT_OS_L4
929 }
930#endif
931 size_t cbToRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cToRead);
932 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
933
934 uint32_t cRead, cbRead;
935 while (cbToRead)
936 {
937 rc = AudioMixBufReadCirc(&pStream->MixBuf, pStrm->pvBuf, cbToRead, &cRead);
938 if (RT_FAILURE(rc))
939 break;
940
941 cbRead = AUDIOMIXBUF_S2B(&pStream->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(&pStream->MixBuf, cbReadTotal);
966 if (cReadTotal)
967 AudioMixBufFinish(&pStream->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
984static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostOSSAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
985{
986 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
987 RT_NOREF(enmDir);
988
989 return PDMAUDIOBACKENDSTS_RUNNING;
990}
991
992static DECLCALLBACK(int) drvHostOSSAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
993 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
994{
995 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
996 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
997 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
998
999 int rc;
1000 if (pCfg->enmDir == PDMAUDIODIR_IN)
1001 rc = ossCreateStreamIn(pInterface, pStream, pCfg, pcSamples);
1002 else
1003 rc = ossCreateStreamOut(pInterface, pStream, pCfg, pcSamples);
1004
1005 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
1006 return rc;
1007}
1008
1009static DECLCALLBACK(int) drvHostOSSAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1010{
1011 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1012 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1013
1014 int rc;
1015 if (pStream->enmDir == PDMAUDIODIR_IN)
1016 rc = ossDestroyStreamIn(pInterface, pStream);
1017 else
1018 rc = ossDestroyStreamOut(pInterface, pStream);
1019
1020 return rc;
1021}
1022
1023static DECLCALLBACK(int) drvHostOSSAudioStreamControl(PPDMIHOSTAUDIO pInterface,
1024 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1025{
1026 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1027 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1028
1029 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1030
1031 int rc;
1032 if (pStream->enmDir == PDMAUDIODIR_IN)
1033 rc = ossControlStreamIn(pInterface, pStream, enmStreamCmd);
1034 else
1035 rc = ossControlStreamOut(pInterface, pStream, enmStreamCmd);
1036
1037 return rc;
1038}
1039
1040static DECLCALLBACK(int) drvHostOSSAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1041{
1042 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1043 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1044
1045 LogFlowFuncEnter();
1046
1047 /* Nothing to do here for OSS. */
1048 return VINF_SUCCESS;
1049}
1050
1051static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostOSSAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1052{
1053 NOREF(pInterface);
1054 NOREF(pStream);
1055
1056 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_INITIALIZED
1057 | PDMAUDIOSTRMSTS_FLAG_ENABLED;
1058
1059 strmSts |= pStream->enmDir == PDMAUDIODIR_IN
1060 ? PDMAUDIOSTRMSTS_FLAG_DATA_READABLE
1061 : PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
1062
1063 return strmSts;
1064}
1065/**
1066 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1067 */
1068static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1069{
1070 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1071 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1072 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1073 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1074
1075 return NULL;
1076}
1077
1078/**
1079 * Constructs an OSS audio driver instance.
1080 *
1081 * @copydoc FNPDMDRVCONSTRUCT
1082 */
1083static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1084{
1085 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1086 RT_NOREF(pCfg, fFlags);
1087 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1088 LogRel(("Audio: Initializing OSS driver\n"));
1089
1090 /*
1091 * Init the static parts.
1092 */
1093 pThis->pDrvIns = pDrvIns;
1094 /* IBase */
1095 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
1096 /* IHostAudio */
1097 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
1098
1099 return VINF_SUCCESS;
1100}
1101
1102/**
1103 * Char driver registration record.
1104 */
1105const PDMDRVREG g_DrvHostOSSAudio =
1106{
1107 /* u32Version */
1108 PDM_DRVREG_VERSION,
1109 /* szName */
1110 "OSSAudio",
1111 /* szRCMod */
1112 "",
1113 /* szR0Mod */
1114 "",
1115 /* pszDescription */
1116 "OSS audio host driver",
1117 /* fFlags */
1118 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1119 /* fClass. */
1120 PDM_DRVREG_CLASS_AUDIO,
1121 /* cMaxInstances */
1122 ~0U,
1123 /* cbInstance */
1124 sizeof(DRVHOSTOSSAUDIO),
1125 /* pfnConstruct */
1126 drvHostOSSAudioConstruct,
1127 /* pfnDestruct */
1128 NULL,
1129 /* pfnRelocate */
1130 NULL,
1131 /* pfnIOCtl */
1132 NULL,
1133 /* pfnPowerOn */
1134 NULL,
1135 /* pfnReset */
1136 NULL,
1137 /* pfnSuspend */
1138 NULL,
1139 /* pfnResume */
1140 NULL,
1141 /* pfnAttach */
1142 NULL,
1143 /* pfnDetach */
1144 NULL,
1145 /* pfnPowerOff */
1146 NULL,
1147 /* pfnSoftReset */
1148 NULL,
1149 /* u32EndVersion */
1150 PDM_DRVREG_VERSION
1151};
1152
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