VirtualBox

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

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

Audio/Dev*: Must destroy the mixer in the destructor as the power off callback isn't always used. bugref:9890

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

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