VirtualBox

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

Last change on this file since 75606 was 73838, checked in by vboxsync, 6 years ago

Audio: Added the backend's (friendly) name into the backend configuration.

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