VirtualBox

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

Last change on this file since 86605 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.3 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * OSS (Open Sound System) host audio backend.
4 */
5
6/*
7 * Copyright (C) 2014-2020 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 "VBoxDD.h"
34
35
36/*********************************************************************************************************************************
37* Defines *
38*********************************************************************************************************************************/
39
40#if ((SOUND_VERSION > 360) && (defined(OSS_SYSINFO)))
41/* OSS > 3.6 has a new syscall available for querying a bit more detailed information
42 * about OSS' audio capabilities. This is handy for e.g. Solaris. */
43# define VBOX_WITH_AUDIO_OSS_SYSINFO 1
44#endif
45
46/** Makes DRVHOSTOSSAUDIO out of PDMIHOSTAUDIO. */
47#define PDMIHOSTAUDIO_2_DRVHOSTOSSAUDIO(pInterface) \
48 ( (PDRVHOSTOSSAUDIO)((uintptr_t)pInterface - RT_UOFFSETOF(DRVHOSTOSSAUDIO, IHostAudio)) )
49
50
51/*********************************************************************************************************************************
52* Structures *
53*********************************************************************************************************************************/
54
55/**
56 * OSS host audio driver instance data.
57 * @implements PDMIAUDIOCONNECTOR
58 */
59typedef struct DRVHOSTOSSAUDIO
60{
61 /** Pointer to the driver instance structure. */
62 PPDMDRVINS pDrvIns;
63 /** Pointer to host audio interface. */
64 PDMIHOSTAUDIO IHostAudio;
65 /** Error count for not flooding the release log.
66 * UINT32_MAX for unlimited logging. */
67 uint32_t cLogErrors;
68} DRVHOSTOSSAUDIO, *PDRVHOSTOSSAUDIO;
69
70typedef struct OSSAUDIOSTREAMCFG
71{
72 PDMAUDIOPCMPROPS Props;
73 uint16_t cFragments;
74 uint32_t cbFragmentSize;
75} OSSAUDIOSTREAMCFG, *POSSAUDIOSTREAMCFG;
76
77typedef struct OSSAUDIOSTREAM
78{
79 /** The stream's acquired configuration. */
80 PPDMAUDIOSTREAMCFG pCfg;
81 /** Buffer alignment. */
82 uint8_t uAlign;
83 union
84 {
85 struct
86 {
87
88 } In;
89 struct
90 {
91#ifndef RT_OS_L4
92 /** Whether we use a memory mapped file instead of our
93 * own allocated PCM buffer below. */
94 /** @todo The memory mapped code seems to be utterly broken.
95 * Needs investigation! */
96 bool fMMIO;
97#endif
98 } Out;
99 };
100 int hFile;
101 int cFragments;
102 int cbFragmentSize;
103 /** Own PCM buffer. */
104 void *pvBuf;
105 /** Size (in bytes) of own PCM buffer. */
106 size_t cbBuf;
107 int old_optr;
108} OSSAUDIOSTREAM, *POSSAUDIOSTREAM;
109
110typedef struct OSSAUDIOCFG
111{
112#ifndef RT_OS_L4
113 bool try_mmap;
114#endif
115 int nfrags;
116 int fragsize;
117 const char *devpath_out;
118 const char *devpath_in;
119 int debug;
120} OSSAUDIOCFG, *POSSAUDIOCFG;
121
122static OSSAUDIOCFG s_OSSConf =
123{
124#ifndef RT_OS_L4
125 false,
126#endif
127 4,
128 4096,
129 "/dev/dsp",
130 "/dev/dsp",
131 0
132};
133
134
135/* http://www.df.lth.se/~john_e/gems/gem002d.html */
136static uint32_t popcount(uint32_t u)
137{
138 u = ((u&0x55555555) + ((u>>1)&0x55555555));
139 u = ((u&0x33333333) + ((u>>2)&0x33333333));
140 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
141 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
142 u = ( u&0x0000ffff) + (u>>16);
143 return u;
144}
145
146
147static uint32_t lsbindex(uint32_t u)
148{
149 return popcount ((u&-u)-1);
150}
151
152
153static int ossOSSToAudioProps(int fmt, PPDMAUDIOPCMPROPS pProps)
154{
155 RT_BZERO(pProps, sizeof(PDMAUDIOPCMPROPS));
156
157 /** @todo r=bird: What's the assumption about the incoming pProps? Code is
158 * clearly ASSUMING something about how it's initialized, but even so,
159 * the fSwapEndian isn't correct in a portable way. */
160 switch (fmt)
161 {
162 case AFMT_S8:
163 pProps->cbSample = 1;
164 pProps->fSigned = true;
165 break;
166
167 case AFMT_U8:
168 pProps->cbSample = 1;
169 pProps->fSigned = false;
170 break;
171
172 case AFMT_S16_LE:
173 pProps->cbSample = 2;
174 pProps->fSigned = true;
175 break;
176
177 case AFMT_U16_LE:
178 pProps->cbSample = 2;
179 pProps->fSigned = false;
180 break;
181
182 case AFMT_S16_BE:
183 pProps->cbSample = 2;
184 pProps->fSigned = true;
185#ifdef RT_LITTLE_ENDIAN
186 pProps->fSwapEndian = true;
187#endif
188 break;
189
190 case AFMT_U16_BE:
191 pProps->cbSample = 2;
192 pProps->fSigned = false;
193#ifdef RT_LITTLE_ENDIAN
194 pProps->fSwapEndian = true;
195#endif
196 break;
197
198 default:
199 AssertMsgFailedReturn(("Format %d not supported\n", fmt), VERR_NOT_SUPPORTED);
200 }
201
202 return VINF_SUCCESS;
203}
204
205
206static int ossStreamClose(int *phFile)
207{
208 if (!phFile || !*phFile || *phFile == -1)
209 return VINF_SUCCESS;
210
211 int rc;
212 if (close(*phFile))
213 {
214 LogRel(("OSS: Closing stream failed: %s\n", strerror(errno)));
215 rc = VERR_GENERAL_FAILURE; /** @todo */
216 }
217 else
218 {
219 *phFile = -1;
220 rc = VINF_SUCCESS;
221 }
222
223 return rc;
224}
225
226
227static int ossStreamOpen(const char *pszDev, int fOpen, POSSAUDIOSTREAMCFG pOSSReq, POSSAUDIOSTREAMCFG pOSSAcq, int *phFile)
228{
229 int rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
230
231 int fdFile = -1;
232 do
233 {
234 fdFile = open(pszDev, fOpen);
235 if (fdFile == -1)
236 {
237 LogRel(("OSS: Failed to open %s: %s (%d)\n", pszDev, strerror(errno), errno));
238 break;
239 }
240
241 int iFormat;
242 switch (pOSSReq->Props.cbSample)
243 {
244 case 1:
245 iFormat = pOSSReq->Props.fSigned ? AFMT_S8 : AFMT_U8;
246 break;
247
248 case 2:
249 /** @todo r=bird: You're ASSUMING stuff about pOSSReq->Props.fSwapEndian and
250 * the host endian here. */
251 iFormat = pOSSReq->Props.fSigned ? AFMT_S16_LE : AFMT_U16_LE;
252 break;
253
254 default:
255 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
256 break;
257 }
258
259 if (RT_FAILURE(rc))
260 break;
261
262 if (ioctl(fdFile, SNDCTL_DSP_SAMPLESIZE, &iFormat))
263 {
264 LogRel(("OSS: Failed to set audio format to %ld: %s (%d)\n", iFormat, strerror(errno), errno));
265 break;
266 }
267
268 int cChannels = pOSSReq->Props.cChannels;
269 if (ioctl(fdFile, SNDCTL_DSP_CHANNELS, &cChannels))
270 {
271 LogRel(("OSS: Failed to set number of audio channels (%RU8): %s (%d)\n",
272 pOSSReq->Props.cChannels, strerror(errno), errno));
273 break;
274 }
275
276 int freq = pOSSReq->Props.uHz;
277 if (ioctl(fdFile, SNDCTL_DSP_SPEED, &freq))
278 {
279 LogRel(("OSS: Failed to set audio frequency (%dHZ): %s (%d)\n", pOSSReq->Props.uHz, strerror(errno), errno));
280 break;
281 }
282
283 /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
284#if !(defined(VBOX) && defined(RT_OS_SOLARIS))
285 if (ioctl(fdFile, SNDCTL_DSP_NONBLOCK))
286 {
287 LogRel(("OSS: Failed to set non-blocking mode: %s (%d)\n", strerror(errno), errno));
288 break;
289 }
290#endif
291
292 /* Check access mode (input or output). */
293 bool fIn = ((fOpen & O_ACCMODE) == O_RDONLY);
294
295 LogRel2(("OSS: Requested %RU16 %s fragments, %RU32 bytes each\n",
296 pOSSReq->cFragments, fIn ? "input" : "output", pOSSReq->cbFragmentSize));
297
298 int mmmmssss = (pOSSReq->cFragments << 16) | lsbindex(pOSSReq->cbFragmentSize);
299 if (ioctl(fdFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss))
300 {
301 LogRel(("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s (%d)\n",
302 pOSSReq->cFragments, pOSSReq->cbFragmentSize, strerror(errno), errno));
303 break;
304 }
305
306 audio_buf_info abinfo;
307 if (ioctl(fdFile, fIn ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo))
308 {
309 LogRel(("OSS: Failed to retrieve %s buffer length: %s (%d)\n", fIn ? "input" : "output", strerror(errno), errno));
310 break;
311 }
312
313 rc = ossOSSToAudioProps(iFormat, &pOSSAcq->Props);
314 if (RT_SUCCESS(rc))
315 {
316 pOSSAcq->Props.cChannels = cChannels;
317 pOSSAcq->Props.uHz = freq;
318 pOSSAcq->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pOSSAcq->Props.cbSample, pOSSAcq->Props.cChannels);
319
320 pOSSAcq->cFragments = abinfo.fragstotal;
321 pOSSAcq->cbFragmentSize = abinfo.fragsize;
322
323 LogRel2(("OSS: Got %RU16 %s fragments, %RU32 bytes each\n",
324 pOSSAcq->cFragments, fIn ? "input" : "output", pOSSAcq->cbFragmentSize));
325
326 *phFile = fdFile;
327 }
328 }
329 while (0);
330
331 if (RT_FAILURE(rc))
332 ossStreamClose(&fdFile);
333
334 LogFlowFuncLeaveRC(rc);
335 return rc;
336}
337
338
339static int ossControlStreamIn(/*PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd*/ void)
340{
341 /** @todo Nothing to do here right now!? */
342
343 return VINF_SUCCESS;
344}
345
346
347static int ossControlStreamOut(PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
348{
349 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
350
351 int rc = VINF_SUCCESS;
352
353 switch (enmStreamCmd)
354 {
355 case PDMAUDIOSTREAMCMD_ENABLE:
356 case PDMAUDIOSTREAMCMD_RESUME:
357 {
358 DrvAudioHlpClearBuf(&pStreamOSS->pCfg->Props, pStreamOSS->pvBuf, pStreamOSS->cbBuf,
359 PDMAUDIOPCMPROPS_B2F(&pStreamOSS->pCfg->Props, pStreamOSS->cbBuf));
360
361 int mask = PCM_ENABLE_OUTPUT;
362 if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
363 {
364 LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
365 rc = RTErrConvertFromErrno(errno);
366 }
367
368 break;
369 }
370
371 case PDMAUDIOSTREAMCMD_DISABLE:
372 case PDMAUDIOSTREAMCMD_PAUSE:
373 {
374 int mask = 0;
375 if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
376 {
377 LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
378 rc = RTErrConvertFromErrno(errno);
379 }
380
381 break;
382 }
383
384 default:
385 rc = VERR_NOT_SUPPORTED;
386 break;
387 }
388
389 return rc;
390}
391
392
393/**
394 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
395 */
396static DECLCALLBACK(int) drvHostOssAudioHA_Init(PPDMIHOSTAUDIO pInterface)
397{
398 RT_NOREF(pInterface);
399
400 return VINF_SUCCESS;
401}
402
403
404/**
405 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
406 */
407static DECLCALLBACK(int) drvHostOssAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
408 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
409{
410 RT_NOREF(pInterface);
411 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
412
413 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
414
415 int rc = VINF_SUCCESS;
416
417 size_t cbToRead = RT_MIN(pStreamOSS->cbBuf, uBufSize);
418
419 LogFlowFunc(("cbToRead=%zi\n", cbToRead));
420
421 uint32_t cbReadTotal = 0;
422 uint32_t cbTemp;
423 ssize_t cbRead;
424 size_t offWrite = 0;
425
426 while (cbToRead)
427 {
428 cbTemp = RT_MIN(cbToRead, pStreamOSS->cbBuf);
429 AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
430 cbRead = read(pStreamOSS->hFile, (uint8_t *)pStreamOSS->pvBuf, cbTemp);
431
432 LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n", cbRead, cbTemp, cbToRead));
433
434 if (cbRead < 0)
435 {
436 switch (errno)
437 {
438 case 0:
439 {
440 LogFunc(("Failed to read %z frames\n", cbRead));
441 rc = VERR_ACCESS_DENIED;
442 break;
443 }
444
445 case EINTR:
446 case EAGAIN:
447 rc = VERR_NO_DATA;
448 break;
449
450 default:
451 LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n", cbTemp, rc));
452 rc = VERR_GENERAL_FAILURE; /** @todo Fix this. */
453 break;
454 }
455
456 if (RT_FAILURE(rc))
457 break;
458 }
459 else if (cbRead)
460 {
461 memcpy((uint8_t *)pvBuf + offWrite, pStreamOSS->pvBuf, cbRead);
462
463 Assert((ssize_t)cbToRead >= cbRead);
464 cbToRead -= cbRead;
465 offWrite += cbRead;
466 cbReadTotal += cbRead;
467 }
468 else /* No more data, try next round. */
469 break;
470 }
471
472 if (rc == VERR_NO_DATA)
473 rc = VINF_SUCCESS;
474
475 if (RT_SUCCESS(rc))
476 {
477 if (puRead)
478 *puRead = cbReadTotal;
479 }
480
481 return rc;
482}
483
484
485static int ossDestroyStreamIn(PPDMAUDIOBACKENDSTREAM pStream)
486{
487 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
488
489 LogFlowFuncEnter();
490
491 if (pStreamOSS->pvBuf)
492 {
493 Assert(pStreamOSS->cbBuf);
494
495 RTMemFree(pStreamOSS->pvBuf);
496 pStreamOSS->pvBuf = NULL;
497 }
498
499 pStreamOSS->cbBuf = 0;
500
501 ossStreamClose(&pStreamOSS->hFile);
502
503 return VINF_SUCCESS;
504}
505
506
507static int ossDestroyStreamOut(PPDMAUDIOBACKENDSTREAM pStream)
508{
509 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
510
511#ifndef RT_OS_L4
512 if (pStreamOSS->Out.fMMIO)
513 {
514 if (pStreamOSS->pvBuf)
515 {
516 Assert(pStreamOSS->cbBuf);
517
518 int rc2 = munmap(pStreamOSS->pvBuf, pStreamOSS->cbBuf);
519 if (rc2 == 0)
520 {
521 pStreamOSS->pvBuf = NULL;
522 pStreamOSS->cbBuf = 0;
523
524 pStreamOSS->Out.fMMIO = false;
525 }
526 else
527 LogRel(("OSS: Failed to memory unmap playback buffer on close: %s\n", strerror(errno)));
528 }
529 }
530 else
531 {
532#endif
533 if (pStreamOSS->pvBuf)
534 {
535 Assert(pStreamOSS->cbBuf);
536
537 RTMemFree(pStreamOSS->pvBuf);
538 pStreamOSS->pvBuf = NULL;
539 }
540
541 pStreamOSS->cbBuf = 0;
542#ifndef RT_OS_L4
543 }
544#endif
545
546 ossStreamClose(&pStreamOSS->hFile);
547
548 return VINF_SUCCESS;
549}
550
551
552/**
553 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
554 */
555static DECLCALLBACK(int) drvHostOssAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
556{
557 RT_NOREF(pInterface);
558
559 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "OSS");
560
561 pBackendCfg->cbStreamIn = sizeof(OSSAUDIOSTREAM);
562 pBackendCfg->cbStreamOut = sizeof(OSSAUDIOSTREAM);
563
564 int hFile = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0);
565 if (hFile == -1)
566 {
567 /* Try opening the mixing device instead. */
568 hFile = open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
569 }
570
571 int ossVer = -1;
572
573#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
574 oss_sysinfo ossInfo;
575 RT_ZERO(ossInfo);
576#endif
577
578 if (hFile != -1)
579 {
580 int err = ioctl(hFile, OSS_GETVERSION, &ossVer);
581 if (err == 0)
582 {
583 LogRel2(("OSS: Using version: %d\n", ossVer));
584#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
585 err = ioctl(hFile, OSS_SYSINFO, &ossInfo);
586 if (err == 0)
587 {
588 LogRel2(("OSS: Number of DSPs: %d\n", ossInfo.numaudios));
589 LogRel2(("OSS: Number of mixers: %d\n", ossInfo.nummixers));
590
591 int cDev = ossInfo.nummixers;
592 if (!cDev)
593 cDev = ossInfo.numaudios;
594
595 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
596 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
597 }
598 else
599 {
600#endif
601 /* Since we cannot query anything, assume that we have at least
602 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */
603
604 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
605 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
606#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
607 }
608#endif
609 }
610 else
611 LogRel(("OSS: Unable to determine installed version: %s (%d)\n", strerror(err), err));
612 }
613 else
614 LogRel(("OSS: No devices found, audio is not available\n"));
615
616 if (hFile != -1)
617 close(hFile);
618
619 return VINF_SUCCESS;
620}
621
622
623static int ossCreateStreamIn(POSSAUDIOSTREAM pStreamOSS, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
624{
625 int rc;
626
627 int hFile = -1;
628
629 do
630 {
631 OSSAUDIOSTREAMCFG ossReq;
632 memcpy(&ossReq.Props, &pCfgReq->Props, sizeof(PDMAUDIOPCMPROPS));
633
634 ossReq.cFragments = s_OSSConf.nfrags;
635 ossReq.cbFragmentSize = s_OSSConf.fragsize;
636
637 OSSAUDIOSTREAMCFG ossAcq;
638 RT_ZERO(ossAcq);
639
640 rc = ossStreamOpen(s_OSSConf.devpath_in, O_RDONLY | O_NONBLOCK, &ossReq, &ossAcq, &hFile);
641 if (RT_SUCCESS(rc))
642 {
643 memcpy(&pCfgAcq->Props, &ossAcq.Props, sizeof(PDMAUDIOPCMPROPS));
644
645 if (ossAcq.cFragments * ossAcq.cbFragmentSize & pStreamOSS->uAlign)
646 {
647 LogRel(("OSS: Warning: Misaligned capturing buffer: Size = %zu, Alignment = %u\n",
648 ossAcq.cFragments * ossAcq.cbFragmentSize, pStreamOSS->uAlign + 1));
649 }
650
651 if (RT_SUCCESS(rc))
652 {
653 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, ossAcq.cFragments * ossAcq.cbFragmentSize);
654 void *pvBuf = RTMemAlloc(cbBuf);
655 if (!pvBuf)
656 {
657 LogRel(("OSS: Failed allocating capturing buffer with (%zu bytes)\n", cbBuf));
658 rc = VERR_NO_MEMORY;
659 }
660
661 pStreamOSS->hFile = hFile;
662 pStreamOSS->pvBuf = pvBuf;
663 pStreamOSS->cbBuf = cbBuf;
664
665 pCfgAcq->Backend.cFramesPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, ossAcq.cbFragmentSize);
666 pCfgAcq->Backend.cFramesBufferSize = pCfgAcq->Backend.cFramesPeriod * 2; /* Use "double buffering". */
667 /** @todo Pre-buffering required? */
668 }
669 }
670
671 } while (0);
672
673 if (RT_FAILURE(rc))
674 ossStreamClose(&hFile);
675
676 LogFlowFuncLeaveRC(rc);
677 return rc;
678}
679
680
681static int ossCreateStreamOut(POSSAUDIOSTREAM pStreamOSS, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
682{
683 int rc;
684 int hFile = -1;
685
686 do
687 {
688 OSSAUDIOSTREAMCFG reqStream;
689 RT_ZERO(reqStream);
690
691 OSSAUDIOSTREAMCFG obtStream;
692 RT_ZERO(obtStream);
693
694 memcpy(&reqStream.Props, &pCfgReq->Props, sizeof(PDMAUDIOPCMPROPS));
695
696 reqStream.cFragments = s_OSSConf.nfrags;
697 reqStream.cbFragmentSize = s_OSSConf.fragsize;
698
699 rc = ossStreamOpen(s_OSSConf.devpath_out, O_WRONLY, &reqStream, &obtStream, &hFile);
700 if (RT_SUCCESS(rc))
701 {
702 memcpy(&pCfgAcq->Props, &obtStream.Props, sizeof(PDMAUDIOPCMPROPS));
703
704 if (obtStream.cFragments * obtStream.cbFragmentSize & pStreamOSS->uAlign)
705 {
706 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
707 obtStream.cFragments * obtStream.cbFragmentSize, pStreamOSS->uAlign + 1));
708 }
709 }
710
711 if (RT_SUCCESS(rc))
712 {
713 pStreamOSS->Out.fMMIO = false;
714
715 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, obtStream.cFragments * obtStream.cbFragmentSize);
716 Assert(cbBuf);
717
718#ifndef RT_OS_L4
719 if (s_OSSConf.try_mmap)
720 {
721 pStreamOSS->pvBuf = mmap(0, cbBuf, PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
722 if (pStreamOSS->pvBuf == MAP_FAILED)
723 {
724 LogRel(("OSS: Failed to memory map %zu bytes of playback buffer: %s\n", cbBuf, strerror(errno)));
725 rc = RTErrConvertFromErrno(errno);
726 break;
727 }
728 else
729 {
730 int mask = 0;
731 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
732 {
733 LogRel(("OSS: Failed to retrieve initial trigger mask for playback buffer: %s\n", strerror(errno)));
734 rc = RTErrConvertFromErrno(errno);
735 /* Note: No break here, need to unmap file first! */
736 }
737 else
738 {
739 mask = PCM_ENABLE_OUTPUT;
740 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
741 {
742 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n", strerror(errno)));
743 rc = RTErrConvertFromErrno(errno);
744 /* Note: No break here, need to unmap file first! */
745 }
746 else
747 {
748 pStreamOSS->Out.fMMIO = true;
749 LogRel(("OSS: Using MMIO\n"));
750 }
751 }
752
753 if (RT_FAILURE(rc))
754 {
755 int rc2 = munmap(pStreamOSS->pvBuf, cbBuf);
756 if (rc2)
757 LogRel(("OSS: Failed to memory unmap playback buffer: %s\n", strerror(errno)));
758 break;
759 }
760 }
761 }
762#endif /* !RT_OS_L4 */
763
764 /* Memory mapping failed above? Try allocating an own buffer. */
765#ifndef RT_OS_L4
766 if (!pStreamOSS->Out.fMMIO)
767 {
768#endif
769 void *pvBuf = RTMemAlloc(cbBuf);
770 if (!pvBuf)
771 {
772 LogRel(("OSS: Failed allocating playback buffer with %zu bytes\n", cbBuf));
773 rc = VERR_NO_MEMORY;
774 break;
775 }
776
777 pStreamOSS->hFile = hFile;
778 pStreamOSS->pvBuf = pvBuf;
779 pStreamOSS->cbBuf = cbBuf;
780#ifndef RT_OS_L4
781 }
782#endif
783 pCfgAcq->Backend.cFramesPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, obtStream.cbFragmentSize);
784 pCfgAcq->Backend.cFramesBufferSize = pCfgAcq->Backend.cFramesPeriod * 2; /* Use "double buffering" */
785 }
786
787 } while (0);
788
789 if (RT_FAILURE(rc))
790 ossStreamClose(&hFile);
791
792 LogFlowFuncLeaveRC(rc);
793 return rc;
794}
795
796
797/**
798 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
799 */
800static DECLCALLBACK(int) drvHostOssAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
801 const void *pvBuf, uint32_t uBufSize, uint32_t *puWritten)
802{
803 RT_NOREF(pInterface);
804 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
805
806 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
807
808 int rc = VINF_SUCCESS;
809 uint32_t cbWrittenTotal = 0;
810
811#ifndef RT_OS_L4
812 count_info cntinfo;
813#endif
814
815 do
816 {
817 uint32_t cbAvail = uBufSize;
818 uint32_t cbToWrite;
819
820#ifndef RT_OS_L4
821 if (pStreamOSS->Out.fMMIO)
822 {
823 /* Get current playback pointer. */
824 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
825 if (!rc2)
826 {
827 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n", strerror(errno)));
828 rc = RTErrConvertFromErrno(errno);
829 break;
830 }
831
832 /* Nothing to play? */
833 if (cntinfo.ptr == pStreamOSS->old_optr)
834 break;
835
836 int cbData;
837 if (cntinfo.ptr > pStreamOSS->old_optr)
838 cbData = cntinfo.ptr - pStreamOSS->old_optr;
839 else
840 cbData = uBufSize + cntinfo.ptr - pStreamOSS->old_optr;
841 Assert(cbData >= 0);
842
843 cbToWrite = RT_MIN((unsigned)cbData, cbAvail);
844 }
845 else
846 {
847#endif
848 audio_buf_info abinfo;
849 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
850 if (rc2 < 0)
851 {
852 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n", strerror(errno)));
853 rc = RTErrConvertFromErrno(errno);
854 break;
855 }
856
857 if ((size_t)abinfo.bytes > uBufSize)
858 {
859 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", abinfo.bytes, uBufSize, uBufSize));
860 abinfo.bytes = uBufSize;
861 /* Keep going. */
862 }
863
864 if (abinfo.bytes < 0)
865 {
866 LogRel2(("OSS: Warning: Invalid available size (%d vs. %RU32)\n", abinfo.bytes, uBufSize));
867 rc = VERR_INVALID_PARAMETER;
868 break;
869 }
870
871 cbToWrite = RT_MIN(unsigned(abinfo.fragments * abinfo.fragsize), cbAvail);
872#ifndef RT_OS_L4
873 }
874#endif
875 cbToWrite = RT_MIN(cbToWrite, pStreamOSS->cbBuf);
876
877 while (cbToWrite)
878 {
879 uint32_t cbWritten = cbToWrite;
880
881 memcpy(pStreamOSS->pvBuf, pvBuf, cbWritten);
882
883 uint32_t cbChunk = cbWritten;
884 uint32_t cbChunkOff = 0;
885 while (cbChunk)
886 {
887 ssize_t cbChunkWritten = write(pStreamOSS->hFile, (uint8_t *)pStreamOSS->pvBuf + cbChunkOff,
888 RT_MIN(cbChunk, (unsigned)s_OSSConf.fragsize));
889 if (cbChunkWritten < 0)
890 {
891 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
892 rc = RTErrConvertFromErrno(errno);
893 break;
894 }
895
896 if (cbChunkWritten & pStreamOSS->uAlign)
897 {
898 LogRel(("OSS: Misaligned write (written %z, expected %RU32)\n", cbChunkWritten, cbChunk));
899 break;
900 }
901
902 cbChunkOff += (uint32_t)cbChunkWritten;
903 Assert(cbChunkOff <= cbWritten);
904 Assert(cbChunk >= (uint32_t)cbChunkWritten);
905 cbChunk -= (uint32_t)cbChunkWritten;
906 }
907
908 Assert(cbToWrite >= cbWritten);
909 cbToWrite -= cbWritten;
910 cbWrittenTotal += cbWritten;
911 }
912
913#ifndef RT_OS_L4
914 /* Update read pointer. */
915 if (pStreamOSS->Out.fMMIO)
916 pStreamOSS->old_optr = cntinfo.ptr;
917#endif
918
919 } while(0);
920
921 if (RT_SUCCESS(rc))
922 {
923 if (puWritten)
924 *puWritten = cbWrittenTotal;
925 }
926
927 return rc;
928}
929
930
931/**
932 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
933 */
934static DECLCALLBACK(void) drvHostOssAudioHA_Shutdown(PPDMIHOSTAUDIO pInterface)
935{
936 RT_NOREF(pInterface);
937}
938
939
940/**
941 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
942 */
943static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostOssAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
944{
945 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
946 RT_NOREF(enmDir);
947
948 return PDMAUDIOBACKENDSTS_RUNNING;
949}
950
951
952/**
953 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
954 */
955static DECLCALLBACK(int) drvHostOssAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
956 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
957{
958 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
959 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
960 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
961 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
962
963 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
964
965 int rc;
966 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
967 rc = ossCreateStreamIn (pStreamOSS, pCfgReq, pCfgAcq);
968 else
969 rc = ossCreateStreamOut(pStreamOSS, pCfgReq, pCfgAcq);
970
971 if (RT_SUCCESS(rc))
972 {
973 pStreamOSS->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
974 if (!pStreamOSS->pCfg)
975 rc = VERR_NO_MEMORY;
976 }
977
978 return rc;
979}
980
981
982/**
983 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
984 */
985static DECLCALLBACK(int) drvHostOssAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
986{
987 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
988 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
989
990 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
991
992 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
993 return VINF_SUCCESS;
994
995 int rc;
996 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
997 rc = ossDestroyStreamIn(pStream);
998 else
999 rc = ossDestroyStreamOut(pStream);
1000
1001 if (RT_SUCCESS(rc))
1002 {
1003 DrvAudioHlpStreamCfgFree(pStreamOSS->pCfg);
1004 pStreamOSS->pCfg = NULL;
1005 }
1006
1007 return rc;
1008}
1009
1010
1011/**
1012 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1013 */
1014static DECLCALLBACK(int) drvHostOssAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1015 PDMAUDIOSTREAMCMD enmStreamCmd)
1016{
1017 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1018 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1019
1020 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
1021
1022 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
1023 return VINF_SUCCESS;
1024
1025 int rc;
1026 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
1027 rc = ossControlStreamIn(/*pInterface, pStream, enmStreamCmd*/);
1028 else
1029 rc = ossControlStreamOut(pStreamOSS, enmStreamCmd);
1030
1031 return rc;
1032}
1033
1034
1035/**
1036 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1037 */
1038static DECLCALLBACK(int) drvHostOssAudioHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1039{
1040 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1041 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1042
1043 LogFlowFuncEnter();
1044
1045 /* Nothing to do here for OSS. */
1046 return VINF_SUCCESS;
1047}
1048
1049
1050/**
1051 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
1052 */
1053static DECLCALLBACK(uint32_t) drvHostOssAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1054{
1055 RT_NOREF(pInterface, pStream);
1056 return UINT32_MAX;
1057}
1058
1059
1060/**
1061 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
1062 */
1063static DECLCALLBACK(uint32_t) drvHostOssAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1064{
1065 RT_NOREF(pInterface, pStream);
1066 return UINT32_MAX;
1067}
1068
1069
1070/**
1071 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1072 */
1073static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostOssAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1074{
1075 RT_NOREF(pInterface, pStream);
1076 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
1077}
1078
1079/**
1080 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1081 */
1082static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1083{
1084 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1085 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1086
1087 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1088 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1089
1090 return NULL;
1091}
1092
1093/**
1094 * Constructs an OSS audio driver instance.
1095 *
1096 * @copydoc FNPDMDRVCONSTRUCT
1097 */
1098static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1099{
1100 RT_NOREF(pCfg, fFlags);
1101 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1102 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1103 LogRel(("Audio: Initializing OSS driver\n"));
1104
1105 /*
1106 * Init the static parts.
1107 */
1108 pThis->pDrvIns = pDrvIns;
1109 /* IBase */
1110 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
1111 /* IHostAudio */
1112 pThis->IHostAudio.pfnInit = drvHostOssAudioHA_Init;
1113 pThis->IHostAudio.pfnShutdown = drvHostOssAudioHA_Shutdown;
1114 pThis->IHostAudio.pfnGetConfig = drvHostOssAudioHA_GetConfig;
1115 pThis->IHostAudio.pfnGetStatus = drvHostOssAudioHA_GetStatus;
1116 pThis->IHostAudio.pfnStreamCreate = drvHostOssAudioHA_StreamCreate;
1117 pThis->IHostAudio.pfnStreamDestroy = drvHostOssAudioHA_StreamDestroy;
1118 pThis->IHostAudio.pfnStreamControl = drvHostOssAudioHA_StreamControl;
1119 pThis->IHostAudio.pfnStreamGetReadable = drvHostOssAudioHA_StreamGetReadable;
1120 pThis->IHostAudio.pfnStreamGetWritable = drvHostOssAudioHA_StreamGetWritable;
1121 pThis->IHostAudio.pfnStreamGetStatus = drvHostOssAudioHA_StreamGetStatus;
1122 pThis->IHostAudio.pfnStreamIterate = drvHostOssAudioHA_StreamIterate;
1123 pThis->IHostAudio.pfnStreamPlay = drvHostOssAudioHA_StreamPlay;
1124 pThis->IHostAudio.pfnStreamCapture = drvHostOssAudioHA_StreamCapture;
1125 pThis->IHostAudio.pfnSetCallback = NULL;
1126 pThis->IHostAudio.pfnGetDevices = NULL;
1127 pThis->IHostAudio.pfnStreamGetPending = NULL;
1128 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
1129 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
1130 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
1131 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
1132
1133 return VINF_SUCCESS;
1134}
1135
1136/**
1137 * Char driver registration record.
1138 */
1139const PDMDRVREG g_DrvHostOSSAudio =
1140{
1141 /* u32Version */
1142 PDM_DRVREG_VERSION,
1143 /* szName */
1144 "OSSAudio",
1145 /* szRCMod */
1146 "",
1147 /* szR0Mod */
1148 "",
1149 /* pszDescription */
1150 "OSS audio host driver",
1151 /* fFlags */
1152 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1153 /* fClass. */
1154 PDM_DRVREG_CLASS_AUDIO,
1155 /* cMaxInstances */
1156 ~0U,
1157 /* cbInstance */
1158 sizeof(DRVHOSTOSSAUDIO),
1159 /* pfnConstruct */
1160 drvHostOSSAudioConstruct,
1161 /* pfnDestruct */
1162 NULL,
1163 /* pfnRelocate */
1164 NULL,
1165 /* pfnIOCtl */
1166 NULL,
1167 /* pfnPowerOn */
1168 NULL,
1169 /* pfnReset */
1170 NULL,
1171 /* pfnSuspend */
1172 NULL,
1173 /* pfnResume */
1174 NULL,
1175 /* pfnAttach */
1176 NULL,
1177 /* pfnDetach */
1178 NULL,
1179 /* pfnPowerOff */
1180 NULL,
1181 /* pfnSoftReset */
1182 NULL,
1183 /* u32EndVersion */
1184 PDM_DRVREG_VERSION
1185};
1186
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