VirtualBox

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

Last change on this file since 98405 was 98405, checked in by vboxsync, 20 months ago

Audio: Fixed race condition between announcing remaining device DMA data on draining and fetching it in the async I/O mixer thread via AudioMixerSinkTransferFromCircBuf(). This is due to the device's DMA buffer being reset via its device-specific functions (i.e. ichac97R3StreamReset()) immediately after disabling the stream (i.e. ichac97R3StreamDisable()). See comment 2 for more details. bugref:10354

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette