VirtualBox

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

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

Audio: s/AudioMixerSinkCtl/AudioMixerSinkEnable/ bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 116.0 KB
Line 
1/* $Id: DevSB16.cpp 88918 2021-05-06 22:17:08Z 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, 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 if (RT_SUCCESS(rc))
2011 pDrvStream->pMixStrm = pMixStrm;
2012 else
2013 AudioMixerStreamDestroy(pMixStrm, pDevIns);
2014 }
2015 }
2016 else
2017 rc = VERR_INVALID_PARAMETER;
2018
2019 PDMAudioStrmCfgFree(pStreamCfg);
2020
2021 LogFlowFuncLeaveRC(rc);
2022 return rc;
2023}
2024
2025/**
2026 * Adds all current driver streams to a specific mixer sink.
2027 *
2028 * @returns VBox status code.
2029 * @param pDevIns The device instance.
2030 * @param pThis The SB16 state.
2031 * @param pMixSink Mixer sink to add stream to.
2032 * @param pCfg Stream configuration to use.
2033 */
2034static int sb16AddDrvStreams(PPDMDEVINS pDevIns, PSB16STATE pThis, PAUDMIXSINK pMixSink, PPDMAUDIOSTREAMCFG pCfg)
2035{
2036 AssertPtrReturn(pMixSink, VERR_INVALID_POINTER);
2037
2038 if (!AudioHlpStreamCfgIsValid(pCfg))
2039 return VERR_INVALID_PARAMETER;
2040
2041 int rc = AudioMixerSinkSetFormat(pMixSink, &pCfg->Props);
2042 if (RT_FAILURE(rc))
2043 return rc;
2044
2045 PSB16DRIVER pDrv;
2046 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2047 {
2048 int rc2 = sb16AddDrvStream(pDevIns, pMixSink, pCfg, pDrv);
2049 if (RT_FAILURE(rc2))
2050 LogFunc(("Attaching stream failed with %Rrc\n", rc2));
2051
2052 /* Do not pass failure to rc here, as there might be drivers which aren't
2053 * configured / ready yet. */
2054 }
2055
2056 LogFlowFuncLeaveRC(rc);
2057 return rc;
2058}
2059
2060/**
2061 * Removes a driver stream from a specific mixer sink.
2062 *
2063 * @param pDevIns The device instance.
2064 * @param pMixSink Mixer sink to remove audio streams from.
2065 * @param enmDir Stream direction to remove.
2066 * @param dstSrc Stream destination / source to remove.
2067 * @param pDrv Driver stream to remove.
2068 */
2069static void sb16RemoveDrvStream(PPDMDEVINS pDevIns, PAUDMIXSINK pMixSink, PDMAUDIODIR enmDir,
2070 PDMAUDIODSTSRCUNION dstSrc, PSB16DRIVER pDrv)
2071{
2072 PSB16DRIVERSTREAM pDrvStream = sb16GetDrvStream(pDrv, enmDir, dstSrc);
2073 if (pDrvStream)
2074 {
2075 if (pDrvStream->pMixStrm)
2076 {
2077 LogFlowFunc(("[LUN#%RU8]\n", pDrv->uLUN));
2078
2079 AudioMixerSinkRemoveStream(pMixSink, pDrvStream->pMixStrm);
2080
2081 AudioMixerStreamDestroy(pDrvStream->pMixStrm, pDevIns);
2082 pDrvStream->pMixStrm = NULL;
2083 }
2084 }
2085}
2086
2087/**
2088 * Removes all driver streams from a specific mixer sink.
2089 *
2090 * @param pDevIns The device instance.
2091 * @param pThis The SB16 state.
2092 * @param pMixSink Mixer sink to remove audio streams from.
2093 * @param enmDir Stream direction to remove.
2094 * @param dstSrc Stream destination / source to remove.
2095 */
2096static void sb16RemoveDrvStreams(PPDMDEVINS pDevIns, PSB16STATE pThis, PAUDMIXSINK pMixSink,
2097 PDMAUDIODIR enmDir, PDMAUDIODSTSRCUNION dstSrc)
2098{
2099 AssertPtrReturnVoid(pMixSink);
2100
2101 PSB16DRIVER pDrv;
2102 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2103 sb16RemoveDrvStream(pDevIns, pMixSink, enmDir, dstSrc, pDrv);
2104}
2105
2106/**
2107 * Adds a specific SB16 driver to the driver chain.
2108 *
2109 * @returns VBox status code.
2110 * @param pDevIns The device instance.
2111 * @param pThis The SB16 device state.
2112 * @param pDrv The SB16 driver to add.
2113 */
2114static int sb16AddDrv(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16DRIVER pDrv)
2115{
2116 int rc = VINF_SUCCESS;
2117
2118 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
2119 {
2120 if (AudioHlpStreamCfgIsValid(&pThis->aStreams[i].Cfg))
2121 {
2122 int rc2 = sb16AddDrvStream(pDevIns, sb16StreamIndexToSink(pThis, pThis->aStreams[i].uIdx),
2123 &pThis->aStreams[i].Cfg, pDrv);
2124 if (RT_SUCCESS(rc))
2125 rc = rc2;
2126 }
2127 }
2128
2129 return rc;
2130}
2131
2132/**
2133 * Removes a specific SB16 driver from the driver chain and destroys its
2134 * associated streams.
2135 *
2136 * This is only used by sb16Detach.
2137 *
2138 * @param pDevIns The device instance.
2139 * @param pThis The SB16 device state.
2140 * @param pDrv SB16 driver to remove.
2141 */
2142static void sb16RemoveDrv(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16DRIVER pDrv)
2143{
2144 RT_NOREF(pDevIns);
2145
2146 /** @todo We only implement one single output (playback) stream at the moment. */
2147
2148 if (pDrv->Out.pMixStrm)
2149 {
2150 AudioMixerSinkRemoveStream(pThis->pSinkOut, pDrv->Out.pMixStrm);
2151 AudioMixerStreamDestroy(pDrv->Out.pMixStrm, pDevIns);
2152 pDrv->Out.pMixStrm = NULL;
2153 }
2154
2155 RTListNodeRemove(&pDrv->Node);
2156}
2157
2158
2159/*********************************************************************************************************************************
2160* Stream handling *
2161*********************************************************************************************************************************/
2162
2163static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream,
2164 int uDmaChan, uint32_t offDma, uint32_t cbDma, uint32_t cbToRead, uint32_t *pcbRead)
2165{
2166 uint32_t cbFree = (uint32_t)RTCircBufFree(pStream->State.pCircBuf);
2167 uint32_t cbReadTotal = 0;
2168
2169 //Assert(cbToRead <= cbFree); /** @todo Add STAM value for overflows. */
2170
2171 cbToRead = RT_MIN(cbToRead, cbFree);
2172
2173 int rc = VINF_SUCCESS;
2174
2175 void *pv;
2176 size_t cb;
2177
2178 while (cbToRead)
2179 {
2180 uint32_t cbChunk = RT_MIN(cbDma - offDma, cbToRead);
2181
2182 RTCircBufAcquireWriteBlock(pStream->State.pCircBuf, cbChunk, &pv, &cb);
2183
2184 uint32_t cbRead;
2185 rc = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, uDmaChan, pv, offDma, (uint32_t)cb, &cbRead);
2186 AssertMsgRCReturn(rc, ("Reading from DMA failed, rc=%Rrc\n", rc), rc);
2187 Assert(cbRead == cb);
2188
2189 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2190 { /* likely */ }
2191 else
2192 AudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMA, pv, cbRead, 0 /* fFlags */);
2193
2194 RTCircBufReleaseWriteBlock(pStream->State.pCircBuf, cbRead);
2195
2196 Assert(cbToRead >= cbRead);
2197 cbToRead -= cbRead;
2198 offDma = (offDma + cbRead) % cbDma;
2199 cbReadTotal += cbRead;
2200 }
2201
2202 if (pcbRead)
2203 *pcbRead = cbReadTotal;
2204
2205 return rc;
2206}
2207
2208/**
2209 * Enables or disables a SB16 audio stream.
2210 *
2211 * @returns VBox status code.
2212 * @param pThis The SB16 state.
2213 * @param pStream The SB16 stream to enable or disable.
2214 * @param fEnable Whether to enable or disable the stream.
2215 * @param fForce Whether to force re-opening the stream or not.
2216 * Otherwise re-opening only will happen if the PCM properties have changed.
2217 */
2218static int sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable, bool fForce)
2219{
2220 if ( !fForce
2221 && fEnable == pStream->State.fEnabled)
2222 return VINF_SUCCESS;
2223
2224 LogFlowFunc(("fEnable=%RTbool, fForce=%RTbool, fStreamEnabled=%RTbool\n", fEnable, fForce, pStream->State.fEnabled));
2225
2226 /* First, enable or disable the stream and the stream's sink. */
2227 int rc = AudioMixerSinkEnable(sb16StreamIndexToSink(pThis, pStream->uIdx), fEnable);
2228 AssertRCReturn(rc, rc);
2229
2230 pStream->State.fEnabled = fEnable;
2231
2232 return rc;
2233}
2234
2235/**
2236 * Retrieves the audio mixer sink of a corresponding SB16 stream.
2237 *
2238 * @returns Pointer to audio mixer sink if found, or NULL if not found / invalid.
2239 * @param pThis The SB16 state.
2240 * @param uIdx Stream index to get audio mixer sink for.
2241 */
2242DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx)
2243{
2244 AssertReturn(uIdx <= SB16_MAX_STREAMS, NULL);
2245
2246 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
2247 if (uIdx == SB16_IDX_OUT)
2248 return pThis->pSinkOut; /* Can be NULL if not configured / set up yet. */
2249
2250 AssertMsgFailed(("No sink attached (yet) for index %RU8\n", uIdx));
2251 return NULL;
2252}
2253
2254/**
2255 * Returns the audio direction of a specified stream descriptor.
2256 *
2257 * @returns Audio direction.
2258 * @param uIdx Stream index to get audio direction for.
2259 */
2260DECLINLINE(PDMAUDIODIR) sb16GetDirFromIndex(uint8_t uIdx)
2261{
2262 AssertReturn(uIdx <= SB16_MAX_STREAMS, PDMAUDIODIR_INVALID);
2263
2264 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
2265 if (uIdx == SB16_IDX_OUT)
2266 return PDMAUDIODIR_OUT;
2267
2268 return PDMAUDIODIR_INVALID;
2269}
2270
2271/**
2272 * Creates a SB16 audio stream.
2273 *
2274 * @returns VBox status code.
2275 * @param pThis The SB16 state.
2276 * @param pStream The SB16 stream to create.
2277 * @param uIdx Stream index to assign.
2278 */
2279static int sb16StreamCreate(PSB16STATE pThis, PSB16STREAM pStream, uint8_t uIdx)
2280{
2281 LogFlowFuncEnter();
2282
2283 pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled;
2284
2285 int rc2;
2286
2287#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
2288 rc2 = sb16StreamAsyncIOCreate(pThis->pDevInsR3, pThis, pStream);
2289 AssertRCReturn(rc2, rc2);
2290#endif
2291
2292 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2293 { /* likely */ }
2294 else
2295 {
2296 char szFile[64];
2297
2298 if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_IN)
2299 RTStrPrintf(szFile, sizeof(szFile), "sb16StreamWriteSD%RU8", pStream->uIdx);
2300 else
2301 RTStrPrintf(szFile, sizeof(szFile), "sb16StreamReadSD%RU8", pStream->uIdx);
2302
2303 char szPath[RTPATH_MAX];
2304 rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThis->Dbg.pszOutPath, szFile,
2305 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
2306 AssertRC(rc2);
2307 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStream->Dbg.Runtime.pFileDMA);
2308 AssertRC(rc2);
2309
2310 /* Delete stale debugging files from a former run. */
2311 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
2312 }
2313
2314 pStream->uIdx = uIdx;
2315
2316 return VINF_SUCCESS;
2317}
2318
2319/**
2320 * Destroys a SB16 audio stream.
2321 *
2322 * @returns VBox status code.
2323 * @param pDevIns The device instance.
2324 * @param pThis The SB16 state.
2325 * @param pStream The SB16 stream to destroy.
2326 */
2327static int sb16StreamDestroy(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2328{
2329 LogFlowFuncEnter();
2330
2331 sb16StreamClose(pDevIns, pThis, pStream);
2332
2333#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
2334 int rc = sb16StreamAsyncIODestroy(pDevIns, pStream);
2335 AssertRCReturn(rc, rc);
2336#else
2337 RT_NOREF(pDevIns);
2338#endif
2339
2340 if (pStream->State.pCircBuf)
2341 {
2342 RTCircBufDestroy(pStream->State.pCircBuf);
2343 pStream->State.pCircBuf = NULL;
2344 }
2345
2346 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2347 { /* likely */ }
2348 else
2349 {
2350 AudioHlpFileDestroy(pStream->Dbg.Runtime.pFileDMA);
2351 pStream->Dbg.Runtime.pFileDMA = NULL;
2352 }
2353
2354 pStream->uIdx = UINT8_MAX;
2355
2356 return VINF_SUCCESS;
2357}
2358
2359/**
2360 * Resets a SB16 stream.
2361 *
2362 * @param pThis The SB16 state.
2363 * @param pStream The SB16 stream to reset.
2364 */
2365static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream)
2366{
2367 LogFlowFuncEnter();
2368
2369 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
2370 if (pStream->dma_auto)
2371 {
2372 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 1);
2373 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 0);
2374
2375 pStream->dma_auto = 0;
2376 }
2377
2378 sb16StreamControl(pThis->pDevInsR3, pThis, pStream, false /* fRun */);
2379 sb16StreamEnable(pThis, pStream, false /* fEnable */, false /* fForce */);
2380
2381 switch (pStream->uIdx)
2382 {
2383 case SB16_IDX_OUT:
2384 {
2385 pStream->Cfg.enmDir = PDMAUDIODIR_OUT;
2386 pStream->Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
2387 pStream->Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
2388
2389 PDMAudioPropsInit(&pStream->Cfg.Props, 1 /* 8-bit */, false /* fSigned */, 1 /* Mono */, 11025 /* uHz */);
2390 RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output");
2391
2392 break;
2393 }
2394
2395 default:
2396 AssertFailed();
2397 break;
2398 }
2399
2400 pStream->cbDmaLeft = 0;
2401 pStream->cbDmaBlockSize = 0;
2402 pStream->can_write = 1; /** @ŧodo r=andy BUGBUG Figure out why we (still) need this. */
2403
2404 /** @todo Also reset corresponding DSP values here? */
2405}
2406
2407/**
2408 * Opens a SB16 stream with its current mixer settings.
2409 *
2410 * @returns VBox status code.
2411 * @param pDevIns The device instance.
2412 * @param pThis The SB16 device state.
2413 * @param pStream The SB16 stream to open.
2414 *
2415 * @note This currently only supports the one and only output stream.
2416 */
2417static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2418{
2419 LogFlowFuncEnter();
2420
2421 PDMAudioPropsInit(&pStream->Cfg.Props,
2422 pStream->Cfg.Props.cbSampleX,
2423 pStream->Cfg.Props.fSigned,
2424 pStream->Cfg.Props.cChannelsX,
2425 pStream->Cfg.Props.uHz);
2426
2427 AssertReturn(PDMAudioPropsAreValid(&pStream->Cfg.Props), VERR_INVALID_PARAMETER);
2428
2429 PDMAUDIODSTSRCUNION dstSrc;
2430 PDMAUDIODIR enmDir;
2431
2432 switch (pStream->uIdx)
2433 {
2434 case SB16_IDX_OUT:
2435 {
2436 pStream->Cfg.enmDir = PDMAUDIODIR_OUT;
2437 pStream->Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
2438 pStream->Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
2439
2440 RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output");
2441
2442 dstSrc.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
2443 enmDir = PDMAUDIODIR_OUT;
2444 break;
2445 }
2446
2447 default:
2448 AssertFailed();
2449 break;
2450 }
2451
2452 LogRel2(("SB16: (Re-)Opening stream '%s' (%RU32Hz, %RU8 channels, %s%RU8)\n", pStream->Cfg.szName, pStream->Cfg.Props.uHz,
2453 PDMAudioPropsChannels(&pStream->Cfg.Props), pStream->Cfg.Props.fSigned ? "S" : "U",
2454 PDMAudioPropsSampleBits(&pStream->Cfg.Props)));
2455
2456 /* (Re-)create the stream's internal ring buffer. */
2457 if (pStream->State.pCircBuf)
2458 {
2459 RTCircBufDestroy(pStream->State.pCircBuf);
2460 pStream->State.pCircBuf = NULL;
2461 }
2462
2463 const uint32_t cbCircBuf
2464 = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, (RT_MS_1SEC / pStream->uTimerHz) * 2 /* Use double buffering here */);
2465
2466 int rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf);
2467 AssertRCReturn(rc, rc);
2468
2469 /* Set scheduling hint (if available). */
2470 if (pStream->cTicksTimerIOInterval)
2471 pStream->Cfg.Device.cMsSchedulingHint = RT_MS_1SEC / pStream->uTimerHz;
2472
2473 PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
2474 AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER);
2475
2476 sb16RemoveDrvStreams(pDevIns, pThis,
2477 sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.u);
2478
2479 rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
2480
2481 if (RT_SUCCESS(rc))
2482 {
2483 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
2484 { /* likely */ }
2485 else
2486 {
2487 /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */
2488 if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
2489 {
2490 AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
2491 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
2492 }
2493
2494 int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
2495 &pStream->Cfg.Props);
2496 AssertRC(rc2);
2497 }
2498 }
2499
2500 LogFlowFuncLeaveRC(rc);
2501 return rc;
2502}
2503
2504/**
2505 * Closes a SB16 stream.
2506 *
2507 * @param pDevIns The device instance.
2508 * @param pThis SB16 state.
2509 * @param pStream The SB16 stream to close.
2510 */
2511static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
2512{
2513 RT_NOREF(pDevIns, pThis, pStream);
2514
2515 LogFlowFuncEnter();
2516
2517 /* Nothing to do in here right now. */
2518}
2519
2520static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cbBytes)
2521{
2522 RT_NOREF(pStream);
2523
2524 uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pThis->pDevInsR3, pThis->hTimerIRQ);
2525
2526 const uint64_t usBytes = PDMAudioPropsBytesToMicro(&pStream->Cfg.Props, cbBytes);
2527 const uint64_t cTransferTicks = PDMDevHlpTimerFromMicro(pThis->pDevInsR3, pThis->hTimerIRQ, usBytes);
2528
2529 LogFlowFunc(("%RU32 bytes -> %RU64 ticks\n", cbBytes, cTransferTicks));
2530
2531 if (cTransferTicks < uTimerHz / 1024) /** @todo Explain this. */
2532 {
2533 LogFlowFunc(("IRQ\n"));
2534 PDMDevHlpISASetIrq(pThis->pDevInsR3, pStream->HwCfgRuntime.uIrq, 1);
2535 }
2536 else
2537 {
2538 LogFlowFunc(("Scheduled\n"));
2539 PDMDevHlpTimerSetRelative(pThis->pDevInsR3, pThis->hTimerIRQ, cTransferTicks, NULL);
2540 }
2541}
2542
2543/**
2544 * Main function for off-DMA data processing by the audio backend(s).
2545 *
2546 * Might be called by a timer callback or by an async I/O worker thread, depending
2547 * on the configuration.
2548 *
2549 * @param pStream The SB16 stream to open.
2550 * @param pSink Mixer sink to use for updating.
2551 */
2552static void sb16StreamUpdate(PSB16STREAM pStream, PAUDMIXSINK pSink)
2553{
2554 if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_OUT)
2555 {
2556 uint32_t const cbSinkWritable = AudioMixerSinkGetWritable(pSink);
2557 uint32_t const cbStreamReadable = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
2558
2559 uint32_t cbToReadFromStream = RT_MIN(cbStreamReadable, cbSinkWritable);
2560 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
2561 cbToReadFromStream = PDMAudioPropsFloorBytesToFrame(&pStream->Cfg.Props, cbToReadFromStream);
2562
2563 Log3Func(("[SD%RU8] cbSinkWritable=%RU32, cbStreamReadable=%RU32\n", pStream->uIdx, cbSinkWritable, cbStreamReadable));
2564
2565 void /*const*/ *pvSrcBuf;
2566 size_t cbSrcBuf;
2567
2568 while (cbToReadFromStream > 0)
2569 {
2570 uint32_t cbWritten = 0;
2571
2572 RTCircBufAcquireReadBlock(pStream->State.pCircBuf, cbToReadFromStream, &pvSrcBuf, &cbSrcBuf);
2573
2574 int rc2 = AudioMixerSinkWrite(pSink, AUDMIXOP_COPY, pvSrcBuf, (uint32_t)cbSrcBuf, &cbWritten);
2575 AssertRC(rc2);
2576 Assert(cbWritten <= (uint32_t)cbSrcBuf);
2577
2578 RTCircBufReleaseReadBlock(pStream->State.pCircBuf, cbWritten);
2579
2580 Assert(cbToReadFromStream >= cbWritten);
2581 cbToReadFromStream -= cbWritten;
2582 }
2583 }
2584 else
2585 AssertFailed(); /** @todo Recording not implemented yet. */
2586
2587 int rc2 = AudioMixerSinkUpdate(pSink);
2588 AssertRC(rc2);
2589}
2590
2591
2592/*********************************************************************************************************************************
2593* Saved state handling *
2594*********************************************************************************************************************************/
2595
2596/**
2597 * @callback_method_impl{FNSSMDEVLIVEEXEC}
2598 */
2599static DECLCALLBACK(int) sb16LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
2600{
2601 RT_NOREF(uPass);
2602
2603 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2604 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2605
2606 /** Currently the saved state only contains the one-and-only output stream. */
2607 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2608
2609 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uIrq);
2610 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uDmaChanLow);
2611 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uDmaChanHigh);
2612 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uPort);
2613 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgDefault.uVer);
2614 return VINF_SSM_DONT_CALL_AGAIN;
2615}
2616
2617/**
2618 * Worker for sb16SaveExec.
2619 */
2620static int sb16Save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PSB16STATE pThis)
2621{
2622 /** Currently the saved state only contains the one-and-only output stream. */
2623 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2624
2625 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uIrq);
2626 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uDmaChanLow);
2627 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uDmaChanHigh);
2628 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uPort);
2629 pHlp->pfnSSMPutS32(pSSM, pStream->HwCfgRuntime.uVer);
2630 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_idx);
2631 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_out_data_len);
2632
2633 /** Currently the saved state only contains the one-and-only output stream. */
2634 pHlp->pfnSSMPutS32(pSSM, pStream->Cfg.Props.cChannelsX >= 2 ? 1 : 0);
2635 pHlp->pfnSSMPutS32(pSSM, pStream->Cfg.Props.fSigned ? 1 : 0);
2636 pHlp->pfnSSMPutS32(pSSM, pStream->Cfg.Props.cbSampleX * 8 /* Convert bytes to bits */);
2637 pHlp->pfnSSMPutU32(pSSM, 0); /* Legacy; was PDMAUDIOFMT, unused now. */
2638
2639 pHlp->pfnSSMPutS32(pSSM, pStream->dma_auto);
2640 pHlp->pfnSSMPutS32(pSSM, pStream->cbDmaBlockSize);
2641 pHlp->pfnSSMPutS32(pSSM, pStream->fifo);
2642 pHlp->pfnSSMPutS32(pSSM, pStream->Cfg.Props.uHz);
2643 pHlp->pfnSSMPutS32(pSSM, pStream->time_const);
2644 pHlp->pfnSSMPutS32(pSSM, 0); /* Legacy; was speaker control (on/off) for output stream. */
2645 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_needed_bytes);
2646 pHlp->pfnSSMPutS32(pSSM, pThis->cmd);
2647 pHlp->pfnSSMPutS32(pSSM, pStream->fDmaUseHigh);
2648 pHlp->pfnSSMPutS32(pSSM, pThis->highspeed);
2649 pHlp->pfnSSMPutS32(pSSM, pStream->can_write);
2650 pHlp->pfnSSMPutS32(pSSM, pThis->v2x6);
2651
2652 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_param);
2653 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_value);
2654 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_mode);
2655 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_param); /* Bug compatible! */
2656 pHlp->pfnSSMPutMem(pSSM, pThis->csp_regs, 256);
2657 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_index);
2658 pHlp->pfnSSMPutMem(pSSM, pThis->csp_reg83, 4);
2659 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83r);
2660 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83w);
2661
2662 pHlp->pfnSSMPutMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data));
2663 pHlp->pfnSSMPutMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data));
2664 pHlp->pfnSSMPutU8 (pSSM, pThis->test_reg);
2665 pHlp->pfnSSMPutU8 (pSSM, pThis->last_read_byte);
2666
2667 pHlp->pfnSSMPutS32(pSSM, pThis->nzero);
2668 pHlp->pfnSSMPutS32(pSSM, pStream->cbDmaLeft);
2669 pHlp->pfnSSMPutS32(pSSM, pStream->State.fEnabled ? 1 : 0);
2670 /* The stream's bitrate. Needed for backwards (legacy) compatibility. */
2671 pHlp->pfnSSMPutS32(pSSM, AudioHlpCalcBitrate(pThis->aStreams[SB16_IDX_OUT].Cfg.Props.cbSampleX * 8,
2672 pThis->aStreams[SB16_IDX_OUT].Cfg.Props.uHz,
2673 pThis->aStreams[SB16_IDX_OUT].Cfg.Props.cChannelsX));
2674 /* Block size alignment, superfluous and thus not saved anymore. Needed for backwards (legacy) compatibility. */
2675 pHlp->pfnSSMPutS32(pSSM, 0);
2676
2677 pHlp->pfnSSMPutS32(pSSM, pThis->mixer_nreg);
2678 return pHlp->pfnSSMPutMem(pSSM, pThis->mixer_regs, 256);
2679}
2680
2681/**
2682 * @callback_method_impl{FNSSMDEVSAVEEXEC}
2683 */
2684static DECLCALLBACK(int) sb16SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2685{
2686 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2687 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2688
2689 sb16LiveExec(pDevIns, pSSM, 0);
2690 return sb16Save(pHlp, pSSM, pThis);
2691}
2692
2693/**
2694 * Worker for sb16LoadExec.
2695 */
2696static int sb16Load(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PSB16STATE pThis)
2697{
2698 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2699
2700 /** Currently the saved state only contains the one-and-only output stream. */
2701 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2702
2703 int32_t s32Tmp;
2704 pHlp->pfnSSMGetS32(pSSM, &s32Tmp);
2705 pStream->HwCfgRuntime.uIrq = s32Tmp; /* IRQ. */
2706 pHlp->pfnSSMGetS32(pSSM, &s32Tmp);
2707 pStream->HwCfgRuntime.uDmaChanLow = s32Tmp; /* Low (8-bit) DMA channel. */
2708 pHlp->pfnSSMGetS32(pSSM, &s32Tmp);
2709 pStream->HwCfgRuntime.uDmaChanHigh = s32Tmp; /* High (16-bit) DMA channel. */
2710 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* Used I/O port. */
2711 pStream->HwCfgRuntime.uPort = s32Tmp;
2712 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* DSP version running. */
2713 pStream->HwCfgRuntime.uVer = s32Tmp;
2714 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_idx);
2715 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_out_data_len);
2716 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* Output stream: Numer of channels. */
2717 pStream->Cfg.Props.cChannelsX = (uint8_t)s32Tmp;
2718 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* Output stream: Signed format bit. */
2719 pStream->Cfg.Props.fSigned = s32Tmp == 0 ? false : true;
2720 pHlp->pfnSSMGetS32(pSSM, &s32Tmp);
2721 pStream->Cfg.Props.cbSampleX = s32Tmp / 8; /* Convert bits to bytes. */
2722 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was PDMAUDIOFMT, unused now. */
2723 pHlp->pfnSSMGetS32(pSSM, &pStream->dma_auto);
2724 pHlp->pfnSSMGetS32(pSSM, &pThis->aStreams[SB16_IDX_OUT].cbDmaBlockSize);
2725 pHlp->pfnSSMGetS32(pSSM, &pStream->fifo);
2726 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); pStream->Cfg.Props.uHz = s32Tmp;
2727 pHlp->pfnSSMGetS32(pSSM, &pStream->time_const);
2728 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was speaker (on / off) for output stream. */
2729 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_needed_bytes);
2730 pHlp->pfnSSMGetS32(pSSM, &pThis->cmd);
2731 pHlp->pfnSSMGetS32(pSSM, &pStream->fDmaUseHigh); /* Output stream: Whether to use the high or low DMA channel. */
2732 pHlp->pfnSSMGetS32(pSSM, &pThis->highspeed);
2733 pHlp->pfnSSMGetS32(pSSM, &pStream->can_write);
2734 pHlp->pfnSSMGetS32(pSSM, &pThis->v2x6);
2735
2736 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_param);
2737 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_value);
2738 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_mode);
2739 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_param); /* Bug compatible! */
2740 pHlp->pfnSSMGetMem(pSSM, pThis->csp_regs, 256);
2741 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_index);
2742 pHlp->pfnSSMGetMem(pSSM, pThis->csp_reg83, 4);
2743 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83r);
2744 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83w);
2745
2746 pHlp->pfnSSMGetMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data));
2747 pHlp->pfnSSMGetMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data));
2748 pHlp->pfnSSMGetU8 (pSSM, &pThis->test_reg);
2749 pHlp->pfnSSMGetU8 (pSSM, &pThis->last_read_byte);
2750
2751 pHlp->pfnSSMGetS32(pSSM, &pThis->nzero);
2752 pHlp->pfnSSMGetS32(pSSM, &pStream->cbDmaLeft);
2753 pHlp->pfnSSMGetS32(pSSM, &s32Tmp); /* Output stream: DMA currently running bit. */
2754 const bool fStreamEnabled = s32Tmp == 0 ? false: true;
2755 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was the output stream's current bitrate (in bytes). */
2756 pHlp->pfnSSMSkip (pSSM, sizeof(int32_t)); /* Legacy; was the output stream's DMA block alignment. */
2757
2758 int32_t mixer_nreg = 0;
2759 int rc = pHlp->pfnSSMGetS32(pSSM, &mixer_nreg);
2760 AssertRCReturn(rc, rc);
2761 pThis->mixer_nreg = (uint8_t)mixer_nreg;
2762 rc = pHlp->pfnSSMGetMem(pSSM, pThis->mixer_regs, 256);
2763 AssertRCReturn(rc, rc);
2764
2765 if (fStreamEnabled)
2766 sb16StreamControl(pDevIns, pThis, pStream, true /* fRun */);
2767
2768 /* Update the master (mixer) and PCM out volumes. */
2769 sb16UpdateVolume(pThis);
2770
2771 return VINF_SUCCESS;
2772}
2773
2774/**
2775 * @callback_method_impl{FNSSMDEVLOADEXEC}
2776 */
2777static DECLCALLBACK(int) sb16LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2778{
2779 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2780 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2781
2782 AssertMsgReturn( uVersion == SB16_SAVE_STATE_VERSION
2783 || uVersion == SB16_SAVE_STATE_VERSION_VBOX_30,
2784 ("%u\n", uVersion),
2785 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2786 if (uVersion > SB16_SAVE_STATE_VERSION_VBOX_30)
2787 {
2788 /** Currently the saved state only contains the one-and-only output stream. */
2789 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
2790
2791 int32_t irq;
2792 pHlp->pfnSSMGetS32(pSSM, &irq);
2793 int32_t dma;
2794 pHlp->pfnSSMGetS32(pSSM, &dma);
2795 int32_t hdma;
2796 pHlp->pfnSSMGetS32(pSSM, &hdma);
2797 int32_t port;
2798 pHlp->pfnSSMGetS32(pSSM, &port);
2799 int32_t ver;
2800 int rc = pHlp->pfnSSMGetS32(pSSM, &ver);
2801 AssertRCReturn (rc, rc);
2802
2803 if ( irq != pStream->HwCfgDefault.uIrq
2804 || dma != pStream->HwCfgDefault.uDmaChanLow
2805 || hdma != pStream->HwCfgDefault.uDmaChanHigh
2806 || port != pStream->HwCfgDefault.uPort
2807 || ver != pStream->HwCfgDefault.uVer)
2808 {
2809 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
2810 N_("config changed: irq=%x/%x dma=%x/%x hdma=%x/%x port=%x/%x ver=%x/%x (saved/config)"),
2811 irq, pStream->HwCfgDefault.uIrq,
2812 dma, pStream->HwCfgDefault.uDmaChanLow,
2813 hdma, pStream->HwCfgDefault.uDmaChanHigh,
2814 port, pStream->HwCfgDefault.uPort,
2815 ver, pStream->HwCfgDefault.uVer);
2816 }
2817 }
2818
2819 if (uPass != SSM_PASS_FINAL)
2820 return VINF_SUCCESS;
2821
2822 return sb16Load(pDevIns, pSSM, pThis);
2823}
2824
2825
2826/*********************************************************************************************************************************
2827* IBase implementation *
2828*********************************************************************************************************************************/
2829
2830/**
2831 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2832 */
2833static DECLCALLBACK(void *) sb16QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2834{
2835 PSB16STATE pThis = RT_FROM_MEMBER(pInterface, SB16STATE, IBase);
2836 Assert(&pThis->IBase == pInterface);
2837
2838 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2839 return NULL;
2840}
2841
2842
2843/*********************************************************************************************************************************
2844* Device (PDM) handling *
2845*********************************************************************************************************************************/
2846
2847/**
2848 * Worker for sb16Construct() and sb16Attach().
2849 *
2850 * @returns VBox status code.
2851 * @param pThis SB16 state.
2852 * @param uLUN The logical unit which is being detached.
2853 * @param ppDrv Attached driver instance on success. Optional.
2854 */
2855static int sb16AttachInternal(PSB16STATE pThis, unsigned uLUN, PSB16DRIVER *ppDrv)
2856{
2857 /*
2858 * Allocate a new driver structure and try attach the driver.
2859 */
2860 PSB16DRIVER pDrv = (PSB16DRIVER)RTMemAllocZ(sizeof(SB16DRIVER));
2861 AssertReturn(pDrv, VERR_NO_MEMORY);
2862 RTStrPrintf(pDrv->szDesc, sizeof(pDrv->szDesc), "Audio driver port (SB16) for LUN #%u", uLUN);
2863
2864 PPDMIBASE pDrvBase;
2865 int rc = PDMDevHlpDriverAttach(pThis->pDevInsR3, uLUN, &pThis->IBase, &pDrvBase, pDrv->szDesc);
2866 if (RT_SUCCESS(rc))
2867 {
2868 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
2869 AssertPtr(pDrv->pConnector);
2870 if (RT_VALID_PTR(pDrv->pConnector))
2871 {
2872 pDrv->pDrvBase = pDrvBase;
2873 pDrv->pSB16State = pThis;
2874 pDrv->uLUN = uLUN;
2875
2876 /*
2877 * For now we always set the driver at LUN 0 as our primary
2878 * host backend. This might change in the future.
2879 */
2880 if (pDrv->uLUN == 0)
2881 pDrv->fFlags |= PDMAUDIODRVFLAGS_PRIMARY;
2882
2883 LogFunc(("LUN#%u: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->fFlags));
2884
2885 /* Attach to driver list if not attached yet. */
2886 if (!pDrv->fAttached)
2887 {
2888 RTListAppend(&pThis->lstDrv, &pDrv->Node);
2889 pDrv->fAttached = true;
2890 }
2891
2892 if (ppDrv)
2893 *ppDrv = pDrv;
2894 LogFunc(("LUN#%u: VINF_SUCCESS\n", uLUN));
2895 return VINF_SUCCESS;
2896 }
2897 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
2898 }
2899 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2900 LogFunc(("No attached driver for LUN #%u\n", uLUN));
2901 else
2902 LogFunc(("Failed to attached driver for LUN #%u: %Rrc\n", uLUN, rc));
2903 RTMemFree(pDrv);
2904
2905 LogFunc(("LUN#%u: rc=%Rrc\n", uLUN, rc));
2906 return rc;
2907}
2908
2909/**
2910 * @interface_method_impl{PDMDEVREG,pfnAttach}
2911 */
2912static DECLCALLBACK(int) sb16Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2913{
2914 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2915 RT_NOREF(fFlags);
2916 LogFunc(("iLUN=%u, fFlags=%#x\n", iLUN, fFlags));
2917
2918 /** @todo r=andy Any locking required here? */
2919
2920 PSB16DRIVER pDrv;
2921 int rc = sb16AttachInternal(pThis, iLUN, &pDrv);
2922 if (RT_SUCCESS(rc))
2923 {
2924 int rc2 = sb16AddDrv(pDevIns, pThis, pDrv);
2925 if (RT_FAILURE(rc2))
2926 LogFunc(("sb16AddDrv failed with %Rrc (ignored)\n", rc2));
2927 }
2928
2929 return rc;
2930}
2931
2932/**
2933 * @interface_method_impl{PDMDEVREG,pfnDetach}
2934 */
2935static DECLCALLBACK(void) sb16Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2936{
2937 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2938 RT_NOREF(fFlags);
2939
2940 LogFunc(("iLUN=%u, fFlags=0x%x\n", iLUN, fFlags));
2941
2942 PSB16DRIVER pDrv;
2943 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2944 {
2945 if (pDrv->uLUN == iLUN)
2946 {
2947 sb16RemoveDrv(pDevIns, pThis, pDrv);
2948 RTMemFree(pDrv);
2949 return;
2950 }
2951 }
2952 LogFunc(("LUN#%u was not found\n", iLUN));
2953}
2954
2955
2956/**
2957 * @interface_method_impl{PDMDEVREG,pfnReset}
2958 */
2959static DECLCALLBACK(void) sb16DevReset(PPDMDEVINS pDevIns)
2960{
2961 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2962
2963 LogRel2(("SB16: Reset\n"));
2964
2965 pThis->mixer_regs[0x82] = 0;
2966 pThis->csp_regs[5] = 1;
2967 pThis->csp_regs[9] = 0xf8;
2968
2969 pThis->dsp_in_idx = 0;
2970 pThis->dsp_out_data_len = 0;
2971 pThis->dsp_in_needed_bytes = 0;
2972 pThis->nzero = 0;
2973 pThis->highspeed = 0;
2974 pThis->v2x6 = 0;
2975 pThis->cmd = -1;
2976
2977 sb16MixerReset(pThis);
2978 sb16SpeakerControl(pThis, 0);
2979 sb16DspCmdResetLegacy(pThis);
2980}
2981
2982/**
2983 * Powers off the device.
2984 *
2985 * @param pDevIns Device instance to power off.
2986 */
2987static DECLCALLBACK(void) sb16PowerOff(PPDMDEVINS pDevIns)
2988{
2989 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2990
2991 LogRel2(("SB16: Powering off ...\n"));
2992
2993 /*
2994 * Destroy all streams.
2995 */
2996 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
2997 sb16StreamDestroy(pDevIns, pThis, &pThis->aStreams[i]);
2998
2999 /*
3000 * Destroy all sinks.
3001 */
3002 if (pThis->pSinkOut)
3003 {
3004 AudioMixerSinkDestroy(pThis->pSinkOut, pDevIns);
3005 pThis->pSinkOut = NULL;
3006 }
3007 /** @todo Ditto for sinks. */
3008
3009 /*
3010 * Note: Destroy the mixer while powering off and *not* in sb16Destruct,
3011 * giving the mixer the chance to release any references held to
3012 * PDM audio streams it maintains.
3013 */
3014 if (pThis->pMixer)
3015 {
3016 AudioMixerDestroy(pThis->pMixer, pDevIns);
3017 pThis->pMixer = NULL;
3018 }
3019}
3020
3021/**
3022 * @interface_method_impl{PDMDEVREG,pfnDestruct}
3023 */
3024static DECLCALLBACK(int) sb16Destruct(PPDMDEVINS pDevIns)
3025{
3026 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); /* this shall come first */
3027 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
3028
3029 LogFlowFuncEnter();
3030
3031 PSB16DRIVER pDrv;
3032 while (!RTListIsEmpty(&pThis->lstDrv))
3033 {
3034 pDrv = RTListGetFirst(&pThis->lstDrv, SB16DRIVER, Node);
3035
3036 RTListNodeRemove(&pDrv->Node);
3037 RTMemFree(pDrv);
3038 }
3039
3040 return VINF_SUCCESS;
3041}
3042
3043/**
3044 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
3045 */
3046static DECLCALLBACK(int) sb16Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
3047{
3048 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* this shall come first */
3049 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
3050 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3051 RT_NOREF(iInstance);
3052
3053 Assert(iInstance == 0);
3054
3055 /*
3056 * Initialize the data so sb16Destruct runs without a hitch if we return early.
3057 */
3058 pThis->pDevInsR3 = pDevIns;
3059 pThis->IBase.pfnQueryInterface = sb16QueryInterface;
3060 pThis->cmd = -1;
3061
3062 pThis->csp_regs[5] = 1;
3063 pThis->csp_regs[9] = 0xf8;
3064
3065 RTListInit(&pThis->lstDrv);
3066
3067 /*
3068 * Validate and read config data.
3069 */
3070 /* Note: For now we only support the one-and-only output stream. */
3071 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
3072
3073 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "IRQ|DMA|DMA16|Port|Version|TimerHz|DebugEnabled|DebugPathOut", "");
3074 int rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pStream->HwCfgDefault.uIrq, 5);
3075 if (RT_FAILURE(rc))
3076 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"IRQ\" value"));
3077 /* Sanity-check supported SB16 IRQs. */
3078 if ( 2 != pStream->HwCfgDefault.uIrq
3079 && 5 != pStream->HwCfgDefault.uIrq
3080 && 7 != pStream->HwCfgDefault.uIrq
3081 && 10 != pStream->HwCfgDefault.uIrq)
3082 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"IRQ\" value."));
3083 pStream->HwCfgRuntime.uIrq = pStream->HwCfgDefault.uIrq;
3084
3085 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA", &pStream->HwCfgDefault.uDmaChanLow, 1);
3086 if (RT_FAILURE(rc))
3087 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"DMA\" value"));
3088 if ( 0 != pStream->HwCfgDefault.uDmaChanLow
3089 && 1 != pStream->HwCfgDefault.uDmaChanLow
3090 && 3 != pStream->HwCfgDefault.uDmaChanLow)
3091 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"DMA\" value."));
3092 pStream->HwCfgRuntime.uDmaChanLow = pStream->HwCfgDefault.uDmaChanLow;
3093
3094 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA16", &pStream->HwCfgDefault.uDmaChanHigh, 5);
3095 if (RT_FAILURE(rc))
3096 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"DMA16\" value"));
3097 if ( 5 != pStream->HwCfgDefault.uDmaChanHigh
3098 && 6 != pStream->HwCfgDefault.uDmaChanHigh
3099 && 7 != pStream->HwCfgDefault.uDmaChanHigh)
3100 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"DMA16\" value."));
3101 pStream->HwCfgRuntime.uDmaChanHigh = pStream->HwCfgDefault.uDmaChanHigh;
3102
3103 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pStream->HwCfgDefault.uPort, 0x220);
3104 if (RT_FAILURE(rc))
3105 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"Port\" value"));
3106 /* Sanity-check supported SB16 ports. */
3107 if ( 0x220 != pStream->HwCfgDefault.uPort
3108 && 0x240 != pStream->HwCfgDefault.uPort
3109 && 0x260 != pStream->HwCfgDefault.uPort
3110 && 0x280 != pStream->HwCfgDefault.uPort)
3111 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Invalid \"Port\" value. Did you specify it as a hex value (e.g. 0x220)?"));
3112 pStream->HwCfgRuntime.uPort = pStream->HwCfgDefault.uPort;
3113
3114 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "Version", &pStream->HwCfgDefault.uVer, 0x0405);
3115 if (RT_FAILURE(rc))
3116 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"Version\" value"));
3117 pStream->HwCfgRuntime.uVer = pStream->HwCfgDefault.uVer;
3118
3119 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "TimerHz", &pStream->uTimerHz, SB16_TIMER_HZ_DEFAULT);
3120 if (RT_FAILURE(rc))
3121 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: failed to read Hertz rate as unsigned integer"));
3122 if (pStream->uTimerHz == 0)
3123 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Hertz rate is invalid"));
3124 if (pStream->uTimerHz > 2048)
3125 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Maximum Hertz rate is 2048"));
3126
3127 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DebugEnabled", &pThis->Dbg.fEnabled, false);
3128 if (RT_FAILURE(rc))
3129 return PDMDEV_SET_ERROR(pDevIns, rc,
3130 N_("SB16 configuration error: failed to read debugging enabled flag as boolean"));
3131
3132 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "DebugPathOut", &pThis->Dbg.pszOutPath, NULL);
3133 if (RT_FAILURE(rc))
3134 return PDMDEV_SET_ERROR(pDevIns, rc,
3135 N_("SB16 configuration error: failed to read debugging output path flag as string"));
3136
3137 if (pThis->Dbg.fEnabled)
3138 LogRel2(("SB16: Debug output will be saved to '%s'\n", pThis->Dbg.pszOutPath));
3139
3140 /*
3141 * Create internal software mixer.
3142 * Must come before we do the device's mixer reset.
3143 */
3144 rc = AudioMixerCreate("SB16 Mixer", 0 /* uFlags */, &pThis->pMixer);
3145 AssertRCReturn(rc, rc);
3146
3147 AssertRCReturn(rc, rc);
3148 rc = AudioMixerCreateSink(pThis->pMixer, "PCM Output",
3149 PDMAUDIODIR_OUT, pDevIns, &pThis->pSinkOut);
3150 AssertRCReturn(rc, rc);
3151
3152 /*
3153 * Create all hardware streams.
3154 * For now we have one stream only, namely the output (playback) stream.
3155 */
3156 AssertCompile(RT_ELEMENTS(pThis->aStreams) == SB16_MAX_STREAMS);
3157 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
3158 {
3159 rc = sb16StreamCreate(pThis, &pThis->aStreams[i], i /* uIdx */);
3160 AssertRCReturn(rc, rc);
3161 }
3162
3163 /*
3164 * Setup the mixer now that we've got the irq and dma channel numbers.
3165 */
3166 pThis->mixer_regs[0x80] = magic_of_irq(pStream->HwCfgRuntime.uIrq);
3167 pThis->mixer_regs[0x81] = (1 << pStream->HwCfgRuntime.uDmaChanLow) | (1 << pStream->HwCfgRuntime.uDmaChanHigh);
3168 pThis->mixer_regs[0x82] = 2 << 5;
3169
3170 sb16MixerReset(pThis);
3171
3172 /*
3173 * Create timers.
3174 */
3175 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIRQ, pThis,
3176 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IRQ", &pThis->hTimerIRQ);
3177 AssertRCReturn(rc, rc);
3178
3179 static const char * const s_apszNames[] = { "SB16 OUT" };
3180 AssertCompile(RT_ELEMENTS(s_apszNames) == SB16_MAX_STREAMS);
3181 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
3182 {
3183 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, &pThis->aStreams[i],
3184 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, s_apszNames[i], &pThis->aStreams[i].hTimerIO);
3185 AssertRCReturn(rc, rc);
3186
3187 pThis->aStreams[i].cTicksTimerIOInterval = PDMDevHlpTimerGetFreq(pDevIns, pThis->aStreams[i].hTimerIO)
3188 / pThis->aStreams[i].uTimerHz;
3189 pThis->aStreams[i].tsTimerIO = PDMDevHlpTimerGet(pDevIns, pThis->aStreams[i].hTimerIO);
3190 }
3191
3192 /*
3193 * Register I/O and DMA.
3194 */
3195 static const IOMIOPORTDESC s_aAllDescs[] =
3196 {
3197 { "FM Music Status Port", "FM Music Register Address Port", NULL, NULL }, // 00h
3198 { NULL, "FM Music Data Port", NULL, NULL }, // 01h
3199 { "Advanced FM Music Status Port", "Advanced FM Music Register Address Port", NULL, NULL }, // 02h
3200 { NULL, "Advanced FM Music Data Port", NULL, NULL }, // 03h
3201 { NULL, "Mixer chip Register Address Port", NULL, NULL }, // 04h
3202 { "Mixer chip Data Port", NULL, NULL, NULL }, // 05h
3203 { NULL, "DSP Reset", NULL, NULL }, // 06h
3204 { "Unused7", "Unused7", NULL, NULL }, // 07h
3205 { "FM Music Status Port", "FM Music Register Port", NULL, NULL }, // 08h
3206 { NULL, "FM Music Data Port", NULL, NULL }, // 09h
3207 { "DSP Read Data Port", NULL, NULL, NULL }, // 0Ah
3208 { "UnusedB", "UnusedB", NULL, NULL }, // 0Bh
3209 { "DSP Write-Buffer Status", "DSP Write Command/Data", NULL, NULL }, // 0Ch
3210 { "UnusedD", "UnusedD", NULL, NULL }, // 0Dh
3211 { "DSP Read-Buffer Status", NULL, NULL, NULL }, // 0Eh
3212 { "IRQ16ACK", NULL, NULL, NULL }, // 0Fh
3213 { "CD-ROM Data Register", "CD-ROM Command Register", NULL, NULL }, // 10h
3214 { "CD-ROM Status Register", NULL, NULL, NULL }, // 11h
3215 { NULL, "CD-ROM Reset Register", NULL, NULL }, // 12h
3216 { NULL, "CD-ROM Enable Register", NULL, NULL }, // 13h
3217 { NULL, NULL, NULL, NULL },
3218 };
3219
3220 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pStream->HwCfgRuntime.uPort + 0x04 /*uPort*/, 2 /*cPorts*/,
3221 sb16IoPortMixerWrite, sb16IoPortMixerRead,
3222 "SB16 - Mixer", &s_aAllDescs[4], &pThis->hIoPortsMixer);
3223 AssertRCReturn(rc, rc);
3224 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pStream->HwCfgRuntime.uPort + 0x06 /*uPort*/, 10 /*cPorts*/,
3225 sb16IoPortDspWrite, sb16IoPortDspRead,
3226 "SB16 - DSP", &s_aAllDescs[6], &pThis->hIoPortsDsp);
3227 AssertRCReturn(rc, rc);
3228
3229 rc = PDMDevHlpDMARegister(pDevIns, pStream->HwCfgRuntime.uDmaChanHigh, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */);
3230 AssertRCReturn(rc, rc);
3231 rc = PDMDevHlpDMARegister(pDevIns, pStream->HwCfgRuntime.uDmaChanLow, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */);
3232 AssertRCReturn(rc, rc);
3233
3234 /*
3235 * Register Saved state.
3236 */
3237 rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec);
3238 AssertRCReturn(rc, rc);
3239
3240# ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
3241 LogRel(("SB16: Asynchronous I/O enabled\n"));
3242# endif
3243 LogRel2(("SB16: Using port %#x, DMA%RU8, IRQ%RU8\n",
3244 pStream->HwCfgRuntime.uPort, pStream->HwCfgRuntime.uDmaChanLow, pStream->HwCfgRuntime.uIrq));
3245
3246 /*
3247 * Attach drivers. We ASSUME they are configured consecutively without any
3248 * gaps, so we stop when we hit the first LUN w/o a driver configured.
3249 */
3250 for (unsigned iLun = 0; ; iLun++)
3251 {
3252 AssertBreak(iLun < UINT8_MAX);
3253 LogFunc(("Trying to attach driver for LUN#%u ...\n", iLun));
3254 rc = sb16AttachInternal(pThis, iLun, NULL /* ppDrv */);
3255 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
3256 {
3257 LogFunc(("cLUNs=%u\n", iLun));
3258 break;
3259 }
3260 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("LUN#%u: rc=%Rrc\n", iLun, rc), rc);
3261 }
3262
3263 sb16DspCmdResetLegacy(pThis);
3264
3265 /*
3266 * Register statistics.
3267 */
3268# ifdef VBOX_WITH_STATISTICS
3269 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimerIO, STAMTYPE_PROFILE, "Timer", STAMUNIT_TICKS_PER_CALL, "Profiling sb16TimerIO.");
3270 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, "BytesRead", STAMUNIT_BYTES, "Bytes read from SB16 emulation.");
3271# endif
3272
3273 return VINF_SUCCESS;
3274}
3275
3276const PDMDEVREG g_DeviceSB16 =
3277{
3278 /* .u32Version = */ PDM_DEVREG_VERSION,
3279 /* .uReserved0 = */ 0,
3280 /* .szName = */ "sb16",
3281 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE
3282 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION /* stream clearnup with working drivers */,
3283 /* .fClass = */ PDM_DEVREG_CLASS_AUDIO,
3284 /* .cMaxInstances = */ 1,
3285 /* .uSharedVersion = */ 42,
3286 /* .cbInstanceShared = */ sizeof(SB16STATE),
3287 /* .cbInstanceCC = */ 0,
3288 /* .cbInstanceRC = */ 0,
3289 /* .cMaxPciDevices = */ 0,
3290 /* .cMaxMsixVectors = */ 0,
3291 /* .pszDescription = */ "Sound Blaster 16 Controller",
3292#if defined(IN_RING3)
3293 /* .pszRCMod = */ "",
3294 /* .pszR0Mod = */ "",
3295 /* .pfnConstruct = */ sb16Construct,
3296 /* .pfnDestruct = */ sb16Destruct,
3297 /* .pfnRelocate = */ NULL,
3298 /* .pfnMemSetup = */ NULL,
3299 /* .pfnPowerOn = */ NULL,
3300 /* .pfnReset = */ sb16DevReset,
3301 /* .pfnSuspend = */ NULL,
3302 /* .pfnResume = */ NULL,
3303 /* .pfnAttach = */ sb16Attach,
3304 /* .pfnDetach = */ sb16Detach,
3305 /* .pfnQueryInterface = */ NULL,
3306 /* .pfnInitComplete = */ NULL,
3307 /* .pfnPowerOff = */ sb16PowerOff,
3308 /* .pfnSoftReset = */ NULL,
3309 /* .pfnReserved0 = */ NULL,
3310 /* .pfnReserved1 = */ NULL,
3311 /* .pfnReserved2 = */ NULL,
3312 /* .pfnReserved3 = */ NULL,
3313 /* .pfnReserved4 = */ NULL,
3314 /* .pfnReserved5 = */ NULL,
3315 /* .pfnReserved6 = */ NULL,
3316 /* .pfnReserved7 = */ NULL,
3317#elif defined(IN_RING0)
3318 /* .pfnEarlyConstruct = */ NULL,
3319 /* .pfnConstruct = */ NULL,
3320 /* .pfnDestruct = */ NULL,
3321 /* .pfnFinalDestruct = */ NULL,
3322 /* .pfnRequest = */ NULL,
3323 /* .pfnReserved0 = */ NULL,
3324 /* .pfnReserved1 = */ NULL,
3325 /* .pfnReserved2 = */ NULL,
3326 /* .pfnReserved3 = */ NULL,
3327 /* .pfnReserved4 = */ NULL,
3328 /* .pfnReserved5 = */ NULL,
3329 /* .pfnReserved6 = */ NULL,
3330 /* .pfnReserved7 = */ NULL,
3331#elif defined(IN_RC)
3332 /* .pfnConstruct = */ NULL,
3333 /* .pfnReserved0 = */ NULL,
3334 /* .pfnReserved1 = */ NULL,
3335 /* .pfnReserved2 = */ NULL,
3336 /* .pfnReserved3 = */ NULL,
3337 /* .pfnReserved4 = */ NULL,
3338 /* .pfnReserved5 = */ NULL,
3339 /* .pfnReserved6 = */ NULL,
3340 /* .pfnReserved7 = */ NULL,
3341#else
3342# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3343#endif
3344 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
3345};
3346
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