VirtualBox

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

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

Audio: Removed PDMAUDIOSTREAMCFG::enmLayout and PDMAUDIOSTREAMLAYOUT. bugref:9890

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

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