VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DevSB16.cpp@ 88858

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

Audio/SB16: Fixed audio when restoring from a saved state and output DMA was already running. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 118.7 KB
Line 
1/* $Id: DevSB16.cpp 88854 2021-05-04 09:04:51Z vboxsync $ */
2/** @file
3 * DevSB16 - VBox SB16 Audio Controller.
4 */
5
6/*
7 * Copyright (C) 2015-2021 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 * This code is based on: sb16.c from QEMU AUDIO subsystem (r3917).
19 * QEMU Soundblaster 16 emulation
20 *
21 * Copyright (c) 2003-2005 Vassili Karpov (malc)
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
36 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42
43/*********************************************************************************************************************************
44* Header Files *
45*********************************************************************************************************************************/
46#define LOG_GROUP LOG_GROUP_DEV_SB16
47#include <VBox/log.h>
48#include <iprt/assert.h>
49#include <iprt/file.h>
50#ifdef IN_RING3
51# include <iprt/mem.h>
52# include <iprt/string.h>
53# include <iprt/uuid.h>
54#endif
55
56#include <VBox/vmm/pdmdev.h>
57#include <VBox/vmm/pdmaudioifs.h>
58#include <VBox/vmm/pdmaudioinline.h>
59#include <VBox/AssertGuest.h>
60
61#include "VBoxDD.h"
62
63#include "AudioMixBuffer.h"
64#include "AudioMixer.h"
65#include "AudioHlp.h"
66
67
68/*********************************************************************************************************************************
69* Defined Constants And Macros *
70*********************************************************************************************************************************/
71/** Default timer frequency (in Hz). */
72#define SB16_TIMER_HZ_DEFAULT 100
73/** The maximum number of separate streams we currently implement.
74 * Currently we only support one stream only, namely the output stream. */
75#define SB16_MAX_STREAMS 1
76/** The (zero-based) index of the output stream in \a aStreams. */
77#define SB16_IDX_OUT 0
78
79/** Current saved state version. */
80#define SB16_SAVE_STATE_VERSION 2
81/** The version used in VirtualBox version 3.0 and earlier. This didn't include the config dump. */
82#define SB16_SAVE_STATE_VERSION_VBOX_30 1
83
84
85/*********************************************************************************************************************************
86* Global Variables *
87*********************************************************************************************************************************/
88static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
89
90
91
92/*********************************************************************************************************************************
93* Structures and Typedefs *
94*********************************************************************************************************************************/
95/** Pointer to the SB16 state. */
96typedef struct SB16STATE *PSB16STATE;
97
98#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
99/**
100 * Asynchronous I/O state for an SB16 stream.
101 */
102typedef struct SB16STREAMSTATEAIO
103{
104 PPDMTHREAD pThread;
105 /** Event for letting the thread know there is some data to process. */
106 SUPSEMEVENT hEvtProcess;
107 /** Critical section for synchronizing access. */
108 RTCRITSECT CritSect;
109 /** Started indicator. */
110 volatile bool fStarted;
111 /** Shutdown indicator. */
112 volatile bool fShutdown;
113 /** Whether the thread should do any data processing or not. */
114 volatile bool fEnabled;
115 bool afPadding[5];
116} SB16STREAMSTATEAIO;
117/** Pointer to the async I/O state for a SB16 stream. */
118typedef SB16STREAMSTATEAIO *PSB16STREAMSTATEAIO;
119#endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */
120
121/**
122 * The internal state of a SB16 stream.
123 */
124typedef struct SB16STREAMSTATE
125{
126 /** Flag indicating whether this stream is in enabled state or not. */
127 bool fEnabled;
128#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
129 /** Asynchronous I/O state members. */
130 SB16STREAMSTATEAIO AIO;
131#endif
132 /** DMA cache to read data from / write data to. */
133 PRTCIRCBUF pCircBuf;
134} SB16STREAMSTATE;
135/** Pointer to internal state of an SB16 stream. */
136typedef SB16STREAMSTATE *PSB16STREAMSTATE;
137
138/**
139 * Structure defining a (host backend) driver stream.
140 * Each driver has its own instances of audio mixer streams, which then
141 * can go into the same (or even different) audio mixer sinks.
142 */
143typedef struct SB16DRIVERSTREAM
144{
145 /** Associated mixer stream handle. */
146 R3PTRTYPE(PAUDMIXSTREAM) pMixStrm;
147 /** The stream's current configuration. */
148} SB16DRIVERSTREAM, *PSB16DRIVERSTREAM;
149
150/**
151 * Struct for tracking a host backend driver, i.e. our per-LUN data.
152 */
153typedef struct SB16DRIVER
154{
155 /** Node for storing this driver in our device driver list of SB16STATE. */
156 RTLISTNODER3 Node;
157 /** Pointer to SB16 controller (state). */
158 R3PTRTYPE(PSB16STATE) pSB16State;
159 /** Pointer to attached driver base interface. */
160 R3PTRTYPE(PPDMIBASE) pDrvBase;
161 /** Audio connector interface to the underlying host backend. */
162 R3PTRTYPE(PPDMIAUDIOCONNECTOR) pConnector;
163 /** Stream for output. */
164 SB16DRIVERSTREAM Out;
165 /** Driver flags. */
166 PDMAUDIODRVFLAGS fFlags;
167 /** LUN # to which this driver has been assigned. */
168 uint8_t uLUN;
169 /** Whether this driver is in an attached state or not. */
170 bool fAttached;
171 /** The LUN description. */
172 char szDesc[2+48];
173} SB16DRIVER;
174/** Pointer to the per-LUN data. */
175typedef SB16DRIVER *PSB16DRIVER;
176
177/**
178 * Runtime configurable debug stuff for a SB16 stream.
179 */
180typedef struct SB16STREAMDEBUGRT
181{
182 /** Whether debugging is enabled or not. */
183 bool fEnabled;
184 uint8_t Padding[7];
185 /** File for dumping DMA reads / writes.
186 * For input streams, this dumps data being written to the device DMA,
187 * whereas for output streams this dumps data being read from the device DMA. */
188 R3PTRTYPE(PAUDIOHLPFILE) pFileDMA;
189} SB16STREAMDEBUGRT;
190
191/**
192 * Debug stuff for a SB16 stream.
193 */
194typedef struct SB16STREAMDEBUG
195{
196 /** Runtime debug stuff. */
197 SB16STREAMDEBUGRT Runtime;
198} SB16STREAMDEBUG;
199
200/**
201 * Structure for keeping a SB16 hardware stream configuration.
202 */
203typedef struct SB16STREAMHWCFG
204{
205 /** IRQ # to use. */
206 uint8_t uIrq;
207 /** Low DMA channel to use. */
208 uint8_t uDmaChanLow;
209 /** High DMA channel to use. */
210 uint8_t uDmaChanHigh;
211 /** IO port to use. */
212 RTIOPORT uPort;
213 /** DSP version to expose. */
214 uint16_t uVer;
215} SB16STREAMHWCFG;
216
217/**
218 * Structure for a SB16 stream.
219 */
220typedef struct SB16STREAM
221{
222 /** The stream's own index in \a aStreams of SB16STATE.
223 * Set to UINT8_MAX if not set (yet). */
224 uint8_t uIdx;
225 uint16_t uTimerHz;
226 /** The timer for pumping data thru the attached LUN drivers. */
227 TMTIMERHANDLE hTimerIO;
228 /** The timer interval for pumping data thru the LUN drivers in timer ticks. */
229 uint64_t cTicksTimerIOInterval;
230 /** Timestamp of the last timer callback (sb16TimerIO).
231 * Used to calculate thetime actually elapsed between two timer callbacks.
232 * This currently ASSMUMES that we only have one single (output) stream. */
233 uint64_t tsTimerIO; /** @todo Make this a per-stream value. */
234 /** The stream's currentconfiguration. */
235 PDMAUDIOSTREAMCFG Cfg;
236 /** The stream's defaulthardware configuration, mostly done by jumper settings back then. */
237 SB16STREAMHWCFG HwCfgDefault;
238 /** The stream's hardware configuration set at runtime.
239 * Might differ from the default configuration above and is needed for live migration. */
240 SB16STREAMHWCFG HwCfgRuntime;
241
242 int fifo;
243 int dma_auto;
244 /** Whether to use the high (\c true) or the low (\c false) DMA channel. */
245 int fDmaUseHigh;
246 int can_write; /** @todo r=andy BUGBUG Value never gets set to 0! */
247 int time_const;
248 /** The DMA transfer (block)size in bytes. */
249 int32_t cbDmaBlockSize;
250 int32_t cbDmaLeft; /** Note: Can be < 0. Needs to 32-bit for backwards compatibility. */
251 /** Internal state of this stream. */
252 SB16STREAMSTATE State;
253 /** Debug stuff. */
254 SB16STREAMDEBUG Dbg;
255} SB16STREAM;
256/** Pointer to a SB16 stream */
257typedef SB16STREAM *PSB16STREAM;
258
259#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
260/**
261 * Asynchronous I/O thread context (arguments).
262 */
263typedef struct SB16STREAMTHREADCTX
264{
265 /** The SB16 device state. */
266 PSB16STATE pThis;
267 /** The SB16 stream state. */
268 PSB16STREAM pStream;
269} SB16STREAMTHREADCTX;
270/** Pointer to the context for an async I/O thread. */
271typedef SB16STREAMTHREADCTX *PSB16STREAMTHREADCTX;
272#endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */
273
274/**
275 * SB16 debug settings.
276 */
277typedef struct SB16STATEDEBUG
278{
279 /** Whether debugging is enabled or not. */
280 bool fEnabled;
281 bool afAlignment[7];
282 /** Path where to dump the debug output to.
283 * Can be NULL, in which the system's temporary directory will be used then. */
284 R3PTRTYPE(char *) pszOutPath;
285} SB16STATEDEBUG;
286
287/**
288 * The SB16 state.
289 */
290typedef struct SB16STATE
291{
292 /** Pointer to the device instance. */
293 PPDMDEVINSR3 pDevInsR3;
294 /** Pointer to the connector of the attached audio driver. */
295 PPDMIAUDIOCONNECTOR pDrv;
296
297 int dsp_in_idx;
298 int dsp_out_data_len;
299 int dsp_in_needed_bytes;
300 int cmd;
301 int highspeed;
302
303 int v2x6;
304
305 uint8_t csp_param;
306 uint8_t csp_value;
307 uint8_t csp_mode;
308 uint8_t csp_index;
309 uint8_t csp_regs[256];
310 uint8_t csp_reg83[4];
311 int csp_reg83r;
312 int csp_reg83w;
313
314 uint8_t dsp_in_data[10];
315 uint8_t dsp_out_data[50];
316 uint8_t test_reg;
317 uint8_t last_read_byte;
318 int nzero;
319
320 RTLISTANCHOR lstDrv;
321 /** IRQ timer */
322 TMTIMERHANDLE hTimerIRQ;
323 /** The base interface for LUN\#0. */
324 PDMIBASE IBase;
325
326 /** Array of all SB16 hardware audio stream. */
327 SB16STREAM aStreams[SB16_MAX_STREAMS];
328 /** The device's software mixer. */
329 R3PTRTYPE(PAUDIOMIXER) pMixer;
330 /** Audio sink for PCM output. */
331 R3PTRTYPE(PAUDMIXSINK) pSinkOut;
332
333 /** The two mixer I/O ports (port + 4). */
334 IOMIOPORTHANDLE hIoPortsMixer;
335 /** The 10 DSP I/O ports (port + 6). */
336 IOMIOPORTHANDLE hIoPortsDsp;
337
338 /** Debug settings. */
339 SB16STATEDEBUG Dbg;
340
341 /* mixer state */
342 uint8_t mixer_nreg;
343 uint8_t mixer_regs[256];
344
345#ifdef VBOX_WITH_STATISTICS
346 STAMPROFILE StatTimerIO;
347 STAMCOUNTER StatBytesRead;
348#endif
349} SB16STATE;
350
351
352/*********************************************************************************************************************************
353* Internal Functions *
354*********************************************************************************************************************************/
355DECLINLINE(PDMAUDIODIR) sb16GetDirFromIndex(uint8_t uIdx);
356
357static int sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable, bool fForce);
358static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream);
359static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
360static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
361DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx);
362static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cSamples);
363static void sb16StreamUpdate(PSB16STREAM pStream, PAUDMIXSINK pSink);
364static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDmaChan, uint32_t offDma, uint32_t cbDma, uint32_t cbToRead, uint32_t *pcbRead);
365
366static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser);
367static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser);
368DECLINLINE(void) sb16TimerSet(PPDMDEVINS pDevIns, PSB16STREAM pStream, uint64_t cTicksToDeadline);
369
370#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
371static int sb16StreamAsyncIOCreate(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
372static int sb16StreamAsyncIODestroy(PPDMDEVINS pDevIns, PSB16STREAM pStream);
373static int sb16StreamAsyncIONotify(PPDMDEVINS pDevIns, PSB16STREAM pStream);
374#endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */
375
376static void sb16SpeakerControl(PSB16STATE pThis, bool fOn);
377static void sb16UpdateVolume(PSB16STATE pThis);
378
379
380#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
381/**
382 * @callback_method_impl{FNPDMTHREADDEV}
383 */
384static DECLCALLBACK(int) sb16StreamAsyncIOThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
385{
386 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
387 return VINF_SUCCESS;
388
389 PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)pThread->pvUser;
390 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
391
392 PSB16STATE pThis = pCtx->pThis;
393 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
394
395 PSB16STREAM pStream = pCtx->pStream;
396 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
397
398 AssertReturn(pThread == pStream->State.AIO.pThread, VERR_INVALID_PARAMETER);
399
400 PAUDMIXSINK pSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
401 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
402
403 while ( pThread->enmState != PDMTHREADSTATE_TERMINATING
404 && pThread->enmState != PDMTHREADSTATE_TERMINATED)
405 {
406 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pStream->State.AIO.hEvtProcess, RT_INDEFINITE_WAIT);
407 if (pStream->State.AIO.fShutdown)
408 break;
409 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
410 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
411 return VINF_SUCCESS;
412 if (rc == VERR_INTERRUPTED)
413 continue;
414
415 sb16StreamUpdate(pStream, pSink);
416 }
417
418 return VINF_SUCCESS;
419}
420
421/**
422 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
423 */
424static DECLCALLBACK(int) sb16StreamAsyncIOWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
425{
426 PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)pThread->pvUser;
427 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
428
429 PSB16STREAM pStream = pCtx->pStream;
430 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
431
432 sb16StreamAsyncIONotify(pDevIns, pStream);
433
434 return VINF_SUCCESS;
435}
436
437/**
438 * Creates the async I/O thread for a specific SB16 audio stream.
439 *
440 * @returns VBox status code.
441 * @param pDevIns The device instance.
442 * @param pThis The shared SB16 state.
443 * @param pStream SB16 audio stream to create the async I/O thread for.
444 */
445static int sb16StreamAsyncIOCreate(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
446{
447 PSB16STREAMSTATEAIO pAIO = &pStream->State.AIO;
448
449 int rc;
450
451 if (!ASMAtomicReadBool(&pAIO->fStarted))
452 {
453 pAIO->fShutdown = false;
454 pAIO->fEnabled = true; /* Enabled by default. */
455
456 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pAIO->hEvtProcess);
457 if (RT_SUCCESS(rc))
458 {
459 rc = RTCritSectInit(&pAIO->CritSect);
460 if (RT_SUCCESS(rc))
461 {
462 char szDevTag[16]; /* see RTTHREAD_NAME_LEN. */
463 RTStrPrintf(szDevTag, sizeof(szDevTag), "SB16%RU32-%RU8", pDevIns->iInstance, pStream->uIdx);
464
465 PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)RTMemAllocZ(sizeof(SB16STREAMTHREADCTX));
466 if (pCtx)
467 {
468 pCtx->pStream = pStream;
469 pCtx->pThis = pThis;
470
471 rc = PDMDevHlpThreadCreate(pDevIns, &pAIO->pThread, pCtx, sb16StreamAsyncIOThread,
472 sb16StreamAsyncIOWakeUp, 0, RTTHREADTYPE_IO, szDevTag);
473 if (RT_FAILURE(rc))
474 RTMemFree(pCtx);
475 }
476 else
477 rc = VERR_NO_MEMORY;
478 }
479 }
480 }
481 else
482 rc = VINF_SUCCESS;
483
484 return rc;
485}
486
487/**
488 * Lets the stream's async I/O thread know that there is some data to process.
489 *
490 * @returns VBox status code.
491 * @param pDevIns The device instance.
492 * @param pStream The SB16 stream to notify async I/O thread.
493 */
494static int sb16StreamAsyncIONotify(PPDMDEVINS pDevIns, PSB16STREAM pStream)
495{
496 return PDMDevHlpSUPSemEventSignal(pDevIns, pStream->State.AIO.hEvtProcess);
497}
498
499/**
500 * Destroys the async I/O thread of a specific SB16 audio stream.
501 *
502 * @returns VBox status code.
503 * @param pDevIns The device instance.
504 * @param pStream SB16 audio stream to destroy the async I/O thread for.
505 */
506static int sb16StreamAsyncIODestroy(PPDMDEVINS pDevIns, PSB16STREAM pStream)
507{
508 PSB16STREAMSTATEAIO pAIO = &pStream->State.AIO;
509
510 if (!ASMAtomicReadBool(&pAIO->fStarted))
511 return VINF_SUCCESS;
512
513 ASMAtomicWriteBool(&pAIO->fShutdown, true);
514
515 int rc = sb16StreamAsyncIONotify(pDevIns, pStream);
516 AssertRC(rc);
517
518 rc = PDMDevHlpThreadDestroy(pDevIns, pAIO->pThread, NULL);
519 AssertRC(rc);
520
521 rc = RTCritSectDelete(&pAIO->CritSect);
522 AssertRC(rc);
523
524 if (pStream->State.AIO.hEvtProcess != NIL_SUPSEMEVENT)
525 {
526 PDMDevHlpSUPSemEventSignal(pDevIns, pStream->State.AIO.hEvtProcess);
527 PDMDevHlpSUPSemEventClose(pDevIns, pStream->State.AIO.hEvtProcess);
528 pStream->State.AIO.hEvtProcess = NIL_SUPSEMEVENT;
529 }
530
531 pAIO->fShutdown = false;
532 return rc;
533}
534#endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */
535
536static void sb16SpeakerControl(PSB16STATE pThis, bool fOn)
537{
538 RT_NOREF(pThis, fOn);
539
540 /** @todo This currently does nothing. */
541}
542
543static void sb16StreamControl(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream, bool fRun)
544{
545 unsigned uDmaChan = pStream->fDmaUseHigh ? pStream->HwCfgRuntime.uDmaChanHigh : pStream->HwCfgRuntime.uDmaChanLow;
546
547 LogFunc(("fRun=%RTbool, fDmaUseHigh=%RTbool, uDmaChan=%u\n", fRun, pStream->fDmaUseHigh, uDmaChan));
548
549 PDMDevHlpDMASetDREQ(pThis->pDevInsR3, uDmaChan, fRun ? 1 : 0);
550
551 if (fRun != pStream->State.fEnabled)
552 {
553 if (fRun)
554 {
555 int rc = VINF_SUCCESS;
556
557 if (pStream->Cfg.Props.uHz > 0)
558 {
559 rc = sb16StreamOpen(pDevIns, pThis, pStream);
560 if (RT_SUCCESS(rc))
561 sb16UpdateVolume(pThis);
562 }
563 else
564 AssertFailed(); /** @todo Buggy code? */
565
566 if (RT_SUCCESS(rc))
567 {
568 rc = sb16StreamEnable(pThis, pStream, true /* fEnable */, false /* fForce */);
569 if (RT_SUCCESS(rc))
570 {
571 sb16TimerSet(pDevIns, pStream, pStream->cTicksTimerIOInterval);
572
573 PDMDevHlpDMASchedule(pThis->pDevInsR3);
574 }
575 }
576 }
577 else
578 {
579 sb16StreamEnable(pThis, pStream, false /* fEnable */, false /* fForce */);
580 }
581 }
582}
583
584#define DMA8_AUTO 1
585#define DMA8_HIGH 2
586
587static void sb16DmaCmdContinue8(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
588{
589 sb16StreamControl(pDevIns, pThis, pStream, true /* fRun */);
590}
591
592static void sb16DmaCmd8(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream,
593 int mask, int dma_len)
594{
595 pStream->fDmaUseHigh = 0;
596
597 if (-1 == pStream->time_const)
598 {
599 if (pStream->Cfg.Props.uHz == 0)
600 pStream->Cfg.Props.uHz = 11025;
601 }
602 else
603 {
604 int tmp = (256 - pStream->time_const);
605 pStream->Cfg.Props.uHz = (1000000 + (tmp / 2)) / tmp;
606 }
607
608 unsigned cShiftChannels = pStream->Cfg.Props.cChannelsX >= 2 ? 1 : 0;
609
610 if (dma_len != -1)
611 {
612 pStream->cbDmaBlockSize = dma_len << cShiftChannels;
613 }
614 else
615 {
616 /* This is apparently the only way to make both Act1/PL
617 and SecondReality/FC work
618
619 r=andy Wow, actually someone who remembers Future Crew :-)
620
621 Act1 sets block size via command 0x48 and it's an odd number
622 SR does the same with even number
623 Both use stereo, and Creatives own documentation states that
624 0x48 sets block size in bytes less one.. go figure */
625 pStream->cbDmaBlockSize &= ~cShiftChannels;
626 }
627
628 pStream->Cfg.Props.uHz >>= cShiftChannels;
629 pStream->cbDmaLeft = pStream->cbDmaBlockSize;
630 /* pThis->highspeed = (mask & DMA8_HIGH) != 0; */
631 pStream->dma_auto = (mask & DMA8_AUTO) != 0;
632
633 PDMAudioPropsInit(&pStream->Cfg.Props, 1 /* 8-bit */,
634 false /* fSigned */,
635 (pThis->mixer_regs[0x0e] & 2) == 0 ? 1 : 2 /* Mono/Stereo */,
636 pStream->Cfg.Props.uHz);
637
638 /** @todo Check if stream's DMA block size is properly aligned to the set PCM props. */
639
640 sb16DmaCmdContinue8(pDevIns, pThis, pStream);
641 sb16SpeakerControl(pThis, 1);
642}
643
644static void sb16DmaCmd(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream,
645 uint8_t cmd, uint8_t d0, int dma_len)
646{
647 pStream->fDmaUseHigh = cmd < 0xc0;
648 pStream->fifo = (cmd >> 1) & 1;
649 pStream->dma_auto = (cmd >> 2) & 1;
650
651 pStream->Cfg.Props.fSigned = RT_BOOL((d0 >> 4) & 1); /** @todo Use RT_BIT? */
652 pStream->Cfg.Props.cChannelsX = (d0 >> 5) & 1 ? 2 : 1;
653
654 switch (cmd >> 4)
655 {
656 case 11:
657 pStream->Cfg.Props.cbSampleX = 2 /* 16 bit */;
658 break;
659
660 case 12:
661 pStream->Cfg.Props.cbSampleX = 1 /* 8 bit */;
662 break;
663
664 default:
665 AssertFailed();
666 break;
667 }
668
669 if (-1 != pStream->time_const)
670 {
671#if 1
672 int tmp = 256 - pStream->time_const;
673 pStream->Cfg.Props.uHz = (1000000 + (tmp / 2)) / tmp;
674#else
675 /* pThis->freq = 1000000 / ((255 - pStream->time_const) << pThis->fmt_stereo); */
676 pThis->freq = 1000000 / ((255 - pStream->time_const));
677#endif
678 pStream->time_const = -1;
679 }
680
681 pStream->cbDmaBlockSize = dma_len + 1;
682 pStream->cbDmaBlockSize <<= ((pStream->Cfg.Props.cbSampleX == 2) ? 1 : 0);
683 if (!pStream->dma_auto)
684 {
685 /*
686 * It is clear that for DOOM and auto-init this value
687 * shouldn't take stereo into account, while Miles Sound Systems
688 * setsound.exe with single transfer mode wouldn't work without it
689 * wonders of SB16 yet again.
690 */
691 pStream->cbDmaBlockSize <<= pStream->Cfg.Props.cChannelsX == 2 ? 1 : 0;
692 }
693
694 pStream->cbDmaLeft = pStream->cbDmaBlockSize;
695
696 pThis->highspeed = 0;
697
698 /** @todo Check if stream's DMA block size is properly aligned to the set PCM props. */
699
700 sb16StreamControl(pDevIns, pThis, pStream, true /* fRun */);
701 sb16SpeakerControl(pThis, 1);
702}
703
704static inline void sb16DspSeData(PSB16STATE pThis, uint8_t val)
705{
706 LogFlowFunc(("%#x\n", val));
707 if ((size_t) pThis->dsp_out_data_len < sizeof (pThis->dsp_out_data))
708 pThis->dsp_out_data[pThis->dsp_out_data_len++] = val;
709}
710
711static inline uint8_t sb16DspGetData(PSB16STATE pThis)
712{
713 if (pThis->dsp_in_idx)
714 return pThis->dsp_in_data[--pThis->dsp_in_idx];
715 AssertMsgFailed(("DSP input buffer underflow\n"));
716 return 0;
717}
718
719static void sb16DspCmdLookup(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream, uint8_t cmd)
720{
721 LogFlowFunc(("command %#x\n", cmd));
722
723 if (cmd > 0xaf && cmd < 0xd0)
724 {
725 if (cmd & 8) /** @todo Handle recording. */
726 LogFlowFunc(("ADC not yet supported (command %#x)\n", cmd));
727
728 switch (cmd >> 4)
729 {
730 case 11:
731 case 12:
732 break;
733 default:
734 LogFlowFunc(("%#x wrong bits\n", cmd));
735 }
736
737 pThis->dsp_in_needed_bytes = 3;
738 }
739 else
740 {
741 pThis->dsp_in_needed_bytes = 0;
742
743 /** @todo Use a mapping table with
744 * - a command verb (binary search)
745 * - required bytes
746 * - function callback handler
747 */
748
749 switch (cmd)
750 {
751 case 0x03: /* ASP Status */
752 sb16DspSeData(pThis, 0x10); /* pThis->csp_param); */
753 goto warn;
754
755 case 0x04: /* DSP Status (Obsolete) / ASP ??? */
756 pThis->dsp_in_needed_bytes = 1;
757 goto warn;
758
759 case 0x05: /* ASP ??? */
760 pThis->dsp_in_needed_bytes = 2;
761 goto warn;
762
763 case 0x08: /* ??? */
764 /* __asm__ ("int3"); */
765 goto warn;
766
767 case 0x09: /* ??? */
768 sb16DspSeData(pThis, 0xf8);
769 goto warn;
770
771 case 0x0e: /* ??? */
772 pThis->dsp_in_needed_bytes = 2;
773 goto warn;
774
775 case 0x0f: /* ??? */
776 pThis->dsp_in_needed_bytes = 1;
777 goto warn;
778
779 case 0x10: /* Direct mode DAC */
780 pThis->dsp_in_needed_bytes = 1;
781 goto warn;
782
783 case 0x14: /* DAC DMA, 8-bit, uncompressed */
784 pThis->dsp_in_needed_bytes = 2;
785 pStream->cbDmaBlockSize = 0;
786 break;
787
788 case 0x1c: /* Auto-Initialize DMA DAC, 8-bit */
789 sb16DmaCmd8(pDevIns, pThis, pStream, DMA8_AUTO, -1);
790 break;
791
792 case 0x20: /* Direct ADC, Juice/PL */
793 sb16DspSeData(pThis, 0xff);
794 goto warn;
795
796 case 0x35: /* MIDI Read Interrupt + Write Poll (UART) */
797 LogRelMax2(32, ("SB16: MIDI support not implemented yet\n"));
798 break;
799
800 case 0x40: /* Set Time Constant */
801 pStream->time_const = -1;
802 pThis->dsp_in_needed_bytes = 1;
803 break;
804
805 case 0x41: /* Set sample rate for input */
806 pStream->Cfg.Props.uHz = 0; /** @todo r=andy Why do we reset output stuff here? */
807 pStream->time_const = -1;
808 pThis->dsp_in_needed_bytes = 2;
809 break;
810
811 case 0x42: /* Set sample rate for output */
812 pStream->Cfg.Props.uHz = 0;
813 pStream->time_const = -1;
814 pThis->dsp_in_needed_bytes = 2;
815 goto warn;
816
817 case 0x45: /* Continue Auto-Initialize DMA, 8-bit */
818 sb16DspSeData(pThis, 0xaa);
819 goto warn;
820
821 case 0x47: /* Continue Auto-Initialize DMA, 16-bit */
822 break;
823
824 case 0x48: /* Set DMA Block Size */
825 pThis->dsp_in_needed_bytes = 2;
826 break;
827
828 case 0x74: /* DMA DAC, 4-bit ADPCM */
829 pThis->dsp_in_needed_bytes = 2;
830 LogFlowFunc(("4-bit ADPCM not implemented yet\n"));
831 break;
832
833 case 0x75: /* DMA DAC, 4-bit ADPCM Reference */
834 pThis->dsp_in_needed_bytes = 2;
835 LogFlowFunc(("DMA DAC, 4-bit ADPCM Reference not implemented\n"));
836 break;
837
838 case 0x76: /* DMA DAC, 2.6-bit ADPCM */
839 pThis->dsp_in_needed_bytes = 2;
840 LogFlowFunc(("DMA DAC, 2.6-bit ADPCM not implemented yet\n"));
841 break;
842
843 case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */
844 pThis->dsp_in_needed_bytes = 2;
845 LogFlowFunc(("ADPCM reference not implemented yet\n"));
846 break;
847
848 case 0x7d: /* Auto-Initialize DMA DAC, 4-bit ADPCM Reference */
849 LogFlowFunc(("Autio-Initialize DMA DAC, 4-bit ADPCM reference not implemented yet\n"));
850 break;
851
852 case 0x7f: /* Auto-Initialize DMA DAC, 16-bit ADPCM Reference */
853 LogFlowFunc(("Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference not implemented yet\n"));
854 break;
855
856 case 0x80: /* Silence DAC */
857 pThis->dsp_in_needed_bytes = 2;
858 break;
859
860 case 0x90: /* Auto-Initialize DMA DAC, 8-bit (High Speed) */
861 RT_FALL_THROUGH();
862 case 0x91: /* Normal DMA DAC, 8-bit (High Speed) */
863 sb16DmaCmd8(pDevIns, pThis, pStream, (((cmd & 1) == 0) ? 1 : 0) | DMA8_HIGH, -1);
864 break;
865
866 case 0xd0: /* Halt DMA operation. 8bit */
867 sb16StreamControl(pDevIns, pThis, pStream, false /* fRun */);
868 break;
869
870 case 0xd1: /* Speaker on */
871 sb16SpeakerControl(pThis, true /* fOn */);
872 break;
873
874 case 0xd3: /* Speaker off */
875 sb16SpeakerControl(pThis, false /* fOn */);
876 break;
877
878 case 0xd4: /* Continue DMA operation, 8-bit */
879 /* KQ6 (or maybe Sierras audblst.drv in general) resets
880 the frequency between halt/continue */
881 sb16DmaCmdContinue8(pDevIns, pThis, pStream);
882 break;
883
884 case 0xd5: /* Halt DMA operation, 16-bit */
885 sb16StreamControl(pDevIns, pThis, pStream, false /* fRun */);
886 break;
887
888 case 0xd6: /* Continue DMA operation, 16-bit */
889 sb16StreamControl(pDevIns, pThis, pStream, true /* fRun */);
890 break;
891
892 case 0xd9: /* Exit auto-init DMA after this block, 16-bit */
893 pStream->dma_auto = 0;
894 break;
895
896 case 0xda: /* Exit auto-init DMA after this block, 8-bit */
897 pStream->dma_auto = 0;
898 break;
899
900 case 0xe0: /* DSP identification */
901 pThis->dsp_in_needed_bytes = 1;
902 break;
903
904 case 0xe1: /* DSP version */
905 sb16DspSeData(pThis, RT_LO_U8(pStream->HwCfgRuntime.uVer));
906 sb16DspSeData(pThis, RT_HI_U8(pStream->HwCfgRuntime.uVer));
907 break;
908
909 case 0xe2: /* ??? */
910 pThis->dsp_in_needed_bytes = 1;
911 goto warn;
912
913 case 0xe3: /* DSP copyright */
914 {
915 for (int i = sizeof(e3) - 1; i >= 0; --i)
916 sb16DspSeData(pThis, e3[i]);
917 break;
918 }
919
920 case 0xe4: /* Write test register */
921 pThis->dsp_in_needed_bytes = 1;
922 break;
923
924 case 0xe7: /* ??? */
925 LogFlowFunc(("Attempt to probe for ESS (0xe7)?\n"));
926 break;
927
928 case 0xe8: /* Read test register */
929 sb16DspSeData(pThis, pThis->test_reg);
930 break;
931
932 case 0xf2: /* IRQ Request, 8-bit */
933 RT_FALL_THROUGH();
934 case 0xf3: /* IRQ Request, 16-bit */
935 {
936 sb16DspSeData(pThis, 0xaa);
937 pThis->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
938 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 1);
939 break;
940 }
941
942 case 0xf8: /* Undocumented, used by old Creative diagnostic programs */
943 sb16DspSeData(pThis, 0);
944 goto warn;
945
946 case 0xf9: /* ??? */
947 pThis->dsp_in_needed_bytes = 1;
948 goto warn;
949
950 case 0xfa: /* ??? */
951 sb16DspSeData(pThis, 0);
952 goto warn;
953
954 case 0xfc: /* ??? */
955 sb16DspSeData(pThis, 0);
956 goto warn;
957
958 default:
959 LogFunc(("Unrecognized DSP command %#x, ignored\n", cmd));
960 break;
961 }
962 }
963
964exit:
965
966 if (!pThis->dsp_in_needed_bytes)
967 pThis->cmd = -1;
968 else
969 pThis->cmd = cmd;
970
971 return;
972
973warn:
974 LogFunc(("warning: command %#x,%d is not truly understood yet\n", cmd, pThis->dsp_in_needed_bytes));
975 goto exit;
976}
977
978DECLINLINE(uint16_t) sb16DspGetLoHi(PSB16STATE pThis)
979{
980 const uint8_t hi = sb16DspGetData(pThis);
981 const uint8_t lo = sb16DspGetData(pThis);
982 return RT_MAKE_U16(lo, hi);
983}
984
985DECLINLINE(uint16_t) sb16DspGetHiLo(PSB16STATE pThis)
986{
987 const uint8_t lo = sb16DspGetData(pThis);
988 const uint8_t hi = sb16DspGetData(pThis);
989 return RT_MAKE_U16(lo, hi);
990}
991
992static void sb16DspCmdComplete(PPDMDEVINS pDevIns, PSB16STATE pThis)
993{
994 LogFlowFunc(("Command %#x, in_index %d, needed_bytes %d\n", pThis->cmd, pThis->dsp_in_idx, pThis->dsp_in_needed_bytes));
995
996 int v0, v1, v2;
997
998 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT]; /** @ŧodo Improve this. */
999
1000 if (pThis->cmd > 0xaf && pThis->cmd < 0xd0)
1001 {
1002 v2 = sb16DspGetData(pThis);
1003 v1 = sb16DspGetData(pThis);
1004 v0 = sb16DspGetData(pThis);
1005
1006 if (pThis->cmd & 8)
1007 LogFlowFunc(("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, v0, v1, v2));
1008 else
1009 {
1010 LogFlowFunc(("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, v0, v1, v2));
1011 sb16DmaCmd(pDevIns, pThis, pStream, pThis->cmd, v0, v1 + (v2 << 8));
1012 }
1013 }
1014 else
1015 {
1016 switch (pThis->cmd)
1017 {
1018 case 0x04:
1019 {
1020 pThis->csp_mode = sb16DspGetData(pThis);
1021 pThis->csp_reg83r = 0;
1022 pThis->csp_reg83w = 0;
1023 LogFlowFunc(("CSP command 0x04: mode=%#x\n", pThis->csp_mode));
1024 break;
1025 }
1026
1027 case 0x05:
1028 {
1029 pThis->csp_param = sb16DspGetData(pThis);
1030 pThis->csp_value = sb16DspGetData(pThis);
1031 LogFlowFunc(("CSP command 0x05: param=%#x value=%#x\n", pThis->csp_param, pThis->csp_value));
1032 break;
1033 }
1034
1035 case 0x0e:
1036 {
1037 v0 = sb16DspGetData(pThis);
1038 v1 = sb16DspGetData(pThis);
1039 LogFlowFunc(("write CSP register %d <- %#x\n", v1, v0));
1040 if (v1 == 0x83)
1041 {
1042 LogFlowFunc(("0x83[%d] <- %#x\n", pThis->csp_reg83r, v0));
1043 pThis->csp_reg83[pThis->csp_reg83r % 4] = v0;
1044 pThis->csp_reg83r += 1;
1045 }
1046 else
1047 pThis->csp_regs[v1] = v0;
1048 break;
1049 }
1050
1051 case 0x0f:
1052 {
1053 v0 = sb16DspGetData(pThis);
1054 LogFlowFunc(("read CSP register %#x -> %#x, mode=%#x\n", v0, pThis->csp_regs[v0], pThis->csp_mode));
1055 if (v0 == 0x83)
1056 {
1057 LogFlowFunc(("0x83[%d] -> %#x\n", pThis->csp_reg83w, pThis->csp_reg83[pThis->csp_reg83w % 4]));
1058 sb16DspSeData(pThis, pThis->csp_reg83[pThis->csp_reg83w % 4]);
1059 pThis->csp_reg83w += 1;
1060 }
1061 else
1062 sb16DspSeData(pThis, pThis->csp_regs[v0]);
1063 break;
1064 }
1065
1066 case 0x10:
1067 v0 = sb16DspGetData(pThis);
1068 LogFlowFunc(("cmd 0x10 d0=%#x\n", v0));
1069 break;
1070
1071 case 0x14:
1072 sb16DmaCmd8(pDevIns, pThis, pStream, 0, sb16DspGetLoHi(pThis) + 1);
1073 break;
1074
1075 case 0x22: /* Sets the master volume. */
1076 /** @todo Setting the master volume is not implemented yet. */
1077 break;
1078
1079 case 0x40: /* Sets the timer constant; SB16 is able to use sample rates via 0x41 instead. */
1080 pStream->time_const = sb16DspGetData(pThis);
1081 LogFlowFunc(("set time const %d\n", pStream->time_const));
1082 break;
1083
1084 case 0x42: /* Sets the input rate (in Hz). */
1085#if 0
1086 LogFlowFunc(("cmd 0x42 might not do what it think it should\n"));
1087#endif
1088 RT_FALL_THROUGH(); /** @todo BUGBUG FT2 sets output freq with this, go figure. */
1089
1090 case 0x41: /* Sets the output rate (in Hz). */
1091 {
1092 pStream->Cfg.Props.uHz = sb16DspGetHiLo(pThis);
1093 LogFlowFunc(("set freq to %RU16Hz\n", pStream->Cfg.Props.uHz));
1094 break;
1095 }
1096
1097 case 0x48:
1098 {
1099 pStream->cbDmaBlockSize = sb16DspGetLoHi(pThis) + 1;
1100 LogFlowFunc(("set dma block len %d\n", pStream->cbDmaBlockSize));
1101 break;
1102 }
1103
1104 case 0x74:
1105 RT_FALL_THROUGH();
1106 case 0x75:
1107 RT_FALL_THROUGH();
1108 case 0x76:
1109 RT_FALL_THROUGH();
1110 case 0x77:
1111 /* ADPCM stuff, ignore. */
1112 break;
1113
1114 case 0x80: /* Sets the IRQ. */
1115 {
1116 sb16StreamTransferScheduleNext(pThis, pStream, sb16DspGetLoHi(pThis) + 1);
1117 break;
1118 }
1119
1120 case 0xe0:
1121 {
1122 v0 = sb16DspGetData(pThis);
1123 pThis->dsp_out_data_len = 0;
1124 LogFlowFunc(("E0=%#x\n", v0));
1125 sb16DspSeData(pThis, ~v0);
1126 break;
1127 }
1128
1129 case 0xe2:
1130 {
1131 v0 = sb16DspGetData(pThis);
1132 LogFlowFunc(("E2=%#x\n", v0));
1133 break;
1134 }
1135
1136 case 0xe4:
1137 pThis->test_reg = sb16DspGetData(pThis);
1138 break;
1139
1140 case 0xf9:
1141 v0 = sb16DspGetData(pThis);
1142 switch (v0)
1143 {
1144 case 0x0e:
1145 sb16DspSeData(pThis, 0xff);
1146 break;
1147
1148 case 0x0f:
1149 sb16DspSeData(pThis, 0x07);
1150 break;
1151
1152 case 0x37:
1153 sb16DspSeData(pThis, 0x38);
1154 break;
1155
1156 default:
1157 sb16DspSeData(pThis, 0x00);
1158 break;
1159 }
1160 break;
1161
1162 default:
1163 LogRel2(("SB16: Unrecognized command %#x, skipping\n", pThis->cmd));
1164 return;
1165 }
1166 }
1167
1168 pThis->cmd = -1;
1169 return;
1170}
1171
1172static void sb16DspCmdResetLegacy(PSB16STATE pThis)
1173{
1174 LogFlowFuncEnter();
1175
1176 /* Disable speaker(s). */
1177 sb16SpeakerControl(pThis, false /* fOn */);
1178
1179 /*
1180 * Reset all streams.
1181 */
1182 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
1183 sb16StreamReset(pThis, &pThis->aStreams[i]);
1184}
1185
1186static void sb16DspCmdReset(PSB16STATE pThis)
1187{
1188 pThis->mixer_regs[0x82] = 0;
1189 pThis->dsp_in_idx = 0;
1190 pThis->dsp_out_data_len = 0;
1191 pThis->dsp_in_needed_bytes = 0;
1192 pThis->nzero = 0;
1193 pThis->highspeed = 0;
1194 pThis->v2x6 = 0;
1195 pThis->cmd = -1;
1196
1197 sb16DspSeData(pThis, 0xaa);
1198
1199 sb16DspCmdResetLegacy(pThis);
1200}
1201
1202/**
1203 * @callback_method_impl{PFNIOMIOPORTNEWOUT}
1204 */
1205static DECLCALLBACK(VBOXSTRICTRC) sb16IoPortDspWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
1206{
1207 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1208 RT_NOREF(pvUser, cb);
1209
1210 /** @todo Figure out how we can distinguish between streams. DSP port #, e.g. 0x220? */
1211 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
1212
1213 LogFlowFunc(("write %#x <- %#x\n", offPort, u32));
1214 switch (offPort)
1215 {
1216 case 0:
1217 switch (u32)
1218 {
1219 case 0x00:
1220 {
1221 if (pThis->v2x6 == 1)
1222 {
1223 if (0 && pThis->highspeed)
1224 {
1225 pThis->highspeed = 0;
1226 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
1227 sb16StreamControl(pDevIns, pThis, pStream, false /* fRun */);
1228 }
1229 else
1230 sb16DspCmdReset(pThis);
1231 }
1232 pThis->v2x6 = 0;
1233 break;
1234 }
1235
1236 case 0x01:
1237 case 0x03: /* FreeBSD kludge */
1238 pThis->v2x6 = 1;
1239 break;
1240
1241 case 0xc6:
1242 pThis->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */
1243 break;
1244
1245 case 0xb8: /* Panic */
1246 sb16DspCmdReset(pThis);
1247 break;
1248
1249 case 0x39:
1250 sb16DspSeData(pThis, 0x38);
1251 sb16DspCmdReset(pThis);
1252 pThis->v2x6 = 0x39;
1253 break;
1254
1255 default:
1256 pThis->v2x6 = u32;
1257 break;
1258 }
1259 break;
1260
1261 case 6: /* Write data or command | write status */
1262#if 0
1263 if (pThis->highspeed)
1264 break;
1265#endif
1266 if (0 == pThis->dsp_in_needed_bytes)
1267 {
1268 sb16DspCmdLookup(pDevIns, pThis, pStream, u32);
1269 }
1270 else
1271 {
1272 if (pThis->dsp_in_idx == sizeof (pThis->dsp_in_data))
1273 {
1274 AssertMsgFailed(("DSP input data overrun\n"));
1275 }
1276 else
1277 {
1278 pThis->dsp_in_data[pThis->dsp_in_idx++] = u32;
1279 if (pThis->dsp_in_idx == pThis->dsp_in_needed_bytes)
1280 {
1281 pThis->dsp_in_needed_bytes = 0;
1282 sb16DspCmdComplete(pDevIns, pThis);
1283 }
1284 }
1285 }
1286 break;
1287
1288 default:
1289 LogFlowFunc(("offPort=%#x, u32=%#x)\n", offPort, u32));
1290 break;
1291 }
1292
1293 return VINF_SUCCESS;
1294}
1295
1296
1297/**
1298 * @callback_method_impl{PFNIOMIOPORTNEWIN}
1299 */
1300static DECLCALLBACK(VBOXSTRICTRC) sb16IoPortDspRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1301{
1302 RT_NOREF(pvUser, cb);
1303
1304 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1305
1306 uint32_t retval;
1307 int ack = 0;
1308
1309 /** @todo Figure out how we can distinguish between streams. */
1310 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
1311
1312 /** @todo reject non-byte access?
1313 * The spec does not mention a non-byte access so we should check how real hardware behaves. */
1314
1315 switch (offPort)
1316 {
1317 case 0: /* reset */
1318 retval = 0xff;
1319 break;
1320
1321 case 4: /* read data */
1322 if (pThis->dsp_out_data_len)
1323 {
1324 retval = pThis->dsp_out_data[--pThis->dsp_out_data_len];
1325 pThis->last_read_byte = retval;
1326 }
1327 else
1328 {
1329 if (pThis->cmd != -1)
1330 LogFlowFunc(("empty output buffer for command %#x\n", pThis->cmd));
1331 retval = pThis->last_read_byte;
1332 /* goto error; */
1333 }
1334 break;
1335
1336 case 6: /* 0 can write */
1337 retval = pStream->can_write ? 0 : 0x80;
1338 break;
1339
1340 case 7: /* timer interrupt clear */
1341 /* LogFlowFunc(("timer interrupt clear\n")); */
1342 retval = 0;
1343 break;
1344
1345 case 8: /* data available status | irq 8 ack */
1346 retval = (!pThis->dsp_out_data_len || pThis->highspeed) ? 0 : 0x80;
1347 if (pThis->mixer_regs[0x82] & 1)
1348 {
1349 ack = 1;
1350 pThis->mixer_regs[0x82] &= ~1;
1351 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
1352 }
1353 break;
1354
1355 case 9: /* irq 16 ack */
1356 retval = 0xff;
1357 if (pThis->mixer_regs[0x82] & 2)
1358 {
1359 ack = 1;
1360 pThis->mixer_regs[0x82] &= ~2;
1361 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
1362 }
1363 break;
1364
1365 default:
1366 LogFlowFunc(("warning: sb16IoPortDspRead %#x error\n", offPort));
1367 return VERR_IOM_IOPORT_UNUSED;
1368 }
1369
1370 if (!ack)
1371 LogFlowFunc(("read %#x -> %#x\n", offPort, retval));
1372
1373 *pu32 = retval;
1374 return VINF_SUCCESS;
1375}
1376
1377
1378/*********************************************************************************************************************************
1379* Mixer functions *
1380*********************************************************************************************************************************/
1381
1382static uint8_t sb16MixRegToVol(PSB16STATE pThis, int reg)
1383{
1384 /* The SB16 mixer has a 0 to -62dB range in 32 levels (2dB each step).
1385 * We use a 0 to -96dB range in 256 levels (0.375dB each step).
1386 * Only the top 5 bits of a mixer register are used.
1387 */
1388 uint8_t steps = 31 - (pThis->mixer_regs[reg] >> 3);
1389 uint8_t vol = 255 - steps * 16 / 3; /* (2dB*8) / (0.375dB*8) */
1390 return vol;
1391}
1392
1393/**
1394 * Returns the device's current master volume.
1395 *
1396 * @param pThis SB16 state.
1397 * @param pVol Where to store the master volume information.
1398 */
1399static void sb16GetMasterVolume(PSB16STATE pThis, PPDMAUDIOVOLUME pVol)
1400{
1401 /* There's no mute switch, only volume controls. */
1402 uint8_t lvol = sb16MixRegToVol(pThis, 0x30);
1403 uint8_t rvol = sb16MixRegToVol(pThis, 0x31);
1404
1405 pVol->fMuted = false;
1406 pVol->uLeft = lvol;
1407 pVol->uRight = rvol;
1408}
1409
1410/**
1411 * Returns the device's current output stream volume.
1412 *
1413 * @param pThis SB16 state.
1414 * @param pVol Where to store the output stream volume information.
1415 */
1416static void sb16GetPcmOutVolume(PSB16STATE pThis, PPDMAUDIOVOLUME pVol)
1417{
1418 /* There's no mute switch, only volume controls. */
1419 uint8_t lvol = sb16MixRegToVol(pThis, 0x32);
1420 uint8_t rvol = sb16MixRegToVol(pThis, 0x33);
1421
1422 pVol->fMuted = false;
1423 pVol->uLeft = lvol;
1424 pVol->uRight = rvol;
1425}
1426
1427static void sb16UpdateVolume(PSB16STATE pThis)
1428{
1429 PDMAUDIOVOLUME VolMaster;
1430 sb16GetMasterVolume(pThis, &VolMaster);
1431
1432 PDMAUDIOVOLUME VolOut;
1433 sb16GetPcmOutVolume(pThis, &VolOut);
1434
1435 /* Combine the master + output stream volume. */
1436 PDMAUDIOVOLUME VolCombined;
1437 RT_ZERO(VolCombined);
1438
1439 VolCombined.fMuted = VolMaster.fMuted || VolOut.fMuted;
1440 if (!VolCombined.fMuted)
1441 {
1442 VolCombined.uLeft = ( (VolOut.uLeft ? VolOut.uLeft : 1)
1443 * (VolMaster.uLeft ? VolMaster.uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1444
1445 VolCombined.uRight = ( (VolOut.uRight ? VolOut.uRight : 1)
1446 * (VolMaster.uRight ? VolMaster.uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1447 }
1448
1449 int rc2 = AudioMixerSinkSetVolume(pThis->pSinkOut, &VolCombined);
1450 AssertRC(rc2);
1451}
1452
1453static void sb16MixerReset(PSB16STATE pThis)
1454{
1455 memset(pThis->mixer_regs, 0xff, 0x7f);
1456 memset(pThis->mixer_regs + 0x83, 0xff, sizeof (pThis->mixer_regs) - 0x83);
1457
1458 pThis->mixer_regs[0x02] = 4; /* master volume 3bits */
1459 pThis->mixer_regs[0x06] = 4; /* MIDI volume 3bits */
1460 pThis->mixer_regs[0x08] = 0; /* CD volume 3bits */
1461 pThis->mixer_regs[0x0a] = 0; /* voice volume 2bits */
1462
1463 /* d5=input filt, d3=lowpass filt, d1,d2=input source */
1464 pThis->mixer_regs[0x0c] = 0;
1465
1466 /* d5=output filt, d1=stereo switch */
1467 pThis->mixer_regs[0x0e] = 0;
1468
1469 /* voice volume L d5,d7, R d1,d3 */
1470 pThis->mixer_regs[0x04] = (12 << 4) | 12;
1471 /* master ... */
1472 pThis->mixer_regs[0x22] = (12 << 4) | 12;
1473 /* MIDI ... */
1474 pThis->mixer_regs[0x26] = (12 << 4) | 12;
1475
1476 /* master/voice/MIDI L/R volume */
1477 for (int i = 0x30; i < 0x36; i++)
1478 pThis->mixer_regs[i] = 24 << 3; /* -14 dB */
1479
1480 /* treble/bass */
1481 for (int i = 0x44; i < 0x48; i++)
1482 pThis->mixer_regs[i] = 0x80;
1483
1484 /* Update the master (mixer) and PCM out volumes. */
1485 sb16UpdateVolume(pThis);
1486
1487 /*
1488 * Reset mixer sinks.
1489 *
1490 * Do the reset here instead of in sb16StreamReset();
1491 * the mixer sink(s) might still have data to be processed when an audio stream gets reset.
1492 */
1493 if (pThis->pSinkOut)
1494 AudioMixerSinkReset(pThis->pSinkOut);
1495}
1496
1497static int magic_of_irq(int irq)
1498{
1499 switch (irq)
1500 {
1501 case 5:
1502 return 2;
1503 case 7:
1504 return 4;
1505 case 9:
1506 return 1;
1507 case 10:
1508 return 8;
1509 default:
1510 break;
1511 }
1512
1513 LogFlowFunc(("bad irq %d\n", irq));
1514 return 2;
1515}
1516
1517static int irq_of_magic(int magic)
1518{
1519 switch (magic)
1520 {
1521 case 1:
1522 return 9;
1523 case 2:
1524 return 5;
1525 case 4:
1526 return 7;
1527 case 8:
1528 return 10;
1529 default:
1530 break;
1531 }
1532
1533 LogFlowFunc(("bad irq magic %d\n", magic));
1534 return -1;
1535}
1536
1537static int sb16MixerWriteIndex(PSB16STATE pThis, PSB16STREAM pStream, uint8_t val)
1538{
1539 RT_NOREF(pStream);
1540 pThis->mixer_nreg = val;
1541 return VINF_SUCCESS;
1542}
1543
1544#ifndef VBOX
1545static uint32_t popcount(uint32_t u)
1546{
1547 u = ((u&0x55555555) + ((u>>1)&0x55555555));
1548 u = ((u&0x33333333) + ((u>>2)&0x33333333));
1549 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
1550 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
1551 u = ( u&0x0000ffff) + (u>>16);
1552 return u;
1553}
1554#endif
1555
1556static uint32_t lsbindex(uint32_t u)
1557{
1558#ifdef VBOX
1559 return u ? ASMBitFirstSetU32(u) - 1 : 32;
1560#else
1561 return popcount((u & -(int32_t)u) - 1);
1562#endif
1563}
1564
1565/* Convert SB16 to SB Pro mixer volume (left). */
1566static inline void sb16ConvVolumeL(PSB16STATE pThis, unsigned reg, uint8_t val)
1567{
1568 /* High nibble in SBP mixer. */
1569 pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0x0f) | (val & 0xf0);
1570}
1571
1572/* Convert SB16 to SB Pro mixer volume (right). */
1573static inline void sb16ConvVolumeR(PSB16STATE pThis, unsigned reg, uint8_t val)
1574{
1575 /* Low nibble in SBP mixer. */
1576 pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0xf0) | (val >> 4);
1577}
1578
1579/* Convert SB Pro to SB16 mixer volume (left + right). */
1580static inline void sb16ConvVolumeOldToNew(PSB16STATE pThis, unsigned reg, uint8_t val)
1581{
1582 /* Left channel. */
1583 pThis->mixer_regs[reg + 0] = (val & 0xf0) | RT_BIT(3);
1584 /* Right channel (the register immediately following). */
1585 pThis->mixer_regs[reg + 1] = (val << 4) | RT_BIT(3);
1586}
1587
1588
1589static int sb16MixerWriteData(PSB16STATE pThis, PSB16STREAM pStream, uint8_t val)
1590{
1591 bool fUpdateMaster = false;
1592 bool fUpdateStream = false;
1593
1594 LogFlowFunc(("[%#x] <- %#x\n", pThis->mixer_nreg, val));
1595
1596 switch (pThis->mixer_nreg)
1597 {
1598 case 0x00:
1599 sb16MixerReset(pThis);
1600 /* And update the actual volume, too. */
1601 fUpdateMaster = true;
1602 fUpdateStream = true;
1603 break;
1604
1605 case 0x04: /* Translate from old style voice volume (L/R). */
1606 sb16ConvVolumeOldToNew(pThis, 0x32, val);
1607 fUpdateStream = true;
1608 break;
1609
1610 case 0x22: /* Translate from old style master volume (L/R). */
1611 sb16ConvVolumeOldToNew(pThis, 0x30, val);
1612 fUpdateMaster = true;
1613 break;
1614
1615 case 0x26: /* Translate from old style MIDI volume (L/R). */
1616 sb16ConvVolumeOldToNew(pThis, 0x34, val);
1617 break;
1618
1619 case 0x28: /* Translate from old style CD volume (L/R). */
1620 sb16ConvVolumeOldToNew(pThis, 0x36, val);
1621 break;
1622
1623 case 0x2E: /* Translate from old style line volume (L/R). */
1624 sb16ConvVolumeOldToNew(pThis, 0x38, val);
1625 break;
1626
1627 case 0x30: /* Translate to old style master volume (L). */
1628 sb16ConvVolumeL(pThis, 0x22, val);
1629 fUpdateMaster = true;
1630 break;
1631
1632 case 0x31: /* Translate to old style master volume (R). */
1633 sb16ConvVolumeR(pThis, 0x22, val);
1634 fUpdateMaster = true;
1635 break;
1636
1637 case 0x32: /* Translate to old style voice volume (L). */
1638 sb16ConvVolumeL(pThis, 0x04, val);
1639 fUpdateStream = true;
1640 break;
1641
1642 case 0x33: /* Translate to old style voice volume (R). */
1643 sb16ConvVolumeR(pThis, 0x04, val);
1644 fUpdateStream = true;
1645 break;
1646
1647 case 0x34: /* Translate to old style MIDI volume (L). */
1648 sb16ConvVolumeL(pThis, 0x26, val);
1649 break;
1650
1651 case 0x35: /* Translate to old style MIDI volume (R). */
1652 sb16ConvVolumeR(pThis, 0x26, val);
1653 break;
1654
1655 case 0x36: /* Translate to old style CD volume (L). */
1656 sb16ConvVolumeL(pThis, 0x28, val);
1657 break;
1658
1659 case 0x37: /* Translate to old style CD volume (R). */
1660 sb16ConvVolumeR(pThis, 0x28, val);
1661 break;
1662
1663 case 0x38: /* Translate to old style line volume (L). */
1664 sb16ConvVolumeL(pThis, 0x2E, val);
1665 break;
1666
1667 case 0x39: /* Translate to old style line volume (R). */
1668 sb16ConvVolumeR(pThis, 0x2E, val);
1669 break;
1670
1671 case 0x80:
1672 {
1673 int irq = irq_of_magic(val);
1674 LogRelMax2(64, ("SB16: Setting IRQ to %d\n", irq));
1675 if (irq > 0)
1676 pStream->HwCfgRuntime.uIrq = irq;
1677 break;
1678 }
1679
1680 case 0x81:
1681 {
1682 int dma = lsbindex(val & 0xf);
1683 int hdma = lsbindex(val & 0xf0);
1684 if ( dma != pStream->HwCfgRuntime.uDmaChanLow
1685 || hdma != pStream->HwCfgRuntime.uDmaChanHigh)
1686 {
1687 LogRelMax2(64, ("SB16: Attempt to change DMA 8bit %d(%d), 16bit %d(%d)\n",
1688 dma, pStream->HwCfgRuntime.uDmaChanLow, hdma, pStream->HwCfgRuntime.uDmaChanHigh));
1689 }
1690#if 0
1691 pStream->dma = dma;
1692 pStream->hdma = hdma;
1693#endif
1694 break;
1695 }
1696
1697 case 0x82:
1698 LogRelMax2(64, ("SB16: Attempt to write into IRQ status register to %#x\n", val));
1699 return VINF_SUCCESS;
1700
1701 default:
1702 if (pThis->mixer_nreg >= 0x80)
1703 LogFlowFunc(("attempt to write mixer[%#x] <- %#x\n", pThis->mixer_nreg, val));
1704 break;
1705 }
1706
1707 pThis->mixer_regs[pThis->mixer_nreg] = val;
1708
1709 /* Update the master (mixer) volume. */
1710 if ( fUpdateMaster
1711 || fUpdateStream)
1712 {
1713 sb16UpdateVolume(pThis);
1714 }
1715
1716 return VINF_SUCCESS;
1717}
1718
1719/**
1720 * @callback_method_impl{PFNIOMIOPORTNEWOUT}
1721 */
1722static DECLCALLBACK(VBOXSTRICTRC) sb16IoPortMixerWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
1723{
1724 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1725 RT_NOREF(pvUser);
1726
1727 /** @todo Figure out how we can distinguish between streams. */
1728 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
1729
1730 switch (cb)
1731 {
1732 case 1:
1733 switch (offPort)
1734 {
1735 case 0:
1736 sb16MixerWriteIndex(pThis, pStream, u32);
1737 break;
1738 case 1:
1739 sb16MixerWriteData(pThis, pStream, u32);
1740 break;
1741 default:
1742 AssertFailed();
1743 }
1744 break;
1745 case 2:
1746 sb16MixerWriteIndex(pThis, pStream, u32 & 0xff);
1747 sb16MixerWriteData(pThis, pStream, (u32 >> 8) & 0xff);
1748 break;
1749 default:
1750 ASSERT_GUEST_MSG_FAILED(("offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
1751 break;
1752 }
1753 return VINF_SUCCESS;
1754}
1755
1756/**
1757 * @callback_method_impl{PFNIOMIOPORTNEWIN}
1758 */
1759static DECLCALLBACK(VBOXSTRICTRC) sb16IoPortMixerRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1760{
1761 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1762 RT_NOREF(pvUser, cb, offPort);
1763
1764#ifndef DEBUG_SB16_MOST
1765 if (pThis->mixer_nreg != 0x82)
1766 LogFlowFunc(("sb16IoPortMixerRead[%#x] -> %#x\n", pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
1767#else
1768 LogFlowFunc(("sb16IoPortMixerRead[%#x] -> %#x\n", pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
1769#endif
1770 *pu32 = pThis->mixer_regs[pThis->mixer_nreg];
1771 return VINF_SUCCESS;
1772}
1773
1774
1775/*********************************************************************************************************************************
1776* DMA handling *
1777*********************************************************************************************************************************/
1778
1779/**
1780 * Worker for sb16DMARead.
1781 */
1782
1783/**
1784 * @callback_method_impl{FNDMATRANSFERHANDLER,
1785 * Worker callback for both DMA channels.}
1786 */
1787static DECLCALLBACK(uint32_t) sb16DMARead(PPDMDEVINS pDevIns, void *pvUser, unsigned uChannel, uint32_t off, uint32_t cb)
1788
1789{
1790 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1791 AssertPtr(pThis);
1792 PSB16STREAM pStream = (PSB16STREAM)pvUser;
1793 AssertPtr(pStream);
1794
1795 int till, copy, free;
1796
1797 if (pStream->cbDmaBlockSize <= 0)
1798 {
1799 LogFlowFunc(("invalid block size=%d uChannel=%d off=%d cb=%d\n", pStream->cbDmaBlockSize, uChannel, off, cb));
1800 return off;
1801 }
1802
1803 if (pStream->cbDmaLeft < 0)
1804 pStream->cbDmaLeft = pStream->cbDmaBlockSize;
1805
1806 free = cb;
1807
1808 copy = free;
1809 till = pStream->cbDmaLeft;
1810
1811 Log4Func(("pos=%d %d, till=%d, len=%d\n", off, free, till, cb));
1812
1813 if (copy >= till)
1814 {
1815 if (0 == pStream->dma_auto)
1816 {
1817 copy = till;
1818 }
1819 else
1820 {
1821 if (copy >= till + pStream->cbDmaBlockSize)
1822 copy = till; /* Make sure we won't skip IRQs. */
1823 }
1824 }
1825
1826 STAM_COUNTER_ADD(&pThis->StatBytesRead, copy);
1827
1828 uint32_t written = 0; /* Shut up GCC. */
1829 int rc = sb16StreamDoDmaOutput(pThis, pStream, uChannel, off, cb, copy, &written);
1830 AssertRC(rc);
1831
1832 /** @todo Convert the rest to uin32_t / size_t. */
1833 off = (off + (int)written) % cb;
1834 pStream->cbDmaLeft -= (int)written; /** @todo r=andy left_till_irq can be < 0. Correct? Revisit this. */
1835
1836 Log3Func(("pos %d/%d, free=%d, till=%d, copy=%d, written=%RU32, block_size=%d\n",
1837 off, cb, free, pStream->cbDmaLeft, copy, copy, pStream->cbDmaBlockSize));
1838
1839 if (pStream->cbDmaLeft <= 0)
1840 {
1841 pThis->mixer_regs[0x82] |= (uChannel & 4) ? 2 : 1;
1842
1843 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 1);
1844
1845 if (0 == pStream->dma_auto) /** @todo r=andy BUGBUG Why do we first assert the IRQ if dma_auto is 0? Revisit this. */
1846 {
1847 sb16StreamControl(pDevIns, pThis, pStream, false /* fRun */);
1848 sb16SpeakerControl(pThis, 0);
1849 }
1850 }
1851
1852 while (pStream->cbDmaLeft <= 0)
1853 pStream->cbDmaLeft += pStream->cbDmaBlockSize;
1854
1855 return off;
1856}
1857
1858
1859/*********************************************************************************************************************************
1860* Timer-related code *
1861*********************************************************************************************************************************/
1862
1863/**
1864 * @callback_method_impl{PFNTMTIMERDEV}
1865 */
1866static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1867{
1868 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1869 RT_NOREF(hTimer, pThis);
1870
1871 PSB16STREAM pStream = (PSB16STREAM)pvUser;
1872 AssertPtrReturnVoid(pStream);
1873
1874 LogFlowFuncEnter();
1875
1876 pStream->can_write = 1;
1877 PDMDevHlpISASetIrq(pDevIns, pStream->HwCfgRuntime.uIrq, 1);
1878}
1879
1880/**
1881 * Sets the stream's I/O timer to a new expiration time.
1882 *
1883 * @param pDevIns The device instance.
1884 * @param pStream SB16 stream to set timer for.
1885 * @param cTicksToDeadline The number of ticks to the new deadline.
1886 */
1887DECLINLINE(void) sb16TimerSet(PPDMDEVINS pDevIns, PSB16STREAM pStream, uint64_t cTicksToDeadline)
1888{
1889 int rc = PDMDevHlpTimerSetRelative(pDevIns, pStream->hTimerIO, cTicksToDeadline, NULL /*pu64Now*/);
1890 AssertRC(rc);
1891}
1892
1893/**
1894 * @callback_method_impl{FNTMTIMERDEV}
1895 */
1896static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1897{
1898 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1899 STAM_PROFILE_START(&pThis->StatTimerIO, a);
1900
1901 PSB16STREAM pStream = (PSB16STREAM)pvUser;
1902 AssertPtrReturnVoid(pStream);
1903 AssertReturnVoid(hTimer == pStream->hTimerIO);
1904
1905 const uint64_t cTicksNow = PDMDevHlpTimerGet(pDevIns, pStream->hTimerIO);
1906
1907 pStream->tsTimerIO = cTicksNow;
1908
1909 PAUDMIXSINK pSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
1910 AssertPtrReturnVoid(pSink);
1911
1912 const bool fSinkActive = AudioMixerSinkIsActive(pSink);
1913
1914 LogFlowFunc(("fSinkActive=%RTbool\n", fSinkActive));
1915
1916#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
1917 sb16StreamAsyncIONotify(pDevIns, pStream);
1918#else
1919 sb16StreamUpdate(pStream, pSink);
1920#endif
1921
1922 /* Schedule the next transfer. */
1923 PDMDevHlpDMASchedule(pDevIns);
1924
1925 if (fSinkActive)
1926 {
1927 /** @todo adjust cTicks down by now much cbOutMin represents. */
1928 sb16TimerSet(pDevIns, pStream, pStream->cTicksTimerIOInterval);
1929 }
1930
1931 STAM_PROFILE_STOP(&pThis->StatTimerIO, a);
1932}
1933
1934
1935/*********************************************************************************************************************************
1936* Driver handling *
1937*********************************************************************************************************************************/
1938
1939/**
1940 * Retrieves a specific driver stream of a SB16 driver.
1941 *
1942 * @returns Pointer to driver stream if found, or NULL if not found.
1943 * @param pDrv Driver to retrieve driver stream for.
1944 * @param enmDir Stream direction to retrieve.
1945 * @param dstSrc Stream destination / source to retrieve.
1946 */
1947static PSB16DRIVERSTREAM sb16GetDrvStream(PSB16DRIVER pDrv, PDMAUDIODIR enmDir, PDMAUDIODSTSRCUNION dstSrc)
1948{
1949 PSB16DRIVERSTREAM pDrvStream = NULL;
1950
1951 if (enmDir == PDMAUDIODIR_IN)
1952 {
1953 return NULL; /** @todo Recording not implemented yet. */
1954 }
1955 else if (enmDir == PDMAUDIODIR_OUT)
1956 {
1957 LogFunc(("enmDst=%RU32\n", dstSrc.enmDst));
1958
1959 switch (dstSrc.enmDst)
1960 {
1961 case PDMAUDIOPLAYBACKDST_FRONT:
1962 pDrvStream = &pDrv->Out;
1963 break;
1964 default:
1965 AssertFailed();
1966 break;
1967 }
1968 }
1969 else
1970 AssertFailed();
1971
1972 return pDrvStream;
1973}
1974
1975/**
1976 * Adds a driver stream to a specific mixer sink.
1977 *
1978 * @returns VBox status code.
1979 * @param pDevIns The device instance.
1980 * @param pMixSink Mixer sink to add driver stream to.
1981 * @param pCfg Stream configuration to use.
1982 * @param pDrv Driver stream to add.
1983 */
1984static int sb16AddDrvStream(PPDMDEVINS pDevIns, PAUDMIXSINK pMixSink, PPDMAUDIOSTREAMCFG pCfg, PSB16DRIVER pDrv)
1985{
1986 AssertReturn(pCfg->enmDir == PDMAUDIODIR_OUT, VERR_NOT_IMPLEMENTED); /* We don't support recording for SB16 so far. */
1987
1988 PPDMAUDIOSTREAMCFG pStreamCfg = PDMAudioStrmCfgDup(pCfg);
1989 if (!pStreamCfg)
1990 return VERR_NO_MEMORY;
1991
1992 if (!RTStrPrintf(pStreamCfg->szName, sizeof(pStreamCfg->szName), "%s", pCfg->szName))
1993 {
1994 PDMAudioStrmCfgFree(pStreamCfg);
1995 return VERR_BUFFER_OVERFLOW;
1996 }
1997
1998 LogFunc(("[LUN#%RU8] %s\n", pDrv->uLUN, pStreamCfg->szName));
1999
2000 int rc;
2001
2002 PSB16DRIVERSTREAM pDrvStream = sb16GetDrvStream(pDrv, pStreamCfg->enmDir, pStreamCfg->u);
2003 if (pDrvStream)
2004 {
2005 AssertMsg(pDrvStream->pMixStrm == NULL, ("[LUN#%RU8] Driver stream already present when it must not\n", pDrv->uLUN));
2006
2007 PAUDMIXSTREAM pMixStrm;
2008 rc = AudioMixerSinkCreateStream(pMixSink, pDrv->pConnector, pStreamCfg, 0 /* fFlags */, pDevIns, &pMixStrm);
2009 LogFlowFunc(("LUN#%RU8: Created stream \"%s\" for sink, rc=%Rrc\n", pDrv->uLUN, pStreamCfg->szName, rc));
2010 if (RT_SUCCESS(rc))
2011 {
2012 rc = AudioMixerSinkAddStream(pMixSink, pMixStrm);
2013 LogFlowFunc(("LUN#%RU8: Added stream \"%s\" to sink, rc=%Rrc\n", pDrv->uLUN, pStreamCfg->szName, rc));
2014
2015 if (RT_SUCCESS(rc))
2016 {
2017 pDrvStream->pMixStrm = pMixStrm;
2018 }
2019 else
2020 AudioMixerStreamDestroy(pMixStrm, pDevIns);
2021 }
2022 }
2023 else
2024 rc = VERR_INVALID_PARAMETER;
2025
2026 PDMAudioStrmCfgFree(pStreamCfg);
2027
2028 LogFlowFuncLeaveRC(rc);
2029 return rc;
2030}
2031
2032/**
2033 * Adds all current driver streams to a specific mixer sink.
2034 *
2035 * @returns VBox status code.
2036 * @param pDevIns The device instance.
2037 * @param pThis The SB16 state.
2038 * @param pMixSink Mixer sink to add stream to.
2039 * @param pCfg Stream configuration to use.
2040 */
2041static int sb16AddDrvStreams(PPDMDEVINS pDevIns, PSB16STATE pThis, PAUDMIXSINK pMixSink, PPDMAUDIOSTREAMCFG pCfg)
2042{
2043 AssertPtrReturn(pMixSink, VERR_INVALID_POINTER);
2044
2045 if (!AudioHlpStreamCfgIsValid(pCfg))
2046 return VERR_INVALID_PARAMETER;
2047
2048 int rc = AudioMixerSinkSetFormat(pMixSink, &pCfg->Props);
2049 if (RT_FAILURE(rc))
2050 return rc;
2051
2052 PSB16DRIVER pDrv;
2053 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2054 {
2055 int rc2 = sb16AddDrvStream(pDevIns, pMixSink, pCfg, pDrv);
2056 if (RT_FAILURE(rc2))
2057 LogFunc(("Attaching stream failed with %Rrc\n", rc2));
2058
2059 /* Do not pass failure to rc here, as there might be drivers which aren't
2060 * configured / ready yet. */
2061 }
2062
2063 LogFlowFuncLeaveRC(rc);
2064 return rc;
2065}
2066
2067/**
2068 * Removes a driver stream from a specific mixer sink.
2069 *
2070 * @param pDevIns The device instance.
2071 * @param pMixSink Mixer sink to remove audio streams from.
2072 * @param enmDir Stream direction to remove.
2073 * @param dstSrc Stream destination / source to remove.
2074 * @param pDrv Driver stream to remove.
2075 */
2076static void sb16RemoveDrvStream(PPDMDEVINS pDevIns, PAUDMIXSINK pMixSink, PDMAUDIODIR enmDir,
2077 PDMAUDIODSTSRCUNION dstSrc, PSB16DRIVER pDrv)
2078{
2079 PSB16DRIVERSTREAM pDrvStream = sb16GetDrvStream(pDrv, enmDir, dstSrc);
2080 if (pDrvStream)
2081 {
2082 if (pDrvStream->pMixStrm)
2083 {
2084 LogFlowFunc(("[LUN#%RU8]\n", pDrv->uLUN));
2085
2086 AudioMixerSinkRemoveStream(pMixSink, pDrvStream->pMixStrm);
2087
2088 AudioMixerStreamDestroy(pDrvStream->pMixStrm, pDevIns);
2089 pDrvStream->pMixStrm = NULL;
2090 }
2091 }
2092}
2093
2094/**
2095 * Removes all driver streams from a specific mixer sink.
2096 *
2097 * @param pDevIns The device instance.
2098 * @param pThis The SB16 state.
2099 * @param pMixSink Mixer sink to remove audio streams from.
2100 * @param enmDir Stream direction to remove.
2101 * @param dstSrc Stream destination / source to remove.
2102 */
2103static void sb16RemoveDrvStreams(PPDMDEVINS pDevIns, PSB16STATE pThis, PAUDMIXSINK pMixSink,
2104 PDMAUDIODIR enmDir, PDMAUDIODSTSRCUNION dstSrc)
2105{
2106 AssertPtrReturnVoid(pMixSink);
2107
2108 PSB16DRIVER pDrv;
2109 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2110 sb16RemoveDrvStream(pDevIns, pMixSink, enmDir, dstSrc, pDrv);
2111}
2112
2113/**
2114 * Adds a specific SB16 driver to the driver chain.
2115 *
2116 * @returns VBox status code.
2117 * @param pDevIns The device instance.
2118 * @param pThis The SB16 device state.
2119 * @param pDrv The SB16 driver to add.
2120 */
2121static int sb16AddDrv(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16DRIVER pDrv)
2122{
2123 int rc = VINF_SUCCESS;
2124
2125 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
2126 {
2127 if (AudioHlpStreamCfgIsValid(&pThis->aStreams[i].Cfg))
2128 {
2129 int rc2 = sb16AddDrvStream(pDevIns,
2130 sb16StreamIndexToSink(pThis, pThis->aStreams[i].uIdx), &pThis->aStreams[i].Cfg, pDrv);
2131 if (RT_SUCCESS(rc))
2132 rc = rc2;
2133 }
2134 }
2135
2136 return rc;
2137}
2138
2139/**
2140 * Removes a specific SB16 driver from the driver chain and destroys its
2141 * associated streams.
2142 *
2143 * @param pDevIns The device instance.
2144 * @param pThis The SB16 device state.
2145 * @param pDrv SB16 driver to remove.
2146 */
2147static void sb16RemoveDrv(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16DRIVER pDrv)
2148{
2149 RT_NOREF(pDevIns);
2150
2151 /** @todo We only implement one single output (playback) stream at the moment. */
2152
2153 if (pDrv->Out.pMixStrm)
2154 {
2155 AudioMixerSinkRemoveStream(pThis->pSinkOut, pDrv->Out.pMixStrm);
2156 AudioMixerStreamDestroy(pDrv->Out.pMixStrm, pDevIns);
2157 pDrv->Out.pMixStrm = NULL;
2158 }
2159
2160 RTListNodeRemove(&pDrv->Node);
2161}
2162
2163
2164/*********************************************************************************************************************************
2165* Stream handling *
2166*********************************************************************************************************************************/
2167
2168static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream,
2169 int uDmaChan, uint32_t offDma, uint32_t cbDma, uint32_t cbToRead, uint32_t *pcbRead)
2170{
2171 uint32_t cbFree = (uint32_t)RTCircBufFree(pStream->State.pCircBuf);
2172 uint32_t cbReadTotal = 0;
2173
2174 //Assert(cbToRead <= cbFree); /** @todo Add STAM value for overflows. */
2175
2176 cbToRead = RT_MIN(cbToRead, cbFree);
2177
2178 int rc = VINF_SUCCESS;
2179
2180 void *pv;
2181 size_t cb;
2182
2183 while (cbToRead)
2184 {
2185 uint32_t cbChunk = RT_MIN(cbDma - offDma, cbToRead);
2186
2187 RTCircBufAcquireWriteBlock(pStream->State.pCircBuf, cbChunk, &pv, &cb);
2188
2189 uint32_t cbRead;
2190 rc = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, uDmaChan, pv, offDma, (uint32_t)cb, &cbRead);
2191 AssertMsgRCReturn(rc, ("Reading from DMA failed, rc=%Rrc\n", rc), rc);
2192 Assert(cbRead == cb);
2193
2194 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2195 { /* likely */ }
2196 else
2197 AudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMA, pv, cbRead, 0 /* fFlags */);
2198
2199 RTCircBufReleaseWriteBlock(pStream->State.pCircBuf, cbRead);
2200
2201 Assert(cbToRead >= cbRead);
2202 cbToRead -= cbRead;
2203 offDma = (offDma + cbRead) % cbDma;
2204 cbReadTotal += cbRead;
2205 }
2206
2207 if (pcbRead)
2208 *pcbRead = cbReadTotal;
2209
2210 return rc;
2211}
2212
2213/**
2214 * Enables or disables a SB16 audio stream.
2215 *
2216 * @returns VBox status code.
2217 * @param pThis The SB16 state.
2218 * @param pStream The SB16 stream to enable or disable.
2219 * @param fEnable Whether to enable or disable the stream.
2220 * @param fForce Whether to force re-opening the stream or not.
2221 * Otherwise re-opening only will happen if the PCM properties have changed.
2222 */
2223static int sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable, bool fForce)
2224{
2225 if ( !fForce
2226 && fEnable == pStream->State.fEnabled)
2227 return VINF_SUCCESS;
2228
2229 LogFlowFunc(("fEnable=%RTbool, fForce=%RTbool, fStreamEnabled=%RTbool\n", fEnable, fForce, pStream->State.fEnabled));
2230
2231 /* First, enable or disable the stream and the stream's sink. */
2232 int rc = AudioMixerSinkCtl(sb16StreamIndexToSink(pThis, pStream->uIdx),
2233 fEnable ? AUDMIXSINKCMD_ENABLE : AUDMIXSINKCMD_DISABLE);
2234 AssertRCReturn(rc, rc);
2235
2236 pStream->State.fEnabled = fEnable;
2237
2238 return rc;
2239}
2240
2241/**
2242 * Retrieves the audio mixer sink of a corresponding SB16 stream.
2243 *
2244 * @returns Pointer to audio mixer sink if found, or NULL if not found / invalid.
2245 * @param pThis The SB16 state.
2246 * @param uIdx Stream index to get audio mixer sink for.
2247 */
2248DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx)
2249{
2250 AssertReturn(uIdx <= SB16_MAX_STREAMS, NULL);
2251
2252 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
2253 if (uIdx == SB16_IDX_OUT)
2254 return pThis->pSinkOut; /* Can be NULL if not configured / set up yet. */
2255
2256 AssertMsgFailed(("No sink attached (yet) for index %RU8\n", uIdx));
2257 return NULL;
2258}
2259
2260/**
2261 * Returns the audio direction of a specified stream descriptor.
2262 *
2263 * @returns Audio direction.
2264 * @param uIdx Stream index to get audio direction for.
2265 */
2266DECLINLINE(PDMAUDIODIR) sb16GetDirFromIndex(uint8_t uIdx)
2267{
2268 AssertReturn(uIdx <= SB16_MAX_STREAMS, PDMAUDIODIR_INVALID);
2269
2270 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
2271 if (uIdx == SB16_IDX_OUT)
2272 return PDMAUDIODIR_OUT;
2273
2274 return PDMAUDIODIR_INVALID;
2275}
2276
2277/**
2278 * Creates a SB16 audio stream.
2279 *
2280 * @returns VBox status code.
2281 * @param pThis The SB16 state.
2282 * @param pStream The SB16 stream to create.
2283 * @param uIdx Stream index to assign.
2284 */
2285static int sb16StreamCreate(PSB16STATE pThis, PSB16STREAM pStream, uint8_t uIdx)
2286{
2287 LogFlowFuncEnter();
2288
2289 pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled;
2290
2291 int rc2;
2292
2293#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
2294 rc2 = sb16StreamAsyncIOCreate(pThis->pDevInsR3, pThis, pStream);
2295 AssertRCReturn(rc2, rc2);
2296#endif
2297
2298 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2299 { /* likely */ }
2300 else
2301 {
2302 char szFile[64];
2303
2304 if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_IN)
2305 RTStrPrintf(szFile, sizeof(szFile), "sb16StreamWriteSD%RU8", pStream->uIdx);
2306 else
2307 RTStrPrintf(szFile, sizeof(szFile), "sb16StreamReadSD%RU8", pStream->uIdx);
2308
2309 char szPath[RTPATH_MAX];
2310 rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThis->Dbg.pszOutPath, szFile,
2311 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
2312 AssertRC(rc2);
2313 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStream->Dbg.Runtime.pFileDMA);
2314 AssertRC(rc2);
2315
2316 /* Delete stale debugging files from a former run. */
2317 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
2318 }
2319
2320 pStream->uIdx = uIdx;
2321
2322 return VINF_SUCCESS;
2323}
2324
2325/**
2326 * Destroys a SB16 audio stream.
2327 *
2328 * @returns VBox status code.
2329 * @param pDevIns The device instance.
2330 * @param pThis The SB16 state.
2331 * @param pStream The SB16 stream to destroy.
2332 */
2333static int sb16StreamDestroy(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2334{
2335 LogFlowFuncEnter();
2336
2337 sb16StreamClose(pDevIns, pThis, pStream);
2338
2339#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
2340 int rc = sb16StreamAsyncIODestroy(pDevIns, pStream);
2341 AssertRCReturn(rc, rc);
2342#else
2343 RT_NOREF(pDevIns);
2344#endif
2345
2346 if (pStream->State.pCircBuf)
2347 {
2348 RTCircBufDestroy(pStream->State.pCircBuf);
2349 pStream->State.pCircBuf = NULL;
2350 }
2351
2352 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2353 { /* likely */ }
2354 else
2355 {
2356 AudioHlpFileDestroy(pStream->Dbg.Runtime.pFileDMA);
2357 pStream->Dbg.Runtime.pFileDMA = NULL;
2358 }
2359
2360 pStream->uIdx = UINT8_MAX;
2361
2362 return VINF_SUCCESS;
2363}
2364
2365/**
2366 * Resets a SB16 stream.
2367 *
2368 * @param pThis The SB16 state.
2369 * @param pStream The SB16 stream to reset.
2370 */
2371static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream)
2372{
2373 LogFlowFuncEnter();
2374
2375 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
2376 if (pStream->dma_auto)
2377 {
2378 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 1);
2379 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
2380
2381 pStream->dma_auto = 0;
2382 }
2383
2384 sb16StreamControl(pThis->pDevInsR3, pThis, pStream, false /* fRun */);
2385 sb16StreamEnable(pThis, pStream, false /* fEnable */, false /* fForce */);
2386
2387 switch (pStream->uIdx)
2388 {
2389 case SB16_IDX_OUT:
2390 {
2391 pStream->Cfg.enmDir = PDMAUDIODIR_OUT;
2392 pStream->Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
2393 pStream->Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
2394
2395 PDMAudioPropsInit(&pStream->Cfg.Props, 1 /* 8-bit */, false /* fSigned */, 1 /* Mono */, 11025 /* uHz */);
2396 RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output");
2397
2398 break;
2399 }
2400
2401 default:
2402 AssertFailed();
2403 break;
2404 }
2405
2406 pStream->cbDmaLeft = 0;
2407 pStream->cbDmaBlockSize = 0;
2408 pStream->can_write = 1; /** @ŧodo r=andy BUGBUG Figure out why we (still) need this. */
2409
2410 /** @todo Also reset corresponding DSP values here? */
2411}
2412
2413/**
2414 * Opens a SB16 stream with its current mixer settings.
2415 *
2416 * @returns VBox status code.
2417 * @param pDevIns The device instance.
2418 * @param pThis The SB16 device state.
2419 * @param pStream The SB16 stream to open.
2420 *
2421 * @note This currently only supports the one and only output stream.
2422 */
2423static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2424{
2425 LogFlowFuncEnter();
2426
2427 PDMAudioPropsInit(&pStream->Cfg.Props,
2428 pStream->Cfg.Props.cbSampleX,
2429 pStream->Cfg.Props.fSigned,
2430 pStream->Cfg.Props.cChannelsX,
2431 pStream->Cfg.Props.uHz);
2432
2433 AssertReturn(PDMAudioPropsAreValid(&pStream->Cfg.Props), VERR_INVALID_PARAMETER);
2434
2435 PDMAUDIODSTSRCUNION dstSrc;
2436 PDMAUDIODIR enmDir;
2437
2438 switch (pStream->uIdx)
2439 {
2440 case SB16_IDX_OUT:
2441 {
2442 pStream->Cfg.enmDir = PDMAUDIODIR_OUT;
2443 pStream->Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
2444 pStream->Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
2445
2446 RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output");
2447
2448 dstSrc.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
2449 enmDir = PDMAUDIODIR_OUT;
2450 break;
2451 }
2452
2453 default:
2454 AssertFailed();
2455 break;
2456 }
2457
2458 LogRel2(("SB16: (Re-)Opening stream '%s' (%RU32Hz, %RU8 channels, %s%RU8)\n", pStream->Cfg.szName, pStream->Cfg.Props.uHz,
2459 PDMAudioPropsChannels(&pStream->Cfg.Props), pStream->Cfg.Props.fSigned ? "S" : "U",
2460 PDMAudioPropsSampleBits(&pStream->Cfg.Props)));
2461
2462 /* (Re-)create the stream's internal ring buffer. */
2463 if (pStream->State.pCircBuf)
2464 {
2465 RTCircBufDestroy(pStream->State.pCircBuf);
2466 pStream->State.pCircBuf = NULL;
2467 }
2468
2469 const uint32_t cbCircBuf
2470 = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, (RT_MS_1SEC / pStream->uTimerHz) * 2 /* Use double buffering here */);
2471
2472 int rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf);
2473 AssertRCReturn(rc, rc);
2474
2475 /* Set scheduling hint (if available). */
2476 if (pStream->cTicksTimerIOInterval)
2477 pStream->Cfg.Device.cMsSchedulingHint = RT_MS_1SEC / pStream->uTimerHz;
2478
2479 PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
2480 AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER);
2481
2482 sb16RemoveDrvStreams(pDevIns, pThis,
2483 sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.u);
2484
2485 rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
2486
2487 if (RT_SUCCESS(rc))
2488 {
2489 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2490 { /* likely */ }
2491 else
2492 {
2493 /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */
2494 if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
2495 {
2496 AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
2497 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
2498 }
2499
2500 int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
2501 &pStream->Cfg.Props);
2502 AssertRC(rc2);
2503 }
2504 }
2505
2506 LogFlowFuncLeaveRC(rc);
2507 return rc;
2508}
2509
2510/**
2511 * Closes a SB16 stream.
2512 *
2513 * @param pDevIns The device instance.
2514 * @param pThis SB16 state.
2515 * @param pStream The SB16 stream to close.
2516 */
2517static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2518{
2519 RT_NOREF(pDevIns, pThis, pStream);
2520
2521 LogFlowFuncEnter();
2522
2523 /* Nothing to do in here right now. */
2524}
2525
2526static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cbBytes)
2527{
2528 RT_NOREF(pStream);
2529
2530 uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pThis->pDevInsR3, pThis->hTimerIRQ);
2531
2532 const uint64_t usBytes = PDMAudioPropsBytesToMicro(&pStream->Cfg.Props, cbBytes);
2533 const uint64_t cTransferTicks = PDMDevHlpTimerFromMicro(pThis->pDevInsR3, pThis->hTimerIRQ, usBytes);
2534
2535 LogFlowFunc(("%RU32 bytes -> %RU64 ticks\n", cbBytes, cTransferTicks));
2536
2537 if (cTransferTicks < uTimerHz / 1024) /** @todo Explain this. */
2538 {
2539 LogFlowFunc(("IRQ\n"));
2540 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 1);
2541 }
2542 else
2543 {
2544 LogFlowFunc(("Scheduled\n"));
2545 PDMDevHlpTimerSetRelative(pThis->pDevInsR3, pThis->hTimerIRQ, cTransferTicks, NULL);
2546 }
2547}
2548
2549/**
2550 * Main function for off-DMA data processing by the audio backend(s).
2551 *
2552 * Might be called by a timer callback or by an async I/O worker thread, depending
2553 * on the configuration.
2554 *
2555 * @param pStream The SB16 stream to open.
2556 * @param pSink Mixer sink to use for updating.
2557 */
2558static void sb16StreamUpdate(PSB16STREAM pStream, PAUDMIXSINK pSink)
2559{
2560 if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_OUT)
2561 {
2562 uint32_t const cbSinkWritable = AudioMixerSinkGetWritable(pSink);
2563 uint32_t const cbStreamReadable = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
2564
2565 uint32_t cbToReadFromStream = RT_MIN(cbStreamReadable, cbSinkWritable);
2566 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
2567 cbToReadFromStream = PDMAudioPropsFloorBytesToFrame(&pStream->Cfg.Props, cbToReadFromStream);
2568
2569 Log3Func(("[SD%RU8] cbSinkWritable=%RU32, cbStreamReadable=%RU32\n", pStream->uIdx, cbSinkWritable, cbStreamReadable));
2570
2571 void /*const*/ *pvSrcBuf;
2572 size_t cbSrcBuf;
2573
2574 while (cbToReadFromStream > 0)
2575 {
2576 uint32_t cbWritten = 0;
2577
2578 RTCircBufAcquireReadBlock(pStream->State.pCircBuf, cbToReadFromStream, &pvSrcBuf, &cbSrcBuf);
2579
2580 int rc2 = AudioMixerSinkWrite(pSink, AUDMIXOP_COPY, pvSrcBuf, (uint32_t)cbSrcBuf, &cbWritten);
2581 AssertRC(rc2);
2582 Assert(cbWritten <= (uint32_t)cbSrcBuf);
2583
2584 RTCircBufReleaseReadBlock(pStream->State.pCircBuf, cbWritten);
2585
2586 Assert(cbToReadFromStream >= cbWritten);
2587 cbToReadFromStream -= cbWritten;
2588 }
2589 }
2590 else
2591 AssertFailed(); /** @todo Recording not implemented yet. */
2592
2593 int rc2 = AudioMixerSinkUpdate(pSink);
2594 AssertRC(rc2);
2595}
2596
2597
2598/*********************************************************************************************************************************
2599* Saved state handling *
2600*********************************************************************************************************************************/
2601
2602/**
2603 * @callback_method_impl{FNSSMDEVLIVEEXEC}
2604 */
2605static DECLCALLBACK(int) sb16LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
2606{
2607 RT_NOREF(uPass);
2608
2609 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2610 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2611
2612 /** Currently the saved state only contains the one-and-only output stream. */
2613 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2614
2615 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uIrq);
2616 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uDmaChanLow);
2617 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uDmaChanHigh);
2618 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uPort);
2619 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uVer);
2620 return VINF_SSM_DONT_CALL_AGAIN;
2621}
2622
2623/**
2624 * Worker for sb16SaveExec.
2625 */
2626static int sb16Save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PSB16STATE pThis)
2627{
2628 /** Currently the saved state only contains the one-and-only output stream. */
2629 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2630
2631 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uIrq);
2632 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uDmaChanLow);
2633 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uDmaChanHigh);
2634 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uPort);
2635 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uVer);
2636 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_idx);
2637 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_out_data_len);
2638
2639 /** Currently the saved state only contains the one-and-only output stream. */
2640 pHlp->pfnSSMPutS32(pSSM, pStream->Cfg.Props.cChannelsX >= 2 ? 1 : 0);
2641 pHlp->pfnSSMPutS32(pSSM, pStream->Cfg.Props.fSigned ? 1 : 0);
2642 pHlp->pfnSSMPutS32(pSSM, pStream->Cfg.Props.cbSampleX * 8 /* Convert bytes to bits */);
2643 pHlp->pfnSSMPutU32(pSSM, 0); /* Legacy; was PDMAUDIOFMT, unused now. */
2644
2645 pHlp->pfnSSMPutS32(pSSM, pStream->dma_auto);
2646 pHlp->pfnSSMPutS32(pSSM, pStream->cbDmaBlockSize);
2647 pHlp->pfnSSMPutS32(pSSM, pStream->fifo);
2648 pHlp->pfnSSMPutS32(pSSM, pStream->Cfg.Props.uHz);
2649 pHlp->pfnSSMPutS32(pSSM, pStream->time_const);
2650 pHlp->pfnSSMPutS32(pSSM, 0); /* Legacy; was speaker control (on/off) for output stream. */
2651 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_needed_bytes);
2652 pHlp->pfnSSMPutS32(pSSM, pThis->cmd);
2653 pHlp->pfnSSMPutS32(pSSM, pStream->fDmaUseHigh);
2654 pHlp->pfnSSMPutS32(pSSM, pThis->highspeed);
2655 pHlp->pfnSSMPutS32(pSSM, pStream->can_write);
2656 pHlp->pfnSSMPutS32(pSSM, pThis->v2x6);
2657
2658 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_param);
2659 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_value);
2660 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_mode);
2661 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_param); /* Bug compatible! */
2662 pHlp->pfnSSMPutMem(pSSM, pThis->csp_regs, 256);
2663 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_index);
2664 pHlp->pfnSSMPutMem(pSSM, pThis->csp_reg83, 4);
2665 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83r);
2666 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83w);
2667
2668 pHlp->pfnSSMPutMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data));
2669 pHlp->pfnSSMPutMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data));
2670 pHlp->pfnSSMPutU8 (pSSM, pThis->test_reg);
2671 pHlp->pfnSSMPutU8 (pSSM, pThis->last_read_byte);
2672
2673 pHlp->pfnSSMPutS32(pSSM, pThis->nzero);
2674 pHlp->pfnSSMPutS32(pSSM, pStream->cbDmaLeft);
2675 pHlp->pfnSSMPutS32(pSSM, pStream->State.fEnabled ? 1 : 0);
2676 /* The stream's bitrate. Needed for backwards (legacy) compatibility. */
2677 pHlp->pfnSSMPutS32(pSSM, AudioHlpCalcBitrate(pThis->aStreams[SB16_IDX_OUT].Cfg.Props.cbSampleX * 8,
2678 pThis->aStreams[SB16_IDX_OUT].Cfg.Props.uHz,
2679 pThis->aStreams[SB16_IDX_OUT].Cfg.Props.cChannelsX));
2680 /* Block size alignment, superfluous and thus not saved anymore. Needed for backwards (legacy) compatibility. */
2681 pHlp->pfnSSMPutS32(pSSM, 0);
2682
2683 pHlp->pfnSSMPutS32(pSSM, pThis->mixer_nreg);
2684 return pHlp->pfnSSMPutMem(pSSM, pThis->mixer_regs, 256);
2685}
2686
2687/**
2688 * @callback_method_impl{FNSSMDEVSAVEEXEC}
2689 */
2690static DECLCALLBACK(int) sb16SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2691{
2692 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2693 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2694
2695 sb16LiveExec(pDevIns, pSSM, 0);
2696 return sb16Save(pHlp, pSSM, pThis);
2697}
2698
2699/**
2700 * Worker for sb16LoadExec.
2701 */
2702static int sb16Load(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PSB16STATE pThis)
2703{
2704 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2705
2706 /** Currently the saved state only contains the one-and-only output stream. */
2707 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2708
2709 int32_t s32Tmp;
2710 pHlp->pfnSSMGetS32(pSSM, &s32Tmp);
2711 pStream->HwCfgRuntime.uIrq = s32Tmp; /* IRQ. */
2712 pHlp->pfnSSMGetS32(pSSM, &s32Tmp);
2713 pStream->HwCfgRuntime.uDmaChanLow = s32Tmp; /* Low (8-bit) DMA channel. */
2714 pHlp->pfnSSMGetS32(pSSM, &s32Tmp);
2715 pStream->HwCfgRuntime.uDmaChanHigh = s32Tmp; /* High (16-bit) DMA channel. */
2716 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* Used I/O port. */
2717 pStream->HwCfgRuntime.uPort = s32Tmp;
2718 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* DSP version running. */
2719 pStream->HwCfgRuntime.uVer = s32Tmp;
2720 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_idx);
2721 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_out_data_len);
2722 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* Output stream: Numer of channels. */
2723 pStream->Cfg.Props.cChannelsX = (uint8_t)s32Tmp;
2724 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* Output stream: Signed format bit. */
2725 pStream->Cfg.Props.fSigned = s32Tmp == 0 ? false : true;
2726 pHlp->pfnSSMGetS32(pSSM, &s32Tmp);
2727 pStream->Cfg.Props.cbSampleX = s32Tmp / 8; /* Convert bits to bytes. */
2728 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was PDMAUDIOFMT, unused now. */
2729 pHlp->pfnSSMGetS32(pSSM, &pStream->dma_auto);
2730 pHlp->pfnSSMGetS32(pSSM, &pThis->aStreams[SB16_IDX_OUT].cbDmaBlockSize);
2731 pHlp->pfnSSMGetS32(pSSM, &pStream->fifo);
2732 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); pStream->Cfg.Props.uHz = s32Tmp;
2733 pHlp->pfnSSMGetS32(pSSM, &pStream->time_const);
2734 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was speaker (on / off) for output stream. */
2735 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_needed_bytes);
2736 pHlp->pfnSSMGetS32(pSSM, &pThis->cmd);
2737 pHlp->pfnSSMGetS32(pSSM, &pStream->fDmaUseHigh); /* Output stream: Whether to use the high or low DMA channel. */
2738 pHlp->pfnSSMGetS32(pSSM, &pThis->highspeed);
2739 pHlp->pfnSSMGetS32(pSSM, &pStream->can_write);
2740 pHlp->pfnSSMGetS32(pSSM, &pThis->v2x6);
2741
2742 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_param);
2743 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_value);
2744 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_mode);
2745 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_param); /* Bug compatible! */
2746 pHlp->pfnSSMGetMem(pSSM, pThis->csp_regs, 256);
2747 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_index);
2748 pHlp->pfnSSMGetMem(pSSM, pThis->csp_reg83, 4);
2749 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83r);
2750 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83w);
2751
2752 pHlp->pfnSSMGetMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data));
2753 pHlp->pfnSSMGetMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data));
2754 pHlp->pfnSSMGetU8 (pSSM, &pThis->test_reg);
2755 pHlp->pfnSSMGetU8 (pSSM, &pThis->last_read_byte);
2756
2757 pHlp->pfnSSMGetS32(pSSM, &pThis->nzero);
2758 pHlp->pfnSSMGetS32(pSSM, &pStream->cbDmaLeft);
2759 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* Output stream: DMA currently running bit. */
2760 const bool fStreamEnabled = s32Tmp == 0 ? false: true;
2761 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was the output stream's current bitrate (in bytes). */
2762 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was the output stream's DMA block alignment. */
2763
2764 int32_t mixer_nreg = 0;
2765 int rc = pHlp->pfnSSMGetS32(pSSM, &mixer_nreg);
2766 AssertRCReturn(rc, rc);
2767 pThis->mixer_nreg = (uint8_t)mixer_nreg;
2768 rc = pHlp->pfnSSMGetMem(pSSM, pThis->mixer_regs, 256);
2769 AssertRCReturn(rc, rc);
2770
2771 if (fStreamEnabled)
2772 sb16StreamControl(pDevIns, pThis, pStream, true /* fRun */);
2773
2774 /* Update the master (mixer) and PCM out volumes. */
2775 sb16UpdateVolume(pThis);
2776
2777 return VINF_SUCCESS;
2778}
2779
2780/**
2781 * @callback_method_impl{FNSSMDEVLOADEXEC}
2782 */
2783static DECLCALLBACK(int) sb16LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2784{
2785 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2786 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2787
2788 AssertMsgReturn( uVersion == SB16_SAVE_STATE_VERSION
2789 || uVersion == SB16_SAVE_STATE_VERSION_VBOX_30,
2790 ("%u\n", uVersion),
2791 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2792 if (uVersion > SB16_SAVE_STATE_VERSION_VBOX_30)
2793 {
2794 /** Currently the saved state only contains the one-and-only output stream. */
2795 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2796
2797 int32_t irq;
2798 pHlp->pfnSSMGetS32(pSSM, &irq);
2799 int32_t dma;
2800 pHlp->pfnSSMGetS32(pSSM, &dma);
2801 int32_t hdma;
2802 pHlp->pfnSSMGetS32(pSSM, &hdma);
2803 int32_t port;
2804 pHlp->pfnSSMGetS32(pSSM, &port);
2805 int32_t ver;
2806 int rc = pHlp->pfnSSMGetS32(pSSM, &ver);
2807 AssertRCReturn (rc, rc);
2808
2809 if ( irq != pStream->HwCfgDefault.uIrq
2810 || dma != pStream->HwCfgDefault.uDmaChanLow
2811 || hdma != pStream->HwCfgDefault.uDmaChanHigh
2812 || port != pStream->HwCfgDefault.uPort
2813 || ver != pStream->HwCfgDefault.uVer)
2814 {
2815 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
2816 N_("config changed: irq=%x/%x dma=%x/%x hdma=%x/%x port=%x/%x ver=%x/%x (saved/config)"),
2817 irq, pStream->HwCfgDefault.uIrq,
2818 dma, pStream->HwCfgDefault.uDmaChanLow,
2819 hdma, pStream->HwCfgDefault.uDmaChanHigh,
2820 port, pStream->HwCfgDefault.uPort,
2821 ver, pStream->HwCfgDefault.uVer);
2822 }
2823 }
2824
2825 if (uPass != SSM_PASS_FINAL)
2826 return VINF_SUCCESS;
2827
2828 return sb16Load(pDevIns, pSSM, pThis);
2829}
2830
2831
2832/*********************************************************************************************************************************
2833* IBase implementation *
2834*********************************************************************************************************************************/
2835
2836/**
2837 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2838 */
2839static DECLCALLBACK(void *) sb16QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2840{
2841 PSB16STATE pThis = RT_FROM_MEMBER(pInterface, SB16STATE, IBase);
2842 Assert(&pThis->IBase == pInterface);
2843
2844 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2845 return NULL;
2846}
2847
2848
2849/*********************************************************************************************************************************
2850* Device (PDM) handling *
2851*********************************************************************************************************************************/
2852
2853/**
2854 * Attach command, internal version.
2855 *
2856 * This is called to let the device attach to a driver for a specified LUN
2857 * during runtime. This is not called during VM construction, the device
2858 * constructor has to attach to all the available drivers.
2859 *
2860 * @returns VBox status code.
2861 * @param pThis SB16 state.
2862 * @param uLUN The logical unit which is being detached.
2863 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2864 * @param ppDrv Attached driver instance on success. Optional.
2865 */
2866static int sb16AttachInternal(PSB16STATE pThis, unsigned uLUN, uint32_t fFlags, PSB16DRIVER *ppDrv)
2867{
2868 RT_NOREF(fFlags);
2869
2870 /*
2871 * Attach driver.
2872 */
2873 PSB16DRIVER pDrv = (PSB16DRIVER)RTMemAllocZ(sizeof(SB16DRIVER));
2874 AssertReturn(pDrv, VERR_NO_MEMORY);
2875 RTStrPrintf(pDrv->szDesc, sizeof(pDrv->szDesc), "Audio driver port (SB16) for LUN #%u", uLUN);
2876
2877 PPDMIBASE pDrvBase;
2878 int rc = PDMDevHlpDriverAttach(pThis->pDevInsR3, uLUN, &pThis->IBase, &pDrvBase, pDrv->szDesc);
2879 if (RT_SUCCESS(rc))
2880 {
2881 pDrv->pDrvBase = pDrvBase;
2882 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
2883 AssertMsg(pDrv->pConnector != NULL, ("Configuration error: LUN #%u has no host audio interface, rc=%Rrc\n", uLUN, rc));
2884 pDrv->pSB16State = pThis;
2885 pDrv->uLUN = uLUN;
2886
2887 /*
2888 * For now we always set the driver at LUN 0 as our primary
2889 * host backend. This might change in the future.
2890 */
2891 if (pDrv->uLUN == 0)
2892 pDrv->fFlags |= PDMAUDIODRVFLAGS_PRIMARY;
2893
2894 LogFunc(("LUN#%u: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->fFlags));
2895
2896 /* Attach to driver list if not attached yet. */
2897 if (!pDrv->fAttached)
2898 {
2899 RTListAppend(&pThis->lstDrv, &pDrv->Node);
2900 pDrv->fAttached = true;
2901 }
2902
2903 if (ppDrv)
2904 *ppDrv = pDrv;
2905 }
2906 else
2907 {
2908 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2909 LogFunc(("No attached driver for LUN #%u\n", uLUN));
2910 RTMemFree(pDrv);
2911 }
2912
2913 LogFunc(("iLUN=%u, fFlags=0x%x, rc=%Rrc\n", uLUN, fFlags, rc));
2914 return rc;
2915}
2916
2917/**
2918 * Detach command, internal version.
2919 *
2920 * This is called to let the device detach from a driver for a specified LUN
2921 * during runtime.
2922 *
2923 * @returns VBox status code.
2924 * @param pDevIns The device instance.
2925 * @param pThis The SB16 device state.
2926 * @param pDrv Driver to detach from device.
2927 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2928 */
2929static int sb16DetachInternal(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16DRIVER pDrv, uint32_t fFlags)
2930{
2931 RT_NOREF(fFlags);
2932
2933 /** @todo r=andy Any locking required here? */
2934
2935 sb16RemoveDrv(pDevIns, pThis, pDrv);
2936
2937 LogFunc(("uLUN=%u, fFlags=0x%x\n", pDrv->uLUN, fFlags));
2938 return VINF_SUCCESS;
2939}
2940
2941/**
2942 * @interface_method_impl{PDMDEVREG,pfnAttach}
2943 */
2944static DECLCALLBACK(int) sb16Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2945{
2946 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2947
2948 LogFunc(("iLUN=%u, fFlags=0x%x\n", iLUN, fFlags));
2949
2950 /** @todo r=andy Any locking required here? */
2951
2952 PSB16DRIVER pDrv;
2953 int rc2 = sb16AttachInternal(pThis, iLUN, fFlags, &pDrv);
2954 if (RT_SUCCESS(rc2))
2955 rc2 = sb16AddDrv(pDevIns, pThis, pDrv);
2956
2957 if (RT_FAILURE(rc2))
2958 LogFunc(("Failed with %Rrc\n", rc2));
2959
2960 return VINF_SUCCESS;
2961}
2962
2963/**
2964 * @interface_method_impl{PDMDEVREG,pfnDetach}
2965 */
2966static DECLCALLBACK(void) sb16Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2967{
2968 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2969 RT_NOREF(fFlags);
2970
2971 LogFunc(("iLUN=%u, fFlags=0x%x\n", iLUN, fFlags));
2972
2973 PSB16DRIVER pDrv, pDrvNext;
2974 RTListForEachSafe(&pThis->lstDrv, pDrv, pDrvNext, SB16DRIVER, Node)
2975 {
2976 if (pDrv->uLUN == iLUN)
2977 {
2978 int rc2 = sb16DetachInternal(pDevIns, pThis, pDrv, fFlags);
2979 if (RT_SUCCESS(rc2))
2980 {
2981 RTMemFree(pDrv);
2982 pDrv = NULL;
2983 }
2984 break;
2985 }
2986 }
2987}
2988
2989
2990#ifdef VBOX_WITH_AUDIO_SB16_ONETIME_INIT
2991/**
2992 * Replaces a driver with a the NullAudio drivers.
2993 *
2994 * @returns VBox status code.
2995 * @param pThis Device instance.
2996 * @param iLun The logical unit which is being replaced.
2997 */
2998static int sb16ReconfigLunWithNullAudio(PSB16STATE pThis, unsigned iLun)
2999{
3000 int rc = PDMDevHlpDriverReconfigure2(pThis->pDevInsR3, iLun, "AUDIO", "NullAudio");
3001 if (RT_SUCCESS(rc))
3002 rc = sb16AttachInternal(pThis, iLun, 0 /* fFlags */, NULL /* ppDrv */);
3003 LogFunc(("pThis=%p, iLun=%u, rc=%Rrc\n", pThis, iLun, rc));
3004 return rc;
3005}
3006#endif
3007
3008
3009/**
3010 * @interface_method_impl{PDMDEVREG,pfnReset}
3011 */
3012static DECLCALLBACK(void) sb16DevReset(PPDMDEVINS pDevIns)
3013{
3014 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
3015
3016 LogRel2(("SB16: Reset\n"));
3017
3018 pThis->mixer_regs[0x82] = 0;
3019 pThis->csp_regs[5] = 1;
3020 pThis->csp_regs[9] = 0xf8;
3021
3022 pThis->dsp_in_idx = 0;
3023 pThis->dsp_out_data_len = 0;
3024 pThis->dsp_in_needed_bytes = 0;
3025 pThis->nzero = 0;
3026 pThis->highspeed = 0;
3027 pThis->v2x6 = 0;
3028 pThis->cmd = -1;
3029
3030 sb16MixerReset(pThis);
3031 sb16SpeakerControl(pThis, 0);
3032 sb16DspCmdResetLegacy(pThis);
3033}
3034
3035/**
3036 * Powers off the device.
3037 *
3038 * @param pDevIns Device instance to power off.
3039 */
3040static DECLCALLBACK(void) sb16PowerOff(PPDMDEVINS pDevIns)
3041{
3042 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
3043
3044 LogRel2(("SB16: Powering off ...\n"));
3045
3046 /*
3047 * Destroy all streams.
3048 */
3049 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
3050 sb16StreamDestroy(pDevIns, pThis, &pThis->aStreams[i]);
3051
3052 /*
3053 * Destroy all sinks.
3054 */
3055 if (pThis->pSinkOut)
3056 {
3057 AudioMixerSinkDestroy(pThis->pSinkOut, pDevIns);
3058 pThis->pSinkOut = NULL;
3059 }
3060 /** @todo Ditto for sinks. */
3061
3062 /*
3063 * Note: Destroy the mixer while powering off and *not* in sb16Destruct,
3064 * giving the mixer the chance to release any references held to
3065 * PDM audio streams it maintains.
3066 */
3067 if (pThis->pMixer)
3068 {
3069 AudioMixerDestroy(pThis->pMixer, pDevIns);
3070 pThis->pMixer = NULL;
3071 }
3072}
3073
3074/**
3075 * @interface_method_impl{PDMDEVREG,pfnDestruct}
3076 */
3077static DECLCALLBACK(int) sb16Destruct(PPDMDEVINS pDevIns)
3078{
3079 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); /* this shall come first */
3080 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
3081
3082 LogFlowFuncEnter();
3083
3084 PSB16DRIVER pDrv;
3085 while (!RTListIsEmpty(&pThis->lstDrv))
3086 {
3087 pDrv = RTListGetFirst(&pThis->lstDrv, SB16DRIVER, Node);
3088
3089 RTListNodeRemove(&pDrv->Node);
3090 RTMemFree(pDrv);
3091 }
3092
3093 return VINF_SUCCESS;
3094}
3095
3096/**
3097 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
3098 */
3099static DECLCALLBACK(int) sb16Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
3100{
3101 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* this shall come first */
3102 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
3103 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3104 RT_NOREF(iInstance);
3105
3106 Assert(iInstance == 0);
3107
3108 /*
3109 * Initialize the data so sb16Destruct runs without a hitch if we return early.
3110 */
3111 pThis->pDevInsR3 = pDevIns;
3112 pThis->IBase.pfnQueryInterface = sb16QueryInterface;
3113 pThis->cmd = -1;
3114
3115 pThis->csp_regs[5] = 1;
3116 pThis->csp_regs[9] = 0xf8;
3117
3118 RTListInit(&pThis->lstDrv);
3119
3120 /*
3121 * Validate and read config data.
3122 */
3123 /* Note: For now we only support the one-and-only output stream. */
3124 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
3125
3126 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "IRQ|DMA|DMA16|Port|Version|TimerHz|DebugEnabled|DebugPathOut", "");
3127 int rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pStream->HwCfgDefault.uIrq, 5);
3128 if (RT_FAILURE(rc))
3129 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"IRQ\" value"));
3130 /* Sanity-check supported SB16 IRQs. */
3131 if ( 2 != pStream->HwCfgDefault.uIrq
3132 && 5 != pStream->HwCfgDefault.uIrq
3133 && 7 != pStream->HwCfgDefault.uIrq
3134 && 10 != pStream->HwCfgDefault.uIrq)
3135 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"IRQ\" value."));
3136 pStream->HwCfgRuntime.uIrq = pStream->HwCfgDefault.uIrq;
3137
3138 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA", &pStream->HwCfgDefault.uDmaChanLow, 1);
3139 if (RT_FAILURE(rc))
3140 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"DMA\" value"));
3141 if ( 0 != pStream->HwCfgDefault.uDmaChanLow
3142 && 1 != pStream->HwCfgDefault.uDmaChanLow
3143 && 3 != pStream->HwCfgDefault.uDmaChanLow)
3144 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"DMA\" value."));
3145 pStream->HwCfgRuntime.uDmaChanLow = pStream->HwCfgDefault.uDmaChanLow;
3146
3147 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA16", &pStream->HwCfgDefault.uDmaChanHigh, 5);
3148 if (RT_FAILURE(rc))
3149 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"DMA16\" value"));
3150 if ( 5 != pStream->HwCfgDefault.uDmaChanHigh
3151 && 6 != pStream->HwCfgDefault.uDmaChanHigh
3152 && 7 != pStream->HwCfgDefault.uDmaChanHigh)
3153 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"DMA16\" value."));
3154 pStream->HwCfgRuntime.uDmaChanHigh = pStream->HwCfgDefault.uDmaChanHigh;
3155
3156 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pStream->HwCfgDefault.uPort, 0x220);
3157 if (RT_FAILURE(rc))
3158 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"Port\" value"));
3159 /* Sanity-check supported SB16 ports. */
3160 if ( 0x220 != pStream->HwCfgDefault.uPort
3161 && 0x240 != pStream->HwCfgDefault.uPort
3162 && 0x260 != pStream->HwCfgDefault.uPort
3163 && 0x280 != pStream->HwCfgDefault.uPort)
3164 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"Port\" value. Did you specify it as a hex value (e.g. 0x220)?"));
3165 pStream->HwCfgRuntime.uPort = pStream->HwCfgDefault.uPort;
3166
3167 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "Version", &pStream->HwCfgDefault.uVer, 0x0405);
3168 if (RT_FAILURE(rc))
3169 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"Version\" value"));
3170 pStream->HwCfgRuntime.uVer = pStream->HwCfgDefault.uVer;
3171
3172 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "TimerHz", &pStream->uTimerHz, SB16_TIMER_HZ_DEFAULT);
3173 if (RT_FAILURE(rc))
3174 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: failed to read Hertz rate as unsigned integer"));
3175 if (pStream->uTimerHz == 0)
3176 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Hertz rate is invalid"));
3177 if (pStream->uTimerHz > 2048)
3178 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Maximum Hertz rate is 2048"));
3179
3180 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DebugEnabled", &pThis->Dbg.fEnabled, false);
3181 if (RT_FAILURE(rc))
3182 return PDMDEV_SET_ERROR(pDevIns, rc,
3183 N_("SB16 configuration error: failed to read debugging enabled flag as boolean"));
3184
3185 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "DebugPathOut", &pThis->Dbg.pszOutPath, NULL);
3186 if (RT_FAILURE(rc))
3187 return PDMDEV_SET_ERROR(pDevIns, rc,
3188 N_("SB16 configuration error: failed to read debugging output path flag as string"));
3189
3190 if (pThis->Dbg.fEnabled)
3191 LogRel2(("SB16: Debug output will be saved to '%s'\n", pThis->Dbg.pszOutPath));
3192
3193 /*
3194 * Create internal software mixer.
3195 * Must come before we do the device's mixer reset.
3196 */
3197 rc = AudioMixerCreate("SB16 Mixer", 0 /* uFlags */, &pThis->pMixer);
3198 AssertRCReturn(rc, rc);
3199
3200 AssertRCReturn(rc, rc);
3201 rc = AudioMixerCreateSink(pThis->pMixer, "PCM Output",
3202 AUDMIXSINKDIR_OUTPUT, pDevIns, &pThis->pSinkOut);
3203 AssertRCReturn(rc, rc);
3204
3205 /*
3206 * Create all hardware streams.
3207 * For now we have one stream only, namely the output (playback) stream.
3208 */
3209 AssertCompile(RT_ELEMENTS(pThis->aStreams) == SB16_MAX_STREAMS);
3210 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
3211 {
3212 rc = sb16StreamCreate(pThis, &pThis->aStreams[i], i /* uIdx */);
3213 AssertRCReturn(rc, rc);
3214 }
3215
3216 /*
3217 * Setup the mixer now that we've got the irq and dma channel numbers.
3218 */
3219 pThis->mixer_regs[0x80] = magic_of_irq(pStream->HwCfgRuntime.uIrq);
3220 pThis->mixer_regs[0x81] = (1 << pStream->HwCfgRuntime.uDmaChanLow) | (1 << pStream->HwCfgRuntime.uDmaChanHigh);
3221 pThis->mixer_regs[0x82] = 2 << 5;
3222
3223 sb16MixerReset(pThis);
3224
3225 /*
3226 * Create timers.
3227 */
3228 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIRQ, pThis,
3229 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IRQ", &pThis->hTimerIRQ);
3230 AssertRCReturn(rc, rc);
3231
3232 static const char * const s_apszNames[] = { "SB16 OUT" };
3233 AssertCompile(RT_ELEMENTS(s_apszNames) == SB16_MAX_STREAMS);
3234 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
3235 {
3236 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, &pThis->aStreams[i],
3237 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, s_apszNames[i], &pThis->aStreams[i].hTimerIO);
3238 AssertRCReturn(rc, rc);
3239
3240 pThis->aStreams[i].cTicksTimerIOInterval = PDMDevHlpTimerGetFreq(pDevIns, pThis->aStreams[i].hTimerIO)
3241 / pThis->aStreams[i].uTimerHz;
3242 pThis->aStreams[i].tsTimerIO = PDMDevHlpTimerGet(pDevIns, pThis->aStreams[i].hTimerIO);
3243 }
3244
3245 /*
3246 * Register I/O and DMA.
3247 */
3248 static const IOMIOPORTDESC s_aAllDescs[] =
3249 {
3250 { "FM Music Status Port", "FM Music Register Address Port", NULL, NULL }, // 00h
3251 { NULL, "FM Music Data Port", NULL, NULL }, // 01h
3252 { "Advanced FM Music Status Port", "Advanced FM Music Register Address Port", NULL, NULL }, // 02h
3253 { NULL, "Advanced FM Music Data Port", NULL, NULL }, // 03h
3254 { NULL, "Mixer chip Register Address Port", NULL, NULL }, // 04h
3255 { "Mixer chip Data Port", NULL, NULL, NULL }, // 05h
3256 { NULL, "DSP Reset", NULL, NULL }, // 06h
3257 { "Unused7", "Unused7", NULL, NULL }, // 07h
3258 { "FM Music Status Port", "FM Music Register Port", NULL, NULL }, // 08h
3259 { NULL, "FM Music Data Port", NULL, NULL }, // 09h
3260 { "DSP Read Data Port", NULL, NULL, NULL }, // 0Ah
3261 { "UnusedB", "UnusedB", NULL, NULL }, // 0Bh
3262 { "DSP Write-Buffer Status", "DSP Write Command/Data", NULL, NULL }, // 0Ch
3263 { "UnusedD", "UnusedD", NULL, NULL }, // 0Dh
3264 { "DSP Read-Buffer Status", NULL, NULL, NULL }, // 0Eh
3265 { "IRQ16ACK", NULL, NULL, NULL }, // 0Fh
3266 { "CD-ROM Data Register", "CD-ROM Command Register", NULL, NULL }, // 10h
3267 { "CD-ROM Status Register", NULL, NULL, NULL }, // 11h
3268 { NULL, "CD-ROM Reset Register", NULL, NULL }, // 12h
3269 { NULL, "CD-ROM Enable Register", NULL, NULL }, // 13h
3270 { NULL, NULL, NULL, NULL },
3271 };
3272
3273 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pStream->HwCfgRuntime.uPort + 0x04 /*uPort*/, 2 /*cPorts*/,
3274 sb16IoPortMixerWrite, sb16IoPortMixerRead,
3275 "SB16 - Mixer", &s_aAllDescs[4], &pThis->hIoPortsMixer);
3276 AssertRCReturn(rc, rc);
3277 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pStream->HwCfgRuntime.uPort + 0x06 /*uPort*/, 10 /*cPorts*/,
3278 sb16IoPortDspWrite, sb16IoPortDspRead,
3279 "SB16 - DSP", &s_aAllDescs[6], &pThis->hIoPortsDsp);
3280 AssertRCReturn(rc, rc);
3281
3282 rc = PDMDevHlpDMARegister(pDevIns, pStream->HwCfgRuntime.uDmaChanHigh, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */);
3283 AssertRCReturn(rc, rc);
3284 rc = PDMDevHlpDMARegister(pDevIns, pStream->HwCfgRuntime.uDmaChanLow, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */);
3285 AssertRCReturn(rc, rc);
3286
3287 /*
3288 * Register Saved state.
3289 */
3290 rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec);
3291 AssertRCReturn(rc, rc);
3292
3293# ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
3294 LogRel(("SB16: Asynchronous I/O enabled\n"));
3295# endif
3296 LogRel2(("SB16: Using port %#x, DMA%RU8, IRQ%RU8\n",
3297 pStream->HwCfgRuntime.uPort, pStream->HwCfgRuntime.uDmaChanLow, pStream->HwCfgRuntime.uIrq));
3298
3299 /*
3300 * Attach drivers. We ASSUME they are configured consecutively without any
3301 * gaps, so we stop when we hit the first LUN w/o a driver configured.
3302 */
3303 for (unsigned iLun = 0; ; iLun++)
3304 {
3305 AssertBreak(iLun < UINT8_MAX);
3306 LogFunc(("Trying to attach driver for LUN#%u ...\n", iLun));
3307 rc = sb16AttachInternal(pThis, iLun, 0 /* fFlags */, NULL /* ppDrv */);
3308 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
3309 {
3310 LogFunc(("cLUNs=%u\n", iLun));
3311 break;
3312 }
3313 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("LUN#%u: rc=%Rrc\n", iLun, rc), rc);
3314 }
3315
3316 sb16DspCmdResetLegacy(pThis);
3317
3318#ifdef VBOX_WITH_AUDIO_SB16_ONETIME_INIT
3319 PSB16DRIVER pDrv, pNext;
3320 RTListForEachSafe(&pThis->lstDrv, pDrv, pNext, SB16DRIVER, Node)
3321 {
3322 /*
3323 * Only primary drivers are critical for the VM to run. Everything else
3324 * might not worth showing an own error message box in the GUI.
3325 */
3326 if (!(pDrv->fFlags & PDMAUDIODRVFLAGS_PRIMARY))
3327 continue;
3328
3329 PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
3330 AssertPtr(pCon);
3331
3332 /** @todo No input streams available for SB16 yet. */
3333 if (!AudioMixerStreamIsValid(pDrv->Out.pMixStrm))
3334 {
3335 LogRel(("SB16: Falling back to NULL backend (no sound audible)\n"));
3336
3337 sb16DspCmdResetLegacy(pThis);
3338 sb16ReconfigLunWithNullAudio(pThis, pDrv->uLUN);
3339 pDrv = NULL; /* no longer valid */
3340
3341 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
3342 N_("No audio devices could be opened. "
3343 "Selecting the NULL audio backend with the consequence that no sound is audible"));
3344 }
3345 }
3346#endif /* VBOX_WITH_AUDIO_SB16_ONETIME_INIT */
3347
3348 /*
3349 * Register statistics.
3350 */
3351# ifdef VBOX_WITH_STATISTICS
3352 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimerIO, STAMTYPE_PROFILE, "Timer", STAMUNIT_TICKS_PER_CALL, "Profiling sb16TimerIO.");
3353 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, "BytesRead", STAMUNIT_BYTES, "Bytes read from SB16 emulation.");
3354# endif
3355
3356 return VINF_SUCCESS;
3357}
3358
3359const PDMDEVREG g_DeviceSB16 =
3360{
3361 /* .u32Version = */ PDM_DEVREG_VERSION,
3362 /* .uReserved0 = */ 0,
3363 /* .szName = */ "sb16",
3364 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE
3365 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION /* stream clearnup with working drivers */,
3366 /* .fClass = */ PDM_DEVREG_CLASS_AUDIO,
3367 /* .cMaxInstances = */ 1,
3368 /* .uSharedVersion = */ 42,
3369 /* .cbInstanceShared = */ sizeof(SB16STATE),
3370 /* .cbInstanceCC = */ 0,
3371 /* .cbInstanceRC = */ 0,
3372 /* .cMaxPciDevices = */ 0,
3373 /* .cMaxMsixVectors = */ 0,
3374 /* .pszDescription = */ "Sound Blaster 16 Controller",
3375#if defined(IN_RING3)
3376 /* .pszRCMod = */ "",
3377 /* .pszR0Mod = */ "",
3378 /* .pfnConstruct = */ sb16Construct,
3379 /* .pfnDestruct = */ sb16Destruct,
3380 /* .pfnRelocate = */ NULL,
3381 /* .pfnMemSetup = */ NULL,
3382 /* .pfnPowerOn = */ NULL,
3383 /* .pfnReset = */ sb16DevReset,
3384 /* .pfnSuspend = */ NULL,
3385 /* .pfnResume = */ NULL,
3386 /* .pfnAttach = */ sb16Attach,
3387 /* .pfnDetach = */ sb16Detach,
3388 /* .pfnQueryInterface = */ NULL,
3389 /* .pfnInitComplete = */ NULL,
3390 /* .pfnPowerOff = */ sb16PowerOff,
3391 /* .pfnSoftReset = */ NULL,
3392 /* .pfnReserved0 = */ NULL,
3393 /* .pfnReserved1 = */ NULL,
3394 /* .pfnReserved2 = */ NULL,
3395 /* .pfnReserved3 = */ NULL,
3396 /* .pfnReserved4 = */ NULL,
3397 /* .pfnReserved5 = */ NULL,
3398 /* .pfnReserved6 = */ NULL,
3399 /* .pfnReserved7 = */ NULL,
3400#elif defined(IN_RING0)
3401 /* .pfnEarlyConstruct = */ NULL,
3402 /* .pfnConstruct = */ NULL,
3403 /* .pfnDestruct = */ NULL,
3404 /* .pfnFinalDestruct = */ NULL,
3405 /* .pfnRequest = */ NULL,
3406 /* .pfnReserved0 = */ NULL,
3407 /* .pfnReserved1 = */ NULL,
3408 /* .pfnReserved2 = */ NULL,
3409 /* .pfnReserved3 = */ NULL,
3410 /* .pfnReserved4 = */ NULL,
3411 /* .pfnReserved5 = */ NULL,
3412 /* .pfnReserved6 = */ NULL,
3413 /* .pfnReserved7 = */ NULL,
3414#elif defined(IN_RC)
3415 /* .pfnConstruct = */ NULL,
3416 /* .pfnReserved0 = */ NULL,
3417 /* .pfnReserved1 = */ NULL,
3418 /* .pfnReserved2 = */ NULL,
3419 /* .pfnReserved3 = */ NULL,
3420 /* .pfnReserved4 = */ NULL,
3421 /* .pfnReserved5 = */ NULL,
3422 /* .pfnReserved6 = */ NULL,
3423 /* .pfnReserved7 = */ NULL,
3424#else
3425# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3426#endif
3427 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
3428};
3429
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