VirtualBox

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

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

Audio: Fixed a small memory leak (driver description) in the driver attach logic of the HDA emulation and matched it to AC'97 and SB16. ​bugref:10008

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