VirtualBox

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

最後變更 在這個檔案從89341是 89341,由 vboxsync 提交於 4 年 前

Audio: Removed PDMAUDIODRVFLAGS (no longer needed when we're mixing inputs). bugref:9890

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

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