VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioOss.cpp@ 88375

Last change on this file since 88375 was 88362, checked in by vboxsync, 4 years ago

Audio: Removed PDMIHOSTAUDIO::pfnSetCallback (replaced by PDMIAUDIONOTIFYFROMHOST). bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.4 KB
Line 
1/* $Id: DrvHostAudioOss.cpp 88362 2021-04-05 00:31:17Z vboxsync $ */
2/** @file
3 * Host audio driver - OSS (Open Sound System).
4 */
5
6/*
7 * Copyright (C) 2014-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <errno.h>
23#include <fcntl.h>
24#include <sys/ioctl.h>
25#include <sys/mman.h>
26#include <sys/soundcard.h>
27#include <unistd.h>
28
29#include <iprt/alloc.h>
30#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
31
32#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
33#include <VBox/log.h>
34#include <VBox/vmm/pdmaudioifs.h>
35#include <VBox/vmm/pdmaudioinline.h>
36
37#include "VBoxDD.h"
38
39
40/*********************************************************************************************************************************
41* Defines *
42*********************************************************************************************************************************/
43#if ((SOUND_VERSION > 360) && (defined(OSS_SYSINFO)))
44/* OSS > 3.6 has a new syscall available for querying a bit more detailed information
45 * about OSS' audio capabilities. This is handy for e.g. Solaris. */
46# define VBOX_WITH_AUDIO_OSS_SYSINFO 1
47#endif
48
49/** Makes DRVHOSTOSSAUDIO out of PDMIHOSTAUDIO. */
50#define PDMIHOSTAUDIO_2_DRVHOSTOSSAUDIO(pInterface) \
51 ( (PDRVHOSTOSSAUDIO)((uintptr_t)pInterface - RT_UOFFSETOF(DRVHOSTOSSAUDIO, IHostAudio)) )
52
53
54/*********************************************************************************************************************************
55* Structures *
56*********************************************************************************************************************************/
57/**
58 * OSS host audio driver instance data.
59 * @implements PDMIAUDIOCONNECTOR
60 */
61typedef struct DRVHOSTOSSAUDIO
62{
63 /** Pointer to the driver instance structure. */
64 PPDMDRVINS pDrvIns;
65 /** Pointer to host audio interface. */
66 PDMIHOSTAUDIO IHostAudio;
67 /** Error count for not flooding the release log.
68 * UINT32_MAX for unlimited logging. */
69 uint32_t cLogErrors;
70} DRVHOSTOSSAUDIO, *PDRVHOSTOSSAUDIO;
71
72typedef struct OSSAUDIOSTREAMCFG
73{
74 PDMAUDIOPCMPROPS Props;
75 uint16_t cFragments;
76 uint32_t cbFragmentSize;
77} OSSAUDIOSTREAMCFG, *POSSAUDIOSTREAMCFG;
78
79typedef struct OSSAUDIOSTREAM
80{
81 /** The stream's acquired configuration. */
82 PPDMAUDIOSTREAMCFG pCfg;
83 /** Buffer alignment. */
84 uint8_t uAlign;
85 int hFile;
86 int cFragments;
87 int cbFragmentSize;
88 int old_optr;
89} OSSAUDIOSTREAM, *POSSAUDIOSTREAM;
90
91typedef struct OSSAUDIOCFG
92{
93 int nfrags;
94 int fragsize;
95 const char *devpath_out;
96 const char *devpath_in;
97 int debug;
98} OSSAUDIOCFG, *POSSAUDIOCFG;
99
100static OSSAUDIOCFG s_OSSConf =
101{
102 4,
103 4096,
104 "/dev/dsp",
105 "/dev/dsp",
106 0
107};
108
109
110/* http://www.df.lth.se/~john_e/gems/gem002d.html */
111static uint32_t popcount(uint32_t u)
112{
113 u = ((u&0x55555555) + ((u>>1)&0x55555555));
114 u = ((u&0x33333333) + ((u>>2)&0x33333333));
115 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
116 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
117 u = ( u&0x0000ffff) + (u>>16);
118 return u;
119}
120
121
122static uint32_t lsbindex(uint32_t u)
123{
124 return popcount((u & -u) - 1);
125}
126
127
128static int ossOSSToAudioProps(PPDMAUDIOPCMPROPS pProps, int fmt, int cChannels, int uHz)
129{
130 switch (fmt)
131 {
132 case AFMT_S8:
133 PDMAudioPropsInit(pProps, 1 /*8-bit*/, true /*signed*/, cChannels, uHz);
134 break;
135
136 case AFMT_U8:
137 PDMAudioPropsInit(pProps, 1 /*8-bit*/, false /*signed*/, cChannels, uHz);
138 break;
139
140 case AFMT_S16_LE:
141 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/);
142 break;
143
144 case AFMT_U16_LE:
145 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, false /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/);
146 break;
147
148 case AFMT_S16_BE:
149 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/);
150 break;
151
152 case AFMT_U16_BE:
153 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, false /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/);
154 break;
155
156 default:
157 AssertMsgFailedReturn(("Format %d not supported\n", fmt), VERR_NOT_SUPPORTED);
158 }
159
160 return VINF_SUCCESS;
161}
162
163
164static int ossStreamClose(int *phFile)
165{
166 if (!phFile || !*phFile || *phFile == -1)
167 return VINF_SUCCESS;
168
169 int rc;
170 if (close(*phFile))
171 {
172 rc = RTErrConvertFromErrno(errno);
173 LogRel(("OSS: Closing stream failed: %s / %Rrc\n", strerror(errno), rc));
174 }
175 else
176 {
177 *phFile = -1;
178 rc = VINF_SUCCESS;
179 }
180
181 return rc;
182}
183
184
185/**
186 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
187 */
188static DECLCALLBACK(int) drvHostOssAudioHA_Init(PPDMIHOSTAUDIO pInterface)
189{
190 RT_NOREF(pInterface);
191
192 return VINF_SUCCESS;
193}
194
195
196/**
197 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
198 */
199static DECLCALLBACK(void) drvHostOssAudioHA_Shutdown(PPDMIHOSTAUDIO pInterface)
200{
201 RT_NOREF(pInterface);
202}
203
204
205/**
206 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
207 */
208static DECLCALLBACK(int) drvHostOssAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
209{
210 RT_NOREF(pInterface);
211
212 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "OSS");
213
214 pBackendCfg->cbStreamIn = sizeof(OSSAUDIOSTREAM);
215 pBackendCfg->cbStreamOut = sizeof(OSSAUDIOSTREAM);
216
217 int hFile = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0);
218 if (hFile == -1)
219 {
220 /* Try opening the mixing device instead. */
221 hFile = open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
222 }
223 if (hFile != -1)
224 {
225 int ossVer = -1;
226 int err = ioctl(hFile, OSS_GETVERSION, &ossVer);
227 if (err == 0)
228 {
229 LogRel2(("OSS: Using version: %d\n", ossVer));
230#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
231 oss_sysinfo ossInfo;
232 RT_ZERO(ossInfo);
233 err = ioctl(hFile, OSS_SYSINFO, &ossInfo);
234 if (err == 0)
235 {
236 LogRel2(("OSS: Number of DSPs: %d\n", ossInfo.numaudios));
237 LogRel2(("OSS: Number of mixers: %d\n", ossInfo.nummixers));
238
239 int cDev = ossInfo.nummixers;
240 if (!cDev)
241 cDev = ossInfo.numaudios;
242
243 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
244 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
245 }
246 else
247#endif
248 {
249 /* Since we cannot query anything, assume that we have at least
250 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */
251
252 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
253 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
254 }
255 }
256 else
257 LogRel(("OSS: Unable to determine installed version: %s (%d)\n", strerror(err), err));
258 close(hFile);
259 }
260 else
261 LogRel(("OSS: No devices found, audio is not available\n"));
262
263 return VINF_SUCCESS;
264}
265
266
267
268
269/**
270 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
271 */
272static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostOssAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
273{
274 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
275 RT_NOREF(enmDir);
276
277 return PDMAUDIOBACKENDSTS_RUNNING;
278}
279
280
281static int ossStreamConfigure(int hFile, bool fInput, POSSAUDIOSTREAMCFG pOSSReq, POSSAUDIOSTREAMCFG pOSSAcq)
282{
283 /*
284 * Format.
285 */
286 int iFormat;
287 switch (PDMAudioPropsSampleSize(&pOSSReq->Props))
288 {
289 case 1:
290 iFormat = pOSSReq->Props.fSigned ? AFMT_S8 : AFMT_U8;
291 break;
292
293 case 2:
294 if (PDMAudioPropsIsLittleEndian(&pOSSReq->Props))
295 iFormat = pOSSReq->Props.fSigned ? AFMT_S16_LE : AFMT_U16_LE;
296 else
297 iFormat = pOSSReq->Props.fSigned ? AFMT_S16_BE : AFMT_U16_BE;
298 break;
299
300 default:
301 LogRel2(("OSS: Unsupported sample size: %u\n", PDMAudioPropsSampleSize(&pOSSReq->Props)));
302 return VERR_AUDIO_STREAM_COULD_NOT_CREATE;
303 }
304 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_SAMPLESIZE, &iFormat) >= 0,
305 ("OSS: Failed to set audio format to %d: %s (%d)\n", iFormat, strerror(errno), errno),
306 RTErrConvertFromErrno(errno));
307
308 /*
309 * Channel count.
310 */
311 int cChannels = PDMAudioPropsChannels(&pOSSReq->Props);
312 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_CHANNELS, &cChannels) >= 0,
313 ("OSS: Failed to set number of audio channels (%RU8): %s (%d)\n",
314 PDMAudioPropsChannels(&pOSSReq->Props), strerror(errno), errno),
315 RTErrConvertFromErrno(errno));
316
317 /*
318 * Frequency.
319 */
320 int iFrequenc = pOSSReq->Props.uHz;
321 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_SPEED, &iFrequenc) >= 0,
322 ("OSS: Failed to set audio frequency to %d Hz: %s (%d)\n", pOSSReq->Props.uHz, strerror(errno), errno),
323 RTErrConvertFromErrno(errno));
324
325
326 /*
327 * Set obsolete non-blocking call for input streams.
328 */
329 if (fInput)
330 {
331#if !(defined(VBOX) && defined(RT_OS_SOLARIS)) /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
332 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_NONBLOCK, NULL) >= 0,
333 ("OSS: Failed to set non-blocking mode: %s (%d)\n", strerror(errno), errno),
334 RTErrConvertFromErrno(errno));
335#endif
336 }
337
338 /*
339 * Set fragment size and count.
340 */
341 LogRel2(("OSS: Requested %RU16 %s fragments, %RU32 bytes each\n",
342 pOSSReq->cFragments, fInput ? "input" : "output", pOSSReq->cbFragmentSize));
343
344 int mmmmssss = (pOSSReq->cFragments << 16) | lsbindex(pOSSReq->cbFragmentSize);
345 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss) >= 0,
346 ("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s (%d)\n",
347 pOSSReq->cFragments, pOSSReq->cbFragmentSize, strerror(errno), errno),
348 RTErrConvertFromErrno(errno));
349
350 /*
351 * Get parameters and popuplate pOSSAcq.
352 */
353 audio_buf_info BufInfo = { 0, 0, 0, 0 };
354 AssertLogRelMsgReturn(ioctl(hFile, fInput ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &BufInfo) >= 0,
355 ("OSS: Failed to retrieve %s buffer length: %s (%d)\n",
356 fInput ? "input" : "output", strerror(errno), errno),
357 RTErrConvertFromErrno(errno));
358
359 int rc = ossOSSToAudioProps(&pOSSAcq->Props, iFormat, cChannels, iFrequenc);
360 if (RT_SUCCESS(rc))
361 {
362 pOSSAcq->cFragments = BufInfo.fragstotal;
363 pOSSAcq->cbFragmentSize = BufInfo.fragsize;
364
365 LogRel2(("OSS: Got %RU16 %s fragments, %RU32 bytes each\n",
366 pOSSAcq->cFragments, fInput ? "input" : "output", pOSSAcq->cbFragmentSize));
367 }
368
369 return rc;
370}
371
372
373/**
374 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
375 */
376static DECLCALLBACK(int) drvHostOssAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
377 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
378{
379 AssertPtr(pInterface); RT_NOREF(pInterface);
380 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
381 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
382 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
383 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
384
385 /*
386 * Open the device
387 */
388 int rc;
389 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
390 pStreamOSS->hFile = open(s_OSSConf.devpath_in, O_RDONLY | O_NONBLOCK);
391 else
392 pStreamOSS->hFile = open(s_OSSConf.devpath_out, O_WRONLY);
393 if (pStreamOSS->hFile >= 0)
394 {
395 /*
396 * Configure it.
397 */
398 OSSAUDIOSTREAMCFG ReqOssCfg;
399 RT_ZERO(ReqOssCfg);
400
401 memcpy(&ReqOssCfg.Props, &pCfgReq->Props, sizeof(PDMAUDIOPCMPROPS));
402 ReqOssCfg.cFragments = s_OSSConf.nfrags;
403 ReqOssCfg.cbFragmentSize = s_OSSConf.fragsize;
404
405 OSSAUDIOSTREAMCFG AcqOssCfg;
406 RT_ZERO(AcqOssCfg);
407 rc = ossStreamConfigure(pStreamOSS->hFile, pCfgReq->enmDir == PDMAUDIODIR_IN, &ReqOssCfg, &AcqOssCfg);
408 if (RT_SUCCESS(rc))
409 {
410 /*
411 * Complete the stream structure and fill in the pCfgAcq bits.
412 */
413 if ((AcqOssCfg.cFragments * AcqOssCfg.cbFragmentSize) & pStreamOSS->uAlign)
414 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
415 AcqOssCfg.cFragments * AcqOssCfg.cbFragmentSize, pStreamOSS->uAlign + 1));
416
417 memcpy(&pCfgAcq->Props, &AcqOssCfg.Props, sizeof(PDMAUDIOPCMPROPS));
418 pCfgAcq->Backend.cFramesPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, AcqOssCfg.cbFragmentSize);
419 pCfgAcq->Backend.cFramesBufferSize = pCfgAcq->Backend.cFramesPeriod * 2; /* Use "double buffering" */
420 /** @todo Pre-buffering required? */
421
422 /*
423 * Duplicate the stream config and we're done!
424 */
425 pStreamOSS->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
426 if (pStreamOSS->pCfg)
427 return VINF_SUCCESS;
428
429 rc = VERR_NO_MEMORY;
430 }
431 ossStreamClose(&pStreamOSS->hFile);
432 }
433 else
434 {
435 rc = RTErrConvertFromErrno(errno);
436 LogRel(("OSS: Failed to open '%s': %s (%d) / %Rrc\n",
437 pCfgReq->enmDir == PDMAUDIODIR_IN ? s_OSSConf.devpath_in : s_OSSConf.devpath_out, strerror(errno), errno, rc));
438 }
439 return rc;
440}
441
442
443/**
444 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
445 */
446static DECLCALLBACK(int) drvHostOssAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
447{
448 RT_NOREF(pInterface);
449 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
450 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
451
452 ossStreamClose(&pStreamOSS->hFile);
453 PDMAudioStrmCfgFree(pStreamOSS->pCfg);
454 pStreamOSS->pCfg = NULL;
455
456 return VINF_SUCCESS;
457}
458
459
460static int ossControlStreamIn(/*PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd*/ void)
461{
462 /** @todo Nothing to do here right now!? */
463
464 return VINF_SUCCESS;
465}
466
467
468static int ossControlStreamOut(PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
469{
470 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
471
472 int rc = VINF_SUCCESS;
473
474 switch (enmStreamCmd)
475 {
476 case PDMAUDIOSTREAMCMD_ENABLE:
477 case PDMAUDIOSTREAMCMD_RESUME:
478 {
479 int mask = PCM_ENABLE_OUTPUT;
480 if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
481 {
482 LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
483 rc = RTErrConvertFromErrno(errno);
484 }
485
486 break;
487 }
488
489 case PDMAUDIOSTREAMCMD_DISABLE:
490 case PDMAUDIOSTREAMCMD_PAUSE:
491 {
492 int mask = 0;
493 if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
494 {
495 LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
496 rc = RTErrConvertFromErrno(errno);
497 }
498
499 break;
500 }
501
502 default:
503 rc = VERR_NOT_SUPPORTED;
504 break;
505 }
506
507 return rc;
508}
509
510
511/**
512 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
513 */
514static DECLCALLBACK(int) drvHostOssAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
515 PDMAUDIOSTREAMCMD enmStreamCmd)
516{
517 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
518 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
519
520 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
521
522 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
523 return VINF_SUCCESS;
524
525 int rc;
526 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
527 rc = ossControlStreamIn(/*pInterface, pStream, enmStreamCmd*/);
528 else
529 rc = ossControlStreamOut(pStreamOSS, enmStreamCmd);
530
531 return rc;
532}
533
534
535/**
536 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
537 */
538static DECLCALLBACK(uint32_t) drvHostOssAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
539{
540 RT_NOREF(pInterface, pStream);
541 return UINT32_MAX;
542}
543
544
545/**
546 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
547 */
548static DECLCALLBACK(uint32_t) drvHostOssAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
549{
550 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
551 AssertPtr(pStreamOSS);
552 RT_NOREF(pInterface);
553
554 /*
555 * Note! This logic was found in StreamPlay and corrected a little.
556 *
557 * The logic here must match what StreamPlay does.
558 */
559 audio_buf_info BufInfo = { 0, 0, 0, 0 };
560 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &BufInfo);
561 AssertMsgReturn(rc2 >= 0, ("SNDCTL_DSP_GETOSPACE failed: %s (%d)\n", strerror(errno), errno), 0);
562
563#if 0 /** @todo we could return BufInfo.bytes here iff StreamPlay didn't use the fragmented approach */
564 /** @todo r=bird: WTF do we make a fuss over BufInfo.bytes for when we don't
565 * even use it?!? */
566 AssertLogRelMsgReturn(BufInfo.bytes >= 0, ("OSS: Warning: Invalid available size: %d\n", BufInfo.bytes), VERR_INTERNAL_ERROR_3);
567 if ((unsigned)BufInfo.bytes > cbBuf)
568 {
569 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", BufInfo.bytes, cbBuf, cbBuf));
570 BufInfo.bytes = cbBuf;
571 /* Keep going. */
572 }
573#endif
574
575 return (uint32_t)(BufInfo.fragments * BufInfo.fragsize);
576}
577
578
579/**
580 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
581 */
582static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostOssAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
583{
584 RT_NOREF(pInterface, pStream);
585 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
586}
587
588
589/**
590 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
591 */
592static DECLCALLBACK(int) drvHostOssAudioHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
593{
594 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
595 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
596
597 LogFlowFuncEnter();
598
599 /* Nothing to do here for OSS. */
600 return VINF_SUCCESS;
601}
602
603
604/**
605 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
606 */
607static DECLCALLBACK(int) drvHostOssAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
608 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
609{
610 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
611 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
612 RT_NOREF(pInterface);
613
614 /*
615 * Figure out now much to write.
616 */
617 uint32_t cbToWrite;
618 audio_buf_info BufInfo;
619 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &BufInfo);
620 AssertLogRelMsgReturn(rc2 >= 0, ("OSS: Failed to retrieve current playback buffer: %s (%d)\n", strerror(errno), errno),
621 RTErrConvertFromErrno(errno));
622
623#if 0 /** @todo r=bird: WTF do we make a fuss over BufInfo.bytes for when we don't even use it?!? */
624 AssertLogRelMsgReturn(BufInfo.bytes >= 0, ("OSS: Warning: Invalid available size: %d\n", BufInfo.bytes), VERR_INTERNAL_ERROR_3);
625 if ((unsigned)BufInfo.bytes > cbBuf)
626 {
627 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", BufInfo.bytes, cbBuf, cbBuf));
628 BufInfo.bytes = cbBuf;
629 /* Keep going. */
630 }
631#endif
632 cbToWrite = (unsigned)(BufInfo.fragments * BufInfo.fragsize);
633 cbToWrite = RT_MIN(cbToWrite, cbBuf);
634
635 /*
636 * Write.
637 */
638 uint8_t const *pbBuf = (uint8_t const *)pvBuf;
639 uint32_t cbChunk = cbToWrite;
640 uint32_t offChunk = 0;
641 while (cbChunk > 0)
642 {
643 ssize_t cbWritten = write(pStreamOSS->hFile, &pbBuf[offChunk], RT_MIN(cbChunk, (unsigned)s_OSSConf.fragsize));
644 if (cbWritten >= 0)
645 {
646 AssertLogRelMsg(!(cbWritten & pStreamOSS->uAlign),
647 ("OSS: Misaligned write (written %#zx, alignment %#x)\n", cbWritten, pStreamOSS->uAlign));
648
649 Assert((uint32_t)cbWritten <= cbChunk);
650 offChunk += (uint32_t)cbWritten;
651 cbChunk -= (uint32_t)cbWritten;
652 }
653 else
654 {
655 LogRel(("OSS: Failed writing output data: %s (%d)\n", strerror(errno), errno));
656 return RTErrConvertFromErrno(errno);
657 }
658 }
659
660 *pcbWritten = cbToWrite;
661 return VINF_SUCCESS;
662}
663
664
665/**
666 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
667 */
668static DECLCALLBACK(int) drvHostOssAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
669 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
670{
671 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
672 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
673 RT_NOREF(pInterface);
674
675
676 size_t cbToRead = uBufSize;
677 LogFlowFunc(("cbToRead=%zi\n", cbToRead));
678
679 uint8_t * const pbDst = (uint8_t *)pvBuf;
680 size_t offWrite = 0;
681 while (cbToRead > 0)
682 {
683 ssize_t cbRead = read(pStreamOSS->hFile, &pbDst[offWrite], cbToRead);
684 if (cbRead)
685 {
686 LogFlowFunc(("cbRead=%zi, offWrite=%zu cbToRead=%zu\n", cbRead, offWrite, cbToRead));
687 Assert((ssize_t)cbToRead >= cbRead);
688 cbToRead -= cbRead;
689 offWrite += cbRead;
690 }
691 else
692 {
693 LogFunc(("cbRead=%zi, offWrite=%zu cbToRead=%zu errno=%d\n", cbRead, offWrite, cbToRead, errno));
694
695 /* Don't complain about errors if we've retrieved some audio data already. */
696 if (cbRead < 0 && offWrite == 0 && errno != EINTR && errno != EAGAIN)
697 {
698 AssertStmt(errno != 0, errno = EACCES);
699 int rc = RTErrConvertFromErrno(errno);
700 LogFunc(("Failed to read %zu input frames, errno=%d rc=%Rrc\n", cbToRead, errno, rc));
701 return rc;
702 }
703 break;
704 }
705 }
706
707 if (puRead)
708 *puRead = offWrite;
709 return VINF_SUCCESS;
710}
711
712
713
714/**
715 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
716 */
717static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
718{
719 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
720 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
721
722 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
723 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
724
725 return NULL;
726}
727
728/**
729 * Constructs an OSS audio driver instance.
730 *
731 * @copydoc FNPDMDRVCONSTRUCT
732 */
733static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
734{
735 RT_NOREF(pCfg, fFlags);
736 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
737 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
738 LogRel(("Audio: Initializing OSS driver\n"));
739
740 /*
741 * Init the static parts.
742 */
743 pThis->pDrvIns = pDrvIns;
744 /* IBase */
745 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
746 /* IHostAudio */
747 pThis->IHostAudio.pfnInit = drvHostOssAudioHA_Init;
748 pThis->IHostAudio.pfnShutdown = drvHostOssAudioHA_Shutdown;
749 pThis->IHostAudio.pfnGetConfig = drvHostOssAudioHA_GetConfig;
750 pThis->IHostAudio.pfnGetStatus = drvHostOssAudioHA_GetStatus;
751 pThis->IHostAudio.pfnStreamCreate = drvHostOssAudioHA_StreamCreate;
752 pThis->IHostAudio.pfnStreamDestroy = drvHostOssAudioHA_StreamDestroy;
753 pThis->IHostAudio.pfnStreamControl = drvHostOssAudioHA_StreamControl;
754 pThis->IHostAudio.pfnStreamGetReadable = drvHostOssAudioHA_StreamGetReadable;
755 pThis->IHostAudio.pfnStreamGetWritable = drvHostOssAudioHA_StreamGetWritable;
756 pThis->IHostAudio.pfnStreamGetStatus = drvHostOssAudioHA_StreamGetStatus;
757 pThis->IHostAudio.pfnStreamIterate = drvHostOssAudioHA_StreamIterate;
758 pThis->IHostAudio.pfnStreamPlay = drvHostOssAudioHA_StreamPlay;
759 pThis->IHostAudio.pfnStreamCapture = drvHostOssAudioHA_StreamCapture;
760 pThis->IHostAudio.pfnGetDevices = NULL;
761 pThis->IHostAudio.pfnStreamGetPending = NULL;
762 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
763 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
764 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
765 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
766
767 return VINF_SUCCESS;
768}
769
770/**
771 * Char driver registration record.
772 */
773const PDMDRVREG g_DrvHostOSSAudio =
774{
775 /* u32Version */
776 PDM_DRVREG_VERSION,
777 /* szName */
778 "OSSAudio",
779 /* szRCMod */
780 "",
781 /* szR0Mod */
782 "",
783 /* pszDescription */
784 "OSS audio host driver",
785 /* fFlags */
786 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
787 /* fClass. */
788 PDM_DRVREG_CLASS_AUDIO,
789 /* cMaxInstances */
790 ~0U,
791 /* cbInstance */
792 sizeof(DRVHOSTOSSAUDIO),
793 /* pfnConstruct */
794 drvHostOSSAudioConstruct,
795 /* pfnDestruct */
796 NULL,
797 /* pfnRelocate */
798 NULL,
799 /* pfnIOCtl */
800 NULL,
801 /* pfnPowerOn */
802 NULL,
803 /* pfnReset */
804 NULL,
805 /* pfnSuspend */
806 NULL,
807 /* pfnResume */
808 NULL,
809 /* pfnAttach */
810 NULL,
811 /* pfnDetach */
812 NULL,
813 /* pfnPowerOff */
814 NULL,
815 /* pfnSoftReset */
816 NULL,
817 /* u32EndVersion */
818 PDM_DRVREG_VERSION
819};
820
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