VirtualBox

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

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

Audio/DrvHostOSSAudio.cpp: Resolved some @todos, formatting, extended logging.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette