VirtualBox

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

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

Audio: Renamed audio samples to audio frame because that's what they really are. No actual code changes.

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