VirtualBox

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

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

Audio: Added an fImmediate indicator to the pfnStreamDestroy methods so the backend knows whether it's okay to continue draining the stream or if it must be destroyed without delay. The latter is typically only for shutdown and driver plumbing. This helps quite a bit for HDA/CoreAudio/knoppix. bugref:9890

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