VirtualBox

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

Last change on this file since 89234 was 89218, checked in by vboxsync, 4 years ago

Audio: Converted PDMAUDIODSTSRCUNION, PDMAUDIORECSRC and PDMAUDIOPLAYBACKDST into a single enum type PDMAUDIOPATH. bugref:9890

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