VirtualBox

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

Last change on this file since 63654 was 63534, checked in by vboxsync, 8 years ago

Audio: Renaming.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.2 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 63534 2016-08-16 10:14:46Z 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 audio_buf_info abinfo;
315 if (ioctl(hFile, (fOpen & O_RDONLY) ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo))
316 {
317 LogRel(("OSS: Failed to retrieve buffer length: %s (%d)\n", strerror(errno), errno));
318 rc = RTErrConvertFromErrno(errno);
319 break;
320 }
321
322 rc = ossOSSToAudioFmt(iFormat, &pObt->enmFormat, &pObt->enmENDIANNESS);
323 if (RT_SUCCESS(rc))
324 {
325 pObt->cChannels = cChannels;
326 pObt->uFreq = freq;
327 pObt->cFragments = abinfo.fragstotal;
328 pObt->cbFragmentSize = abinfo.fragsize;
329
330 *phFile = hFile;
331 }
332 }
333 while (0);
334
335 if (RT_FAILURE(rc))
336 ossStreamClose(&hFile);
337
338 LogFlowFuncLeaveRC(rc);
339 return rc;
340}
341
342
343static int ossControlStreamIn(/*PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd*/ void)
344{
345 /** @todo Nothing to do here right now!? */
346
347 return VINF_SUCCESS;
348}
349
350
351static int ossControlStreamOut(PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
352{
353 POSSAUDIOSTREAMOUT pStreamOut = (POSSAUDIOSTREAMOUT)pStream;
354
355#ifdef RT_OS_L4
356 return VINF_SUCCESS;
357#else
358 if (!pStreamOut->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 DrvAudioHlpClearBuf(&pStreamOut->Props,
370 pStreamOut->pvBuf, pStreamOut->cbBuf, AudioMixBufSize(&pStream->MixBuf));
371
372 mask = PCM_ENABLE_OUTPUT;
373 if (ioctl(pStreamOut->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(pStreamOut->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
405
406/**
407 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
408 */
409static DECLCALLBACK(int) drvHostOSSAudioInit(PPDMIHOSTAUDIO pInterface)
410{
411 RT_NOREF(pInterface);
412
413 LogFlowFuncEnter();
414
415 return VINF_SUCCESS;
416}
417
418
419/**
420 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
421 */
422static DECLCALLBACK(int) drvHostOSSAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
423{
424 RT_NOREF(pInterface, cbBuf, pvBuf);
425 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
426
427 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
428
429 int rc = VINF_SUCCESS;
430 size_t cbToRead = RT_MIN(pStrm->cbBuf,
431 AudioMixBufFreeBytes(&pStream->MixBuf));
432
433 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
434
435 uint32_t cWrittenTotal = 0;
436 uint32_t cbTemp;
437 ssize_t cbRead;
438 size_t offWrite = 0;
439
440 while (cbToRead)
441 {
442 cbTemp = RT_MIN(cbToRead, pStrm->cbBuf);
443 AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
444 cbRead = read(pStrm->hFile, (uint8_t *)pStrm->pvBuf + offWrite, cbTemp);
445
446 LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n", cbRead, cbTemp, cbToRead));
447
448 if (cbRead < 0)
449 {
450 switch (errno)
451 {
452 case 0:
453 {
454 LogFunc(("Failed to read %z frames\n", cbRead));
455 rc = VERR_ACCESS_DENIED;
456 break;
457 }
458
459 case EINTR:
460 case EAGAIN:
461 rc = VERR_NO_DATA;
462 break;
463
464 default:
465 LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n", cbTemp, rc));
466 rc = VERR_GENERAL_FAILURE; /** @todo Fix this. */
467 break;
468 }
469
470 if (RT_FAILURE(rc))
471 break;
472 }
473 else if (cbRead)
474 {
475 uint32_t cWritten;
476 rc = AudioMixBufWriteCirc(&pStream->MixBuf, pStrm->pvBuf, cbRead, &cWritten);
477 if (RT_FAILURE(rc))
478 break;
479
480 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten);
481
482 Assert(cbToRead >= cbWritten);
483 cbToRead -= cbWritten;
484 offWrite += cbWritten;
485 cWrittenTotal += cWritten;
486 }
487 else /* No more data, try next round. */
488 break;
489 }
490
491 if (rc == VERR_NO_DATA)
492 rc = VINF_SUCCESS;
493
494 if (RT_SUCCESS(rc))
495 {
496 uint32_t cProcessed = 0;
497 if (cWrittenTotal)
498 rc = AudioMixBufMixToParent(&pStream->MixBuf, cWrittenTotal, &cProcessed);
499
500 if (pcbRead)
501 *pcbRead = cWrittenTotal;
502
503 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
504 cWrittenTotal, cProcessed, rc));
505 }
506
507 LogFlowFuncLeaveRC(rc);
508 return rc;
509}
510
511
512static int ossDestroyStreamIn(PPDMAUDIOSTREAM pStream)
513{
514 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
515
516 LogFlowFuncEnter();
517
518 if (pStrm->pvBuf)
519 {
520 Assert(pStrm->cbBuf);
521
522 RTMemFree(pStrm->pvBuf);
523 pStrm->pvBuf = NULL;
524 }
525
526 pStrm->cbBuf = 0;
527
528 ossStreamClose(&pStrm->hFile);
529
530 return VINF_SUCCESS;
531}
532
533
534static int ossDestroyStreamOut(PPDMAUDIOSTREAM pStream)
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
580
581/**
582 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
583 */
584static DECLCALLBACK(int) drvHostOSSAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
585{
586 RT_NOREF(pInterface);
587
588 pBackendCfg->cbStreamIn = sizeof(OSSAUDIOSTREAMIN);
589 pBackendCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT);
590
591 pBackendCfg->cSources = 0;
592 pBackendCfg->cSinks = 0;
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->cSources = cDev;
626 pBackendCfg->cSinks = cDev;
627
628 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
629 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
630 }
631 else
632 {
633#endif
634 /* Since we cannot query anything, assume that we have at least
635 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */
636 pBackendCfg->cSources = 1;
637 pBackendCfg->cSinks = 1;
638
639 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
640 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
641#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
642 }
643#endif
644 }
645 else
646 LogRel(("OSS: Unable to determine installed version: %s (%d)\n", strerror(err), err));
647 }
648 else
649 LogRel(("OSS: No devices found, audio is not available\n"));
650
651 return VINF_SUCCESS;
652}
653
654
655static int ossCreateStreamIn(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
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 = pCfgReq->enmFormat;
668 reqStream.uFreq = pCfgReq->uHz;
669 reqStream.cChannels = pCfgReq->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 pCfgAcq->enmFormat = obtStream.enmFormat;
677 pCfgAcq->uHz = obtStream.uFreq;
678 pCfgAcq->cChannels = pCfgReq->cChannels; /** @todo r=andy Why not using obtStream? */
679 pCfgAcq->enmEndianness = obtStream.enmENDIANNESS;
680
681 rc = DrvAudioHlpStreamCfgToProps(pCfgAcq, &pStrm->Props);
682 if (RT_SUCCESS(rc))
683 {
684 if (obtStream.cFragments * obtStream.cbFragmentSize & pStrm->Props.uAlign)
685 {
686 LogRel(("OSS: Warning: Misaligned capturing buffer: Size = %zu, Alignment = %u\n",
687 obtStream.cFragments * obtStream.cbFragmentSize, pStrm->Props.uAlign + 1));
688 }
689
690 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize) >> pStrm->Props.cShift;
691 if (!cSamples)
692 rc = VERR_INVALID_PARAMETER;
693 }
694
695 if (RT_SUCCESS(rc))
696 {
697 size_t cbSample = (1 << pStrm->Props.cShift);
698
699 size_t cbBuf = cSamples * cbSample;
700 void *pvBuf = RTMemAlloc(cbBuf);
701 if (!pvBuf)
702 {
703 LogRel(("OSS: Failed allocating capturing buffer with %RU32 samples (%zu bytes per sample)\n",
704 cSamples, cbSample));
705 rc = VERR_NO_MEMORY;
706 break;
707 }
708
709 pStrm->hFile = hFile;
710 pStrm->pvBuf = pvBuf;
711 pStrm->cbBuf = cbBuf;
712
713 pCfgAcq->cSampleBufferSize = cSamples;
714 }
715 }
716
717 } while (0);
718
719 if (RT_FAILURE(rc))
720 ossStreamClose(&hFile);
721
722 LogFlowFuncLeaveRC(rc);
723 return rc;
724}
725
726
727static int ossCreateStreamOut(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
728{
729 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
730
731 int rc;
732 int hFile = -1;
733
734 do
735 {
736 uint32_t cSamples;
737
738 OSSAUDIOSTREAMCFG reqStream, obtStream;
739 reqStream.enmFormat = pCfgReq->enmFormat;
740 reqStream.uFreq = pCfgReq->uHz;
741 reqStream.cChannels = pCfgReq->cChannels;
742 reqStream.cFragments = s_OSSConf.nfrags;
743 reqStream.cbFragmentSize = s_OSSConf.fragsize;
744
745 rc = ossStreamOpen(s_OSSConf.devpath_out, O_WRONLY | O_NONBLOCK, &reqStream, &obtStream, &hFile);
746 if (RT_SUCCESS(rc))
747 {
748 pCfgAcq->enmFormat = obtStream.enmFormat;
749 pCfgAcq->uHz = obtStream.uFreq;
750 pCfgAcq->cChannels = pCfgReq->cChannels; /** @todo r=andy Why not using obtStream? */
751 pCfgAcq->enmEndianness = obtStream.enmENDIANNESS;
752
753 rc = DrvAudioHlpStreamCfgToProps(pCfgAcq, &pStrm->Props);
754 if (RT_SUCCESS(rc))
755 {
756 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize) >> pStrm->Props.cShift;
757
758 if (obtStream.cFragments * obtStream.cbFragmentSize & pStrm->Props.uAlign)
759 {
760 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
761 obtStream.cFragments * obtStream.cbFragmentSize, pStrm->Props.uAlign + 1));
762 }
763 }
764 }
765
766 if (RT_SUCCESS(rc))
767 {
768 pStrm->fMemMapped = false;
769
770 size_t cbSamples = cSamples << pStrm->Props.cShift;
771 Assert(cbSamples);
772
773#ifndef RT_OS_L4
774 if (s_OSSConf.try_mmap)
775 {
776 pStrm->pvBuf = mmap(0, cbSamples, PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
777 if (pStrm->pvBuf == MAP_FAILED)
778 {
779 LogRel(("OSS: Failed to memory map %zu bytes of playback buffer: %s\n", cbSamples, strerror(errno)));
780 rc = RTErrConvertFromErrno(errno);
781 break;
782 }
783 else
784 {
785 int mask = 0;
786 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
787 {
788 LogRel(("OSS: Failed to retrieve initial trigger mask for playback buffer: %s\n", strerror(errno)));
789 rc = RTErrConvertFromErrno(errno);
790 /* Note: No break here, need to unmap file first! */
791 }
792 else
793 {
794 mask = PCM_ENABLE_OUTPUT;
795 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
796 {
797 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n", strerror(errno)));
798 rc = RTErrConvertFromErrno(errno);
799 /* Note: No break here, need to unmap file first! */
800 }
801 else
802 pStrm->fMemMapped = true;
803 }
804
805 if (RT_FAILURE(rc))
806 {
807 int rc2 = munmap(pStrm->pvBuf, cbSamples);
808 if (rc2)
809 LogRel(("OSS: Failed to memory unmap playback buffer: %s\n", strerror(errno)));
810 break;
811 }
812 }
813 }
814#endif /* !RT_OS_L4 */
815
816 /* Memory mapping failed above? Try allocating an own buffer. */
817#ifndef RT_OS_L4
818 if (!pStrm->fMemMapped)
819 {
820#endif
821 void *pvBuf = RTMemAlloc(cbSamples);
822 if (!pvBuf)
823 {
824 LogRel(("OSS: Failed allocating playback buffer with %RU32 samples (%zu bytes)\n", cSamples, cbSamples));
825 rc = VERR_NO_MEMORY;
826 break;
827 }
828
829 pStrm->hFile = hFile;
830 pStrm->pvBuf = pvBuf;
831 pStrm->cbBuf = cbSamples;
832#ifndef RT_OS_L4
833 }
834#endif
835 pCfgAcq->cSampleBufferSize = cSamples;
836 }
837
838 } while (0);
839
840 if (RT_FAILURE(rc))
841 ossStreamClose(&hFile);
842
843 LogFlowFuncLeaveRC(rc);
844 return rc;
845}
846
847
848/**
849 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
850 */
851static DECLCALLBACK(int) drvHostOSSAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
852{
853 RT_NOREF(pInterface, cbBuf, pvBuf);
854 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
855
856 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
857
858 int rc = VINF_SUCCESS;
859 uint32_t cbReadTotal = 0;
860 count_info cntinfo;
861
862 do
863 {
864 size_t cbBufSize = AudioMixBufSizeBytes(&pStream->MixBuf);
865
866 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
867 uint32_t cToRead;
868
869#ifndef RT_OS_L4
870 if (pStrm->fMemMapped)
871 {
872 /* Get current playback pointer. */
873 int rc2 = ioctl(pStrm->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
874 if (!rc2)
875 {
876 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
877 strerror(errno)));
878 rc = RTErrConvertFromErrno(errno);
879 break;
880 }
881
882 /* Nothing to play? */
883 if (cntinfo.ptr == pStrm->old_optr)
884 break;
885
886 int cbData;
887 if (cntinfo.ptr > pStrm->old_optr)
888 cbData = cntinfo.ptr - pStrm->old_optr;
889 else
890 cbData = cbBufSize + cntinfo.ptr - pStrm->old_optr;
891 Assert(cbData);
892
893 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pStream->MixBuf, cbData),
894 cLive);
895 }
896 else
897 {
898#endif
899 audio_buf_info abinfo;
900 int rc2 = ioctl(pStrm->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
901 if (rc2 < 0)
902 {
903 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n", strerror(errno)));
904 rc = RTErrConvertFromErrno(errno);
905 break;
906 }
907
908 if ((size_t)abinfo.bytes > cbBufSize)
909 {
910 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%zu\n", abinfo.bytes, cbBufSize));
911 abinfo.bytes = cbBufSize;
912 /* Keep going. */
913 }
914
915 if (abinfo.bytes < 0)
916 {
917 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%zu\n", abinfo.bytes, cbBufSize));
918 rc = VERR_INVALID_PARAMETER;
919 break;
920 }
921
922 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pStream->MixBuf, abinfo.bytes), cLive);
923 if (!cToRead)
924 break;
925#ifndef RT_OS_L4
926 }
927#endif
928 size_t cbToRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cToRead);
929 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
930
931 uint32_t cRead, cbRead;
932 while (cbToRead)
933 {
934 rc = AudioMixBufReadCirc(&pStream->MixBuf, pStrm->pvBuf, cbToRead, &cRead);
935 if (RT_FAILURE(rc))
936 break;
937
938 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
939 ssize_t cbWritten = write(pStrm->hFile, pStrm->pvBuf, cbRead);
940 if (cbWritten == -1)
941 {
942 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
943 rc = RTErrConvertFromErrno(errno);
944 break;
945 }
946
947 Assert(cbToRead >= cbRead);
948 cbToRead -= cbRead;
949 cbReadTotal += cbRead;
950 }
951
952#ifndef RT_OS_L4
953 /* Update read pointer. */
954 if (pStrm->fMemMapped)
955 pStrm->old_optr = cntinfo.ptr;
956#endif
957
958 } while(0);
959
960 if (RT_SUCCESS(rc))
961 {
962 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
963 if (cReadTotal)
964 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
965
966 if (pcbWritten)
967 *pcbWritten = cReadTotal;
968
969 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n", cReadTotal, cbReadTotal, rc));
970 }
971
972 LogFlowFuncLeaveRC(rc);
973 return rc;
974}
975
976
977/**
978 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
979 */
980static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
981{
982 RT_NOREF(pInterface);
983}
984
985
986/**
987 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
988 */
989static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostOSSAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
990{
991 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
992 RT_NOREF(enmDir);
993
994 return PDMAUDIOBACKENDSTS_RUNNING;
995}
996
997
998/**
999 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
1000 */
1001static DECLCALLBACK(int) drvHostOSSAudioStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1002{
1003 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1004 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1005 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1006 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1007
1008 int rc;
1009 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1010 rc = ossCreateStreamIn(pStream, pCfgReq, pCfgAcq);
1011 else
1012 rc = ossCreateStreamOut(pStream, pCfgReq, pCfgAcq);
1013
1014 return rc;
1015}
1016
1017
1018/**
1019 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1020 */
1021static DECLCALLBACK(int) drvHostOSSAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1022{
1023 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1024 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1025
1026 int rc;
1027 if (pStream->enmDir == PDMAUDIODIR_IN)
1028 rc = ossDestroyStreamIn(pStream);
1029 else
1030 rc = ossDestroyStreamOut(pStream);
1031
1032 return rc;
1033}
1034
1035
1036/**
1037 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1038 */
1039static DECLCALLBACK(int) drvHostOSSAudioStreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1040{
1041 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1042 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1043
1044 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1045
1046 int rc;
1047 if (pStream->enmDir == PDMAUDIODIR_IN)
1048 rc = ossControlStreamIn(/*pInterface, pStream, enmStreamCmd*/);
1049 else
1050 rc = ossControlStreamOut(pStream, enmStreamCmd);
1051
1052 return rc;
1053}
1054
1055
1056/**
1057 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1058 */
1059static DECLCALLBACK(int) drvHostOSSAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1060{
1061 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1062 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1063
1064 LogFlowFuncEnter();
1065
1066 /* Nothing to do here for OSS. */
1067 return VINF_SUCCESS;
1068}
1069
1070
1071/**
1072 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1073 */
1074static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostOSSAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1075{
1076 RT_NOREF(pInterface);
1077 RT_NOREF(pStream);
1078
1079 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_INITIALIZED
1080 | PDMAUDIOSTRMSTS_FLAG_ENABLED;
1081
1082 strmSts |= pStream->enmDir == PDMAUDIODIR_IN
1083 ? PDMAUDIOSTRMSTS_FLAG_DATA_READABLE
1084 : PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
1085
1086 return strmSts;
1087}
1088
1089/**
1090 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1091 */
1092static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1093{
1094 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1095 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1096
1097 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1098 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1099
1100 return NULL;
1101}
1102
1103/**
1104 * Constructs an OSS audio driver instance.
1105 *
1106 * @copydoc FNPDMDRVCONSTRUCT
1107 */
1108static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1109{
1110 RT_NOREF(pCfg, fFlags);
1111 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1112 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1113 LogRel(("Audio: Initializing OSS driver\n"));
1114
1115 /*
1116 * Init the static parts.
1117 */
1118 pThis->pDrvIns = pDrvIns;
1119 /* IBase */
1120 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
1121 /* IHostAudio */
1122 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
1123
1124 return VINF_SUCCESS;
1125}
1126
1127/**
1128 * Char driver registration record.
1129 */
1130const PDMDRVREG g_DrvHostOSSAudio =
1131{
1132 /* u32Version */
1133 PDM_DRVREG_VERSION,
1134 /* szName */
1135 "OSSAudio",
1136 /* szRCMod */
1137 "",
1138 /* szR0Mod */
1139 "",
1140 /* pszDescription */
1141 "OSS audio host driver",
1142 /* fFlags */
1143 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1144 /* fClass. */
1145 PDM_DRVREG_CLASS_AUDIO,
1146 /* cMaxInstances */
1147 ~0U,
1148 /* cbInstance */
1149 sizeof(DRVHOSTOSSAUDIO),
1150 /* pfnConstruct */
1151 drvHostOSSAudioConstruct,
1152 /* pfnDestruct */
1153 NULL,
1154 /* pfnRelocate */
1155 NULL,
1156 /* pfnIOCtl */
1157 NULL,
1158 /* pfnPowerOn */
1159 NULL,
1160 /* pfnReset */
1161 NULL,
1162 /* pfnSuspend */
1163 NULL,
1164 /* pfnResume */
1165 NULL,
1166 /* pfnAttach */
1167 NULL,
1168 /* pfnDetach */
1169 NULL,
1170 /* pfnPowerOff */
1171 NULL,
1172 /* pfnSoftReset */
1173 NULL,
1174 /* u32EndVersion */
1175 PDM_DRVREG_VERSION
1176};
1177
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