VirtualBox

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

Last change on this file since 89779 was 89779, checked in by vboxsync, 3 years ago

Audio: Size the mixer buffer for sinks according to the scheulding hint. This should typically reduce the buffer size from the fixed size of 100ms we've used so far, helping to reduce latency build up. Also, 100ms might not have been enough for some configurations. bugref:9890

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