VirtualBox

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

Last change on this file was 103137, checked in by vboxsync, 3 months ago

Audio: More locking needed to avoid debug assertions when draining a stream on stream disable. This debug assertion will happen because EMT and the mixer's async I/O thread work on the same circular buffer. There can be situations, if no locking is being used, that the async I/O thread just has consumed the remaining data just a tad before EMT looks for remaining data within the same buffer when disabling the stream [Doxygen fix]. bugref:10354

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

© 2023 Oracle
ContactPrivacy policyTerms of Use