VirtualBox

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

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

Audio/Dev*: Overhauled the attach/detach code a little, making attach fail if the configured driver doesn't have a PDMIADUIOCONNECTOR interface among other things. bugref:9890

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