VirtualBox

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

Last change on this file since 63009 was 62585, checked in by vboxsync, 8 years ago

Audio: Cleanup: Renamed VBOX_WITH_[OSS|ALSA|PULSE] -> VBOX_WITH_AUDIO_[OSS|ALSA|PULSE].

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.6 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 62585 2016-07-27 11:51:17Z 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 PDRVHOSTOSSAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTOSSAUDIO(pInterface);
658 POSSAUDIOSTREAMIN pStrm = (POSSAUDIOSTREAMIN)pStream;
659
660 int rc;
661 int hFile = -1;
662
663 do
664 {
665 uint32_t cSamples;
666
667 OSSAUDIOSTREAMCFG reqStream, obtStream;
668 reqStream.enmFormat = pCfg->enmFormat;
669 reqStream.uFreq = pCfg->uHz;
670 reqStream.cChannels = pCfg->cChannels;
671 reqStream.cFragments = s_OSSConf.nfrags;
672 reqStream.cbFragmentSize = s_OSSConf.fragsize;
673
674 rc = ossStreamOpen(s_OSSConf.devpath_in, O_RDONLY | O_NONBLOCK, &reqStream, &obtStream, &hFile);
675 if (RT_SUCCESS(rc))
676 {
677 if (obtStream.cFragments * obtStream.cbFragmentSize & pStream->Props.uAlign)
678 LogRel(("OSS: Warning: Misaligned capturing buffer: Size = %zu, Alignment = %u\n",
679 obtStream.cFragments * obtStream.cbFragmentSize,
680 pStream->Props.uAlign + 1));
681
682 PDMAUDIOSTREAMCFG streamCfg;
683 streamCfg.enmFormat = obtStream.enmFormat;
684 streamCfg.uHz = obtStream.uFreq;
685 streamCfg.cChannels = pCfg->cChannels;
686 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
687
688 rc = DrvAudioHlpStreamCfgToProps(&streamCfg, &pStream->Props);
689 if (RT_SUCCESS(rc))
690 {
691 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
692 >> pStream->Props.cShift;
693 if (!cSamples)
694 rc = VERR_INVALID_PARAMETER;
695 }
696 }
697
698 if (RT_SUCCESS(rc))
699 {
700 size_t cbSample = (1 << pStream->Props.cShift);
701
702 size_t cbBuf = cSamples * cbSample;
703 void *pvBuf = RTMemAlloc(cbBuf);
704 if (!pvBuf)
705 {
706 LogRel(("OSS: Failed allocating capturing buffer with %RU32 samples (%zu bytes per sample)\n",
707 cSamples, cbSample));
708 rc = VERR_NO_MEMORY;
709 break;
710 }
711
712 pStrm->hFile = hFile;
713 pStrm->pvBuf = pvBuf;
714 pStrm->cbBuf = cbBuf;
715
716 if (pcSamples)
717 *pcSamples = cSamples;
718 }
719
720 } while (0);
721
722 if (RT_FAILURE(rc))
723 ossStreamClose(&hFile);
724
725 LogFlowFuncLeaveRC(rc);
726 return rc;
727}
728
729static int ossCreateStreamOut(PPDMIHOSTAUDIO pInterface,
730 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg,
731 uint32_t *pcSamples)
732{
733 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
734 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
735 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
736
737 PDRVHOSTOSSAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTOSSAUDIO(pInterface);
738 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
739
740 int rc;
741 int hFile = -1;
742
743 do
744 {
745 uint32_t cSamples;
746
747 OSSAUDIOSTREAMCFG reqStream, obtStream;
748 reqStream.enmFormat = pCfg->enmFormat;
749 reqStream.uFreq = pCfg->uHz;
750 reqStream.cChannels = pCfg->cChannels;
751 reqStream.cFragments = s_OSSConf.nfrags;
752 reqStream.cbFragmentSize = s_OSSConf.fragsize;
753
754 rc = ossStreamOpen(s_OSSConf.devpath_out, O_WRONLY | O_NONBLOCK, &reqStream, &obtStream, &hFile);
755 if (RT_SUCCESS(rc))
756 {
757 if (obtStream.cFragments * obtStream.cbFragmentSize & pStream->Props.uAlign)
758 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
759 obtStream.cFragments * obtStream.cbFragmentSize,
760 pStream->Props.uAlign + 1));
761
762 PDMAUDIOSTREAMCFG streamCfg;
763 streamCfg.enmFormat = obtStream.enmFormat;
764 streamCfg.uHz = obtStream.uFreq;
765 streamCfg.cChannels = pCfg->cChannels;
766 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
767
768 rc = DrvAudioHlpStreamCfgToProps(&streamCfg, &pStream->Props);
769 if (RT_SUCCESS(rc))
770 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
771 >> pStream->Props.cShift;
772 }
773
774 if (RT_SUCCESS(rc))
775 {
776 pStrm->fMemMapped = false;
777
778 size_t cbSamples = cSamples << pStream->Props.cShift;
779 Assert(cbSamples);
780
781#ifndef RT_OS_L4
782 if (s_OSSConf.try_mmap)
783 {
784 pStrm->pvBuf = mmap(0, cbSamples, PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
785 if (pStrm->pvBuf == MAP_FAILED)
786 {
787 LogRel(("OSS: Failed to memory map %zu bytes of playback buffer: %s\n", cbSamples, strerror(errno)));
788 rc = RTErrConvertFromErrno(errno);
789 break;
790 }
791 else
792 {
793 int mask = 0;
794 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
795 {
796 LogRel(("OSS: Failed to retrieve initial trigger mask for playback buffer: %s\n", strerror(errno)));
797 rc = RTErrConvertFromErrno(errno);
798 /* Note: No break here, need to unmap file first! */
799 }
800 else
801 {
802 mask = PCM_ENABLE_OUTPUT;
803 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
804 {
805 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n", strerror(errno)));
806 rc = RTErrConvertFromErrno(errno);
807 /* Note: No break here, need to unmap file first! */
808 }
809 else
810 pStrm->fMemMapped = true;
811 }
812
813 if (RT_FAILURE(rc))
814 {
815 int rc2 = munmap(pStrm->pvBuf, cbSamples);
816 if (rc2)
817 LogRel(("OSS: Failed to memory unmap playback buffer: %s\n", strerror(errno)));
818 break;
819 }
820 }
821 }
822#endif /* !RT_OS_L4 */
823
824 /* Memory mapping failed above? Try allocating an own buffer. */
825#ifndef RT_OS_L4
826 if (!pStrm->fMemMapped)
827 {
828#endif
829 void *pvBuf = RTMemAlloc(cbSamples);
830 if (!pvBuf)
831 {
832 LogRel(("OSS: Failed allocating playback buffer with %RU32 samples (%zu bytes)\n", cSamples, cbSamples));
833 rc = VERR_NO_MEMORY;
834 break;
835 }
836
837 pStrm->hFile = hFile;
838 pStrm->pvBuf = pvBuf;
839 pStrm->cbBuf = cbSamples;
840#ifndef RT_OS_L4
841 }
842#endif
843 if (pcSamples)
844 *pcSamples = cSamples;
845 }
846
847 } while (0);
848
849 if (RT_FAILURE(rc))
850 ossStreamClose(&hFile);
851
852 LogFlowFuncLeaveRC(rc);
853 return rc;
854}
855
856static DECLCALLBACK(bool) drvHostOSSAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
857{
858 NOREF(pInterface);
859 NOREF(enmDir);
860 return true; /* Always all enabled. */
861}
862
863static DECLCALLBACK(int) drvHostOSSAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
864 uint32_t *pcSamplesPlayed)
865{
866 NOREF(pInterface);
867 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
868
869 POSSAUDIOSTREAMOUT pStrm = (POSSAUDIOSTREAMOUT)pStream;
870
871 int rc = VINF_SUCCESS;
872 uint32_t cbReadTotal = 0;
873 count_info cntinfo;
874
875 do
876 {
877 size_t cbBuf = AudioMixBufSizeBytes(&pStream->MixBuf);
878
879 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
880 uint32_t cToRead;
881
882#ifndef RT_OS_L4
883 if (pStrm->fMemMapped)
884 {
885 /* Get current playback pointer. */
886 int rc2 = ioctl(pStrm->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
887 if (!rc2)
888 {
889 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
890 strerror(errno)));
891 rc = RTErrConvertFromErrno(errno);
892 break;
893 }
894
895 /* Nothing to play? */
896 if (cntinfo.ptr == pStrm->old_optr)
897 break;
898
899 int cbData;
900 if (cntinfo.ptr > pStrm->old_optr)
901 cbData = cntinfo.ptr - pStrm->old_optr;
902 else
903 cbData = cbBuf + cntinfo.ptr - pStrm->old_optr;
904 Assert(cbData);
905
906 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pStream->MixBuf, cbData),
907 cLive);
908 }
909 else
910 {
911#endif
912 audio_buf_info abinfo;
913 int rc2 = ioctl(pStrm->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
914 if (rc2 < 0)
915 {
916 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n", strerror(errno)));
917 rc = RTErrConvertFromErrno(errno);
918 break;
919 }
920
921 if ((size_t)abinfo.bytes > cbBuf)
922 {
923 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%zu\n", abinfo.bytes, cbBuf));
924 abinfo.bytes = cbBuf;
925 /* Keep going. */
926 }
927
928 if (abinfo.bytes < 0)
929 {
930 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%zu\n", abinfo.bytes, cbBuf));
931 rc = VERR_INVALID_PARAMETER;
932 break;
933 }
934
935 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pStream->MixBuf, abinfo.bytes), cLive);
936 if (!cToRead)
937 break;
938#ifndef RT_OS_L4
939 }
940#endif
941 size_t cbToRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cToRead);
942 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
943
944 uint32_t cRead, cbRead;
945 while (cbToRead)
946 {
947 rc = AudioMixBufReadCirc(&pStream->MixBuf, pStrm->pvBuf, cbToRead, &cRead);
948 if (RT_FAILURE(rc))
949 break;
950
951 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
952 ssize_t cbWritten = write(pStrm->hFile, pStrm->pvBuf, cbRead);
953 if (cbWritten == -1)
954 {
955 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
956 rc = RTErrConvertFromErrno(errno);
957 break;
958 }
959
960 Assert(cbToRead >= cbRead);
961 cbToRead -= cbRead;
962 cbReadTotal += cbRead;
963 }
964
965#ifndef RT_OS_L4
966 /* Update read pointer. */
967 if (pStrm->fMemMapped)
968 pStrm->old_optr = cntinfo.ptr;
969#endif
970
971 } while(0);
972
973 if (RT_SUCCESS(rc))
974 {
975 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
976 if (cReadTotal)
977 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
978
979 if (pcSamplesPlayed)
980 *pcSamplesPlayed = cReadTotal;
981
982 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n", cReadTotal, cbReadTotal, rc));
983 }
984
985 LogFlowFuncLeaveRC(rc);
986 return rc;
987}
988
989static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
990{
991 NOREF(pInterface);
992}
993
994static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostOSSAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
995{
996 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
997
998 return PDMAUDIOBACKENDSTS_RUNNING;
999}
1000
1001static DECLCALLBACK(int) drvHostOSSAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
1002 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
1003{
1004 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1005 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1006 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1007
1008 int rc;
1009 if (pCfg->enmDir == PDMAUDIODIR_IN)
1010 rc = ossCreateStreamIn(pInterface, pStream, pCfg, pcSamples);
1011 else
1012 rc = ossCreateStreamOut(pInterface, pStream, pCfg, pcSamples);
1013
1014 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
1015 return rc;
1016}
1017
1018static DECLCALLBACK(int) drvHostOSSAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1019{
1020 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1021 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1022
1023 int rc;
1024 if (pStream->enmDir == PDMAUDIODIR_IN)
1025 rc = ossDestroyStreamIn(pInterface, pStream);
1026 else
1027 rc = ossDestroyStreamOut(pInterface, pStream);
1028
1029 return rc;
1030}
1031
1032static DECLCALLBACK(int) drvHostOSSAudioStreamControl(PPDMIHOSTAUDIO pInterface,
1033 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1034{
1035 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1036 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1037
1038 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1039
1040 int rc;
1041 if (pStream->enmDir == PDMAUDIODIR_IN)
1042 rc = ossControlStreamIn(pInterface, pStream, enmStreamCmd);
1043 else
1044 rc = ossControlStreamOut(pInterface, pStream, enmStreamCmd);
1045
1046 return rc;
1047}
1048
1049static DECLCALLBACK(int) drvHostOSSAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1050{
1051 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1052 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1053
1054 LogFlowFuncEnter();
1055
1056 /* Nothing to do here for OSS. */
1057 return VINF_SUCCESS;
1058}
1059
1060static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostOSSAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1061{
1062 NOREF(pInterface);
1063 NOREF(pStream);
1064
1065 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_INITIALIZED
1066 | PDMAUDIOSTRMSTS_FLAG_ENABLED;
1067
1068 strmSts |= pStream->enmDir == PDMAUDIODIR_IN
1069 ? PDMAUDIOSTRMSTS_FLAG_DATA_READABLE
1070 : PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
1071
1072 return strmSts;
1073}
1074/**
1075 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1076 */
1077static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1078{
1079 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1080 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1081 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1082 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1083
1084 return NULL;
1085}
1086
1087/**
1088 * Constructs an OSS audio driver instance.
1089 *
1090 * @copydoc FNPDMDRVCONSTRUCT
1091 */
1092static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1093{
1094 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1095 LogRel(("Audio: Initializing OSS driver\n"));
1096
1097 /*
1098 * Init the static parts.
1099 */
1100 pThis->pDrvIns = pDrvIns;
1101 /* IBase */
1102 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
1103 /* IHostAudio */
1104 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
1105
1106 return VINF_SUCCESS;
1107}
1108
1109/**
1110 * Char driver registration record.
1111 */
1112const PDMDRVREG g_DrvHostOSSAudio =
1113{
1114 /* u32Version */
1115 PDM_DRVREG_VERSION,
1116 /* szName */
1117 "OSSAudio",
1118 /* szRCMod */
1119 "",
1120 /* szR0Mod */
1121 "",
1122 /* pszDescription */
1123 "OSS audio host driver",
1124 /* fFlags */
1125 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1126 /* fClass. */
1127 PDM_DRVREG_CLASS_AUDIO,
1128 /* cMaxInstances */
1129 ~0U,
1130 /* cbInstance */
1131 sizeof(DRVHOSTOSSAUDIO),
1132 /* pfnConstruct */
1133 drvHostOSSAudioConstruct,
1134 /* pfnDestruct */
1135 NULL,
1136 /* pfnRelocate */
1137 NULL,
1138 /* pfnIOCtl */
1139 NULL,
1140 /* pfnPowerOn */
1141 NULL,
1142 /* pfnReset */
1143 NULL,
1144 /* pfnSuspend */
1145 NULL,
1146 /* pfnResume */
1147 NULL,
1148 /* pfnAttach */
1149 NULL,
1150 /* pfnDetach */
1151 NULL,
1152 /* pfnPowerOff */
1153 NULL,
1154 /* pfnSoftReset */
1155 NULL,
1156 /* u32EndVersion */
1157 PDM_DRVREG_VERSION
1158};
1159
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