VirtualBox

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

Last change on this file since 88137 was 88028, checked in by vboxsync, 4 years ago

Audio: Moving some of the DrvAudio.h stuff into PDM - VBox/vmm/pdmaudioinline.h. bugref:9890

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