VirtualBox

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

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

DrvHostAudioOss: Build fix after PPDMAUDIOBACKENDSTREAM change. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.1 KB
Line 
1/* $Id: DrvHostAudioOss.cpp 88413 2021-04-08 12:02:59Z 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(POSSAUDIOSTREAM pStreamOSS, PDMAUDIOSTREAMCMD enmStreamCmd)
449{
450 int rc = VINF_SUCCESS;
451
452 switch (enmStreamCmd)
453 {
454 case PDMAUDIOSTREAMCMD_ENABLE:
455 case PDMAUDIOSTREAMCMD_RESUME:
456 {
457 int mask = PCM_ENABLE_OUTPUT;
458 if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
459 {
460 LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
461 rc = RTErrConvertFromErrno(errno);
462 }
463
464 break;
465 }
466
467 case PDMAUDIOSTREAMCMD_DISABLE:
468 case PDMAUDIOSTREAMCMD_PAUSE:
469 {
470 int mask = 0;
471 if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
472 {
473 LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
474 rc = RTErrConvertFromErrno(errno);
475 }
476
477 break;
478 }
479
480 default:
481 rc = VERR_NOT_SUPPORTED;
482 break;
483 }
484
485 return rc;
486}
487
488
489/**
490 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
491 */
492static DECLCALLBACK(int) drvHostOssAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
493 PDMAUDIOSTREAMCMD enmStreamCmd)
494{
495 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
496 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
497
498 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
499
500 if (!pStreamOSS->pCfg) /* Not (yet) configured? Skip. */
501 return VINF_SUCCESS;
502
503 int rc;
504 if (pStreamOSS->pCfg->enmDir == PDMAUDIODIR_IN)
505 rc = ossControlStreamIn(/*pInterface, pStream, enmStreamCmd*/);
506 else
507 rc = ossControlStreamOut(pStreamOSS, enmStreamCmd);
508
509 return rc;
510}
511
512
513/**
514 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
515 */
516static DECLCALLBACK(uint32_t) drvHostOssAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
517{
518 RT_NOREF(pInterface, pStream);
519 return UINT32_MAX;
520}
521
522
523/**
524 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
525 */
526static DECLCALLBACK(uint32_t) drvHostOssAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
527{
528 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
529 AssertPtr(pStreamOSS);
530 RT_NOREF(pInterface);
531
532 /*
533 * Note! This logic was found in StreamPlay and corrected a little.
534 *
535 * The logic here must match what StreamPlay does.
536 */
537 audio_buf_info BufInfo = { 0, 0, 0, 0 };
538 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &BufInfo);
539 AssertMsgReturn(rc2 >= 0, ("SNDCTL_DSP_GETOSPACE failed: %s (%d)\n", strerror(errno), errno), 0);
540
541#if 0 /** @todo we could return BufInfo.bytes here iff StreamPlay didn't use the fragmented approach */
542 /** @todo r=bird: WTF do we make a fuss over BufInfo.bytes for when we don't
543 * even use it?!? */
544 AssertLogRelMsgReturn(BufInfo.bytes >= 0, ("OSS: Warning: Invalid available size: %d\n", BufInfo.bytes), VERR_INTERNAL_ERROR_3);
545 if ((unsigned)BufInfo.bytes > cbBuf)
546 {
547 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", BufInfo.bytes, cbBuf, cbBuf));
548 BufInfo.bytes = cbBuf;
549 /* Keep going. */
550 }
551#endif
552
553 return (uint32_t)(BufInfo.fragments * BufInfo.fragsize);
554}
555
556
557/**
558 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
559 */
560static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostOssAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
561{
562 RT_NOREF(pInterface, pStream);
563 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
564}
565
566
567/**
568 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
569 */
570static DECLCALLBACK(int) drvHostOssAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
571 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
572{
573 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
574 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
575 RT_NOREF(pInterface);
576
577 /*
578 * Figure out now much to write.
579 */
580 uint32_t cbToWrite;
581 audio_buf_info BufInfo;
582 int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETOSPACE, &BufInfo);
583 AssertLogRelMsgReturn(rc2 >= 0, ("OSS: Failed to retrieve current playback buffer: %s (%d)\n", strerror(errno), errno),
584 RTErrConvertFromErrno(errno));
585
586#if 0 /** @todo r=bird: WTF do we make a fuss over BufInfo.bytes for when we don't even use it?!? */
587 AssertLogRelMsgReturn(BufInfo.bytes >= 0, ("OSS: Warning: Invalid available size: %d\n", BufInfo.bytes), VERR_INTERNAL_ERROR_3);
588 if ((unsigned)BufInfo.bytes > cbBuf)
589 {
590 LogRel2(("OSS: Warning: Too big output size (%d > %RU32), limiting to %RU32\n", BufInfo.bytes, cbBuf, cbBuf));
591 BufInfo.bytes = cbBuf;
592 /* Keep going. */
593 }
594#endif
595 cbToWrite = (unsigned)(BufInfo.fragments * BufInfo.fragsize);
596 cbToWrite = RT_MIN(cbToWrite, cbBuf);
597
598 /*
599 * Write.
600 */
601 uint8_t const *pbBuf = (uint8_t const *)pvBuf;
602 uint32_t cbChunk = cbToWrite;
603 uint32_t offChunk = 0;
604 while (cbChunk > 0)
605 {
606 ssize_t cbWritten = write(pStreamOSS->hFile, &pbBuf[offChunk], RT_MIN(cbChunk, (unsigned)s_OSSConf.fragsize));
607 if (cbWritten >= 0)
608 {
609 AssertLogRelMsg(!(cbWritten & pStreamOSS->uAlign),
610 ("OSS: Misaligned write (written %#zx, alignment %#x)\n", cbWritten, pStreamOSS->uAlign));
611
612 Assert((uint32_t)cbWritten <= cbChunk);
613 offChunk += (uint32_t)cbWritten;
614 cbChunk -= (uint32_t)cbWritten;
615 }
616 else
617 {
618 LogRel(("OSS: Failed writing output data: %s (%d)\n", strerror(errno), errno));
619 return RTErrConvertFromErrno(errno);
620 }
621 }
622
623 *pcbWritten = cbToWrite;
624 return VINF_SUCCESS;
625}
626
627
628/**
629 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
630 */
631static DECLCALLBACK(int) drvHostOssAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
632 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
633{
634 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
635 AssertPtrReturn(pStreamOSS, VERR_INVALID_POINTER);
636 RT_NOREF(pInterface);
637
638
639 size_t cbToRead = uBufSize;
640 LogFlowFunc(("cbToRead=%zi\n", cbToRead));
641
642 uint8_t * const pbDst = (uint8_t *)pvBuf;
643 size_t offWrite = 0;
644 while (cbToRead > 0)
645 {
646 ssize_t cbRead = read(pStreamOSS->hFile, &pbDst[offWrite], cbToRead);
647 if (cbRead)
648 {
649 LogFlowFunc(("cbRead=%zi, offWrite=%zu cbToRead=%zu\n", cbRead, offWrite, cbToRead));
650 Assert((ssize_t)cbToRead >= cbRead);
651 cbToRead -= cbRead;
652 offWrite += cbRead;
653 }
654 else
655 {
656 LogFunc(("cbRead=%zi, offWrite=%zu cbToRead=%zu errno=%d\n", cbRead, offWrite, cbToRead, errno));
657
658 /* Don't complain about errors if we've retrieved some audio data already. */
659 if (cbRead < 0 && offWrite == 0 && errno != EINTR && errno != EAGAIN)
660 {
661 AssertStmt(errno != 0, errno = EACCES);
662 int rc = RTErrConvertFromErrno(errno);
663 LogFunc(("Failed to read %zu input frames, errno=%d rc=%Rrc\n", cbToRead, errno, rc));
664 return rc;
665 }
666 break;
667 }
668 }
669
670 if (puRead)
671 *puRead = offWrite;
672 return VINF_SUCCESS;
673}
674
675
676
677/**
678 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
679 */
680static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
681{
682 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
683 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
684
685 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
686 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
687
688 return NULL;
689}
690
691/**
692 * Constructs an OSS audio driver instance.
693 *
694 * @copydoc FNPDMDRVCONSTRUCT
695 */
696static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
697{
698 RT_NOREF(pCfg, fFlags);
699 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
700 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
701 LogRel(("Audio: Initializing OSS driver\n"));
702
703 /*
704 * Init the static parts.
705 */
706 pThis->pDrvIns = pDrvIns;
707 /* IBase */
708 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
709 /* IHostAudio */
710 pThis->IHostAudio.pfnGetConfig = drvHostOssAudioHA_GetConfig;
711 pThis->IHostAudio.pfnGetStatus = drvHostOssAudioHA_GetStatus;
712 pThis->IHostAudio.pfnStreamCreate = drvHostOssAudioHA_StreamCreate;
713 pThis->IHostAudio.pfnStreamDestroy = drvHostOssAudioHA_StreamDestroy;
714 pThis->IHostAudio.pfnStreamControl = drvHostOssAudioHA_StreamControl;
715 pThis->IHostAudio.pfnStreamGetReadable = drvHostOssAudioHA_StreamGetReadable;
716 pThis->IHostAudio.pfnStreamGetWritable = drvHostOssAudioHA_StreamGetWritable;
717 pThis->IHostAudio.pfnStreamGetStatus = drvHostOssAudioHA_StreamGetStatus;
718 pThis->IHostAudio.pfnStreamPlay = drvHostOssAudioHA_StreamPlay;
719 pThis->IHostAudio.pfnStreamCapture = drvHostOssAudioHA_StreamCapture;
720 pThis->IHostAudio.pfnGetDevices = NULL;
721 pThis->IHostAudio.pfnStreamGetPending = NULL;
722
723 return VINF_SUCCESS;
724}
725
726/**
727 * Char driver registration record.
728 */
729const PDMDRVREG g_DrvHostOSSAudio =
730{
731 /* u32Version */
732 PDM_DRVREG_VERSION,
733 /* szName */
734 "OSSAudio",
735 /* szRCMod */
736 "",
737 /* szR0Mod */
738 "",
739 /* pszDescription */
740 "OSS audio host driver",
741 /* fFlags */
742 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
743 /* fClass. */
744 PDM_DRVREG_CLASS_AUDIO,
745 /* cMaxInstances */
746 ~0U,
747 /* cbInstance */
748 sizeof(DRVHOSTOSSAUDIO),
749 /* pfnConstruct */
750 drvHostOSSAudioConstruct,
751 /* pfnDestruct */
752 NULL,
753 /* pfnRelocate */
754 NULL,
755 /* pfnIOCtl */
756 NULL,
757 /* pfnPowerOn */
758 NULL,
759 /* pfnReset */
760 NULL,
761 /* pfnSuspend */
762 NULL,
763 /* pfnResume */
764 NULL,
765 /* pfnAttach */
766 NULL,
767 /* pfnDetach */
768 NULL,
769 /* pfnPowerOff */
770 NULL,
771 /* pfnSoftReset */
772 NULL,
773 /* u32EndVersion */
774 PDM_DRVREG_VERSION
775};
776
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