VirtualBox

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

Last change on this file since 97441 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • 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 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * DevSB16 - VBox SB16 Audio Controller.
4 */
5
6/*
7 * Copyright (C) 2015-2022 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 = AudioMixerSinkDrainAndStop(pSink, pStream->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStream->State.pCircBuf) : 0);
2015 AssertRCReturn(rc, rc);
2016 }
2017
2018 pStream->State.fEnabled = fEnable;
2019
2020 return rc;
2021}
2022
2023/**
2024 * Retrieves the audio mixer sink of a corresponding SB16 stream.
2025 *
2026 * @returns Pointer to audio mixer sink if found, or NULL if not found / invalid.
2027 * @param pThis The SB16 state.
2028 * @param uIdx Stream index to get audio mixer sink for.
2029 */
2030DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx)
2031{
2032 AssertReturn(uIdx <= SB16_MAX_STREAMS, NULL);
2033
2034 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
2035 if (uIdx == SB16_IDX_OUT)
2036 return pThis->pSinkOut; /* Can be NULL if not configured / set up yet. */
2037
2038 AssertMsgFailed(("No sink attached (yet) for index %RU8\n", uIdx));
2039 return NULL;
2040}
2041
2042/**
2043 * Returns the audio direction of a specified stream descriptor.
2044 *
2045 * @returns Audio direction.
2046 * @param uIdx Stream index to get audio direction for.
2047 */
2048DECLINLINE(PDMAUDIODIR) sb16GetDirFromIndex(uint8_t uIdx)
2049{
2050 AssertReturn(uIdx <= SB16_MAX_STREAMS, PDMAUDIODIR_INVALID);
2051
2052 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
2053 if (uIdx == SB16_IDX_OUT)
2054 return PDMAUDIODIR_OUT;
2055
2056 return PDMAUDIODIR_INVALID;
2057}
2058
2059/**
2060 * Creates a SB16 audio stream.
2061 *
2062 * @returns VBox status code.
2063 * @param pThis The SB16 state.
2064 * @param pStream The SB16 stream to create.
2065 * @param uIdx Stream index to assign.
2066 */
2067static int sb16StreamCreate(PSB16STATE pThis, PSB16STREAM pStream, uint8_t uIdx)
2068{
2069 LogFlowFuncEnter();
2070
2071 pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled;
2072
2073 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2074 { /* likely */ }
2075 else
2076 {
2077 int rc2 = AudioHlpFileCreateF(&pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_FLAGS_NONE, AUDIOHLPFILETYPE_WAV,
2078 pThis->Dbg.pszOutPath, AUDIOHLPFILENAME_FLAGS_NONE, 0 /*uInstance*/,
2079 sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_IN
2080 ? "sb16StreamWriteSD%RU8" : "sb16StreamReadSD%RU8", pStream->uIdx);
2081 AssertRC(rc2);
2082
2083 /* Delete stale debugging files from a former run. */
2084 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
2085 }
2086
2087 pStream->uIdx = uIdx;
2088
2089 return VINF_SUCCESS;
2090}
2091
2092/**
2093 * Destroys a SB16 audio stream.
2094 *
2095 * @returns VBox status code.
2096 * @param pDevIns The device instance.
2097 * @param pThis The SB16 state.
2098 * @param pStream The SB16 stream to destroy.
2099 */
2100static int sb16StreamDestroy(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2101{
2102 LogFlowFuncEnter();
2103
2104 sb16StreamClose(pDevIns, pThis, pStream);
2105
2106 if (pStream->State.fRegisteredAsyncUpdateJob)
2107 {
2108 PAUDMIXSINK pSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
2109 if (pSink)
2110 AudioMixerSinkRemoveUpdateJob(pSink, sb16StreamUpdateAsyncIoJob, pStream);
2111 pStream->State.fRegisteredAsyncUpdateJob = false;
2112 }
2113
2114 if (pStream->State.pCircBuf)
2115 {
2116 RTCircBufDestroy(pStream->State.pCircBuf);
2117 pStream->State.pCircBuf = NULL;
2118 }
2119
2120 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2121 { /* likely */ }
2122 else
2123 {
2124 AudioHlpFileDestroy(pStream->Dbg.Runtime.pFileDMA);
2125 pStream->Dbg.Runtime.pFileDMA = NULL;
2126 }
2127
2128 pStream->uIdx = UINT8_MAX;
2129
2130 return VINF_SUCCESS;
2131}
2132
2133/**
2134 * Resets a SB16 stream.
2135 *
2136 * @param pThis The SB16 state.
2137 * @param pStream The SB16 stream to reset.
2138 */
2139static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream)
2140{
2141 LogFlowFuncEnter();
2142
2143 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
2144 if (pStream->dma_auto)
2145 {
2146 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 1);
2147 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
2148
2149 pStream->dma_auto = 0;
2150 }
2151
2152 sb16StreamControl(pThis->pDevInsR3, pThis, pStream, false /* fRun */);
2153 sb16StreamEnable(pThis, pStream, false /* fEnable */, false /* fForce */);
2154
2155 switch (pStream->uIdx)
2156 {
2157 case SB16_IDX_OUT:
2158 {
2159 pStream->Cfg.enmDir = PDMAUDIODIR_OUT;
2160 pStream->Cfg.enmPath = PDMAUDIOPATH_OUT_FRONT;
2161
2162 PDMAudioPropsInit(&pStream->Cfg.Props, 1 /* 8-bit */, false /* fSigned */, 1 /* Mono */, 11025 /* uHz */);
2163 RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output");
2164 break;
2165 }
2166
2167 default:
2168 AssertFailed();
2169 break;
2170 }
2171
2172 pStream->cbDmaLeft = 0;
2173 pStream->cbDmaBlockSize = 0;
2174 pStream->can_write = 1; /** @ŧodo r=andy BUGBUG Figure out why we (still) need this. */
2175
2176 /** @todo Also reset corresponding DSP values here? */
2177}
2178
2179/**
2180 * Opens a SB16 stream with its current mixer settings.
2181 *
2182 * @returns VBox status code.
2183 * @param pDevIns The device instance.
2184 * @param pThis The SB16 device state.
2185 * @param pStream The SB16 stream to open.
2186 *
2187 * @note This currently only supports the one and only output stream.
2188 */
2189static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2190{
2191 LogFlowFuncEnter();
2192 AssertLogRelReturn(PDMAudioPropsAreValid(&pStream->Cfg.Props), VERR_INTERNAL_ERROR_5);
2193
2194 switch (pStream->uIdx)
2195 {
2196 case SB16_IDX_OUT:
2197 pStream->Cfg.enmDir = PDMAUDIODIR_OUT;
2198 pStream->Cfg.enmPath = PDMAUDIOPATH_OUT_FRONT;
2199
2200 RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output");
2201 break;
2202
2203 default:
2204 AssertFailed();
2205 break;
2206 }
2207
2208 LogRel2(("SB16: (Re-)Opening stream '%s' (%RU32Hz, %RU8 channels, %s%RU8)\n", pStream->Cfg.szName, pStream->Cfg.Props.uHz,
2209 PDMAudioPropsChannels(&pStream->Cfg.Props), pStream->Cfg.Props.fSigned ? "S" : "U",
2210 PDMAudioPropsSampleBits(&pStream->Cfg.Props)));
2211
2212 /* (Re-)create the stream's internal ring buffer. */
2213 if (pStream->State.pCircBuf)
2214 {
2215 RTCircBufDestroy(pStream->State.pCircBuf);
2216 pStream->State.pCircBuf = NULL;
2217 }
2218
2219 /** @todo r=bird: two DMA periods is probably too little. */
2220 const uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props,
2221 (RT_MS_1SEC / pStream->uTimerHz) * 2 /* Use double buffering here */);
2222
2223 int rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf);
2224 AssertRCReturn(rc, rc);
2225 pStream->State.StatDmaBufSize = (uint32_t)RTCircBufSize(pStream->State.pCircBuf);
2226
2227 /* Set scheduling hint. */
2228 pStream->Cfg.Device.cMsSchedulingHint = RT_MS_1SEC / RT_MIN(pStream->uTimerHz, 1);
2229
2230 PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
2231 AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER);
2232
2233 sb16RemoveDrvStreams(pDevIns, pThis,
2234 sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.enmPath);
2235
2236 rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
2237 if (RT_SUCCESS(rc))
2238 {
2239 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2240 { /* likely */ }
2241 else
2242 {
2243 /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */
2244 if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
2245 {
2246 AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
2247 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
2248 }
2249
2250 int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
2251 &pStream->Cfg.Props);
2252 AssertRC(rc2);
2253 }
2254 }
2255
2256 LogFlowFuncLeaveRC(rc);
2257 return rc;
2258}
2259
2260/**
2261 * Closes a SB16 stream.
2262 *
2263 * @param pDevIns The device instance.
2264 * @param pThis SB16 state.
2265 * @param pStream The SB16 stream to close.
2266 */
2267static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2268{
2269 RT_NOREF(pDevIns, pThis, pStream);
2270
2271 LogFlowFuncEnter();
2272
2273 /* Nothing to do in here right now. */
2274}
2275
2276static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cbBytes)
2277{
2278 RT_NOREF(pStream);
2279
2280 uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pThis->pDevInsR3, pThis->hTimerIRQ);
2281
2282 const uint64_t usBytes = PDMAudioPropsBytesToMicro(&pStream->Cfg.Props, cbBytes);
2283 const uint64_t cTransferTicks = PDMDevHlpTimerFromMicro(pThis->pDevInsR3, pThis->hTimerIRQ, usBytes);
2284
2285 LogFlowFunc(("%RU32 bytes -> %RU64 ticks\n", cbBytes, cTransferTicks));
2286
2287 if (cTransferTicks < uTimerHz / 1024) /** @todo Explain this. */
2288 {
2289 LogFlowFunc(("IRQ\n"));
2290 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 1);
2291 }
2292 else
2293 {
2294 LogFlowFunc(("Scheduled\n"));
2295 PDMDevHlpTimerSetRelative(pThis->pDevInsR3, pThis->hTimerIRQ, cTransferTicks, NULL);
2296 }
2297}
2298
2299
2300/**
2301 * Output streams: Pushes data to the mixer.
2302 *
2303 * @param pStream The SB16 stream.
2304 * @param pSink The mixer sink to push to.
2305 */
2306static void sb16StreamPushToMixer(PSB16STREAM pStream, PAUDMIXSINK pSink)
2307{
2308#ifdef LOG_ENABLED
2309 uint64_t const offReadOld = pStream->State.offRead;
2310#endif
2311 pStream->State.offRead = AudioMixerSinkTransferFromCircBuf(pSink,
2312 pStream->State.pCircBuf,
2313 pStream->State.offRead,
2314 pStream->uIdx,
2315 /** @todo pStream->Dbg.Runtime.fEnabled
2316 ? pStream->Dbg.Runtime.pFileStream :*/ NULL);
2317
2318 Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64\n", pStream->uIdx,
2319 pStream->State.offRead - offReadOld, pStream->State.offRead));
2320
2321 /* Update buffer stats. */
2322 pStream->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
2323}
2324
2325
2326/**
2327 * @callback_method_impl{FNAUDMIXSINKUPDATE}
2328 *
2329 * For output streams this moves data from the internal DMA buffer (in which
2330 * ichac97R3StreamUpdateDma put it), thru the mixer and to the various backend
2331 * audio devices.
2332 */
2333static DECLCALLBACK(void) sb16StreamUpdateAsyncIoJob(PPDMDEVINS pDevIns, PAUDMIXSINK pSink, void *pvUser)
2334{
2335 PSB16STATE const pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2336 PSB16STREAM const pStream = (PSB16STREAM)pvUser;
2337 Assert(pStream->uIdx == (uintptr_t)(pStream - &pThis->aStreams[0]));
2338 Assert(pSink == sb16StreamIndexToSink(pThis, pStream->uIdx));
2339 RT_NOREF(pThis);
2340
2341 /*
2342 * Output.
2343 */
2344 if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_OUT)
2345 sb16StreamPushToMixer(pStream, pSink);
2346 /*
2347 * No input streams at present.
2348 */
2349 else
2350 AssertFailed();
2351}
2352
2353
2354/*********************************************************************************************************************************
2355* Saved state handling *
2356*********************************************************************************************************************************/
2357
2358/**
2359 * @callback_method_impl{FNSSMDEVLIVEEXEC}
2360 */
2361static DECLCALLBACK(int) sb16LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
2362{
2363 RT_NOREF(uPass);
2364
2365 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2366 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2367
2368 /** Currently the saved state only contains the one-and-only output stream. */
2369 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2370
2371 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uIrq);
2372 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uDmaChanLow);
2373 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uDmaChanHigh);
2374 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uPort);
2375 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uVer);
2376 return VINF_SSM_DONT_CALL_AGAIN;
2377}
2378
2379/**
2380 * Worker for sb16SaveExec.
2381 */
2382static int sb16Save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PSB16STATE pThis)
2383{
2384 /* The saved state only contains the one-and-only output stream. */
2385 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2386
2387 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uIrq);
2388 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uDmaChanLow);
2389 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uDmaChanHigh);
2390 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uPort);
2391 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uVer);
2392 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_idx);
2393 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_out_data_len);
2394
2395 pHlp->pfnSSMPutS32(pSSM, PDMAudioPropsChannels(&pStream->Cfg.Props) >= 2 ? 1 : 0);
2396 pHlp->pfnSSMPutS32(pSSM, PDMAudioPropsIsSigned(&pStream->Cfg.Props) ? 1 : 0);
2397 pHlp->pfnSSMPutS32(pSSM, PDMAudioPropsSampleBits(&pStream->Cfg.Props));
2398 pHlp->pfnSSMPutU32(pSSM, 0); /* Legacy; was PDMAUDIOFMT, unused now. */
2399
2400 pHlp->pfnSSMPutS32(pSSM, pStream->dma_auto);
2401 pHlp->pfnSSMPutS32(pSSM, pStream->cbDmaBlockSize);
2402 pHlp->pfnSSMPutS32(pSSM, pStream->fifo);
2403 pHlp->pfnSSMPutS32(pSSM, PDMAudioPropsHz(&pStream->Cfg.Props));
2404 pHlp->pfnSSMPutS32(pSSM, pStream->time_const);
2405 pHlp->pfnSSMPutS32(pSSM, 0); /* Legacy; was speaker control (on/off) for output stream. */
2406 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_needed_bytes);
2407 pHlp->pfnSSMPutS32(pSSM, pThis->cmd);
2408 pHlp->pfnSSMPutS32(pSSM, pStream->fDmaUseHigh);
2409 pHlp->pfnSSMPutS32(pSSM, pThis->highspeed);
2410 pHlp->pfnSSMPutS32(pSSM, pStream->can_write);
2411 pHlp->pfnSSMPutS32(pSSM, pThis->v2x6);
2412
2413 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_param);
2414 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_value);
2415 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_mode);
2416 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_param); /* Bug compatible! */
2417 pHlp->pfnSSMPutMem(pSSM, pThis->csp_regs, 256);
2418 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_index);
2419 pHlp->pfnSSMPutMem(pSSM, pThis->csp_reg83, 4);
2420 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83r);
2421 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83w);
2422
2423 pHlp->pfnSSMPutMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data));
2424 pHlp->pfnSSMPutMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data));
2425 pHlp->pfnSSMPutU8 (pSSM, pThis->test_reg);
2426 pHlp->pfnSSMPutU8 (pSSM, pThis->last_read_byte);
2427
2428 pHlp->pfnSSMPutS32(pSSM, pThis->nzero);
2429 pHlp->pfnSSMPutS32(pSSM, pStream->cbDmaLeft);
2430 pHlp->pfnSSMPutS32(pSSM, pStream->State.fEnabled ? 1 : 0);
2431 /* The stream's bitrate. Needed for backwards (legacy) compatibility. */
2432 pHlp->pfnSSMPutS32(pSSM, AudioHlpCalcBitrate(PDMAudioPropsSampleBits(&pThis->aStreams[SB16_IDX_OUT].Cfg.Props),
2433 PDMAudioPropsHz(&pThis->aStreams[SB16_IDX_OUT].Cfg.Props),
2434 PDMAudioPropsChannels(&pThis->aStreams[SB16_IDX_OUT].Cfg.Props)));
2435 /* Block size alignment, superfluous and thus not saved anymore. Needed for backwards (legacy) compatibility. */
2436 pHlp->pfnSSMPutS32(pSSM, 0);
2437
2438 pHlp->pfnSSMPutS32(pSSM, pThis->mixer_nreg);
2439 return pHlp->pfnSSMPutMem(pSSM, pThis->mixer_regs, 256);
2440}
2441
2442/**
2443 * @callback_method_impl{FNSSMDEVSAVEEXEC}
2444 */
2445static DECLCALLBACK(int) sb16SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2446{
2447 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2448 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2449
2450 sb16LiveExec(pDevIns, pSSM, 0);
2451 return sb16Save(pHlp, pSSM, pThis);
2452}
2453
2454/**
2455 * Worker for sb16LoadExec.
2456 */
2457static int sb16Load(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PSB16STATE pThis)
2458{
2459 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2460 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT]; /* The saved state only contains the one-and-only output stream. */
2461 int rc;
2462
2463 int32_t i32Tmp;
2464 pHlp->pfnSSMGetS32(pSSM, &i32Tmp);
2465 pStream->HwCfgRuntime.uIrq = i32Tmp; /* IRQ. */
2466 pHlp->pfnSSMGetS32(pSSM, &i32Tmp);
2467 pStream->HwCfgRuntime.uDmaChanLow = i32Tmp; /* Low (8-bit) DMA channel. */
2468 pHlp->pfnSSMGetS32(pSSM, &i32Tmp);
2469 pStream->HwCfgRuntime.uDmaChanHigh = i32Tmp; /* High (16-bit) DMA channel. */
2470 pHlp->pfnSSMGetS32(pSSM, &i32Tmp); /* Used I/O port. */
2471 pStream->HwCfgRuntime.uPort = i32Tmp;
2472 pHlp->pfnSSMGetS32(pSSM, &i32Tmp); /* DSP version running. */
2473 pStream->HwCfgRuntime.uVer = i32Tmp;
2474 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_idx);
2475 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_out_data_len);
2476
2477 rc = pHlp->pfnSSMGetS32(pSSM, &i32Tmp); /* Output stream: Number of channels. */
2478 AssertRCReturn(rc, rc);
2479 AssertReturn((uint32_t)i32Tmp <= 1, VERR_INVALID_PARAMETER); /* Paranoia. */
2480 if (i32Tmp) /* PDMAudioPropsSetChannels() will assert if channels are 0 (will be re-set on DMA run command). */
2481 PDMAudioPropsSetChannels(&pStream->Cfg.Props, (uint8_t)i32Tmp);
2482 pHlp->pfnSSMGetS32(pSSM, &i32Tmp); /* Output stream: Signed format bit. */
2483 pStream->Cfg.Props.fSigned = i32Tmp != 0;
2484 rc = pHlp->pfnSSMGetS32(pSSM, &i32Tmp); /* Output stream: Sample size in bits. */
2485 AssertRCReturn(rc, rc);
2486 if (i32Tmp) /* PDMAudioPropsSetSampleSize() will assert if sample size is 0 (will be re-set on DMA run command). */
2487 PDMAudioPropsSetSampleSize(&pStream->Cfg.Props, (uint8_t)(i32Tmp / 8));
2488
2489 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was PDMAUDIOFMT, unused now. */
2490 pHlp->pfnSSMGetS32(pSSM, &pStream->dma_auto);
2491 pHlp->pfnSSMGetS32(pSSM, &pStream->cbDmaBlockSize);
2492 pHlp->pfnSSMGetS32(pSSM, &pStream->fifo);
2493 pHlp->pfnSSMGetS32(pSSM, &i32Tmp); pStream->Cfg.Props.uHz = i32Tmp;
2494 pHlp->pfnSSMGetS32(pSSM, &pStream->time_const);
2495 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was speaker (on / off) for output stream. */
2496 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_needed_bytes);
2497 pHlp->pfnSSMGetS32(pSSM, &pThis->cmd);
2498 pHlp->pfnSSMGetS32(pSSM, &pStream->fDmaUseHigh); /* Output stream: Whether to use the high or low DMA channel. */
2499 pHlp->pfnSSMGetS32(pSSM, &pThis->highspeed);
2500 pHlp->pfnSSMGetS32(pSSM, &pStream->can_write);
2501 pHlp->pfnSSMGetS32(pSSM, &pThis->v2x6);
2502
2503 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_param);
2504 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_value);
2505 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_mode);
2506 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_param); /* Bug compatible! */
2507 pHlp->pfnSSMGetMem(pSSM, pThis->csp_regs, 256);
2508 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_index);
2509 pHlp->pfnSSMGetMem(pSSM, pThis->csp_reg83, 4);
2510 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83r);
2511 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83w);
2512
2513 pHlp->pfnSSMGetMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data));
2514 pHlp->pfnSSMGetMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data));
2515 pHlp->pfnSSMGetU8 (pSSM, &pThis->test_reg);
2516 pHlp->pfnSSMGetU8 (pSSM, &pThis->last_read_byte);
2517
2518 pHlp->pfnSSMGetS32(pSSM, &pThis->nzero);
2519 pHlp->pfnSSMGetS32(pSSM, &pStream->cbDmaLeft);
2520 pHlp->pfnSSMGetS32(pSSM, &i32Tmp); /* Output stream: DMA currently running bit. */
2521 const bool fStreamEnabled = i32Tmp != 0;
2522 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was the output stream's current bitrate (in bytes). */
2523 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was the output stream's DMA block alignment. */
2524
2525 int32_t mixer_nreg = 0;
2526 rc = pHlp->pfnSSMGetS32(pSSM, &mixer_nreg);
2527 AssertRCReturn(rc, rc);
2528 pThis->mixer_nreg = (uint8_t)mixer_nreg;
2529 rc = pHlp->pfnSSMGetMem(pSSM, pThis->mixer_regs, 256);
2530 AssertRCReturn(rc, rc);
2531
2532 if (fStreamEnabled)
2533 {
2534 /* Sanity: If stream is going be enabled, PCM props must be valid. Otherwise the saved state is borked somehow. */
2535 AssertMsgReturn(AudioHlpPcmPropsAreValidAndSupported(&pStream->Cfg.Props),
2536 ("PCM properties for stream #%RU8 are invalid\n", pStream->uIdx), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2537 sb16StreamControl(pDevIns, pThis, pStream, true /* fRun */);
2538 }
2539
2540 /* Update the master (mixer) and PCM out volumes. */
2541 sb16UpdateVolume(pThis);
2542
2543 return VINF_SUCCESS;
2544}
2545
2546/**
2547 * @callback_method_impl{FNSSMDEVLOADEXEC}
2548 */
2549static DECLCALLBACK(int) sb16LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2550{
2551 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2552 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2553
2554 AssertMsgReturn( uVersion == SB16_SAVE_STATE_VERSION
2555 || uVersion == SB16_SAVE_STATE_VERSION_VBOX_30,
2556 ("%u\n", uVersion),
2557 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2558 if (uVersion > SB16_SAVE_STATE_VERSION_VBOX_30)
2559 {
2560 /** Currently the saved state only contains the one-and-only output stream. */
2561 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2562
2563 int32_t irq;
2564 pHlp->pfnSSMGetS32(pSSM, &irq);
2565 int32_t dma;
2566 pHlp->pfnSSMGetS32(pSSM, &dma);
2567 int32_t hdma;
2568 pHlp->pfnSSMGetS32(pSSM, &hdma);
2569 int32_t port;
2570 pHlp->pfnSSMGetS32(pSSM, &port);
2571 int32_t ver;
2572 int rc = pHlp->pfnSSMGetS32(pSSM, &ver);
2573 AssertRCReturn (rc, rc);
2574
2575 if ( irq != pStream->HwCfgDefault.uIrq
2576 || dma != pStream->HwCfgDefault.uDmaChanLow
2577 || hdma != pStream->HwCfgDefault.uDmaChanHigh
2578 || port != pStream->HwCfgDefault.uPort
2579 || ver != pStream->HwCfgDefault.uVer)
2580 {
2581 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
2582 N_("config changed: irq=%x/%x dma=%x/%x hdma=%x/%x port=%x/%x ver=%x/%x (saved/config)"),
2583 irq, pStream->HwCfgDefault.uIrq,
2584 dma, pStream->HwCfgDefault.uDmaChanLow,
2585 hdma, pStream->HwCfgDefault.uDmaChanHigh,
2586 port, pStream->HwCfgDefault.uPort,
2587 ver, pStream->HwCfgDefault.uVer);
2588 }
2589 }
2590
2591 if (uPass != SSM_PASS_FINAL)
2592 return VINF_SUCCESS;
2593
2594 return sb16Load(pDevIns, pSSM, pThis);
2595}
2596
2597
2598/*********************************************************************************************************************************
2599* Debug Info Items *
2600*********************************************************************************************************************************/
2601
2602/**
2603 * @callback_method_impl{FNDBGFHANDLERDEV, sb16mixer}
2604 */
2605static DECLCALLBACK(void) sb16DbgInfoMixer(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
2606{
2607 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2608 if (pThis->pMixer)
2609 AudioMixerDebug(pThis->pMixer, pHlp, pszArgs);
2610 else
2611 pHlp->pfnPrintf(pHlp, "Mixer not available\n");
2612}
2613
2614
2615/*********************************************************************************************************************************
2616* IBase implementation *
2617*********************************************************************************************************************************/
2618
2619/**
2620 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2621 */
2622static DECLCALLBACK(void *) sb16QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2623{
2624 PSB16STATE pThis = RT_FROM_MEMBER(pInterface, SB16STATE, IBase);
2625 Assert(&pThis->IBase == pInterface);
2626
2627 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2628 return NULL;
2629}
2630
2631
2632/*********************************************************************************************************************************
2633* Device (PDM) handling *
2634*********************************************************************************************************************************/
2635
2636/**
2637 * Worker for sb16Construct() and sb16Attach().
2638 *
2639 * @returns VBox status code.
2640 * @param pThis SB16 state.
2641 * @param uLUN The logical unit which is being detached.
2642 * @param ppDrv Attached driver instance on success. Optional.
2643 */
2644static int sb16AttachInternal(PSB16STATE pThis, unsigned uLUN, PSB16DRIVER *ppDrv)
2645{
2646 /*
2647 * Allocate a new driver structure and try attach the driver.
2648 */
2649 PSB16DRIVER pDrv = (PSB16DRIVER)RTMemAllocZ(sizeof(SB16DRIVER));
2650 AssertPtrReturn(pDrv, VERR_NO_MEMORY);
2651 RTStrPrintf(pDrv->szDesc, sizeof(pDrv->szDesc), "Audio driver port (SB16) for LUN #%u", uLUN);
2652
2653 PPDMIBASE pDrvBase;
2654 int rc = PDMDevHlpDriverAttach(pThis->pDevInsR3, uLUN, &pThis->IBase, &pDrvBase, pDrv->szDesc);
2655 if (RT_SUCCESS(rc))
2656 {
2657 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
2658 AssertPtr(pDrv->pConnector);
2659 if (RT_VALID_PTR(pDrv->pConnector))
2660 {
2661 pDrv->pDrvBase = pDrvBase;
2662 pDrv->pSB16State = pThis;
2663 pDrv->uLUN = uLUN;
2664
2665 /* Attach to driver list if not attached yet. */
2666 if (!pDrv->fAttached)
2667 {
2668 RTListAppend(&pThis->lstDrv, &pDrv->Node);
2669 pDrv->fAttached = true;
2670 }
2671
2672 if (ppDrv)
2673 *ppDrv = pDrv;
2674 LogFunc(("LUN#%u: returns VINF_SUCCESS (pCon=%p)\n", uLUN, pDrv->pConnector));
2675 return VINF_SUCCESS;
2676 }
2677 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
2678 }
2679 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2680 LogFunc(("No attached driver for LUN #%u\n", uLUN));
2681 else
2682 LogFunc(("Failed to attached driver for LUN #%u: %Rrc\n", uLUN, rc));
2683 RTMemFree(pDrv);
2684
2685 LogFunc(("LUN#%u: rc=%Rrc\n", uLUN, rc));
2686 return rc;
2687}
2688
2689/**
2690 * @interface_method_impl{PDMDEVREG,pfnAttach}
2691 */
2692static DECLCALLBACK(int) sb16Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2693{
2694 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2695 RT_NOREF(fFlags);
2696 LogFunc(("iLUN=%u, fFlags=%#x\n", iLUN, fFlags));
2697
2698 /** @todo r=andy Any locking required here? */
2699
2700 PSB16DRIVER pDrv;
2701 int rc = sb16AttachInternal(pThis, iLUN, &pDrv);
2702 if (RT_SUCCESS(rc))
2703 {
2704 int rc2 = sb16AddDrv(pDevIns, pThis, pDrv);
2705 if (RT_FAILURE(rc2))
2706 LogFunc(("sb16AddDrv failed with %Rrc (ignored)\n", rc2));
2707 }
2708
2709 return rc;
2710}
2711
2712/**
2713 * @interface_method_impl{PDMDEVREG,pfnDetach}
2714 */
2715static DECLCALLBACK(void) sb16Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2716{
2717 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2718 RT_NOREF(fFlags);
2719
2720 LogFunc(("iLUN=%u, fFlags=0x%x\n", iLUN, fFlags));
2721
2722 PSB16DRIVER pDrv;
2723 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2724 {
2725 if (pDrv->uLUN == iLUN)
2726 {
2727 sb16RemoveDrv(pDevIns, pThis, pDrv);
2728 RTMemFree(pDrv);
2729 return;
2730 }
2731 }
2732 LogFunc(("LUN#%u was not found\n", iLUN));
2733}
2734
2735
2736/**
2737 * @interface_method_impl{PDMDEVREG,pfnReset}
2738 */
2739static DECLCALLBACK(void) sb16DevReset(PPDMDEVINS pDevIns)
2740{
2741 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2742
2743 LogRel2(("SB16: Reset\n"));
2744
2745 pThis->mixer_regs[0x82] = 0;
2746 pThis->csp_regs[5] = 1;
2747 pThis->csp_regs[9] = 0xf8;
2748
2749 pThis->dsp_in_idx = 0;
2750 pThis->dsp_out_data_len = 0;
2751 pThis->dsp_in_needed_bytes = 0;
2752 pThis->nzero = 0;
2753 pThis->highspeed = 0;
2754 pThis->v2x6 = 0;
2755 pThis->cmd = -1;
2756
2757 sb16MixerReset(pThis);
2758 sb16SpeakerControl(pThis, false /* fOn */);
2759 sb16DspCmdResetLegacy(pThis);
2760}
2761
2762/**
2763 * Powers off the device.
2764 *
2765 * @param pDevIns Device instance to power off.
2766 */
2767static DECLCALLBACK(void) sb16PowerOff(PPDMDEVINS pDevIns)
2768{
2769 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2770
2771 LogRel2(("SB16: Powering off ...\n"));
2772
2773 /*
2774 * Destroy all streams.
2775 */
2776 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
2777 sb16StreamDestroy(pDevIns, pThis, &pThis->aStreams[i]);
2778
2779 /*
2780 * Destroy all sinks.
2781 */
2782 if (pThis->pSinkOut)
2783 {
2784 AudioMixerSinkDestroy(pThis->pSinkOut, pDevIns);
2785 pThis->pSinkOut = NULL;
2786 }
2787 /** @todo Ditto for sinks. */
2788
2789 /*
2790 * Note: Destroy the mixer while powering off and *not* in sb16Destruct,
2791 * giving the mixer the chance to release any references held to
2792 * PDM audio streams it maintains.
2793 */
2794 if (pThis->pMixer)
2795 {
2796 AudioMixerDestroy(pThis->pMixer, pDevIns);
2797 pThis->pMixer = NULL;
2798 }
2799}
2800
2801/**
2802 * @interface_method_impl{PDMDEVREG,pfnDestruct}
2803 */
2804static DECLCALLBACK(int) sb16Destruct(PPDMDEVINS pDevIns)
2805{
2806 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); /* this shall come first */
2807 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2808
2809 LogFlowFuncEnter();
2810
2811 PSB16DRIVER pDrv;
2812 while (!RTListIsEmpty(&pThis->lstDrv))
2813 {
2814 pDrv = RTListGetFirst(&pThis->lstDrv, SB16DRIVER, Node);
2815
2816 RTListNodeRemove(&pDrv->Node);
2817 RTMemFree(pDrv);
2818 }
2819
2820 /* We don't always go via PowerOff, so make sure the mixer is destroyed. */
2821 if (pThis->pMixer)
2822 {
2823 AudioMixerDestroy(pThis->pMixer, pDevIns);
2824 pThis->pMixer = NULL;
2825 }
2826
2827 return VINF_SUCCESS;
2828}
2829
2830/**
2831 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
2832 */
2833static DECLCALLBACK(int) sb16Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2834{
2835 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* this shall come first */
2836 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2837 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2838 RT_NOREF(iInstance);
2839
2840 Assert(iInstance == 0);
2841
2842 /*
2843 * Initialize the data so sb16Destruct runs without a hitch if we return early.
2844 */
2845 pThis->pDevInsR3 = pDevIns;
2846 pThis->IBase.pfnQueryInterface = sb16QueryInterface;
2847 pThis->cmd = -1;
2848
2849 pThis->csp_regs[5] = 1;
2850 pThis->csp_regs[9] = 0xf8;
2851
2852 RTListInit(&pThis->lstDrv);
2853
2854 /*
2855 * Validate and read config data.
2856 */
2857 /* Note: For now we only support the one-and-only output stream. */
2858 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2859
2860 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "IRQ|DMA|DMA16|Port|Version|TimerHz|DebugEnabled|DebugPathOut", "");
2861 int rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pStream->HwCfgDefault.uIrq, 5);
2862 if (RT_FAILURE(rc))
2863 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"IRQ\" value"));
2864 /* Sanity-check supported SB16 IRQs. */
2865 if ( 2 != pStream->HwCfgDefault.uIrq
2866 && 5 != pStream->HwCfgDefault.uIrq
2867 && 7 != pStream->HwCfgDefault.uIrq
2868 && 10 != pStream->HwCfgDefault.uIrq)
2869 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"IRQ\" value."));
2870 pStream->HwCfgRuntime.uIrq = pStream->HwCfgDefault.uIrq;
2871
2872 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA", &pStream->HwCfgDefault.uDmaChanLow, 1);
2873 if (RT_FAILURE(rc))
2874 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"DMA\" value"));
2875 if ( 0 != pStream->HwCfgDefault.uDmaChanLow
2876 && 1 != pStream->HwCfgDefault.uDmaChanLow
2877 && 3 != pStream->HwCfgDefault.uDmaChanLow)
2878 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"DMA\" value."));
2879 pStream->HwCfgRuntime.uDmaChanLow = pStream->HwCfgDefault.uDmaChanLow;
2880
2881 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA16", &pStream->HwCfgDefault.uDmaChanHigh, 5);
2882 if (RT_FAILURE(rc))
2883 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"DMA16\" value"));
2884 if ( 5 != pStream->HwCfgDefault.uDmaChanHigh
2885 && 6 != pStream->HwCfgDefault.uDmaChanHigh
2886 && 7 != pStream->HwCfgDefault.uDmaChanHigh)
2887 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"DMA16\" value."));
2888 pStream->HwCfgRuntime.uDmaChanHigh = pStream->HwCfgDefault.uDmaChanHigh;
2889
2890 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pStream->HwCfgDefault.uPort, 0x220);
2891 if (RT_FAILURE(rc))
2892 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"Port\" value"));
2893 /* Sanity-check supported SB16 ports. */
2894 if ( 0x220 != pStream->HwCfgDefault.uPort
2895 && 0x240 != pStream->HwCfgDefault.uPort
2896 && 0x260 != pStream->HwCfgDefault.uPort
2897 && 0x280 != pStream->HwCfgDefault.uPort)
2898 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"Port\" value. Did you specify it as a hex value (e.g. 0x220)?"));
2899 pStream->HwCfgRuntime.uPort = pStream->HwCfgDefault.uPort;
2900
2901 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "Version", &pStream->HwCfgDefault.uVer, 0x0405);
2902 if (RT_FAILURE(rc))
2903 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"Version\" value"));
2904 pStream->HwCfgRuntime.uVer = pStream->HwCfgDefault.uVer;
2905
2906 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "TimerHz", &pStream->uTimerHz, SB16_TIMER_HZ_DEFAULT);
2907 if (RT_FAILURE(rc))
2908 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: failed to read Hertz rate as unsigned integer"));
2909 if (pStream->uTimerHz == 0)
2910 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Hertz rate is invalid"));
2911 if (pStream->uTimerHz > 2048)
2912 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Maximum Hertz rate is 2048"));
2913
2914 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DebugEnabled", &pThis->Dbg.fEnabled, false);
2915 if (RT_FAILURE(rc))
2916 return PDMDEV_SET_ERROR(pDevIns, rc,
2917 N_("SB16 configuration error: failed to read debugging enabled flag as boolean"));
2918
2919 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "DebugPathOut", &pThis->Dbg.pszOutPath, NULL);
2920 if (RT_FAILURE(rc))
2921 return PDMDEV_SET_ERROR(pDevIns, rc,
2922 N_("SB16 configuration error: failed to read debugging output path flag as string"));
2923
2924 if (pThis->Dbg.fEnabled)
2925 LogRel2(("SB16: Debug output will be saved to '%s'\n", pThis->Dbg.pszOutPath));
2926
2927 /*
2928 * Create internal software mixer.
2929 * Must come before we do the device's mixer reset.
2930 */
2931 rc = AudioMixerCreate("SB16 Mixer", 0 /* uFlags */, &pThis->pMixer);
2932 AssertRCReturn(rc, rc);
2933
2934 AssertRCReturn(rc, rc);
2935 rc = AudioMixerCreateSink(pThis->pMixer, "PCM Output",
2936 PDMAUDIODIR_OUT, pDevIns, &pThis->pSinkOut);
2937 AssertRCReturn(rc, rc);
2938
2939 /*
2940 * Create all hardware streams.
2941 * For now we have one stream only, namely the output (playback) stream.
2942 */
2943 AssertCompile(RT_ELEMENTS(pThis->aStreams) == SB16_MAX_STREAMS);
2944 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
2945 {
2946 rc = sb16StreamCreate(pThis, &pThis->aStreams[i], i /* uIdx */);
2947 AssertRCReturn(rc, rc);
2948 }
2949
2950 /*
2951 * Setup the mixer now that we've got the irq and dma channel numbers.
2952 */
2953 pThis->mixer_regs[0x80] = magic_of_irq(pStream->HwCfgRuntime.uIrq);
2954 pThis->mixer_regs[0x81] = (1 << pStream->HwCfgRuntime.uDmaChanLow) | (1 << pStream->HwCfgRuntime.uDmaChanHigh);
2955 pThis->mixer_regs[0x82] = 2 << 5;
2956
2957 /*
2958 * Perform a device reset before we set up the mixer below,
2959 * to have a defined state. This includes the mixer reset + legacy reset.
2960 */
2961 sb16DevReset(pThis->pDevInsR3);
2962
2963 /*
2964 * Make sure that the mixer sink(s) have a valid format set.
2965 *
2966 * This is needed in order to make the driver attaching logic working done by Main
2967 * for machine construction. Must come after sb16DevReset().
2968 */
2969 PSB16STREAM const pStreamOut = &pThis->aStreams[SB16_IDX_OUT];
2970 AudioMixerSinkSetFormat(pThis->pSinkOut, &pStreamOut->Cfg.Props, pStreamOut->Cfg.Device.cMsSchedulingHint);
2971
2972 /*
2973 * Create timers.
2974 */
2975 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIRQ, pThis,
2976 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IRQ", &pThis->hTimerIRQ);
2977 AssertRCReturn(rc, rc);
2978
2979 static const char * const s_apszNames[] = { "SB16 OUT" };
2980 AssertCompile(RT_ELEMENTS(s_apszNames) == SB16_MAX_STREAMS);
2981 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
2982 {
2983 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, &pThis->aStreams[i],
2984 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, s_apszNames[i], &pThis->aStreams[i].hTimerIO);
2985 AssertRCReturn(rc, rc);
2986
2987 pThis->aStreams[i].cTicksTimerIOInterval = PDMDevHlpTimerGetFreq(pDevIns, pThis->aStreams[i].hTimerIO)
2988 / pThis->aStreams[i].uTimerHz;
2989 pThis->aStreams[i].tsTimerIO = PDMDevHlpTimerGet(pDevIns, pThis->aStreams[i].hTimerIO);
2990 }
2991
2992 /*
2993 * Register I/O and DMA.
2994 */
2995 static const IOMIOPORTDESC s_aAllDescs[] =
2996 {
2997 { "FM Music Status Port", "FM Music Register Address Port", NULL, NULL }, // 00h
2998 { NULL, "FM Music Data Port", NULL, NULL }, // 01h
2999 { "Advanced FM Music Status Port", "Advanced FM Music Register Address Port", NULL, NULL }, // 02h
3000 { NULL, "Advanced FM Music Data Port", NULL, NULL }, // 03h
3001 { NULL, "Mixer chip Register Address Port", NULL, NULL }, // 04h
3002 { "Mixer chip Data Port", NULL, NULL, NULL }, // 05h
3003 { NULL, "DSP Reset", NULL, NULL }, // 06h
3004 { "Unused7", "Unused7", NULL, NULL }, // 07h
3005 { "FM Music Status Port", "FM Music Register Port", NULL, NULL }, // 08h
3006 { NULL, "FM Music Data Port", NULL, NULL }, // 09h
3007 { "DSP Read Data Port", NULL, NULL, NULL }, // 0Ah
3008 { "UnusedB", "UnusedB", NULL, NULL }, // 0Bh
3009 { "DSP Write-Buffer Status", "DSP Write Command/Data", NULL, NULL }, // 0Ch
3010 { "UnusedD", "UnusedD", NULL, NULL }, // 0Dh
3011 { "DSP Read-Buffer Status", NULL, NULL, NULL }, // 0Eh
3012 { "IRQ16ACK", NULL, NULL, NULL }, // 0Fh
3013 { "CD-ROM Data Register", "CD-ROM Command Register", NULL, NULL }, // 10h
3014 { "CD-ROM Status Register", NULL, NULL, NULL }, // 11h
3015 { NULL, "CD-ROM Reset Register", NULL, NULL }, // 12h
3016 { NULL, "CD-ROM Enable Register", NULL, NULL }, // 13h
3017 { NULL, NULL, NULL, NULL },
3018 };
3019
3020 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pStream->HwCfgRuntime.uPort + 0x04 /*uPort*/, 2 /*cPorts*/,
3021 sb16IoPortMixerWrite, sb16IoPortMixerRead,
3022 "SB16 - Mixer", &s_aAllDescs[4], &pThis->hIoPortsMixer);
3023 AssertRCReturn(rc, rc);
3024 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pStream->HwCfgRuntime.uPort + 0x06 /*uPort*/, 10 /*cPorts*/,
3025 sb16IoPortDspWrite, sb16IoPortDspRead,
3026 "SB16 - DSP", &s_aAllDescs[6], &pThis->hIoPortsDsp);
3027 AssertRCReturn(rc, rc);
3028
3029 rc = PDMDevHlpDMARegister(pDevIns, pStream->HwCfgRuntime.uDmaChanHigh, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */);
3030 AssertRCReturn(rc, rc);
3031 rc = PDMDevHlpDMARegister(pDevIns, pStream->HwCfgRuntime.uDmaChanLow, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */);
3032 AssertRCReturn(rc, rc);
3033
3034 /*
3035 * Register Saved state.
3036 */
3037 rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec);
3038 AssertRCReturn(rc, rc);
3039
3040 LogRel2(("SB16: Using port %#x, DMA%RU8, IRQ%RU8\n",
3041 pStream->HwCfgRuntime.uPort, pStream->HwCfgRuntime.uDmaChanLow, pStream->HwCfgRuntime.uIrq));
3042
3043 /*
3044 * Attach drivers. We ASSUME they are configured consecutively without any
3045 * gaps, so we stop when we hit the first LUN w/o a driver configured.
3046 */
3047 for (unsigned iLun = 0; ; iLun++)
3048 {
3049 AssertBreak(iLun < UINT8_MAX);
3050 LogFunc(("Trying to attach driver for LUN#%u ...\n", iLun));
3051 rc = sb16AttachInternal(pThis, iLun, NULL /* ppDrv */);
3052 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
3053 {
3054 LogFunc(("cLUNs=%u\n", iLun));
3055 break;
3056 }
3057 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("LUN#%u: rc=%Rrc\n", iLun, rc), rc);
3058 }
3059
3060 /*
3061 * Register statistics.
3062 */
3063# ifdef VBOX_WITH_STATISTICS
3064 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimerIO, STAMTYPE_PROFILE, "Timer", STAMUNIT_TICKS_PER_CALL, "Profiling sb16TimerIO.");
3065 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, "BytesRead", STAMUNIT_BYTES, "Bytes read from SB16 emulation.");
3066# endif
3067 for (unsigned idxStream = 0; idxStream < RT_ELEMENTS(pThis->aStreams); idxStream++)
3068 {
3069 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.offRead, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
3070 "Virtual internal buffer read position.", "Stream%u/offRead", idxStream);
3071 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.offWrite, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
3072 "Virtual internal buffer write position.", "Stream%u/offWrite", idxStream);
3073 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.StatDmaBufSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
3074 "Size of the internal DMA buffer.", "Stream%u/DMABufSize", idxStream);
3075 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.StatDmaBufUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
3076 "Number of bytes used in the internal DMA buffer.", "Stream%u/DMABufUsed", idxStream);
3077 }
3078
3079 /*
3080 * Debug info items.
3081 */
3082 //PDMDevHlpDBGFInfoRegister(pDevIns, "sb16", "SB16 registers. (sb16 [register case-insensitive])", sb16DbgInfo);
3083 //PDMDevHlpDBGFInfoRegister(pDevIns, "sb16stream", "SB16 stream info. (sb16stream [stream number])", sb16DbgInfoStream);
3084 PDMDevHlpDBGFInfoRegister(pDevIns, "sb16mixer", "SB16 mixer state.", sb16DbgInfoMixer);
3085
3086 return VINF_SUCCESS;
3087}
3088
3089const PDMDEVREG g_DeviceSB16 =
3090{
3091 /* .u32Version = */ PDM_DEVREG_VERSION,
3092 /* .uReserved0 = */ 0,
3093 /* .szName = */ "sb16",
3094 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE
3095 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION /* stream clearnup with working drivers */,
3096 /* .fClass = */ PDM_DEVREG_CLASS_AUDIO,
3097 /* .cMaxInstances = */ 1,
3098 /* .uSharedVersion = */ 42,
3099 /* .cbInstanceShared = */ sizeof(SB16STATE),
3100 /* .cbInstanceCC = */ 0,
3101 /* .cbInstanceRC = */ 0,
3102 /* .cMaxPciDevices = */ 0,
3103 /* .cMaxMsixVectors = */ 0,
3104 /* .pszDescription = */ "Sound Blaster 16 Controller",
3105#if defined(IN_RING3)
3106 /* .pszRCMod = */ "",
3107 /* .pszR0Mod = */ "",
3108 /* .pfnConstruct = */ sb16Construct,
3109 /* .pfnDestruct = */ sb16Destruct,
3110 /* .pfnRelocate = */ NULL,
3111 /* .pfnMemSetup = */ NULL,
3112 /* .pfnPowerOn = */ NULL,
3113 /* .pfnReset = */ sb16DevReset,
3114 /* .pfnSuspend = */ NULL,
3115 /* .pfnResume = */ NULL,
3116 /* .pfnAttach = */ sb16Attach,
3117 /* .pfnDetach = */ sb16Detach,
3118 /* .pfnQueryInterface = */ NULL,
3119 /* .pfnInitComplete = */ NULL,
3120 /* .pfnPowerOff = */ sb16PowerOff,
3121 /* .pfnSoftReset = */ NULL,
3122 /* .pfnReserved0 = */ NULL,
3123 /* .pfnReserved1 = */ NULL,
3124 /* .pfnReserved2 = */ NULL,
3125 /* .pfnReserved3 = */ NULL,
3126 /* .pfnReserved4 = */ NULL,
3127 /* .pfnReserved5 = */ NULL,
3128 /* .pfnReserved6 = */ NULL,
3129 /* .pfnReserved7 = */ NULL,
3130#elif defined(IN_RING0)
3131 /* .pfnEarlyConstruct = */ NULL,
3132 /* .pfnConstruct = */ NULL,
3133 /* .pfnDestruct = */ NULL,
3134 /* .pfnFinalDestruct = */ NULL,
3135 /* .pfnRequest = */ NULL,
3136 /* .pfnReserved0 = */ NULL,
3137 /* .pfnReserved1 = */ NULL,
3138 /* .pfnReserved2 = */ NULL,
3139 /* .pfnReserved3 = */ NULL,
3140 /* .pfnReserved4 = */ NULL,
3141 /* .pfnReserved5 = */ NULL,
3142 /* .pfnReserved6 = */ NULL,
3143 /* .pfnReserved7 = */ NULL,
3144#elif defined(IN_RC)
3145 /* .pfnConstruct = */ NULL,
3146 /* .pfnReserved0 = */ NULL,
3147 /* .pfnReserved1 = */ NULL,
3148 /* .pfnReserved2 = */ NULL,
3149 /* .pfnReserved3 = */ NULL,
3150 /* .pfnReserved4 = */ NULL,
3151 /* .pfnReserved5 = */ NULL,
3152 /* .pfnReserved6 = */ NULL,
3153 /* .pfnReserved7 = */ NULL,
3154#else
3155# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3156#endif
3157 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
3158};
3159
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