VirtualBox

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

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

Audio/SB16: Implemented support for async I/O (disabled by default); a lot of code cleanup / reorganization to support more streams in the future.

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

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