VirtualBox

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

Last change on this file since 73696 was 73696, checked in by vboxsync, 7 years ago

Audio/DrvHostOSSAudio.cpp: Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.5 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 73696 2018-08-15 15:36:19Z 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 pBackendCfg->cbStreamIn = sizeof(OSSAUDIOSTREAM);
556 pBackendCfg->cbStreamOut = sizeof(OSSAUDIOSTREAM);
557
558 int hFile = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0);
559 if (hFile == -1)
560 {
561 /* Try opening the mixing device instead. */
562 hFile = open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
563 }
564
565 int ossVer = -1;
566
567#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
568 oss_sysinfo ossInfo;
569 RT_ZERO(ossInfo);
570#endif
571
572 if (hFile != -1)
573 {
574 int err = ioctl(hFile, OSS_GETVERSION, &ossVer);
575 if (err == 0)
576 {
577 LogRel2(("OSS: Using version: %d\n", ossVer));
578#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
579 err = ioctl(hFile, OSS_SYSINFO, &ossInfo);
580 if (err == 0)
581 {
582 LogRel2(("OSS: Number of DSPs: %d\n", ossInfo.numaudios));
583 LogRel2(("OSS: Number of mixers: %d\n", ossInfo.nummixers));
584
585 int cDev = ossInfo.nummixers;
586 if (!cDev)
587 cDev = ossInfo.numaudios;
588
589 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
590 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
591 }
592 else
593 {
594#endif
595 /* Since we cannot query anything, assume that we have at least
596 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */
597
598 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
599 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
600#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
601 }
602#endif
603 }
604 else
605 LogRel(("OSS: Unable to determine installed version: %s (%d)\n", strerror(err), err));
606 }
607 else
608 LogRel(("OSS: No devices found, audio is not available\n"));
609
610 if (hFile != -1)
611 close(hFile);
612
613 return VINF_SUCCESS;
614}
615
616
617static int ossCreateStreamIn(POSSAUDIOSTREAM pStreamOSS, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
618{
619 int rc;
620
621 int hFile = -1;
622
623 do
624 {
625 OSSAUDIOSTREAMCFG ossReq;
626 memcpy(&ossReq.Props, &pCfgReq->Props, sizeof(PDMAUDIOPCMPROPS));
627
628 ossReq.cFragments = s_OSSConf.nfrags;
629 ossReq.cbFragmentSize = s_OSSConf.fragsize;
630
631 OSSAUDIOSTREAMCFG ossAcq;
632 RT_ZERO(ossAcq);
633
634 rc = ossStreamOpen(s_OSSConf.devpath_in, O_RDONLY | O_NONBLOCK, &ossReq, &ossAcq, &hFile);
635 if (RT_SUCCESS(rc))
636 {
637 memcpy(&pCfgAcq->Props, &ossAcq.Props, sizeof(PDMAUDIOPCMPROPS));
638
639 if (ossAcq.cFragments * ossAcq.cbFragmentSize & pStreamOSS->uAlign)
640 {
641 LogRel(("OSS: Warning: Misaligned capturing buffer: Size = %zu, Alignment = %u\n",
642 ossAcq.cFragments * ossAcq.cbFragmentSize, pStreamOSS->uAlign + 1));
643 }
644
645 if (RT_SUCCESS(rc))
646 {
647 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, ossAcq.cFragments * ossAcq.cbFragmentSize);
648 void *pvBuf = RTMemAlloc(cbBuf);
649 if (!pvBuf)
650 {
651 LogRel(("OSS: Failed allocating capturing buffer with (%zu bytes)\n", cbBuf));
652 rc = VERR_NO_MEMORY;
653 }
654
655 pStreamOSS->hFile = hFile;
656 pStreamOSS->pvBuf = pvBuf;
657 pStreamOSS->cbBuf = cbBuf;
658
659 pCfgAcq->Backend.cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, ossAcq.cbFragmentSize);
660 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */
661 /** @todo Pre-buffering required? */
662 }
663 }
664
665 } while (0);
666
667 if (RT_FAILURE(rc))
668 ossStreamClose(&hFile);
669
670 LogFlowFuncLeaveRC(rc);
671 return rc;
672}
673
674
675static int ossCreateStreamOut(POSSAUDIOSTREAM pStreamOSS, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
676{
677 int rc;
678 int hFile = -1;
679
680 do
681 {
682 OSSAUDIOSTREAMCFG reqStream;
683 RT_ZERO(reqStream);
684
685 OSSAUDIOSTREAMCFG obtStream;
686 RT_ZERO(obtStream);
687
688 memcpy(&reqStream.Props, &pCfgReq->Props, sizeof(PDMAUDIOPCMPROPS));
689
690 reqStream.cFragments = s_OSSConf.nfrags;
691 reqStream.cbFragmentSize = s_OSSConf.fragsize;
692
693 rc = ossStreamOpen(s_OSSConf.devpath_out, O_WRONLY, &reqStream, &obtStream, &hFile);
694 if (RT_SUCCESS(rc))
695 {
696 memcpy(&pCfgAcq->Props, &obtStream.Props, sizeof(PDMAUDIOPCMPROPS));
697
698 if (obtStream.cFragments * obtStream.cbFragmentSize & pStreamOSS->uAlign)
699 {
700 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
701 obtStream.cFragments * obtStream.cbFragmentSize, pStreamOSS->uAlign + 1));
702 }
703 }
704
705 if (RT_SUCCESS(rc))
706 {
707 pStreamOSS->Out.fMMIO = false;
708
709 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, obtStream.cFragments * obtStream.cbFragmentSize);
710 Assert(cbBuf);
711
712#ifndef RT_OS_L4
713 if (s_OSSConf.try_mmap)
714 {
715 pStreamOSS->pvBuf = mmap(0, cbBuf, PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
716 if (pStreamOSS->pvBuf == MAP_FAILED)
717 {
718 LogRel(("OSS: Failed to memory map %zu bytes of playback buffer: %s\n", cbBuf, strerror(errno)));
719 rc = RTErrConvertFromErrno(errno);
720 break;
721 }
722 else
723 {
724 int mask = 0;
725 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
726 {
727 LogRel(("OSS: Failed to retrieve initial trigger mask for playback buffer: %s\n", strerror(errno)));
728 rc = RTErrConvertFromErrno(errno);
729 /* Note: No break here, need to unmap file first! */
730 }
731 else
732 {
733 mask = PCM_ENABLE_OUTPUT;
734 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
735 {
736 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n", strerror(errno)));
737 rc = RTErrConvertFromErrno(errno);
738 /* Note: No break here, need to unmap file first! */
739 }
740 else
741 {
742 pStreamOSS->Out.fMMIO = true;
743 LogRel(("OSS: Using MMIO\n"));
744 }
745 }
746
747 if (RT_FAILURE(rc))
748 {
749 int rc2 = munmap(pStreamOSS->pvBuf, cbBuf);
750 if (rc2)
751 LogRel(("OSS: Failed to memory unmap playback buffer: %s\n", strerror(errno)));
752 break;
753 }
754 }
755 }
756#endif /* !RT_OS_L4 */
757
758 /* Memory mapping failed above? Try allocating an own buffer. */
759#ifndef RT_OS_L4
760 if (!pStreamOSS->Out.fMMIO)
761 {
762#endif
763 void *pvBuf = RTMemAlloc(cbBuf);
764 if (!pvBuf)
765 {
766 LogRel(("OSS: Failed allocating playback buffer with %zu bytes\n", cbBuf));
767 rc = VERR_NO_MEMORY;
768 break;
769 }
770
771 pStreamOSS->hFile = hFile;
772 pStreamOSS->pvBuf = pvBuf;
773 pStreamOSS->cbBuf = cbBuf;
774#ifndef RT_OS_L4
775 }
776#endif
777 pCfgAcq->Backend.cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, obtStream.cbFragmentSize);
778 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering" */
779 }
780
781 } while (0);
782
783 if (RT_FAILURE(rc))
784 ossStreamClose(&hFile);
785
786 LogFlowFuncLeaveRC(rc);
787 return rc;
788}
789
790
791/**
792 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
793 */
794static DECLCALLBACK(int) drvHostOSSAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
795 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf,
796 uint32_t *pcxWritten)
797{
798 RT_NOREF(pInterface);
799 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
800
801 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
802
803 int rc = VINF_SUCCESS;
804 uint32_t cbWrittenTotal = 0;
805
806#ifndef RT_OS_L4
807 count_info cntinfo;
808#endif
809
810 do
811 {
812 uint32_t cbAvail = cxBuf;
813 uint32_t cbToWrite;
814
815#ifndef RT_OS_L4
816 if (pStreamOSS->Out.fMMIO)
817 {
818 /* Get current playback pointer. */
819 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
820 if (!rc2)
821 {
822 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n", strerror(errno)));
823 rc = RTErrConvertFromErrno(errno);
824 break;
825 }
826
827 /* Nothing to play? */
828 if (cntinfo.ptr == pStreamOSS->old_optr)
829 break;
830
831 int cbData;
832 if (cntinfo.ptr > pStreamOSS->old_optr)
833 cbData = cntinfo.ptr - pStreamOSS->old_optr;
834 else
835 cbData = cxBuf + cntinfo.ptr - pStreamOSS->old_optr;
836 Assert(cbData >= 0);
837
838 cbToWrite = RT_MIN((unsigned)cbData, cbAvail);
839 }
840 else
841 {
842#endif
843 audio_buf_info abinfo;
844 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
845 if (rc2 < 0)
846 {
847 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n", strerror(errno)));
848 rc = RTErrConvertFromErrno(errno);
849 break;
850 }
851
852 if ((size_t)abinfo.bytes > cxBuf)
853 {
854 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", abinfo.bytes, cxBuf, cxBuf));
855 abinfo.bytes = cxBuf;
856 /* Keep going. */
857 }
858
859 if (abinfo.bytes < 0)
860 {
861 LogRel2(("OSS: Warning: Invalid available size (%d vs. %RU32)\n", abinfo.bytes, cxBuf));
862 rc = VERR_INVALID_PARAMETER;
863 break;
864 }
865
866 cbToWrite = RT_MIN(unsigned(abinfo.fragments * abinfo.fragsize), cbAvail);
867#ifndef RT_OS_L4
868 }
869#endif
870 cbToWrite = RT_MIN(cbToWrite, pStreamOSS->cbBuf);
871
872 while (cbToWrite)
873 {
874 uint32_t cbWritten = cbToWrite;
875
876 memcpy(pStreamOSS->pvBuf, pvBuf, cbWritten);
877
878 uint32_t cbChunk = cbWritten;
879 uint32_t cbChunkOff = 0;
880 while (cbChunk)
881 {
882 ssize_t cbChunkWritten = write(pStreamOSS->hFile, (uint8_t *)pStreamOSS->pvBuf + cbChunkOff,
883 RT_MIN(cbChunk, (unsigned)s_OSSConf.fragsize));
884 if (cbChunkWritten < 0)
885 {
886 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
887 rc = RTErrConvertFromErrno(errno);
888 break;
889 }
890
891 if (cbChunkWritten & pStreamOSS->uAlign)
892 {
893 LogRel(("OSS: Misaligned write (written %z, expected %RU32)\n", cbChunkWritten, cbChunk));
894 break;
895 }
896
897 cbChunkOff += (uint32_t)cbChunkWritten;
898 Assert(cbChunkOff <= cbWritten);
899 Assert(cbChunk >= (uint32_t)cbChunkWritten);
900 cbChunk -= (uint32_t)cbChunkWritten;
901 }
902
903 Assert(cbToWrite >= cbWritten);
904 cbToWrite -= cbWritten;
905 cbWrittenTotal += cbWritten;
906 }
907
908#ifndef RT_OS_L4
909 /* Update read pointer. */
910 if (pStreamOSS->Out.fMMIO)
911 pStreamOSS->old_optr = cntinfo.ptr;
912#endif
913
914 } while(0);
915
916 if (RT_SUCCESS(rc))
917 {
918 if (pcxWritten)
919 *pcxWritten = cbWrittenTotal;
920 }
921
922 return rc;
923}
924
925
926/**
927 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
928 */
929static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
930{
931 RT_NOREF(pInterface);
932}
933
934
935/**
936 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
937 */
938static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostOSSAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
939{
940 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
941 RT_NOREF(enmDir);
942
943 return PDMAUDIOBACKENDSTS_RUNNING;
944}
945
946
947/**
948 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
949 */
950static DECLCALLBACK(int) drvHostOSSAudioStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
951 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
952{
953 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
954 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
955 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
956 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
957
958 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
959
960 int rc;
961 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
962 rc = ossCreateStreamIn (pStreamOSS, pCfgReq, pCfgAcq);
963 else
964 rc = ossCreateStreamOut(pStreamOSS, pCfgReq, pCfgAcq);
965
966 if (RT_SUCCESS(rc))
967 {
968 pStreamOSS->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
969 if (!pStreamOSS->pCfg)
970 rc = VERR_NO_MEMORY;
971 }
972
973 return rc;
974}
975
976
977/**
978 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
979 */
980static DECLCALLBACK(int) drvHostOSSAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
981{
982 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
983 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
984
985 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
986
987 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
988 return VINF_SUCCESS;
989
990 int rc;
991 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
992 rc = ossDestroyStreamIn(pStream);
993 else
994 rc = ossDestroyStreamOut(pStream);
995
996 if (RT_SUCCESS(rc))
997 {
998 DrvAudioHlpStreamCfgFree(pStreamOSS->pCfg);
999 pStreamOSS->pCfg = NULL;
1000 }
1001
1002 return rc;
1003}
1004
1005
1006/**
1007 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1008 */
1009static DECLCALLBACK(int) drvHostOSSAudioStreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1010 PDMAUDIOSTREAMCMD enmStreamCmd)
1011{
1012 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1013 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1014
1015 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
1016
1017 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
1018 return VINF_SUCCESS;
1019
1020 int rc;
1021 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
1022 rc = ossControlStreamIn(/*pInterface, pStream, enmStreamCmd*/);
1023 else
1024 rc = ossControlStreamOut(pStreamOSS, enmStreamCmd);
1025
1026 return rc;
1027}
1028
1029
1030/**
1031 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1032 */
1033static DECLCALLBACK(int) drvHostOSSAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1034{
1035 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1036 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1037
1038 LogFlowFuncEnter();
1039
1040 /* Nothing to do here for OSS. */
1041 return VINF_SUCCESS;
1042}
1043
1044
1045/**
1046 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
1047 */
1048static DECLCALLBACK(uint32_t) drvHostOSSAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1049{
1050 RT_NOREF(pInterface, pStream);
1051
1052 return UINT32_MAX;
1053}
1054
1055
1056/**
1057 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
1058 */
1059static DECLCALLBACK(uint32_t) drvHostOSSAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1060{
1061 RT_NOREF(pInterface, pStream);
1062
1063 return UINT32_MAX;
1064}
1065
1066
1067/**
1068 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1069 */
1070static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostOSSAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1071{
1072 RT_NOREF(pInterface, pStream);
1073
1074 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_INITIALIZED
1075 | PDMAUDIOSTREAMSTS_FLAG_ENABLED;
1076 return strmSts;
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 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
1113
1114 return VINF_SUCCESS;
1115}
1116
1117/**
1118 * Char driver registration record.
1119 */
1120const PDMDRVREG g_DrvHostOSSAudio =
1121{
1122 /* u32Version */
1123 PDM_DRVREG_VERSION,
1124 /* szName */
1125 "OSSAudio",
1126 /* szRCMod */
1127 "",
1128 /* szR0Mod */
1129 "",
1130 /* pszDescription */
1131 "OSS audio host driver",
1132 /* fFlags */
1133 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1134 /* fClass. */
1135 PDM_DRVREG_CLASS_AUDIO,
1136 /* cMaxInstances */
1137 ~0U,
1138 /* cbInstance */
1139 sizeof(DRVHOSTOSSAUDIO),
1140 /* pfnConstruct */
1141 drvHostOSSAudioConstruct,
1142 /* pfnDestruct */
1143 NULL,
1144 /* pfnRelocate */
1145 NULL,
1146 /* pfnIOCtl */
1147 NULL,
1148 /* pfnPowerOn */
1149 NULL,
1150 /* pfnReset */
1151 NULL,
1152 /* pfnSuspend */
1153 NULL,
1154 /* pfnResume */
1155 NULL,
1156 /* pfnAttach */
1157 NULL,
1158 /* pfnDetach */
1159 NULL,
1160 /* pfnPowerOff */
1161 NULL,
1162 /* pfnSoftReset */
1163 NULL,
1164 /* u32EndVersion */
1165 PDM_DRVREG_VERSION
1166};
1167
Note: See TracBrowser for help on using the repository browser.

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