VirtualBox

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

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

Audio: Made PDMAUDIOVOLUME multichannel. bugref:9890

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