VirtualBox

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

Last change on this file since 65015 was 63766, checked in by vboxsync, 8 years ago

Audio/DrvHostAudioOSS.cpp: Fixed stream access mode, also for FreeBSD (thanks to
Jung-uk Kim).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.0 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 63766 2016-09-08 13:45:48Z 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 /** The PCM properties of this stream. */
87 PDMAUDIOPCMPROPS Props;
88 int hFile;
89 int cFragments;
90 int cbFragmentSize;
91 /** Own PCM buffer. */
92 void *pvBuf;
93 /** Size (in bytes) of own PCM buffer. */
94 size_t cbBuf;
95 int old_optr;
96} OSSAUDIOSTREAMIN, *POSSAUDIOSTREAMIN;
97
98typedef struct OSSAUDIOSTREAMOUT
99{
100 /** Note: Always must come first! */
101 PDMAUDIOSTREAM pStreamOut;
102 /** The PCM properties of this stream. */
103 PDMAUDIOPCMPROPS Props;
104 int hFile;
105 int cFragments;
106 int cbFragmentSize;
107#ifndef RT_OS_L4
108 /** Whether we use a memory mapped file instead of our
109 * own allocated PCM buffer below. */
110 bool fMemMapped;
111#endif
112 /** Own PCM buffer in case memory mapping is unavailable. */
113 void *pvBuf;
114 /** Size (in bytes) of own PCM buffer. */
115 size_t cbBuf;
116 int old_optr;
117} OSSAUDIOSTREAMOUT, *POSSAUDIOSTREAMOUT;
118
119typedef struct OSSAUDIOCFG
120{
121#ifndef RT_OS_L4
122 bool try_mmap;
123#endif
124 int nfrags;
125 int fragsize;
126 const char *devpath_out;
127 const char *devpath_in;
128 int debug;
129} OSSAUDIOCFG, *POSSAUDIOCFG;
130
131static OSSAUDIOCFG s_OSSConf =
132{
133#ifndef RT_OS_L4
134 false,
135#endif
136 4,
137 4096,
138 "/dev/dsp",
139 "/dev/dsp",
140 0
141};
142
143
144/* http://www.df.lth.se/~john_e/gems/gem002d.html */
145static uint32_t popcount(uint32_t u)
146{
147 u = ((u&0x55555555) + ((u>>1)&0x55555555));
148 u = ((u&0x33333333) + ((u>>2)&0x33333333));
149 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
150 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
151 u = ( u&0x0000ffff) + (u>>16);
152 return u;
153}
154
155
156static uint32_t lsbindex(uint32_t u)
157{
158 return popcount ((u&-u)-1);
159}
160
161
162static int ossAudioFmtToOSS(PDMAUDIOFMT fmt)
163{
164 switch (fmt)
165 {
166 case PDMAUDIOFMT_S8:
167 return AFMT_S8;
168
169 case PDMAUDIOFMT_U8:
170 return AFMT_U8;
171
172 case PDMAUDIOFMT_S16:
173 return AFMT_S16_LE;
174
175 case PDMAUDIOFMT_U16:
176 return AFMT_U16_LE;
177
178 default:
179 break;
180 }
181
182 AssertMsgFailed(("Format %ld not supported\n", fmt));
183 return AFMT_U8;
184}
185
186
187static int ossOSSToAudioFmt(int fmt, PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pENDIANNESS)
188{
189 switch (fmt)
190 {
191 case AFMT_S8:
192 *pFmt = PDMAUDIOFMT_S8;
193 if (pENDIANNESS)
194 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
195 break;
196
197 case AFMT_U8:
198 *pFmt = PDMAUDIOFMT_U8;
199 if (pENDIANNESS)
200 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
201 break;
202
203 case AFMT_S16_LE:
204 *pFmt = PDMAUDIOFMT_S16;
205 if (pENDIANNESS)
206 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
207 break;
208
209 case AFMT_U16_LE:
210 *pFmt = PDMAUDIOFMT_U16;
211 if (pENDIANNESS)
212 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
213 break;
214
215 case AFMT_S16_BE:
216 *pFmt = PDMAUDIOFMT_S16;
217 if (pENDIANNESS)
218 *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
219 break;
220
221 case AFMT_U16_BE:
222 *pFmt = PDMAUDIOFMT_U16;
223 if (pENDIANNESS)
224 *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
225 break;
226
227 default:
228 AssertMsgFailed(("Format %ld not supported\n", fmt));
229 return VERR_NOT_SUPPORTED;
230 }
231
232 return VINF_SUCCESS;
233}
234
235
236static int ossStreamClose(int *phFile)
237{
238 if (!phFile || !*phFile)
239 return VINF_SUCCESS;
240
241 int rc;
242 if (close(*phFile))
243 {
244 LogRel(("OSS: Closing stream failed: %s\n", strerror(errno)));
245 rc = VERR_GENERAL_FAILURE; /** @todo */
246 }
247 else
248 {
249 *phFile = -1;
250 rc = VINF_SUCCESS;
251 }
252
253 return rc;
254}
255
256
257static int ossStreamOpen(const char *pszDev, int fOpen, POSSAUDIOSTREAMCFG pReq, POSSAUDIOSTREAMCFG pObt, int *phFile)
258{
259 int rc;
260
261 int hFile = -1;
262 do
263 {
264 hFile = open(pszDev, fOpen);
265 if (hFile == -1)
266 {
267 LogRel(("OSS: Failed to open %s: %s (%d)\n", pszDev, strerror(errno), errno));
268 rc = RTErrConvertFromErrno(errno);
269 break;
270 }
271
272 int iFormat = ossAudioFmtToOSS(pReq->enmFormat);
273 if (ioctl(hFile, SNDCTL_DSP_SAMPLESIZE, &iFormat))
274 {
275 LogRel(("OSS: Failed to set audio format to %ld: %s (%d)\n", iFormat, strerror(errno), errno));
276 rc = RTErrConvertFromErrno(errno);
277 break;
278 }
279
280 int cChannels = pReq->cChannels;
281 if (ioctl(hFile, SNDCTL_DSP_CHANNELS, &cChannels))
282 {
283 LogRel(("OSS: Failed to set number of audio channels (%d): %s (%d)\n", pReq->cChannels, strerror(errno), errno));
284 rc = RTErrConvertFromErrno(errno);
285 break;
286 }
287
288 int freq = pReq->uFreq;
289 if (ioctl(hFile, SNDCTL_DSP_SPEED, &freq))
290 {
291 LogRel(("OSS: Failed to set audio frequency (%dHZ): %s (%d)\n", pReq->uFreq, strerror(errno), errno));
292 rc = RTErrConvertFromErrno(errno);
293 break;
294 }
295
296 /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
297#if !(defined(VBOX) && defined(RT_OS_SOLARIS))
298 if (ioctl(hFile, SNDCTL_DSP_NONBLOCK))
299 {
300 LogRel(("OSS: Failed to set non-blocking mode: %s (%d)\n", strerror(errno), errno));
301 rc = RTErrConvertFromErrno(errno);
302 break;
303 }
304#endif
305 int mmmmssss = (pReq->cFragments << 16) | lsbindex(pReq->cbFragmentSize);
306 if (ioctl(hFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss))
307 {
308 LogRel(("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s (%d)\n",
309 pReq->cFragments, pReq->cbFragmentSize, strerror(errno), errno));
310 rc = RTErrConvertFromErrno(errno);
311 break;
312 }
313
314 /* Check access mode (input or output). */
315 bool fIn = ((fOpen & O_ACCMODE) == O_RDONLY);
316
317 audio_buf_info abinfo;
318 if (ioctl(hFile, fIn ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo))
319 {
320 LogRel(("OSS: Failed to retrieve %s buffer length: %s (%d)\n", fIn ? "input" : "output", strerror(errno), errno));
321 rc = RTErrConvertFromErrno(errno);
322 break;
323 }
324
325 rc = ossOSSToAudioFmt(iFormat, &pObt->enmFormat, &pObt->enmENDIANNESS);
326 if (RT_SUCCESS(rc))
327 {
328 pObt->cChannels = cChannels;
329 pObt->uFreq = freq;
330 pObt->cFragments = abinfo.fragstotal;
331 pObt->cbFragmentSize = abinfo.fragsize;
332
333 *phFile = hFile;
334 }
335 }
336 while (0);
337
338 if (RT_FAILURE(rc))
339 ossStreamClose(&hFile);
340
341 LogFlowFuncLeaveRC(rc);
342 return rc;
343}
344
345
346static int ossControlStreamIn(/*PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd*/ void)
347{
348 /** @todo Nothing to do here right now!? */
349
350 return VINF_SUCCESS;
351}
352
353
354static int ossControlStreamOut(PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
355{
356 POSSAUDIOSTREAMOUT pStreamOut = (POSSAUDIOSTREAMOUT)pStream;
357
358#ifdef RT_OS_L4
359 return VINF_SUCCESS;
360#else
361 if (!pStreamOut->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(&pStreamOut->Props,
373 pStreamOut->pvBuf, pStreamOut->cbBuf, AudioMixBufSize(&pStream->MixBuf));
374
375 mask = PCM_ENABLE_OUTPUT;
376 if (ioctl(pStreamOut->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(pStreamOut->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
408
409/**
410 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
411 */
412static DECLCALLBACK(int) drvHostOSSAudioInit(PPDMIHOSTAUDIO pInterface)
413{
414 RT_NOREF(pInterface);
415
416 LogFlowFuncEnter();
417
418 return VINF_SUCCESS;
419}
420
421
422/**
423 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
424 */
425static DECLCALLBACK(int) drvHostOSSAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
426{
427 RT_NOREF(pInterface, cbBuf, pvBuf);
428 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
429
430 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
431
432 int rc = VINF_SUCCESS;
433 size_t cbToRead = RT_MIN(pStrm->cbBuf,
434 AudioMixBufFreeBytes(&pStream->MixBuf));
435
436 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
437
438 uint32_t cWrittenTotal = 0;
439 uint32_t cbTemp;
440 ssize_t cbRead;
441 size_t offWrite = 0;
442
443 while (cbToRead)
444 {
445 cbTemp = RT_MIN(cbToRead, pStrm->cbBuf);
446 AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
447 cbRead = read(pStrm->hFile, (uint8_t *)pStrm->pvBuf + offWrite, cbTemp);
448
449 LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n", cbRead, cbTemp, cbToRead));
450
451 if (cbRead < 0)
452 {
453 switch (errno)
454 {
455 case 0:
456 {
457 LogFunc(("Failed to read %z frames\n", cbRead));
458 rc = VERR_ACCESS_DENIED;
459 break;
460 }
461
462 case EINTR:
463 case EAGAIN:
464 rc = VERR_NO_DATA;
465 break;
466
467 default:
468 LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n", cbTemp, rc));
469 rc = VERR_GENERAL_FAILURE; /** @todo Fix this. */
470 break;
471 }
472
473 if (RT_FAILURE(rc))
474 break;
475 }
476 else if (cbRead)
477 {
478 uint32_t cWritten;
479 rc = AudioMixBufWriteCirc(&pStream->MixBuf, pStrm->pvBuf, cbRead, &cWritten);
480 if (RT_FAILURE(rc))
481 break;
482
483 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten);
484
485 Assert(cbToRead >= cbWritten);
486 cbToRead -= cbWritten;
487 offWrite += cbWritten;
488 cWrittenTotal += cWritten;
489 }
490 else /* No more data, try next round. */
491 break;
492 }
493
494 if (rc == VERR_NO_DATA)
495 rc = VINF_SUCCESS;
496
497 if (RT_SUCCESS(rc))
498 {
499 uint32_t cProcessed = 0;
500 if (cWrittenTotal)
501 rc = AudioMixBufMixToParent(&pStream->MixBuf, cWrittenTotal, &cProcessed);
502
503 if (pcbRead)
504 *pcbRead = cWrittenTotal;
505
506 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
507 cWrittenTotal, cProcessed, rc));
508 }
509
510 LogFlowFuncLeaveRC(rc);
511 return rc;
512}
513
514
515static int ossDestroyStreamIn(PPDMAUDIOSTREAM pStream)
516{
517 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
518
519 LogFlowFuncEnter();
520
521 if (pStrm->pvBuf)
522 {
523 Assert(pStrm->cbBuf);
524
525 RTMemFree(pStrm->pvBuf);
526 pStrm->pvBuf = NULL;
527 }
528
529 pStrm->cbBuf = 0;
530
531 ossStreamClose(&pStrm->hFile);
532
533 return VINF_SUCCESS;
534}
535
536
537static int ossDestroyStreamOut(PPDMAUDIOSTREAM pStream)
538{
539 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
540
541 LogFlowFuncEnter();
542
543#ifndef RT_OS_L4
544 if (pStrm->fMemMapped)
545 {
546 if (pStrm->pvBuf)
547 {
548 Assert(pStrm->cbBuf);
549
550 int rc2 = munmap(pStrm->pvBuf, pStrm->cbBuf);
551 if (rc2 == 0)
552 {
553 pStrm->pvBuf = NULL;
554 pStrm->cbBuf = 0;
555
556 pStrm->fMemMapped = false;
557 }
558 else
559 LogRel(("OSS: Failed to memory unmap playback buffer on close: %s\n", strerror(errno)));
560 }
561 }
562 else
563 {
564#endif
565 if (pStrm->pvBuf)
566 {
567 Assert(pStrm->cbBuf);
568
569 RTMemFree(pStrm->pvBuf);
570 pStrm->pvBuf = NULL;
571 }
572
573 pStrm->cbBuf = 0;
574#ifndef RT_OS_L4
575 }
576#endif
577
578 ossStreamClose(&pStrm->hFile);
579
580 return VINF_SUCCESS;
581}
582
583
584/**
585 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
586 */
587static DECLCALLBACK(int) drvHostOSSAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
588{
589 RT_NOREF(pInterface);
590
591 pBackendCfg->cbStreamIn = sizeof(OSSAUDIOSTREAMIN);
592 pBackendCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT);
593
594 int hFile = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0);
595 if (hFile == -1)
596 {
597 /* Try opening the mixing device instead. */
598 hFile = open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
599 }
600
601 int ossVer = -1;
602
603#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
604 oss_sysinfo ossInfo;
605 RT_ZERO(ossInfo);
606#endif
607
608 if (hFile != -1)
609 {
610 int err = ioctl(hFile, OSS_GETVERSION, &ossVer);
611 if (err == 0)
612 {
613 LogRel2(("OSS: Using version: %d\n", ossVer));
614#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
615 err = ioctl(hFile, OSS_SYSINFO, &ossInfo);
616 if (err == 0)
617 {
618 LogRel2(("OSS: Number of DSPs: %d\n", ossInfo.numaudios));
619 LogRel2(("OSS: Number of mixers: %d\n", ossInfo.nummixers));
620
621 int cDev = ossInfo.nummixers;
622 if (!cDev)
623 cDev = ossInfo.numaudios;
624
625 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
626 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
627 }
628 else
629 {
630#endif
631 /* Since we cannot query anything, assume that we have at least
632 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */
633
634 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
635 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
636#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
637 }
638#endif
639 }
640 else
641 LogRel(("OSS: Unable to determine installed version: %s (%d)\n", strerror(err), err));
642 }
643 else
644 LogRel(("OSS: No devices found, audio is not available\n"));
645
646 return VINF_SUCCESS;
647}
648
649
650static int ossCreateStreamIn(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
651{
652 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
653
654 int rc;
655 int hFile = -1;
656
657 do
658 {
659 uint32_t cSamples;
660
661 OSSAUDIOSTREAMCFG reqStream, obtStream;
662 reqStream.enmFormat = pCfgReq->enmFormat;
663 reqStream.uFreq = pCfgReq->uHz;
664 reqStream.cChannels = pCfgReq->cChannels;
665 reqStream.cFragments = s_OSSConf.nfrags;
666 reqStream.cbFragmentSize = s_OSSConf.fragsize;
667
668 rc = ossStreamOpen(s_OSSConf.devpath_in, O_RDONLY | O_NONBLOCK, &reqStream, &obtStream, &hFile);
669 if (RT_SUCCESS(rc))
670 {
671 pCfgAcq->enmFormat = obtStream.enmFormat;
672 pCfgAcq->uHz = obtStream.uFreq;
673 pCfgAcq->cChannels = pCfgReq->cChannels; /** @todo r=andy Why not using obtStream? */
674 pCfgAcq->enmEndianness = obtStream.enmENDIANNESS;
675
676 rc = DrvAudioHlpStreamCfgToProps(pCfgAcq, &pStrm->Props);
677 if (RT_SUCCESS(rc))
678 {
679 if (obtStream.cFragments * obtStream.cbFragmentSize & pStrm->Props.uAlign)
680 {
681 LogRel(("OSS: Warning: Misaligned capturing buffer: Size = %zu, Alignment = %u\n",
682 obtStream.cFragments * obtStream.cbFragmentSize, pStrm->Props.uAlign + 1));
683 }
684
685 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize) >> pStrm->Props.cShift;
686 if (!cSamples)
687 rc = VERR_INVALID_PARAMETER;
688 }
689
690 if (RT_SUCCESS(rc))
691 {
692 size_t cbSample = (1 << pStrm->Props.cShift);
693
694 size_t cbBuf = cSamples * cbSample;
695 void *pvBuf = RTMemAlloc(cbBuf);
696 if (!pvBuf)
697 {
698 LogRel(("OSS: Failed allocating capturing buffer with %RU32 samples (%zu bytes per sample)\n",
699 cSamples, cbSample));
700 rc = VERR_NO_MEMORY;
701 break;
702 }
703
704 pStrm->hFile = hFile;
705 pStrm->pvBuf = pvBuf;
706 pStrm->cbBuf = cbBuf;
707
708 pCfgAcq->cSampleBufferSize = cSamples;
709 }
710 }
711
712 } while (0);
713
714 if (RT_FAILURE(rc))
715 ossStreamClose(&hFile);
716
717 LogFlowFuncLeaveRC(rc);
718 return rc;
719}
720
721
722static int ossCreateStreamOut(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
723{
724 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
725
726 int rc;
727 int hFile = -1;
728
729 do
730 {
731 uint32_t cSamples;
732
733 OSSAUDIOSTREAMCFG reqStream, obtStream;
734 reqStream.enmFormat = pCfgReq->enmFormat;
735 reqStream.uFreq = pCfgReq->uHz;
736 reqStream.cChannels = pCfgReq->cChannels;
737 reqStream.cFragments = s_OSSConf.nfrags;
738 reqStream.cbFragmentSize = s_OSSConf.fragsize;
739
740 rc = ossStreamOpen(s_OSSConf.devpath_out, O_WRONLY | O_NONBLOCK, &reqStream, &obtStream, &hFile);
741 if (RT_SUCCESS(rc))
742 {
743 pCfgAcq->enmFormat = obtStream.enmFormat;
744 pCfgAcq->uHz = obtStream.uFreq;
745 pCfgAcq->cChannels = pCfgReq->cChannels; /** @todo r=andy Why not using obtStream? */
746 pCfgAcq->enmEndianness = obtStream.enmENDIANNESS;
747
748 rc = DrvAudioHlpStreamCfgToProps(pCfgAcq, &pStrm->Props);
749 if (RT_SUCCESS(rc))
750 {
751 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize) >> pStrm->Props.cShift;
752
753 if (obtStream.cFragments * obtStream.cbFragmentSize & pStrm->Props.uAlign)
754 {
755 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
756 obtStream.cFragments * obtStream.cbFragmentSize, pStrm->Props.uAlign + 1));
757 }
758 }
759 }
760
761 if (RT_SUCCESS(rc))
762 {
763 pStrm->fMemMapped = false;
764
765 size_t cbSamples = cSamples << pStrm->Props.cShift;
766 Assert(cbSamples);
767
768#ifndef RT_OS_L4
769 if (s_OSSConf.try_mmap)
770 {
771 pStrm->pvBuf = mmap(0, cbSamples, PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
772 if (pStrm->pvBuf == MAP_FAILED)
773 {
774 LogRel(("OSS: Failed to memory map %zu bytes of playback buffer: %s\n", cbSamples, strerror(errno)));
775 rc = RTErrConvertFromErrno(errno);
776 break;
777 }
778 else
779 {
780 int mask = 0;
781 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
782 {
783 LogRel(("OSS: Failed to retrieve initial trigger mask for playback buffer: %s\n", strerror(errno)));
784 rc = RTErrConvertFromErrno(errno);
785 /* Note: No break here, need to unmap file first! */
786 }
787 else
788 {
789 mask = PCM_ENABLE_OUTPUT;
790 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
791 {
792 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n", strerror(errno)));
793 rc = RTErrConvertFromErrno(errno);
794 /* Note: No break here, need to unmap file first! */
795 }
796 else
797 pStrm->fMemMapped = true;
798 }
799
800 if (RT_FAILURE(rc))
801 {
802 int rc2 = munmap(pStrm->pvBuf, cbSamples);
803 if (rc2)
804 LogRel(("OSS: Failed to memory unmap playback buffer: %s\n", strerror(errno)));
805 break;
806 }
807 }
808 }
809#endif /* !RT_OS_L4 */
810
811 /* Memory mapping failed above? Try allocating an own buffer. */
812#ifndef RT_OS_L4
813 if (!pStrm->fMemMapped)
814 {
815#endif
816 void *pvBuf = RTMemAlloc(cbSamples);
817 if (!pvBuf)
818 {
819 LogRel(("OSS: Failed allocating playback buffer with %RU32 samples (%zu bytes)\n", cSamples, cbSamples));
820 rc = VERR_NO_MEMORY;
821 break;
822 }
823
824 pStrm->hFile = hFile;
825 pStrm->pvBuf = pvBuf;
826 pStrm->cbBuf = cbSamples;
827#ifndef RT_OS_L4
828 }
829#endif
830 pCfgAcq->cSampleBufferSize = cSamples;
831 }
832
833 } while (0);
834
835 if (RT_FAILURE(rc))
836 ossStreamClose(&hFile);
837
838 LogFlowFuncLeaveRC(rc);
839 return rc;
840}
841
842
843/**
844 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
845 */
846static DECLCALLBACK(int) drvHostOSSAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
847{
848 RT_NOREF(pInterface, cbBuf, pvBuf);
849 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
850
851 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
852
853 int rc = VINF_SUCCESS;
854 uint32_t cbReadTotal = 0;
855 count_info cntinfo;
856
857 do
858 {
859 size_t cbBufSize = AudioMixBufSizeBytes(&pStream->MixBuf);
860
861 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
862 uint32_t cToRead;
863
864#ifndef RT_OS_L4
865 if (pStrm->fMemMapped)
866 {
867 /* Get current playback pointer. */
868 int rc2 = ioctl(pStrm->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
869 if (!rc2)
870 {
871 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
872 strerror(errno)));
873 rc = RTErrConvertFromErrno(errno);
874 break;
875 }
876
877 /* Nothing to play? */
878 if (cntinfo.ptr == pStrm->old_optr)
879 break;
880
881 int cbData;
882 if (cntinfo.ptr > pStrm->old_optr)
883 cbData = cntinfo.ptr - pStrm->old_optr;
884 else
885 cbData = cbBufSize + cntinfo.ptr - pStrm->old_optr;
886 Assert(cbData);
887
888 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pStream->MixBuf, cbData),
889 cLive);
890 }
891 else
892 {
893#endif
894 audio_buf_info abinfo;
895 int rc2 = ioctl(pStrm->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
896 if (rc2 < 0)
897 {
898 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n", strerror(errno)));
899 rc = RTErrConvertFromErrno(errno);
900 break;
901 }
902
903 if ((size_t)abinfo.bytes > cbBufSize)
904 {
905 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%zu\n", abinfo.bytes, cbBufSize));
906 abinfo.bytes = cbBufSize;
907 /* Keep going. */
908 }
909
910 if (abinfo.bytes < 0)
911 {
912 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%zu\n", abinfo.bytes, cbBufSize));
913 rc = VERR_INVALID_PARAMETER;
914 break;
915 }
916
917 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pStream->MixBuf, abinfo.bytes), cLive);
918 if (!cToRead)
919 break;
920#ifndef RT_OS_L4
921 }
922#endif
923 size_t cbToRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cToRead);
924 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
925
926 uint32_t cRead, cbRead;
927 while (cbToRead)
928 {
929 rc = AudioMixBufReadCirc(&pStream->MixBuf, pStrm->pvBuf, cbToRead, &cRead);
930 if (RT_FAILURE(rc))
931 break;
932
933 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
934 ssize_t cbWritten = write(pStrm->hFile, pStrm->pvBuf, cbRead);
935 if (cbWritten == -1)
936 {
937 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
938 rc = RTErrConvertFromErrno(errno);
939 break;
940 }
941
942 Assert(cbToRead >= cbRead);
943 cbToRead -= cbRead;
944 cbReadTotal += cbRead;
945 }
946
947#ifndef RT_OS_L4
948 /* Update read pointer. */
949 if (pStrm->fMemMapped)
950 pStrm->old_optr = cntinfo.ptr;
951#endif
952
953 } while(0);
954
955 if (RT_SUCCESS(rc))
956 {
957 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
958 if (cReadTotal)
959 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
960
961 if (pcbWritten)
962 *pcbWritten = cReadTotal;
963
964 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n", cReadTotal, cbReadTotal, rc));
965 }
966
967 LogFlowFuncLeaveRC(rc);
968 return rc;
969}
970
971
972/**
973 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
974 */
975static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
976{
977 RT_NOREF(pInterface);
978}
979
980
981/**
982 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
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
992
993/**
994 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
995 */
996static DECLCALLBACK(int) drvHostOSSAudioStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
997{
998 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
999 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1000 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1001 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1002
1003 int rc;
1004 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1005 rc = ossCreateStreamIn(pStream, pCfgReq, pCfgAcq);
1006 else
1007 rc = ossCreateStreamOut(pStream, pCfgReq, pCfgAcq);
1008
1009 return rc;
1010}
1011
1012
1013/**
1014 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1015 */
1016static DECLCALLBACK(int) drvHostOSSAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1017{
1018 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1019 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1020
1021 int rc;
1022 if (pStream->enmDir == PDMAUDIODIR_IN)
1023 rc = ossDestroyStreamIn(pStream);
1024 else
1025 rc = ossDestroyStreamOut(pStream);
1026
1027 return rc;
1028}
1029
1030
1031/**
1032 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1033 */
1034static DECLCALLBACK(int) drvHostOSSAudioStreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1035{
1036 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1037 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1038
1039 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1040
1041 int rc;
1042 if (pStream->enmDir == PDMAUDIODIR_IN)
1043 rc = ossControlStreamIn(/*pInterface, pStream, enmStreamCmd*/);
1044 else
1045 rc = ossControlStreamOut(pStream, enmStreamCmd);
1046
1047 return rc;
1048}
1049
1050
1051/**
1052 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1053 */
1054static DECLCALLBACK(int) drvHostOSSAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1055{
1056 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1057 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1058
1059 LogFlowFuncEnter();
1060
1061 /* Nothing to do here for OSS. */
1062 return VINF_SUCCESS;
1063}
1064
1065
1066/**
1067 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1068 */
1069static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostOSSAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1070{
1071 RT_NOREF(pInterface);
1072 RT_NOREF(pStream);
1073
1074 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_INITIALIZED
1075 | PDMAUDIOSTRMSTS_FLAG_ENABLED;
1076
1077 strmSts |= pStream->enmDir == PDMAUDIODIR_IN
1078 ? PDMAUDIOSTRMSTS_FLAG_DATA_READABLE
1079 : PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
1080
1081 return strmSts;
1082}
1083
1084/**
1085 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1086 */
1087static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1088{
1089 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1090 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1091
1092 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1093 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1094
1095 return NULL;
1096}
1097
1098/**
1099 * Constructs an OSS audio driver instance.
1100 *
1101 * @copydoc FNPDMDRVCONSTRUCT
1102 */
1103static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1104{
1105 RT_NOREF(pCfg, fFlags);
1106 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1107 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1108 LogRel(("Audio: Initializing OSS driver\n"));
1109
1110 /*
1111 * Init the static parts.
1112 */
1113 pThis->pDrvIns = pDrvIns;
1114 /* IBase */
1115 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
1116 /* IHostAudio */
1117 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
1118
1119 return VINF_SUCCESS;
1120}
1121
1122/**
1123 * Char driver registration record.
1124 */
1125const PDMDRVREG g_DrvHostOSSAudio =
1126{
1127 /* u32Version */
1128 PDM_DRVREG_VERSION,
1129 /* szName */
1130 "OSSAudio",
1131 /* szRCMod */
1132 "",
1133 /* szR0Mod */
1134 "",
1135 /* pszDescription */
1136 "OSS audio host driver",
1137 /* fFlags */
1138 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1139 /* fClass. */
1140 PDM_DRVREG_CLASS_AUDIO,
1141 /* cMaxInstances */
1142 ~0U,
1143 /* cbInstance */
1144 sizeof(DRVHOSTOSSAUDIO),
1145 /* pfnConstruct */
1146 drvHostOSSAudioConstruct,
1147 /* pfnDestruct */
1148 NULL,
1149 /* pfnRelocate */
1150 NULL,
1151 /* pfnIOCtl */
1152 NULL,
1153 /* pfnPowerOn */
1154 NULL,
1155 /* pfnReset */
1156 NULL,
1157 /* pfnSuspend */
1158 NULL,
1159 /* pfnResume */
1160 NULL,
1161 /* pfnAttach */
1162 NULL,
1163 /* pfnDetach */
1164 NULL,
1165 /* pfnPowerOff */
1166 NULL,
1167 /* pfnSoftReset */
1168 NULL,
1169 /* u32EndVersion */
1170 PDM_DRVREG_VERSION
1171};
1172
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