VirtualBox

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

Last change on this file since 88401 was 88390, checked in by vboxsync, 4 years ago

Audio: Removed the pfnInit and pfnShutdown methods from PDMIHOSTAUDIO. These methods duplicates PDMDRVREG callbacks and were therefore superfluous. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.2 KB
Line 
1/* $Id: DrvHostAudioOss.cpp 88390 2021-04-07 10:35:06Z 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,pfnGetConfig}
187 */
188static DECLCALLBACK(int) drvHostOssAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
189{
190 RT_NOREF(pInterface);
191
192 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "OSS");
193
194 pBackendCfg->cbStreamIn = sizeof(OSSAUDIOSTREAM);
195 pBackendCfg->cbStreamOut = sizeof(OSSAUDIOSTREAM);
196
197 int hFile = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0);
198 if (hFile == -1)
199 {
200 /* Try opening the mixing device instead. */
201 hFile = open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
202 }
203 if (hFile != -1)
204 {
205 int ossVer = -1;
206 int err = ioctl(hFile, OSS_GETVERSION, &ossVer);
207 if (err == 0)
208 {
209 LogRel2(("OSS: Using version: %d\n", ossVer));
210#ifdef VBOX_WITH_AUDIO_OSS_SYSINFO
211 oss_sysinfo ossInfo;
212 RT_ZERO(ossInfo);
213 err = ioctl(hFile, OSS_SYSINFO, &ossInfo);
214 if (err == 0)
215 {
216 LogRel2(("OSS: Number of DSPs: %d\n", ossInfo.numaudios));
217 LogRel2(("OSS: Number of mixers: %d\n", ossInfo.nummixers));
218
219 int cDev = ossInfo.nummixers;
220 if (!cDev)
221 cDev = ossInfo.numaudios;
222
223 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
224 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
225 }
226 else
227#endif
228 {
229 /* Since we cannot query anything, assume that we have at least
230 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */
231
232 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
233 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
234 }
235 }
236 else
237 LogRel(("OSS: Unable to determine installed version: %s (%d)\n", strerror(err), err));
238 close(hFile);
239 }
240 else
241 LogRel(("OSS: No devices found, audio is not available\n"));
242
243 return VINF_SUCCESS;
244}
245
246
247
248
249/**
250 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
251 */
252static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostOssAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
253{
254 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
255 RT_NOREF(enmDir);
256
257 return PDMAUDIOBACKENDSTS_RUNNING;
258}
259
260
261static int ossStreamConfigure(int hFile, bool fInput, POSSAUDIOSTREAMCFG pOSSReq, POSSAUDIOSTREAMCFG pOSSAcq)
262{
263 /*
264 * Format.
265 */
266 int iFormat;
267 switch (PDMAudioPropsSampleSize(&pOSSReq->Props))
268 {
269 case 1:
270 iFormat = pOSSReq->Props.fSigned ? AFMT_S8 : AFMT_U8;
271 break;
272
273 case 2:
274 if (PDMAudioPropsIsLittleEndian(&pOSSReq->Props))
275 iFormat = pOSSReq->Props.fSigned ? AFMT_S16_LE : AFMT_U16_LE;
276 else
277 iFormat = pOSSReq->Props.fSigned ? AFMT_S16_BE : AFMT_U16_BE;
278 break;
279
280 default:
281 LogRel2(("OSS: Unsupported sample size: %u\n", PDMAudioPropsSampleSize(&pOSSReq->Props)));
282 return VERR_AUDIO_STREAM_COULD_NOT_CREATE;
283 }
284 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_SAMPLESIZE, &iFormat) >= 0,
285 ("OSS: Failed to set audio format to %d: %s (%d)\n", iFormat, strerror(errno), errno),
286 RTErrConvertFromErrno(errno));
287
288 /*
289 * Channel count.
290 */
291 int cChannels = PDMAudioPropsChannels(&pOSSReq->Props);
292 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_CHANNELS, &cChannels) >= 0,
293 ("OSS: Failed to set number of audio channels (%RU8): %s (%d)\n",
294 PDMAudioPropsChannels(&pOSSReq->Props), strerror(errno), errno),
295 RTErrConvertFromErrno(errno));
296
297 /*
298 * Frequency.
299 */
300 int iFrequenc = pOSSReq->Props.uHz;
301 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_SPEED, &iFrequenc) >= 0,
302 ("OSS: Failed to set audio frequency to %d Hz: %s (%d)\n", pOSSReq->Props.uHz, strerror(errno), errno),
303 RTErrConvertFromErrno(errno));
304
305
306 /*
307 * Set obsolete non-blocking call for input streams.
308 */
309 if (fInput)
310 {
311#if !(defined(VBOX) && defined(RT_OS_SOLARIS)) /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
312 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_NONBLOCK, NULL) >= 0,
313 ("OSS: Failed to set non-blocking mode: %s (%d)\n", strerror(errno), errno),
314 RTErrConvertFromErrno(errno));
315#endif
316 }
317
318 /*
319 * Set fragment size and count.
320 */
321 LogRel2(("OSS: Requested %RU16 %s fragments, %RU32 bytes each\n",
322 pOSSReq->cFragments, fInput ? "input" : "output", pOSSReq->cbFragmentSize));
323
324 int mmmmssss = (pOSSReq->cFragments << 16) | lsbindex(pOSSReq->cbFragmentSize);
325 AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss) >= 0,
326 ("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s (%d)\n",
327 pOSSReq->cFragments, pOSSReq->cbFragmentSize, strerror(errno), errno),
328 RTErrConvertFromErrno(errno));
329
330 /*
331 * Get parameters and popuplate pOSSAcq.
332 */
333 audio_buf_info BufInfo = { 0, 0, 0, 0 };
334 AssertLogRelMsgReturn(ioctl(hFile, fInput ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &BufInfo) >= 0,
335 ("OSS: Failed to retrieve %s buffer length: %s (%d)\n",
336 fInput ? "input" : "output", strerror(errno), errno),
337 RTErrConvertFromErrno(errno));
338
339 int rc = ossOSSToAudioProps(&pOSSAcq->Props, iFormat, cChannels, iFrequenc);
340 if (RT_SUCCESS(rc))
341 {
342 pOSSAcq->cFragments = BufInfo.fragstotal;
343 pOSSAcq->cbFragmentSize = BufInfo.fragsize;
344
345 LogRel2(("OSS: Got %RU16 %s fragments, %RU32 bytes each\n",
346 pOSSAcq->cFragments, fInput ? "input" : "output", pOSSAcq->cbFragmentSize));
347 }
348
349 return rc;
350}
351
352
353/**
354 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
355 */
356static DECLCALLBACK(int) drvHostOssAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
357 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
358{
359 AssertPtr(pInterface); RT_NOREF(pInterface);
360 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
361 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
362 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
363 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
364
365 /*
366 * Open the device
367 */
368 int rc;
369 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
370 pStreamOSS->hFile = open(s_OSSConf.devpath_in, O_RDONLY | O_NONBLOCK);
371 else
372 pStreamOSS->hFile = open(s_OSSConf.devpath_out, O_WRONLY);
373 if (pStreamOSS->hFile >= 0)
374 {
375 /*
376 * Configure it.
377 */
378 OSSAUDIOSTREAMCFG ReqOssCfg;
379 RT_ZERO(ReqOssCfg);
380
381 memcpy(&ReqOssCfg.Props, &pCfgReq->Props, sizeof(PDMAUDIOPCMPROPS));
382 ReqOssCfg.cFragments = s_OSSConf.nfrags;
383 ReqOssCfg.cbFragmentSize = s_OSSConf.fragsize;
384
385 OSSAUDIOSTREAMCFG AcqOssCfg;
386 RT_ZERO(AcqOssCfg);
387 rc = ossStreamConfigure(pStreamOSS->hFile, pCfgReq->enmDir == PDMAUDIODIR_IN, &ReqOssCfg, &AcqOssCfg);
388 if (RT_SUCCESS(rc))
389 {
390 /*
391 * Complete the stream structure and fill in the pCfgAcq bits.
392 */
393 if ((AcqOssCfg.cFragments * AcqOssCfg.cbFragmentSize) & pStreamOSS->uAlign)
394 LogRel(("OSS: Warning: Misaligned playback buffer: Size = %zu, Alignment = %u\n",
395 AcqOssCfg.cFragments * AcqOssCfg.cbFragmentSize, pStreamOSS->uAlign + 1));
396
397 memcpy(&pCfgAcq->Props, &AcqOssCfg.Props, sizeof(PDMAUDIOPCMPROPS));
398 pCfgAcq->Backend.cFramesPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, AcqOssCfg.cbFragmentSize);
399 pCfgAcq->Backend.cFramesBufferSize = pCfgAcq->Backend.cFramesPeriod * 2; /* Use "double buffering" */
400 /** @todo Pre-buffering required? */
401
402 /*
403 * Duplicate the stream config and we're done!
404 */
405 pStreamOSS->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
406 if (pStreamOSS->pCfg)
407 return VINF_SUCCESS;
408
409 rc = VERR_NO_MEMORY;
410 }
411 ossStreamClose(&pStreamOSS->hFile);
412 }
413 else
414 {
415 rc = RTErrConvertFromErrno(errno);
416 LogRel(("OSS: Failed to open '%s': %s (%d) / %Rrc\n",
417 pCfgReq->enmDir == PDMAUDIODIR_IN ? s_OSSConf.devpath_in : s_OSSConf.devpath_out, strerror(errno), errno, rc));
418 }
419 return rc;
420}
421
422
423/**
424 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
425 */
426static DECLCALLBACK(int) drvHostOssAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
427{
428 RT_NOREF(pInterface);
429 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
430 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
431
432 ossStreamClose(&pStreamOSS->hFile);
433 PDMAudioStrmCfgFree(pStreamOSS->pCfg);
434 pStreamOSS->pCfg = NULL;
435
436 return VINF_SUCCESS;
437}
438
439
440static int ossControlStreamIn(/*PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd*/ void)
441{
442 /** @todo Nothing to do here right now!? */
443
444 return VINF_SUCCESS;
445}
446
447
448static int ossControlStreamOut(PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
449{
450 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
451
452 int rc = VINF_SUCCESS;
453
454 switch (enmStreamCmd)
455 {
456 case PDMAUDIOSTREAMCMD_ENABLE:
457 case PDMAUDIOSTREAMCMD_RESUME:
458 {
459 int mask = PCM_ENABLE_OUTPUT;
460 if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
461 {
462 LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
463 rc = RTErrConvertFromErrno(errno);
464 }
465
466 break;
467 }
468
469 case PDMAUDIOSTREAMCMD_DISABLE:
470 case PDMAUDIOSTREAMCMD_PAUSE:
471 {
472 int mask = 0;
473 if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
474 {
475 LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
476 rc = RTErrConvertFromErrno(errno);
477 }
478
479 break;
480 }
481
482 default:
483 rc = VERR_NOT_SUPPORTED;
484 break;
485 }
486
487 return rc;
488}
489
490
491/**
492 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
493 */
494static DECLCALLBACK(int) drvHostOssAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
495 PDMAUDIOSTREAMCMD enmStreamCmd)
496{
497 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
498 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
499
500 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
501
502 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
503 return VINF_SUCCESS;
504
505 int rc;
506 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
507 rc = ossControlStreamIn(/*pInterface, pStream, enmStreamCmd*/);
508 else
509 rc = ossControlStreamOut(pStreamOSS, enmStreamCmd);
510
511 return rc;
512}
513
514
515/**
516 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
517 */
518static DECLCALLBACK(uint32_t) drvHostOssAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
519{
520 RT_NOREF(pInterface, pStream);
521 return UINT32_MAX;
522}
523
524
525/**
526 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
527 */
528static DECLCALLBACK(uint32_t) drvHostOssAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
529{
530 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
531 AssertPtr(pStreamOSS);
532 RT_NOREF(pInterface);
533
534 /*
535 * Note! This logic was found in StreamPlay and corrected a little.
536 *
537 * The logic here must match what StreamPlay does.
538 */
539 audio_buf_info BufInfo = { 0, 0, 0, 0 };
540 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &BufInfo);
541 AssertMsgReturn(rc2 >= 0, ("SNDCTL_DSP_GETOSPACE failed: %s (%d)\n", strerror(errno), errno), 0);
542
543#if 0 /** @todo we could return BufInfo.bytes here iff StreamPlay didn't use the fragmented approach */
544 /** @todo r=bird: WTF do we make a fuss over BufInfo.bytes for when we don't
545 * even use it?!? */
546 AssertLogRelMsgReturn(BufInfo.bytes >= 0, ("OSS: Warning: Invalid available size: %d\n", BufInfo.bytes), VERR_INTERNAL_ERROR_3);
547 if ((unsigned)BufInfo.bytes > cbBuf)
548 {
549 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", BufInfo.bytes, cbBuf, cbBuf));
550 BufInfo.bytes = cbBuf;
551 /* Keep going. */
552 }
553#endif
554
555 return (uint32_t)(BufInfo.fragments * BufInfo.fragsize);
556}
557
558
559/**
560 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
561 */
562static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostOssAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
563{
564 RT_NOREF(pInterface, pStream);
565 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
566}
567
568
569/**
570 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
571 */
572static DECLCALLBACK(int) drvHostOssAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
573 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
574{
575 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
576 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
577 RT_NOREF(pInterface);
578
579 /*
580 * Figure out now much to write.
581 */
582 uint32_t cbToWrite;
583 audio_buf_info BufInfo;
584 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &BufInfo);
585 AssertLogRelMsgReturn(rc2 >= 0, ("OSS: Failed to retrieve current playback buffer: %s (%d)\n", strerror(errno), errno),
586 RTErrConvertFromErrno(errno));
587
588#if 0 /** @todo r=bird: WTF do we make a fuss over BufInfo.bytes for when we don't even use it?!? */
589 AssertLogRelMsgReturn(BufInfo.bytes >= 0, ("OSS: Warning: Invalid available size: %d\n", BufInfo.bytes), VERR_INTERNAL_ERROR_3);
590 if ((unsigned)BufInfo.bytes > cbBuf)
591 {
592 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", BufInfo.bytes, cbBuf, cbBuf));
593 BufInfo.bytes = cbBuf;
594 /* Keep going. */
595 }
596#endif
597 cbToWrite = (unsigned)(BufInfo.fragments * BufInfo.fragsize);
598 cbToWrite = RT_MIN(cbToWrite, cbBuf);
599
600 /*
601 * Write.
602 */
603 uint8_t const *pbBuf = (uint8_t const *)pvBuf;
604 uint32_t cbChunk = cbToWrite;
605 uint32_t offChunk = 0;
606 while (cbChunk > 0)
607 {
608 ssize_t cbWritten = write(pStreamOSS->hFile, &pbBuf[offChunk], RT_MIN(cbChunk, (unsigned)s_OSSConf.fragsize));
609 if (cbWritten >= 0)
610 {
611 AssertLogRelMsg(!(cbWritten & pStreamOSS->uAlign),
612 ("OSS: Misaligned write (written %#zx, alignment %#x)\n", cbWritten, pStreamOSS->uAlign));
613
614 Assert((uint32_t)cbWritten <= cbChunk);
615 offChunk += (uint32_t)cbWritten;
616 cbChunk -= (uint32_t)cbWritten;
617 }
618 else
619 {
620 LogRel(("OSS: Failed writing output data: %s (%d)\n", strerror(errno), errno));
621 return RTErrConvertFromErrno(errno);
622 }
623 }
624
625 *pcbWritten = cbToWrite;
626 return VINF_SUCCESS;
627}
628
629
630/**
631 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
632 */
633static DECLCALLBACK(int) drvHostOssAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
634 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
635{
636 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
637 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
638 RT_NOREF(pInterface);
639
640
641 size_t cbToRead = uBufSize;
642 LogFlowFunc(("cbToRead=%zi\n", cbToRead));
643
644 uint8_t * const pbDst = (uint8_t *)pvBuf;
645 size_t offWrite = 0;
646 while (cbToRead > 0)
647 {
648 ssize_t cbRead = read(pStreamOSS->hFile, &pbDst[offWrite], cbToRead);
649 if (cbRead)
650 {
651 LogFlowFunc(("cbRead=%zi, offWrite=%zu cbToRead=%zu\n", cbRead, offWrite, cbToRead));
652 Assert((ssize_t)cbToRead >= cbRead);
653 cbToRead -= cbRead;
654 offWrite += cbRead;
655 }
656 else
657 {
658 LogFunc(("cbRead=%zi, offWrite=%zu cbToRead=%zu errno=%d\n", cbRead, offWrite, cbToRead, errno));
659
660 /* Don't complain about errors if we've retrieved some audio data already. */
661 if (cbRead < 0 && offWrite == 0 && errno != EINTR && errno != EAGAIN)
662 {
663 AssertStmt(errno != 0, errno = EACCES);
664 int rc = RTErrConvertFromErrno(errno);
665 LogFunc(("Failed to read %zu input frames, errno=%d rc=%Rrc\n", cbToRead, errno, rc));
666 return rc;
667 }
668 break;
669 }
670 }
671
672 if (puRead)
673 *puRead = offWrite;
674 return VINF_SUCCESS;
675}
676
677
678
679/**
680 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
681 */
682static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
683{
684 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
685 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
686
687 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
688 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
689
690 return NULL;
691}
692
693/**
694 * Constructs an OSS audio driver instance.
695 *
696 * @copydoc FNPDMDRVCONSTRUCT
697 */
698static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
699{
700 RT_NOREF(pCfg, fFlags);
701 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
702 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
703 LogRel(("Audio: Initializing OSS driver\n"));
704
705 /*
706 * Init the static parts.
707 */
708 pThis->pDrvIns = pDrvIns;
709 /* IBase */
710 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
711 /* IHostAudio */
712 pThis->IHostAudio.pfnGetConfig = drvHostOssAudioHA_GetConfig;
713 pThis->IHostAudio.pfnGetStatus = drvHostOssAudioHA_GetStatus;
714 pThis->IHostAudio.pfnStreamCreate = drvHostOssAudioHA_StreamCreate;
715 pThis->IHostAudio.pfnStreamDestroy = drvHostOssAudioHA_StreamDestroy;
716 pThis->IHostAudio.pfnStreamControl = drvHostOssAudioHA_StreamControl;
717 pThis->IHostAudio.pfnStreamGetReadable = drvHostOssAudioHA_StreamGetReadable;
718 pThis->IHostAudio.pfnStreamGetWritable = drvHostOssAudioHA_StreamGetWritable;
719 pThis->IHostAudio.pfnStreamGetStatus = drvHostOssAudioHA_StreamGetStatus;
720 pThis->IHostAudio.pfnStreamPlay = drvHostOssAudioHA_StreamPlay;
721 pThis->IHostAudio.pfnStreamCapture = drvHostOssAudioHA_StreamCapture;
722 pThis->IHostAudio.pfnGetDevices = NULL;
723 pThis->IHostAudio.pfnStreamGetPending = NULL;
724
725 return VINF_SUCCESS;
726}
727
728/**
729 * Char driver registration record.
730 */
731const PDMDRVREG g_DrvHostOSSAudio =
732{
733 /* u32Version */
734 PDM_DRVREG_VERSION,
735 /* szName */
736 "OSSAudio",
737 /* szRCMod */
738 "",
739 /* szR0Mod */
740 "",
741 /* pszDescription */
742 "OSS audio host driver",
743 /* fFlags */
744 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
745 /* fClass. */
746 PDM_DRVREG_CLASS_AUDIO,
747 /* cMaxInstances */
748 ~0U,
749 /* cbInstance */
750 sizeof(DRVHOSTOSSAUDIO),
751 /* pfnConstruct */
752 drvHostOSSAudioConstruct,
753 /* pfnDestruct */
754 NULL,
755 /* pfnRelocate */
756 NULL,
757 /* pfnIOCtl */
758 NULL,
759 /* pfnPowerOn */
760 NULL,
761 /* pfnReset */
762 NULL,
763 /* pfnSuspend */
764 NULL,
765 /* pfnResume */
766 NULL,
767 /* pfnAttach */
768 NULL,
769 /* pfnDetach */
770 NULL,
771 /* pfnPowerOff */
772 NULL,
773 /* pfnSoftReset */
774 NULL,
775 /* u32EndVersion */
776 PDM_DRVREG_VERSION
777};
778
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