VirtualBox

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

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

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

  • 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 73097 2018-07-12 21:06:33Z 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->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 if (hFile != -1)
619 close(hFile);
620
621 return VINF_SUCCESS;
622}
623
624
625static int ossCreateStreamIn(POSSAUDIOSTREAM pStreamOSS, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
626{
627 int rc;
628
629 int hFile = -1;
630
631 do
632 {
633 OSSAUDIOSTREAMCFG ossReq;
634 memcpy(&ossReq.Props, &pCfgReq->Props, sizeof(PDMAUDIOPCMPROPS));
635
636 ossReq.cFragments = s_OSSConf.nfrags;
637 ossReq.cbFragmentSize = s_OSSConf.fragsize;
638
639 OSSAUDIOSTREAMCFG 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 uint32_t cSamples = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, ossAcq.cFragments * ossAcq.cbFragmentSize);
653 if (!cSamples)
654 rc = VERR_INVALID_PARAMETER;
655
656 if (RT_SUCCESS(rc))
657 {
658 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, cSamples);
659 void *pvBuf = RTMemAlloc(cbBuf);
660 if (!pvBuf)
661 {
662 LogRel(("OSS: Failed allocating capturing buffer with (%zu bytes)\n", cbBuf));
663 rc = VERR_NO_MEMORY;
664 }
665
666 pStreamOSS->hFile = hFile;
667 pStreamOSS->pvBuf = pvBuf;
668 pStreamOSS->cbBuf = cbBuf;
669
670 pCfgAcq->cFrameBufferHint = cSamples;
671 }
672 }
673
674 } while (0);
675
676 if (RT_FAILURE(rc))
677 ossStreamClose(&hFile);
678
679 LogFlowFuncLeaveRC(rc);
680 return rc;
681}
682
683
684static int ossCreateStreamOut(POSSAUDIOSTREAM pStreamOSS, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
685{
686 int rc;
687 int hFile = -1;
688
689 do
690 {
691 uint32_t cSamples;
692
693 OSSAUDIOSTREAMCFG reqStream, 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 cSamples = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, obtStream.cFragments * obtStream.cbFragmentSize);
706
707 if (obtStream.cFragments * obtStream.cbFragmentSize & pStreamOSS->uAlign)
708 {
709 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
710 obtStream.cFragments * obtStream.cbFragmentSize, pStreamOSS->uAlign + 1));
711 }
712 }
713
714 if (RT_SUCCESS(rc))
715 {
716 pStreamOSS->Out.fMMIO = false;
717
718 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, cSamples);
719 Assert(cbBuf);
720
721#ifndef RT_OS_L4
722 if (s_OSSConf.try_mmap)
723 {
724 pStreamOSS->pvBuf = mmap(0, cbBuf, PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
725 if (pStreamOSS->pvBuf == MAP_FAILED)
726 {
727 LogRel(("OSS: Failed to memory map %zu bytes of playback buffer: %s\n", cbBuf, strerror(errno)));
728 rc = RTErrConvertFromErrno(errno);
729 break;
730 }
731 else
732 {
733 int mask = 0;
734 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
735 {
736 LogRel(("OSS: Failed to retrieve initial trigger mask for playback buffer: %s\n", strerror(errno)));
737 rc = RTErrConvertFromErrno(errno);
738 /* Note: No break here, need to unmap file first! */
739 }
740 else
741 {
742 mask = PCM_ENABLE_OUTPUT;
743 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
744 {
745 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n", strerror(errno)));
746 rc = RTErrConvertFromErrno(errno);
747 /* Note: No break here, need to unmap file first! */
748 }
749 else
750 {
751 pStreamOSS->Out.fMMIO = true;
752 LogRel(("OSS: Using MMIO\n"));
753 }
754 }
755
756 if (RT_FAILURE(rc))
757 {
758 int rc2 = munmap(pStreamOSS->pvBuf, cbBuf);
759 if (rc2)
760 LogRel(("OSS: Failed to memory unmap playback buffer: %s\n", strerror(errno)));
761 break;
762 }
763 }
764 }
765#endif /* !RT_OS_L4 */
766
767 /* Memory mapping failed above? Try allocating an own buffer. */
768#ifndef RT_OS_L4
769 if (!pStreamOSS->Out.fMMIO)
770 {
771#endif
772 void *pvBuf = RTMemAlloc(cbBuf);
773 if (!pvBuf)
774 {
775 LogRel(("OSS: Failed allocating playback buffer with %RU32 samples (%zu bytes)\n", cSamples, cbBuf));
776 rc = VERR_NO_MEMORY;
777 break;
778 }
779
780 pStreamOSS->hFile = hFile;
781 pStreamOSS->pvBuf = pvBuf;
782 pStreamOSS->cbBuf = cbBuf;
783#ifndef RT_OS_L4
784 }
785#endif
786 pCfgAcq->cFrameBufferHint = cSamples;
787 }
788
789 } while (0);
790
791 if (RT_FAILURE(rc))
792 ossStreamClose(&hFile);
793
794 LogFlowFuncLeaveRC(rc);
795 return rc;
796}
797
798
799/**
800 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
801 */
802static DECLCALLBACK(int) drvHostOSSAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
803 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf,
804 uint32_t *pcxWritten)
805{
806 RT_NOREF(pInterface);
807 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
808
809 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
810
811 int rc = VINF_SUCCESS;
812 uint32_t cbWrittenTotal = 0;
813
814#ifndef RT_OS_L4
815 count_info cntinfo;
816#endif
817
818 do
819 {
820 uint32_t cbAvail = cxBuf;
821 uint32_t cbToWrite;
822
823#ifndef RT_OS_L4
824 if (pStreamOSS->Out.fMMIO)
825 {
826 /* Get current playback pointer. */
827 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
828 if (!rc2)
829 {
830 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n", strerror(errno)));
831 rc = RTErrConvertFromErrno(errno);
832 break;
833 }
834
835 /* Nothing to play? */
836 if (cntinfo.ptr == pStreamOSS->old_optr)
837 break;
838
839 int cbData;
840 if (cntinfo.ptr > pStreamOSS->old_optr)
841 cbData = cntinfo.ptr - pStreamOSS->old_optr;
842 else
843 cbData = cxBuf + cntinfo.ptr - pStreamOSS->old_optr;
844 Assert(cbData >= 0);
845
846 cbToWrite = RT_MIN((unsigned)cbData, cbAvail);
847 }
848 else
849 {
850#endif
851 audio_buf_info abinfo;
852 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
853 if (rc2 < 0)
854 {
855 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n", strerror(errno)));
856 rc = RTErrConvertFromErrno(errno);
857 break;
858 }
859
860 if ((size_t)abinfo.bytes > cxBuf)
861 {
862 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", abinfo.bytes, cxBuf, cxBuf));
863 abinfo.bytes = cxBuf;
864 /* Keep going. */
865 }
866
867 if (abinfo.bytes < 0)
868 {
869 LogRel2(("OSS: Warning: Invalid available size (%d vs. %RU32)\n", abinfo.bytes, cxBuf));
870 rc = VERR_INVALID_PARAMETER;
871 break;
872 }
873
874 cbToWrite = RT_MIN(unsigned(abinfo.fragments * abinfo.fragsize), cbAvail);
875#ifndef RT_OS_L4
876 }
877#endif
878 cbToWrite = RT_MIN(cbToWrite, pStreamOSS->cbBuf);
879
880 while (cbToWrite)
881 {
882 uint32_t cbWritten = cbToWrite;
883
884 memcpy(pStreamOSS->pvBuf, pvBuf, cbWritten);
885
886 uint32_t cbChunk = cbWritten;
887 uint32_t cbChunkOff = 0;
888 while (cbChunk)
889 {
890 ssize_t cbChunkWritten = write(pStreamOSS->hFile, (uint8_t *)pStreamOSS->pvBuf + cbChunkOff,
891 RT_MIN(cbChunk, (unsigned)s_OSSConf.fragsize));
892 if (cbChunkWritten < 0)
893 {
894 LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
895 rc = RTErrConvertFromErrno(errno);
896 break;
897 }
898
899 if (cbChunkWritten & pStreamOSS->uAlign)
900 {
901 LogRel(("OSS: Misaligned write (written %z, expected %RU32)\n", cbChunkWritten, cbChunk));
902 break;
903 }
904
905 cbChunkOff += (uint32_t)cbChunkWritten;
906 Assert(cbChunkOff <= cbWritten);
907 Assert(cbChunk >= (uint32_t)cbChunkWritten);
908 cbChunk -= (uint32_t)cbChunkWritten;
909 }
910
911 Assert(cbToWrite >= cbWritten);
912 cbToWrite -= cbWritten;
913 cbWrittenTotal += cbWritten;
914 }
915
916#ifndef RT_OS_L4
917 /* Update read pointer. */
918 if (pStreamOSS->Out.fMMIO)
919 pStreamOSS->old_optr = cntinfo.ptr;
920#endif
921
922 } while(0);
923
924 if (RT_SUCCESS(rc))
925 {
926 if (pcxWritten)
927 *pcxWritten = cbWrittenTotal;
928 }
929
930 return rc;
931}
932
933
934/**
935 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
936 */
937static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
938{
939 RT_NOREF(pInterface);
940}
941
942
943/**
944 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
945 */
946static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostOSSAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
947{
948 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
949 RT_NOREF(enmDir);
950
951 return PDMAUDIOBACKENDSTS_RUNNING;
952}
953
954
955/**
956 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
957 */
958static DECLCALLBACK(int) drvHostOSSAudioStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
959 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
960{
961 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
962 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
963 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
964 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
965
966 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
967
968 int rc;
969 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
970 rc = ossCreateStreamIn (pStreamOSS, pCfgReq, pCfgAcq);
971 else
972 rc = ossCreateStreamOut(pStreamOSS, pCfgReq, pCfgAcq);
973
974 if (RT_SUCCESS(rc))
975 {
976 pStreamOSS->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
977 if (!pStreamOSS->pCfg)
978 rc = VERR_NO_MEMORY;
979 }
980
981 return rc;
982}
983
984
985/**
986 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
987 */
988static DECLCALLBACK(int) drvHostOSSAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
989{
990 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
991 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
992
993 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
994
995 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
996 return VINF_SUCCESS;
997
998 int rc;
999 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
1000 rc = ossDestroyStreamIn(pStream);
1001 else
1002 rc = ossDestroyStreamOut(pStream);
1003
1004 if (RT_SUCCESS(rc))
1005 {
1006 DrvAudioHlpStreamCfgFree(pStreamOSS->pCfg);
1007 pStreamOSS->pCfg = NULL;
1008 }
1009
1010 return rc;
1011}
1012
1013
1014/**
1015 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1016 */
1017static DECLCALLBACK(int) drvHostOSSAudioStreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1018 PDMAUDIOSTREAMCMD enmStreamCmd)
1019{
1020 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1021 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1022
1023 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
1024
1025 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
1026 return VINF_SUCCESS;
1027
1028 int rc;
1029 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
1030 rc = ossControlStreamIn(/*pInterface, pStream, enmStreamCmd*/);
1031 else
1032 rc = ossControlStreamOut(pStreamOSS, enmStreamCmd);
1033
1034 return rc;
1035}
1036
1037
1038/**
1039 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1040 */
1041static DECLCALLBACK(int) drvHostOSSAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1042{
1043 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1044 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1045
1046 LogFlowFuncEnter();
1047
1048 /* Nothing to do here for OSS. */
1049 return VINF_SUCCESS;
1050}
1051
1052
1053/**
1054 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
1055 */
1056static DECLCALLBACK(uint32_t) drvHostOSSAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1057{
1058 RT_NOREF(pInterface, pStream);
1059
1060 return UINT32_MAX;
1061}
1062
1063
1064/**
1065 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
1066 */
1067static DECLCALLBACK(uint32_t) drvHostOSSAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1068{
1069 RT_NOREF(pInterface, pStream);
1070
1071 return UINT32_MAX;
1072}
1073
1074
1075/**
1076 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1077 */
1078static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostOSSAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1079{
1080 RT_NOREF(pInterface, pStream);
1081
1082 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_INITIALIZED
1083 | PDMAUDIOSTREAMSTS_FLAG_ENABLED;
1084 return strmSts;
1085}
1086
1087/**
1088 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1089 */
1090static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1091{
1092 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1093 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1094
1095 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1096 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1097
1098 return NULL;
1099}
1100
1101/**
1102 * Constructs an OSS audio driver instance.
1103 *
1104 * @copydoc FNPDMDRVCONSTRUCT
1105 */
1106static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1107{
1108 RT_NOREF(pCfg, fFlags);
1109 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1110 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
1111 LogRel(("Audio: Initializing OSS driver\n"));
1112
1113 /*
1114 * Init the static parts.
1115 */
1116 pThis->pDrvIns = pDrvIns;
1117 /* IBase */
1118 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
1119 /* IHostAudio */
1120 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
1121
1122 return VINF_SUCCESS;
1123}
1124
1125/**
1126 * Char driver registration record.
1127 */
1128const PDMDRVREG g_DrvHostOSSAudio =
1129{
1130 /* u32Version */
1131 PDM_DRVREG_VERSION,
1132 /* szName */
1133 "OSSAudio",
1134 /* szRCMod */
1135 "",
1136 /* szR0Mod */
1137 "",
1138 /* pszDescription */
1139 "OSS audio host driver",
1140 /* fFlags */
1141 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1142 /* fClass. */
1143 PDM_DRVREG_CLASS_AUDIO,
1144 /* cMaxInstances */
1145 ~0U,
1146 /* cbInstance */
1147 sizeof(DRVHOSTOSSAUDIO),
1148 /* pfnConstruct */
1149 drvHostOSSAudioConstruct,
1150 /* pfnDestruct */
1151 NULL,
1152 /* pfnRelocate */
1153 NULL,
1154 /* pfnIOCtl */
1155 NULL,
1156 /* pfnPowerOn */
1157 NULL,
1158 /* pfnReset */
1159 NULL,
1160 /* pfnSuspend */
1161 NULL,
1162 /* pfnResume */
1163 NULL,
1164 /* pfnAttach */
1165 NULL,
1166 /* pfnDetach */
1167 NULL,
1168 /* pfnPowerOff */
1169 NULL,
1170 /* pfnSoftReset */
1171 NULL,
1172 /* u32EndVersion */
1173 PDM_DRVREG_VERSION
1174};
1175
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