VirtualBox

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

Last change on this file since 69119 was 69119, checked in by vboxsync, 7 years ago

Audio: More cleanups (missing keywords, incorrect #endif docs, stuff)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.3 KB
Line 
1/* $Id: DevSB16.cpp 69119 2017-10-17 19:08:38Z vboxsync $ */
2/** @file
3 * DevSB16 - VBox SB16 Audio Controller.
4 */
5
6/*
7 * Copyright (C) 2015-2017 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
59#include "VBoxDD.h"
60
61#include "AudioMixBuffer.h"
62#include "AudioMixer.h"
63#include "DrvAudio.h"
64
65
66/*********************************************************************************************************************************
67* Defined Constants And Macros *
68*********************************************************************************************************************************/
69/** Current saved state version. */
70#define SB16_SAVE_STATE_VERSION 2
71/** The version used in VirtualBox version 3.0 and earlier. This didn't include the config dump. */
72#define SB16_SAVE_STATE_VERSION_VBOX_30 1
73
74#define IO_READ_PROTO(name) \
75 DECLCALLBACK(int) name (PPDMDEVINS pDevIns, void *opaque, \
76 RTIOPORT nport, uint32_t *pu32, unsigned cb)
77
78#define IO_WRITE_PROTO(name) \
79 DECLCALLBACK(int) name (PPDMDEVINS pDevIns, void *opaque, \
80 RTIOPORT nport, uint32_t val, unsigned cb)
81
82/*********************************************************************************************************************************
83* Global Variables *
84*********************************************************************************************************************************/
85static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
86
87
88
89/*********************************************************************************************************************************
90* Structures and Typedefs *
91*********************************************************************************************************************************/
92/**
93 * Structure defining a (host backend) driver stream.
94 * Each driver has its own instances of audio mixer streams, which then
95 * can go into the same (or even different) audio mixer sinks.
96 */
97typedef struct SB16DRIVERSTREAM
98{
99 /** Associated PDM audio stream. */
100 R3PTRTYPE(PPDMAUDIOSTREAM) pStream;
101 /** The stream's current configuration. */
102} SB16DRIVERSTREAM, *PSB16DRIVERSTREAM;
103
104/**
105 * Struct for maintaining a host backend driver.
106 */
107typedef struct SB16STATE *PSB16STATE;
108typedef struct SB16DRIVER
109{
110 /** Node for storing this driver in our device driver list of SB16STATE. */
111 RTLISTNODER3 Node;
112 /** Pointer to SB16 controller (state). */
113 R3PTRTYPE(PSB16STATE) pSB16State;
114 /** Driver flags. */
115 PDMAUDIODRVFLAGS fFlags;
116 uint32_t PaddingFlags;
117 /** LUN # to which this driver has been assigned. */
118 uint8_t uLUN;
119 /** Whether this driver is in an attached state or not. */
120 bool fAttached;
121 uint8_t Padding[4];
122 /** Pointer to attached driver base interface. */
123 R3PTRTYPE(PPDMIBASE) pDrvBase;
124 /** Audio connector interface to the underlying host backend. */
125 R3PTRTYPE(PPDMIAUDIOCONNECTOR) pConnector;
126 /** Stream for output. */
127 SB16DRIVERSTREAM Out;
128} SB16DRIVER, *PSB16DRIVER;
129
130/**
131 * Structure for a SB16 stream.
132 */
133typedef struct SB16STREAM
134{
135 /** The stream's current configuration. */
136 PDMAUDIOSTREAMCFG Cfg;
137} SB16STREAM, *PSB16STREAM;
138
139typedef struct SB16STATE
140{
141#ifdef VBOX
142 /** Pointer to the device instance. */
143 PPDMDEVINSR3 pDevInsR3;
144 /** Pointer to the connector of the attached audio driver. */
145 PPDMIAUDIOCONNECTOR pDrv;
146 int irqCfg;
147 int dmaCfg;
148 int hdmaCfg;
149 int portCfg;
150 int verCfg;
151#endif
152 int irq;
153 int dma;
154 int hdma;
155 int port;
156 int ver;
157
158 int in_index;
159 int out_data_len;
160 int fmt_stereo;
161 int fmt_signed;
162 int fmt_bits;
163 PDMAUDIOFMT fmt;
164 int dma_auto;
165 int block_size;
166 int fifo;
167 int freq;
168 int time_const;
169 int speaker;
170 int needed_bytes;
171 int cmd;
172 int use_hdma;
173 int highspeed;
174 int can_write; /** @todo Value never gets 0? */
175
176 int v2x6;
177
178 uint8_t csp_param;
179 uint8_t csp_value;
180 uint8_t csp_mode;
181 uint8_t csp_regs[256];
182 uint8_t csp_index;
183 uint8_t csp_reg83[4];
184 int csp_reg83r;
185 int csp_reg83w;
186
187 uint8_t in2_data[10];
188 uint8_t out_data[50];
189 uint8_t test_reg;
190 uint8_t last_read_byte;
191 int nzero;
192
193 int left_till_irq; /** Note: Can be < 0. */
194
195 int dma_running;
196 int bytes_per_second;
197 int align;
198
199 RTLISTANCHOR lstDrv;
200 /** Number of active (running) SDn streams. */
201 uint8_t cStreamsActive;
202#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
203 /** The timer for pumping data thru the attached LUN drivers. */
204 PTMTIMERR3 pTimerIO;
205 /** Flag indicating whether the timer is active or not. */
206 bool fTimerActive;
207 uint8_t u8Padding1[7];
208 /** The timer interval for pumping data thru the LUN drivers in timer ticks. */
209 uint64_t cTimerTicksIO;
210 /** Timestamp of the last timer callback (sb16TimerIO).
211 * Used to calculate the time actually elapsed between two timer callbacks. */
212 uint64_t uTimerTSIO;
213#endif
214 PTMTIMER pTimerIRQ;
215 /** The base interface for LUN\#0. */
216 PDMIBASE IBase;
217 /** Output stream. */
218 SB16STREAM Out;
219
220 /* mixer state */
221 int mixer_nreg;
222 uint8_t mixer_regs[256];
223} SB16STATE, *PSB16STATE;
224
225
226/*********************************************************************************************************************************
227* Internal Functions *
228*********************************************************************************************************************************/
229static int sb16CreateDrvStream(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg, PSB16DRIVER pDrv);
230static void sb16DestroyDrvStream(PSB16STATE pThis, PSB16DRIVER pDrv);
231static int sb16OpenOut(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg);
232static void sb16CloseOut(PSB16STATE pThis);
233#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
234static void sb16TimerMaybeStart(PSB16STATE pThis);
235static void sb16TimerMaybeStop(PSB16STATE pThis);
236#endif
237
238
239
240/**
241 * Attach command, internal version.
242 *
243 * This is called to let the device attach to a driver for a specified LUN
244 * during runtime. This is not called during VM construction, the device
245 * constructor has to attach to all the available drivers.
246 *
247 * @returns VBox status code.
248 * @param pThis SB16 state.
249 * @param uLUN The logical unit which is being detached.
250 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
251 * @param ppDrv Attached driver instance on success. Optional.
252 */
253static int sb16AttachInternal(PSB16STATE pThis, unsigned uLUN, uint32_t fFlags, PSB16DRIVER *ppDrv)
254{
255 RT_NOREF(fFlags);
256
257 /*
258 * Attach driver.
259 */
260 char *pszDesc;
261 if (RTStrAPrintf(&pszDesc, "Audio driver port (SB16) for LUN #%u", uLUN) <= 0)
262 AssertLogRelFailedReturn(VERR_NO_MEMORY);
263
264 PPDMIBASE pDrvBase;
265 int rc = PDMDevHlpDriverAttach(pThis->pDevInsR3, uLUN,
266 &pThis->IBase, &pDrvBase, pszDesc);
267 if (RT_SUCCESS(rc))
268 {
269 PSB16DRIVER pDrv = (PSB16DRIVER)RTMemAllocZ(sizeof(SB16DRIVER));
270 if (pDrv)
271 {
272 pDrv->pDrvBase = pDrvBase;
273 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
274 AssertMsg(pDrv->pConnector != NULL, ("Configuration error: LUN #%u has no host audio interface, rc=%Rrc\n", uLUN, rc));
275 pDrv->pSB16State = pThis;
276 pDrv->uLUN = uLUN;
277
278 /*
279 * For now we always set the driver at LUN 0 as our primary
280 * host backend. This might change in the future.
281 */
282 if (pDrv->uLUN == 0)
283 pDrv->fFlags |= PDMAUDIODRVFLAGS_PRIMARY;
284
285 LogFunc(("LUN#%RU8: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->fFlags));
286
287 /* Attach to driver list if not attached yet. */
288 if (!pDrv->fAttached)
289 {
290 RTListAppend(&pThis->lstDrv, &pDrv->Node);
291 pDrv->fAttached = true;
292 }
293
294 if (ppDrv)
295 *ppDrv = pDrv;
296 }
297 else
298 rc = VERR_NO_MEMORY;
299 }
300 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
301 LogFunc(("No attached driver for LUN #%u\n", uLUN));
302
303 if (RT_FAILURE(rc))
304 {
305 /* Only free this string on failure;
306 * must remain valid for the live of the driver instance. */
307 RTStrFree(pszDesc);
308 }
309
310 LogFunc(("iLUN=%u, fFlags=0x%x, rc=%Rrc\n", uLUN, fFlags, rc));
311 return rc;
312}
313
314/**
315 * Detach command, internal version.
316 *
317 * This is called to let the device detach from a driver for a specified LUN
318 * during runtime.
319 *
320 * @returns VBox status code.
321 * @param pThis SB16 state.
322 * @param pDrv Driver to detach device from.
323 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
324 */
325static int sb16DetachInternal(PSB16STATE pThis, PSB16DRIVER pDrv, uint32_t fFlags)
326{
327 RT_NOREF(fFlags);
328
329 sb16DestroyDrvStream(pThis, pDrv);
330
331 RTListNodeRemove(&pDrv->Node);
332
333 LogFunc(("uLUN=%u, fFlags=0x%x\n", pDrv->uLUN, fFlags));
334 return VINF_SUCCESS;
335}
336
337/**
338 * @interface_method_impl{PDMDEVREG,pfnAttach}
339 */
340static DECLCALLBACK(int) sb16Attach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
341{
342 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
343
344 LogFunc(("uLUN=%u, fFlags=0x%x\n", uLUN, fFlags));
345
346 PSB16DRIVER pDrv;
347 int rc2 = sb16AttachInternal(pThis, uLUN, fFlags, &pDrv);
348 if (RT_SUCCESS(rc2))
349 rc2 = sb16CreateDrvStream(pThis, &pThis->Out.Cfg, pDrv);
350
351 return VINF_SUCCESS;
352}
353
354/**
355 * @interface_method_impl{PDMDEVREG,pfnDetach}
356 */
357static DECLCALLBACK(void) sb16Detach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
358{
359 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
360
361 LogFunc(("uLUN=%u, fFlags=0x%x\n", uLUN, fFlags));
362
363 PSB16DRIVER pDrv, pDrvNext;
364 RTListForEachSafe(&pThis->lstDrv, pDrv, pDrvNext, SB16DRIVER, Node)
365 {
366 if (pDrv->uLUN == uLUN)
367 {
368 int rc2 = sb16DetachInternal(pThis, pDrv, fFlags);
369 if (RT_SUCCESS(rc2))
370 {
371 RTMemFree(pDrv);
372 pDrv = NULL;
373 }
374
375 break;
376 }
377 }
378}
379
380/**
381 * Re-attaches (replaces) a driver with a new driver.
382 *
383 * @returns VBox status code.
384 * @param pThis Device instance.
385 * @param pDrv Driver instance used for attaching to.
386 * If NULL is specified, a new driver will be created and appended
387 * to the driver list.
388 * @param uLUN The logical unit which is being re-detached.
389 * @param pszDriver New driver name to attach.
390 */
391static int sb16Reattach(PSB16STATE pThis, PSB16DRIVER pDrv, uint8_t uLUN, const char *pszDriver)
392{
393 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
394 AssertPtrReturn(pszDriver, VERR_INVALID_POINTER);
395
396 int rc;
397
398 if (pDrv)
399 {
400 rc = sb16DetachInternal(pThis, pDrv, 0 /* fFlags */);
401 if (RT_SUCCESS(rc))
402 rc = PDMDevHlpDriverDetach(pThis->pDevInsR3, PDMIBASE_2_PDMDRV(pDrv->pDrvBase), 0 /* fFlags */);
403
404 if (RT_FAILURE(rc))
405 return rc;
406
407 pDrv = NULL;
408 }
409
410 PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
411 PCFGMNODE pRoot = CFGMR3GetRoot(pVM);
412 PCFGMNODE pDev0 = CFGMR3GetChild(pRoot, "Devices/sb16/0/");
413
414 /* Remove LUN branch. */
415 CFGMR3RemoveNode(CFGMR3GetChildF(pDev0, "LUN#%u/", uLUN));
416
417 if (pDrv)
418 {
419 /* Re-use the driver instance so detach it before. */
420 rc = PDMDevHlpDriverDetach(pThis->pDevInsR3, PDMIBASE_2_PDMDRV(pDrv->pDrvBase), 0 /* fFlags */);
421 if (RT_FAILURE(rc))
422 return rc;
423 }
424
425#define RC_CHECK() if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; }
426
427 do
428 {
429 PCFGMNODE pLunL0;
430 rc = CFGMR3InsertNodeF(pDev0, &pLunL0, "LUN#%u/", uLUN); RC_CHECK();
431 rc = CFGMR3InsertString(pLunL0, "Driver", "AUDIO"); RC_CHECK();
432 rc = CFGMR3InsertNode(pLunL0, "Config/", NULL); RC_CHECK();
433
434 PCFGMNODE pLunL1, pLunL2;
435 rc = CFGMR3InsertNode (pLunL0, "AttachedDriver/", &pLunL1); RC_CHECK();
436 rc = CFGMR3InsertNode (pLunL1, "Config/", &pLunL2); RC_CHECK();
437 rc = CFGMR3InsertString(pLunL1, "Driver", pszDriver); RC_CHECK();
438
439 rc = CFGMR3InsertString(pLunL2, "AudioDriver", pszDriver); RC_CHECK();
440
441 } while (0);
442
443 if (RT_SUCCESS(rc))
444 rc = sb16AttachInternal(pThis, uLUN, 0 /* fFlags */, NULL /* ppDrv */);
445
446 LogFunc(("pThis=%p, uLUN=%u, pszDriver=%s, rc=%Rrc\n", pThis, uLUN, pszDriver, rc));
447
448#undef RC_CHECK
449
450 return rc;
451}
452
453static int magic_of_irq(int irq)
454{
455 switch (irq)
456 {
457 case 5:
458 return 2;
459 case 7:
460 return 4;
461 case 9:
462 return 1;
463 case 10:
464 return 8;
465 default:
466 break;
467 }
468
469 LogFlowFunc(("bad irq %d\n", irq));
470 return 2;
471}
472
473static int irq_of_magic(int magic)
474{
475 switch (magic)
476 {
477 case 1:
478 return 9;
479 case 2:
480 return 5;
481 case 4:
482 return 7;
483 case 8:
484 return 10;
485 default:
486 break;
487 }
488
489 LogFlowFunc(("bad irq magic %d\n", magic));
490 return -1;
491}
492
493#if 0 // unused // def DEBUG
494DECLINLINE(void) log_dsp(PSB16STATE pThis)
495{
496 LogFlowFunc(("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
497 pThis->fmt_stereo ? "Stereo" : "Mono",
498 pThis->fmt_signed ? "Signed" : "Unsigned",
499 pThis->fmt_bits,
500 pThis->dma_auto ? "Auto" : "Single",
501 pThis->block_size,
502 pThis->freq,
503 pThis->time_const,
504 pThis->speaker));
505}
506#endif
507
508static void sb16SpeakerControl(PSB16STATE pThis, int on)
509{
510 pThis->speaker = on;
511 /* AUD_enable (pThis->voice, on); */
512}
513
514static void sb16Control(PSB16STATE pThis, int hold)
515{
516 int dma = pThis->use_hdma ? pThis->hdma : pThis->dma;
517 pThis->dma_running = hold;
518
519 LogFlowFunc(("hold %d high %d dma %d\n", hold, pThis->use_hdma, dma));
520
521 PDMDevHlpDMASetDREQ(pThis->pDevInsR3, dma, hold);
522
523 PSB16DRIVER pDrv;
524 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
525 {
526 if (!pDrv->Out.pStream)
527 continue;
528
529 int rc2 = pDrv->pConnector->pfnStreamControl(pDrv->pConnector, pDrv->Out.pStream,
530 hold == 1 ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
531 LogFlowFunc(("%s: rc=%Rrc\n", pDrv->Out.pStream->szName, rc2)); NOREF(rc2);
532 }
533
534 if (hold)
535 {
536#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
537 pThis->cStreamsActive++;
538 sb16TimerMaybeStart(pThis);
539#else
540# error "Implement me!"
541#endif
542 PDMDevHlpDMASchedule(pThis->pDevInsR3);
543 }
544#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
545 else
546 {
547 if (pThis->cStreamsActive)
548 pThis->cStreamsActive--;
549 sb16TimerMaybeStop(pThis);
550 }
551#else
552# error "Implement me!"
553#endif
554}
555
556static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvThis)
557{
558 RT_NOREF(pDevIns, pTimer);
559 PSB16STATE pThis = (PSB16STATE)pvThis;
560 pThis->can_write = 1;
561 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
562}
563
564#define DMA8_AUTO 1
565#define DMA8_HIGH 2
566
567static void continue_dma8(PSB16STATE pThis)
568{
569 if (pThis->freq > 0)
570 {
571 /* At the moment we only have one stream, the output stream. */
572 PPDMAUDIOSTREAMCFG pCfg = &pThis->Out.Cfg;
573
574 pCfg->enmDir = PDMAUDIODIR_OUT;
575 pCfg->DestSource.Dest = PDMAUDIOPLAYBACKDEST_FRONT;
576 pCfg->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
577
578 pCfg->Props.uHz = pThis->freq;
579 pCfg->Props.cChannels = 1 << pThis->fmt_stereo;
580 pCfg->Props.cBits = pThis->fmt_bits;
581 pCfg->Props.fSigned = RT_BOOL(pThis->fmt_signed);
582 pCfg->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfg->Props.cBits, pCfg->Props.cChannels);
583
584 RTStrPrintf(pCfg->szName, sizeof(pCfg->szName), "Output");
585
586 sb16CloseOut(pThis);
587
588 int rc = sb16OpenOut(pThis, pCfg);
589 AssertRC(rc);
590 }
591
592 sb16Control(pThis, 1);
593}
594
595static void dma_cmd8(PSB16STATE pThis, int mask, int dma_len)
596{
597 pThis->fmt = PDMAUDIOFMT_U8;
598 pThis->use_hdma = 0;
599 pThis->fmt_bits = 8;
600 pThis->fmt_signed = 0;
601 pThis->fmt_stereo = (pThis->mixer_regs[0x0e] & 2) != 0;
602
603 if (-1 == pThis->time_const)
604 {
605 if (pThis->freq <= 0)
606 pThis->freq = 11025;
607 }
608 else
609 {
610 int tmp = (256 - pThis->time_const);
611 pThis->freq = (1000000 + (tmp / 2)) / tmp;
612 }
613
614 if (dma_len != -1)
615 {
616 pThis->block_size = dma_len << pThis->fmt_stereo;
617 }
618 else
619 {
620 /* This is apparently the only way to make both Act1/PL
621 and SecondReality/FC work
622
623 r=andy Wow, actually someone who remembers Future Crew :-)
624
625 Act1 sets block size via command 0x48 and it's an odd number
626 SR does the same with even number
627 Both use stereo, and Creatives own documentation states that
628 0x48 sets block size in bytes less one.. go figure */
629 pThis->block_size &= ~pThis->fmt_stereo;
630 }
631
632 pThis->freq >>= pThis->fmt_stereo;
633 pThis->left_till_irq = pThis->block_size;
634 pThis->bytes_per_second = (pThis->freq << pThis->fmt_stereo);
635 /* pThis->highspeed = (mask & DMA8_HIGH) != 0; */
636 pThis->dma_auto = (mask & DMA8_AUTO) != 0;
637 pThis->align = (1 << pThis->fmt_stereo) - 1;
638
639 if (pThis->block_size & pThis->align)
640 LogFlowFunc(("warning: misaligned block size %d, alignment %d\n",
641 pThis->block_size, pThis->align + 1));
642
643 LogFlowFunc(("freq %d, stereo %d, sign %d, bits %d, dma %d, auto %d, fifo %d, high %d\n",
644 pThis->freq, pThis->fmt_stereo, pThis->fmt_signed, pThis->fmt_bits,
645 pThis->block_size, pThis->dma_auto, pThis->fifo, pThis->highspeed));
646
647 continue_dma8(pThis);
648 sb16SpeakerControl(pThis, 1);
649}
650
651static void dma_cmd(PSB16STATE pThis, uint8_t cmd, uint8_t d0, int dma_len)
652{
653 pThis->use_hdma = cmd < 0xc0;
654 pThis->fifo = (cmd >> 1) & 1;
655 pThis->dma_auto = (cmd >> 2) & 1;
656 pThis->fmt_signed = (d0 >> 4) & 1;
657 pThis->fmt_stereo = (d0 >> 5) & 1;
658
659 switch (cmd >> 4)
660 {
661 case 11:
662 pThis->fmt_bits = 16;
663 break;
664
665 case 12:
666 pThis->fmt_bits = 8;
667 break;
668 }
669
670 if (-1 != pThis->time_const)
671 {
672#if 1
673 int tmp = 256 - pThis->time_const;
674 pThis->freq = (1000000 + (tmp / 2)) / tmp;
675#else
676 /* pThis->freq = 1000000 / ((255 - pThis->time_const) << pThis->fmt_stereo); */
677 pThis->freq = 1000000 / ((255 - pThis->time_const));
678#endif
679 pThis->time_const = -1;
680 }
681
682 pThis->block_size = dma_len + 1;
683 pThis->block_size <<= ((pThis->fmt_bits == 16) ? 1 : 0);
684 if (!pThis->dma_auto)
685 {
686 /*
687 * It is clear that for DOOM and auto-init this value
688 * shouldn't take stereo into account, while Miles Sound Systems
689 * setsound.exe with single transfer mode wouldn't work without it
690 * wonders of SB16 yet again.
691 */
692 pThis->block_size <<= pThis->fmt_stereo;
693 }
694
695 LogFlowFunc(("freq %d, stereo %d, sign %d, bits %d, dma %d, auto %d, fifo %d, high %d\n",
696 pThis->freq, pThis->fmt_stereo, pThis->fmt_signed, pThis->fmt_bits,
697 pThis->block_size, pThis->dma_auto, pThis->fifo, pThis->highspeed));
698
699 if (16 == pThis->fmt_bits)
700 pThis->fmt = pThis->fmt_signed ? PDMAUDIOFMT_S16 : PDMAUDIOFMT_U16;
701 else
702 pThis->fmt = pThis->fmt_signed ? PDMAUDIOFMT_S8 : PDMAUDIOFMT_U8;
703
704 pThis->left_till_irq = pThis->block_size;
705
706 pThis->bytes_per_second = (pThis->freq << pThis->fmt_stereo) << ((pThis->fmt_bits == 16) ? 1 : 0);
707 pThis->highspeed = 0;
708 pThis->align = (1 << (pThis->fmt_stereo + (pThis->fmt_bits == 16))) - 1;
709 if (pThis->block_size & pThis->align)
710 {
711 LogFlowFunc(("warning: misaligned block size %d, alignment %d\n",
712 pThis->block_size, pThis->align + 1));
713 }
714
715 if (pThis->freq)
716 {
717 /* At the moment we only have one stream, the output stream. */
718 PPDMAUDIOSTREAMCFG pCfg = &pThis->Out.Cfg;
719
720 pCfg->enmDir = PDMAUDIODIR_OUT;
721 pCfg->DestSource.Dest = PDMAUDIOPLAYBACKDEST_FRONT;
722 pCfg->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
723
724 pCfg->Props.uHz = pThis->freq;
725 pCfg->Props.cChannels = 1 << pThis->fmt_stereo;
726 pCfg->Props.cBits = pThis->fmt_bits;
727 pCfg->Props.fSigned = RT_BOOL(pThis->fmt_signed);
728 pCfg->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfg->Props.cBits, pCfg->Props.cChannels);
729
730 RTStrPrintf(pCfg->szName, sizeof(pCfg->szName), "Output");
731
732 sb16CloseOut(pThis);
733
734 int rc = sb16OpenOut(pThis, pCfg);
735 AssertRC(rc);
736 }
737
738 sb16Control(pThis, 1);
739 sb16SpeakerControl(pThis, 1);
740}
741
742static inline void dsp_out_data (PSB16STATE pThis, uint8_t val)
743{
744 LogFlowFunc(("outdata %#x\n", val));
745 if ((size_t) pThis->out_data_len < sizeof (pThis->out_data)) {
746 pThis->out_data[pThis->out_data_len++] = val;
747 }
748}
749
750static inline uint8_t dsp_get_data (PSB16STATE pThis)
751{
752 if (pThis->in_index) {
753 return pThis->in2_data[--pThis->in_index];
754 }
755 else {
756 LogFlowFunc(("buffer underflow\n"));
757 return 0;
758 }
759}
760
761static void sb16HandleCommand(PSB16STATE pThis, uint8_t cmd)
762{
763 LogFlowFunc(("command %#x\n", cmd));
764
765 if (cmd > 0xaf && cmd < 0xd0)
766 {
767 if (cmd & 8) /** @todo Handle recording. */
768 LogFlowFunc(("ADC not yet supported (command %#x)\n", cmd));
769
770 switch (cmd >> 4)
771 {
772 case 11:
773 case 12:
774 break;
775 default:
776 LogFlowFunc(("%#x wrong bits\n", cmd));
777 }
778
779 pThis->needed_bytes = 3;
780 }
781 else
782 {
783 pThis->needed_bytes = 0;
784
785 switch (cmd)
786 {
787 case 0x03:
788 dsp_out_data(pThis, 0x10); /* pThis->csp_param); */
789 goto warn;
790
791 case 0x04:
792 pThis->needed_bytes = 1;
793 goto warn;
794
795 case 0x05:
796 pThis->needed_bytes = 2;
797 goto warn;
798
799 case 0x08:
800 /* __asm__ ("int3"); */
801 goto warn;
802
803 case 0x0e:
804 pThis->needed_bytes = 2;
805 goto warn;
806
807 case 0x09:
808 dsp_out_data(pThis, 0xf8);
809 goto warn;
810
811 case 0x0f:
812 pThis->needed_bytes = 1;
813 goto warn;
814
815 case 0x10:
816 pThis->needed_bytes = 1;
817 goto warn;
818
819 case 0x14:
820 pThis->needed_bytes = 2;
821 pThis->block_size = 0;
822 break;
823
824 case 0x1c: /* Auto-Initialize DMA DAC, 8-bit */
825 dma_cmd8(pThis, DMA8_AUTO, -1);
826 break;
827
828 case 0x20: /* Direct ADC, Juice/PL */
829 dsp_out_data(pThis, 0xff);
830 goto warn;
831
832 case 0x35:
833 LogFlowFunc(("0x35 - MIDI command not implemented\n"));
834 break;
835
836 case 0x40:
837 pThis->freq = -1;
838 pThis->time_const = -1;
839 pThis->needed_bytes = 1;
840 break;
841
842 case 0x41:
843 pThis->freq = -1;
844 pThis->time_const = -1;
845 pThis->needed_bytes = 2;
846 break;
847
848 case 0x42:
849 pThis->freq = -1;
850 pThis->time_const = -1;
851 pThis->needed_bytes = 2;
852 goto warn;
853
854 case 0x45:
855 dsp_out_data(pThis, 0xaa);
856 goto warn;
857
858 case 0x47: /* Continue Auto-Initialize DMA 16bit */
859 break;
860
861 case 0x48:
862 pThis->needed_bytes = 2;
863 break;
864
865 case 0x74:
866 pThis->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
867 LogFlowFunc(("0x75 - DMA DAC, 4-bit ADPCM not implemented\n"));
868 break;
869
870 case 0x75: /* DMA DAC, 4-bit ADPCM Reference */
871 pThis->needed_bytes = 2;
872 LogFlowFunc(("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n"));
873 break;
874
875 case 0x76: /* DMA DAC, 2.6-bit ADPCM */
876 pThis->needed_bytes = 2;
877 LogFlowFunc(("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n"));
878 break;
879
880 case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */
881 pThis->needed_bytes = 2;
882 LogFlowFunc(("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n"));
883 break;
884
885 case 0x7d:
886 LogFlowFunc(("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n"));
887 LogFlowFunc(("not implemented\n"));
888 break;
889
890 case 0x7f:
891 LogFlowFunc(("0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"));
892 LogFlowFunc(("not implemented\n"));
893 break;
894
895 case 0x80:
896 pThis->needed_bytes = 2;
897 break;
898
899 case 0x90:
900 case 0x91:
901 dma_cmd8(pThis, (((cmd & 1) == 0) ? 1 : 0) | DMA8_HIGH, -1);
902 break;
903
904 case 0xd0: /* halt DMA operation. 8bit */
905 sb16Control(pThis, 0);
906 break;
907
908 case 0xd1: /* speaker on */
909 sb16SpeakerControl(pThis, 1);
910 break;
911
912 case 0xd3: /* speaker off */
913 sb16SpeakerControl(pThis, 0);
914 break;
915
916 case 0xd4: /* continue DMA operation. 8bit */
917 /* KQ6 (or maybe Sierras audblst.drv in general) resets
918 the frequency between halt/continue */
919 continue_dma8(pThis);
920 break;
921
922 case 0xd5: /* halt DMA operation. 16bit */
923 sb16Control(pThis, 0);
924 break;
925
926 case 0xd6: /* continue DMA operation. 16bit */
927 sb16Control(pThis, 1);
928 break;
929
930 case 0xd9: /* exit auto-init DMA after this block. 16bit */
931 pThis->dma_auto = 0;
932 break;
933
934 case 0xda: /* exit auto-init DMA after this block. 8bit */
935 pThis->dma_auto = 0;
936 break;
937
938 case 0xe0: /* DSP identification */
939 pThis->needed_bytes = 1;
940 break;
941
942 case 0xe1:
943 dsp_out_data(pThis, pThis->ver & 0xff);
944 dsp_out_data(pThis, pThis->ver >> 8);
945 break;
946
947 case 0xe2:
948 pThis->needed_bytes = 1;
949 goto warn;
950
951 case 0xe3:
952 {
953 for (int i = sizeof (e3) - 1; i >= 0; --i)
954 dsp_out_data(pThis, e3[i]);
955
956 break;
957 }
958
959 case 0xe4: /* write test reg */
960 pThis->needed_bytes = 1;
961 break;
962
963 case 0xe7:
964 LogFlowFunc(("Attempt to probe for ESS (0xe7)?\n"));
965 break;
966
967 case 0xe8: /* read test reg */
968 dsp_out_data(pThis, pThis->test_reg);
969 break;
970
971 case 0xf2:
972 case 0xf3:
973 dsp_out_data(pThis, 0xaa);
974 pThis->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
975 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
976 break;
977
978 case 0xf8:
979 /* Undocumented, used by old Creative diagnostic programs. */
980 dsp_out_data (pThis, 0);
981 goto warn;
982
983 case 0xf9:
984 pThis->needed_bytes = 1;
985 goto warn;
986
987 case 0xfa:
988 dsp_out_data (pThis, 0);
989 goto warn;
990
991 case 0xfc: /* FIXME */
992 dsp_out_data (pThis, 0);
993 goto warn;
994
995 default:
996 LogFlowFunc(("Unrecognized command %#x\n", cmd));
997 break;
998 }
999 }
1000
1001 if (!pThis->needed_bytes)
1002 LogFlow(("\n"));
1003
1004exit:
1005
1006 if (!pThis->needed_bytes)
1007 pThis->cmd = -1;
1008 else
1009 pThis->cmd = cmd;
1010
1011 return;
1012
1013warn:
1014 LogFlowFunc(("warning: command %#x,%d is not truly understood yet\n",
1015 cmd, pThis->needed_bytes));
1016 goto exit;
1017}
1018
1019static uint16_t dsp_get_lohi (PSB16STATE pThis)
1020{
1021 uint8_t hi = dsp_get_data (pThis);
1022 uint8_t lo = dsp_get_data (pThis);
1023 return (hi << 8) | lo;
1024}
1025
1026static uint16_t dsp_get_hilo (PSB16STATE pThis)
1027{
1028 uint8_t lo = dsp_get_data (pThis);
1029 uint8_t hi = dsp_get_data (pThis);
1030 return (hi << 8) | lo;
1031}
1032
1033static void complete(PSB16STATE pThis)
1034{
1035 int d0, d1, d2;
1036 LogFlowFunc(("complete command %#x, in_index %d, needed_bytes %d\n",
1037 pThis->cmd, pThis->in_index, pThis->needed_bytes));
1038
1039 if (pThis->cmd > 0xaf && pThis->cmd < 0xd0)
1040 {
1041 d2 = dsp_get_data (pThis);
1042 d1 = dsp_get_data (pThis);
1043 d0 = dsp_get_data (pThis);
1044
1045 if (pThis->cmd & 8)
1046 LogFlowFunc(("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, d0, d1, d2));
1047 else
1048 {
1049 LogFlowFunc(("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, d0, d1, d2));
1050 dma_cmd(pThis, pThis->cmd, d0, d1 + (d2 << 8));
1051 }
1052 }
1053 else
1054 {
1055 switch (pThis->cmd)
1056 {
1057 case 0x04:
1058 pThis->csp_mode = dsp_get_data (pThis);
1059 pThis->csp_reg83r = 0;
1060 pThis->csp_reg83w = 0;
1061 LogFlowFunc(("CSP command 0x04: mode=%#x\n", pThis->csp_mode));
1062 break;
1063
1064 case 0x05:
1065 pThis->csp_param = dsp_get_data (pThis);
1066 pThis->csp_value = dsp_get_data (pThis);
1067 LogFlowFunc(("CSP command 0x05: param=%#x value=%#x\n",
1068 pThis->csp_param,
1069 pThis->csp_value));
1070 break;
1071
1072 case 0x0e:
1073 {
1074 d0 = dsp_get_data(pThis);
1075 d1 = dsp_get_data(pThis);
1076 LogFlowFunc(("write CSP register %d <- %#x\n", d1, d0));
1077 if (d1 == 0x83)
1078 {
1079 LogFlowFunc(("0x83[%d] <- %#x\n", pThis->csp_reg83r, d0));
1080 pThis->csp_reg83[pThis->csp_reg83r % 4] = d0;
1081 pThis->csp_reg83r += 1;
1082 }
1083 else
1084 pThis->csp_regs[d1] = d0;
1085 break;
1086 }
1087
1088 case 0x0f:
1089 d0 = dsp_get_data(pThis);
1090 LogFlowFunc(("read CSP register %#x -> %#x, mode=%#x\n", d0, pThis->csp_regs[d0], pThis->csp_mode));
1091 if (d0 == 0x83)
1092 {
1093 LogFlowFunc(("0x83[%d] -> %#x\n",
1094 pThis->csp_reg83w,
1095 pThis->csp_reg83[pThis->csp_reg83w % 4]));
1096 dsp_out_data (pThis, pThis->csp_reg83[pThis->csp_reg83w % 4]);
1097 pThis->csp_reg83w += 1;
1098 }
1099 else
1100 dsp_out_data(pThis, pThis->csp_regs[d0]);
1101 break;
1102
1103 case 0x10:
1104 d0 = dsp_get_data(pThis);
1105 LogFlowFunc(("cmd 0x10 d0=%#x\n", d0));
1106 break;
1107
1108 case 0x14:
1109 dma_cmd8(pThis, 0, dsp_get_lohi (pThis) + 1);
1110 break;
1111
1112 case 0x40:
1113 pThis->time_const = dsp_get_data(pThis);
1114 LogFlowFunc(("set time const %d\n", pThis->time_const));
1115 break;
1116
1117 case 0x42: /* FT2 sets output freq with this, go figure */
1118#if 0
1119 LogFlowFunc(("cmd 0x42 might not do what it think it should\n"));
1120#endif
1121 case 0x41:
1122 pThis->freq = dsp_get_hilo(pThis);
1123 LogFlowFunc(("set freq %d\n", pThis->freq));
1124 break;
1125
1126 case 0x48:
1127 pThis->block_size = dsp_get_lohi(pThis) + 1;
1128 LogFlowFunc(("set dma block len %d\n", pThis->block_size));
1129 break;
1130
1131 case 0x74:
1132 case 0x75:
1133 case 0x76:
1134 case 0x77:
1135 /* ADPCM stuff, ignore */
1136 break;
1137
1138 case 0x80:
1139 {
1140 int freq, samples, bytes;
1141 uint64_t ticks;
1142
1143 freq = pThis->freq > 0 ? pThis->freq : 11025;
1144 samples = dsp_get_lohi (pThis) + 1;
1145 bytes = samples << pThis->fmt_stereo << ((pThis->fmt_bits == 16) ? 1 : 0);
1146 ticks = (bytes * TMTimerGetFreq(pThis->pTimerIRQ)) / freq;
1147 if (ticks < TMTimerGetFreq(pThis->pTimerIRQ) / 1024)
1148 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
1149 else
1150 TMTimerSet(pThis->pTimerIRQ, TMTimerGet(pThis->pTimerIRQ) + ticks);
1151 LogFlowFunc(("mix silence: %d samples, %d bytes, %RU64 ticks\n", samples, bytes, ticks));
1152 break;
1153 }
1154
1155 case 0xe0:
1156 d0 = dsp_get_data(pThis);
1157 pThis->out_data_len = 0;
1158 LogFlowFunc(("E0 data = %#x\n", d0));
1159 dsp_out_data(pThis, ~d0);
1160 break;
1161
1162 case 0xe2:
1163 d0 = dsp_get_data(pThis);
1164 LogFlow(("SB16:E2 = %#x\n", d0));
1165 break;
1166
1167 case 0xe4:
1168 pThis->test_reg = dsp_get_data(pThis);
1169 break;
1170
1171 case 0xf9:
1172 d0 = dsp_get_data(pThis);
1173 LogFlowFunc(("command 0xf9 with %#x\n", d0));
1174 switch (d0) {
1175 case 0x0e:
1176 dsp_out_data(pThis, 0xff);
1177 break;
1178
1179 case 0x0f:
1180 dsp_out_data(pThis, 0x07);
1181 break;
1182
1183 case 0x37:
1184 dsp_out_data(pThis, 0x38);
1185 break;
1186
1187 default:
1188 dsp_out_data(pThis, 0x00);
1189 break;
1190 }
1191 break;
1192
1193 default:
1194 LogFlowFunc(("complete: unrecognized command %#x\n", pThis->cmd));
1195 return;
1196 }
1197 }
1198
1199 LogFlow(("\n"));
1200 pThis->cmd = -1;
1201 return;
1202}
1203
1204static uint8_t sb16MixRegToVol(PSB16STATE pThis, int reg)
1205{
1206 /* The SB16 mixer has a 0 to -62dB range in 32 levels (2dB each step).
1207 * We use a 0 to -96dB range in 256 levels (0.375dB each step).
1208 * Only the top 5 bits of a mixer register are used.
1209 */
1210 uint8_t steps = 31 - (pThis->mixer_regs[reg] >> 3);
1211 uint8_t vol = 255 - steps * 16 / 3; /* (2dB*8) / (0.375dB*8) */
1212 return vol;
1213}
1214
1215static void sb16SetMasterVolume(PSB16STATE pThis)
1216{
1217 /* There's no mute switch, only volume controls. */
1218 uint8_t lvol = sb16MixRegToVol(pThis, 0x30);
1219 uint8_t rvol = sb16MixRegToVol(pThis, 0x31);
1220
1221 PDMAUDIOVOLUME Vol = { false /* fMute */, lvol, rvol };
1222
1223 PSB16DRIVER pDrv;
1224 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1225 {
1226 int rc2 = pDrv->pConnector->pfnStreamSetVolume(pDrv->pConnector, pDrv->Out.pStream, &Vol);
1227 AssertRC(rc2);
1228 }
1229}
1230
1231static void sb16SetPcmOutVolume(PSB16STATE pThis)
1232{
1233 /* There's no mute switch, only volume controls. */
1234 uint8_t lvol = sb16MixRegToVol(pThis, 0x32);
1235 uint8_t rvol = sb16MixRegToVol(pThis, 0x33);
1236
1237 PDMAUDIOVOLUME Vol = { false /* fMute */, lvol, rvol };
1238
1239 PSB16DRIVER pDrv;
1240 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1241 {
1242 int rc2 = pDrv->pConnector->pfnStreamSetVolume(pDrv->pConnector, pDrv->Out.pStream, &Vol);
1243 AssertRC(rc2);
1244 }
1245}
1246
1247static void sb16ResetLegacy(PSB16STATE pThis)
1248{
1249 LogFlowFuncEnter();
1250
1251 sb16CloseOut(pThis);
1252
1253 pThis->freq = 11025;
1254 pThis->fmt_signed = 0;
1255 pThis->fmt_bits = 8;
1256 pThis->fmt_stereo = 0;
1257
1258 /* At the moment we only have one stream, the output stream. */
1259 PPDMAUDIOSTREAMCFG pCfg = &pThis->Out.Cfg;
1260
1261 pCfg->enmDir = PDMAUDIODIR_OUT;
1262 pCfg->DestSource.Dest = PDMAUDIOPLAYBACKDEST_FRONT;
1263 pCfg->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
1264
1265 pCfg->Props.uHz = pThis->freq;
1266 pCfg->Props.cChannels = 1; /* Mono */
1267 pCfg->Props.cBits = 8;
1268 pCfg->Props.fSigned = false;
1269 pCfg->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfg->Props.cBits, pCfg->Props.cChannels);
1270
1271 RTStrPrintf(pCfg->szName, sizeof(pCfg->szName), "Output");
1272
1273 sb16CloseOut(pThis);
1274
1275 int rc2 = sb16OpenOut(pThis, pCfg);
1276 AssertRC(rc2);
1277}
1278
1279static void sb16Reset(PSB16STATE pThis)
1280{
1281 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1282 if (pThis->dma_auto)
1283 {
1284 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
1285 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1286 }
1287
1288 pThis->mixer_regs[0x82] = 0;
1289 pThis->dma_auto = 0;
1290 pThis->in_index = 0;
1291 pThis->out_data_len = 0;
1292 pThis->left_till_irq = 0;
1293 pThis->needed_bytes = 0;
1294 pThis->block_size = -1;
1295 pThis->nzero = 0;
1296 pThis->highspeed = 0;
1297 pThis->v2x6 = 0;
1298 pThis->cmd = -1;
1299
1300 dsp_out_data(pThis, 0xaa);
1301 sb16SpeakerControl(pThis, 0);
1302
1303 sb16Control(pThis, 0);
1304 sb16ResetLegacy(pThis);
1305}
1306
1307static IO_WRITE_PROTO(dsp_write)
1308{
1309 RT_NOREF(pDevIns, cb);
1310 PSB16STATE pThis = (PSB16STATE)opaque;
1311 int iport = nport - pThis->port;
1312
1313 LogFlowFunc(("write %#x <- %#x\n", nport, val));
1314 switch (iport)
1315 {
1316 case 0x06:
1317 switch (val)
1318 {
1319 case 0x00:
1320 {
1321 if (pThis->v2x6 == 1)
1322 {
1323 if (0 && pThis->highspeed)
1324 {
1325 pThis->highspeed = 0;
1326 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1327 sb16Control(pThis, 0);
1328 }
1329 else
1330 sb16Reset(pThis);
1331 }
1332 pThis->v2x6 = 0;
1333 break;
1334 }
1335
1336 case 0x01:
1337 case 0x03: /* FreeBSD kludge */
1338 pThis->v2x6 = 1;
1339 break;
1340
1341 case 0xc6:
1342 pThis->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */
1343 break;
1344
1345 case 0xb8: /* Panic */
1346 sb16Reset(pThis);
1347 break;
1348
1349 case 0x39:
1350 dsp_out_data(pThis, 0x38);
1351 sb16Reset(pThis);
1352 pThis->v2x6 = 0x39;
1353 break;
1354
1355 default:
1356 pThis->v2x6 = val;
1357 break;
1358 }
1359 break;
1360
1361 case 0x0c: /* Write data or command | write status */
1362#if 0
1363 if (pThis->highspeed)
1364 break;
1365#endif
1366 if (0 == pThis->needed_bytes)
1367 {
1368 sb16HandleCommand(pThis, val);
1369#if 0
1370 if (0 == pThis->needed_bytes) {
1371 log_dsp (pThis);
1372 }
1373#endif
1374 }
1375 else
1376 {
1377 if (pThis->in_index == sizeof (pThis->in2_data))
1378 {
1379 LogFlowFunc(("in data overrun\n"));
1380 }
1381 else
1382 {
1383 pThis->in2_data[pThis->in_index++] = val;
1384 if (pThis->in_index == pThis->needed_bytes)
1385 {
1386 pThis->needed_bytes = 0;
1387 complete (pThis);
1388#if 0
1389 log_dsp (pThis);
1390#endif
1391 }
1392 }
1393 }
1394 break;
1395
1396 default:
1397 LogFlowFunc(("nport=%#x, val=%#x)\n", nport, val));
1398 break;
1399 }
1400
1401 return VINF_SUCCESS;
1402}
1403
1404static IO_READ_PROTO(dsp_read)
1405{
1406 RT_NOREF(pDevIns, cb);
1407 PSB16STATE pThis = (PSB16STATE)opaque;
1408 int iport, retval, ack = 0;
1409
1410 iport = nport - pThis->port;
1411
1412 /** @todo reject non-byte access?
1413 * The spec does not mention a non-byte access so we should check how real hardware behaves. */
1414
1415 switch (iport)
1416 {
1417 case 0x06: /* reset */
1418 retval = 0xff;
1419 break;
1420
1421 case 0x0a: /* read data */
1422 if (pThis->out_data_len)
1423 {
1424 retval = pThis->out_data[--pThis->out_data_len];
1425 pThis->last_read_byte = retval;
1426 }
1427 else
1428 {
1429 if (pThis->cmd != -1)
1430 LogFlowFunc(("empty output buffer for command %#x\n", pThis->cmd));
1431 retval = pThis->last_read_byte;
1432 /* goto error; */
1433 }
1434 break;
1435
1436 case 0x0c: /* 0 can write */
1437 retval = pThis->can_write ? 0 : 0x80;
1438 break;
1439
1440 case 0x0d: /* timer interrupt clear */
1441 /* LogFlowFunc(("timer interrupt clear\n")); */
1442 retval = 0;
1443 break;
1444
1445 case 0x0e: /* data available status | irq 8 ack */
1446 retval = (!pThis->out_data_len || pThis->highspeed) ? 0 : 0x80;
1447 if (pThis->mixer_regs[0x82] & 1)
1448 {
1449 ack = 1;
1450 pThis->mixer_regs[0x82] &= ~1;
1451 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1452 }
1453 break;
1454
1455 case 0x0f: /* irq 16 ack */
1456 retval = 0xff;
1457 if (pThis->mixer_regs[0x82] & 2)
1458 {
1459 ack = 1;
1460 pThis->mixer_regs[0x82] &= ~2;
1461 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1462 }
1463 break;
1464
1465 default:
1466 goto error;
1467 }
1468
1469 if (!ack)
1470 LogFlowFunc(("read %#x -> %#x\n", nport, retval));
1471
1472 *pu32 = retval;
1473 return VINF_SUCCESS;
1474
1475 error:
1476 LogFlowFunc(("warning: dsp_read %#x error\n", nport));
1477 return VERR_IOM_IOPORT_UNUSED;
1478}
1479
1480static void sb16MixerReset(PSB16STATE pThis)
1481{
1482 memset(pThis->mixer_regs, 0xff, 0x7f);
1483 memset(pThis->mixer_regs + 0x83, 0xff, sizeof (pThis->mixer_regs) - 0x83);
1484
1485 pThis->mixer_regs[0x02] = 4; /* master volume 3bits */
1486 pThis->mixer_regs[0x06] = 4; /* MIDI volume 3bits */
1487 pThis->mixer_regs[0x08] = 0; /* CD volume 3bits */
1488 pThis->mixer_regs[0x0a] = 0; /* voice volume 2bits */
1489
1490 /* d5=input filt, d3=lowpass filt, d1,d2=input source */
1491 pThis->mixer_regs[0x0c] = 0;
1492
1493 /* d5=output filt, d1=stereo switch */
1494 pThis->mixer_regs[0x0e] = 0;
1495
1496 /* voice volume L d5,d7, R d1,d3 */
1497 pThis->mixer_regs[0x04] = (12 << 4) | 12;
1498 /* master ... */
1499 pThis->mixer_regs[0x22] = (12 << 4) | 12;
1500 /* MIDI ... */
1501 pThis->mixer_regs[0x26] = (12 << 4) | 12;
1502
1503 /* master/voice/MIDI L/R volume */
1504 for (int i = 0x30; i < 0x36; i++)
1505 pThis->mixer_regs[i] = 24 << 3; /* -14 dB */
1506
1507 /* treble/bass */
1508 for (int i = 0x44; i < 0x48; i++)
1509 pThis->mixer_regs[i] = 0x80;
1510
1511 /* Update the master (mixer) and PCM out volumes. */
1512 sb16SetMasterVolume(pThis);
1513 sb16SetPcmOutVolume(pThis);
1514}
1515
1516static IO_WRITE_PROTO(mixer_write_indexb)
1517{
1518 RT_NOREF(pDevIns, cb);
1519 PSB16STATE pThis = (PSB16STATE)opaque;
1520 (void) nport;
1521 pThis->mixer_nreg = val;
1522
1523 return VINF_SUCCESS;
1524}
1525
1526uint32_t popcount(uint32_t u) /** @todo r=andy WTF? */
1527{
1528 u = ((u&0x55555555) + ((u>>1)&0x55555555));
1529 u = ((u&0x33333333) + ((u>>2)&0x33333333));
1530 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
1531 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
1532 u = ( u&0x0000ffff) + (u>>16);
1533 return u;
1534}
1535
1536uint32_t lsbindex(uint32_t u)
1537{
1538 return popcount((u & -(int32_t)u) - 1);
1539}
1540
1541/* Convert SB16 to SB Pro mixer volume (left). */
1542static inline void sb16ConvVolumeL(PSB16STATE pThis, unsigned reg, uint8_t val)
1543{
1544 /* High nibble in SBP mixer. */
1545 pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0x0f) | (val & 0xf0);
1546}
1547
1548/* Convert SB16 to SB Pro mixer volume (right). */
1549static inline void sb16ConvVolumeR(PSB16STATE pThis, unsigned reg, uint8_t val)
1550{
1551 /* Low nibble in SBP mixer. */
1552 pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0xf0) | (val >> 4);
1553}
1554
1555/* Convert SB Pro to SB16 mixer volume (left + right). */
1556static inline void sb16ConvVolumeOldToNew(PSB16STATE pThis, unsigned reg, uint8_t val)
1557{
1558 /* Left channel. */
1559 pThis->mixer_regs[reg + 0] = (val & 0xf0) | RT_BIT(3);
1560 /* Right channel (the register immediately following). */
1561 pThis->mixer_regs[reg + 1] = (val << 4) | RT_BIT(3);
1562}
1563
1564static IO_WRITE_PROTO(mixer_write_datab)
1565{
1566 RT_NOREF(pDevIns, cb);
1567 PSB16STATE pThis = (PSB16STATE)opaque;
1568 bool fUpdateMaster = false;
1569 bool fUpdateStream = false;
1570
1571 (void) nport;
1572 LogFlowFunc(("mixer_write [%#x] <- %#x\n", pThis->mixer_nreg, val));
1573
1574 switch (pThis->mixer_nreg)
1575 {
1576 case 0x00:
1577 sb16MixerReset(pThis);
1578 /* And update the actual volume, too. */
1579 fUpdateMaster = true;
1580 fUpdateStream = true;
1581 break;
1582
1583 case 0x04: /* Translate from old style voice volume (L/R). */
1584 sb16ConvVolumeOldToNew(pThis, 0x32, val);
1585 fUpdateStream = true;
1586 break;
1587
1588 case 0x22: /* Translate from old style master volume (L/R). */
1589 sb16ConvVolumeOldToNew(pThis, 0x30, val);
1590 fUpdateMaster = true;
1591 break;
1592
1593 case 0x26: /* Translate from old style MIDI volume (L/R). */
1594 sb16ConvVolumeOldToNew(pThis, 0x34, val);
1595 break;
1596
1597 case 0x28: /* Translate from old style CD volume (L/R). */
1598 sb16ConvVolumeOldToNew(pThis, 0x36, val);
1599 break;
1600
1601 case 0x2E: /* Translate from old style line volume (L/R). */
1602 sb16ConvVolumeOldToNew(pThis, 0x38, val);
1603 break;
1604
1605 case 0x30: /* Translate to old style master volume (L). */
1606 sb16ConvVolumeL(pThis, 0x22, val);
1607 fUpdateMaster = true;
1608 break;
1609
1610 case 0x31: /* Translate to old style master volume (R). */
1611 sb16ConvVolumeR(pThis, 0x22, val);
1612 fUpdateMaster = true;
1613 break;
1614
1615 case 0x32: /* Translate to old style voice volume (L). */
1616 sb16ConvVolumeL(pThis, 0x04, val);
1617 fUpdateStream = true;
1618 break;
1619
1620 case 0x33: /* Translate to old style voice volume (R). */
1621 sb16ConvVolumeR(pThis, 0x04, val);
1622 fUpdateStream = true;
1623 break;
1624
1625 case 0x34: /* Translate to old style MIDI volume (L). */
1626 sb16ConvVolumeL(pThis, 0x26, val);
1627 break;
1628
1629 case 0x35: /* Translate to old style MIDI volume (R). */
1630 sb16ConvVolumeR(pThis, 0x26, val);
1631 break;
1632
1633 case 0x36: /* Translate to old style CD volume (L). */
1634 sb16ConvVolumeL(pThis, 0x28, val);
1635 break;
1636
1637 case 0x37: /* Translate to old style CD volume (R). */
1638 sb16ConvVolumeR(pThis, 0x28, val);
1639 break;
1640
1641 case 0x38: /* Translate to old style line volume (L). */
1642 sb16ConvVolumeL(pThis, 0x2E, val);
1643 break;
1644
1645 case 0x39: /* Translate to old style line volume (R). */
1646 sb16ConvVolumeR(pThis, 0x2E, val);
1647 break;
1648
1649 case 0x80:
1650 {
1651 int irq = irq_of_magic(val);
1652 LogFlowFunc(("setting irq to %d (val=%#x)\n", irq, val));
1653 if (irq > 0)
1654 pThis->irq = irq;
1655 break;
1656 }
1657
1658 case 0x81:
1659 {
1660 int dma, hdma;
1661
1662 dma = lsbindex (val & 0xf);
1663 hdma = lsbindex (val & 0xf0);
1664 if (dma != pThis->dma || hdma != pThis->hdma)
1665 LogFlow(("SB16: attempt to change DMA 8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
1666 dma, pThis->dma, hdma, pThis->hdma, val));
1667#if 0
1668 pThis->dma = dma;
1669 pThis->hdma = hdma;
1670#endif
1671 break;
1672 }
1673
1674 case 0x82:
1675 LogFlowFunc(("attempt to write into IRQ status register (val=%#x)\n", val));
1676 return VINF_SUCCESS;
1677
1678 default:
1679 if (pThis->mixer_nreg >= 0x80)
1680 LogFlowFunc(("attempt to write mixer[%#x] <- %#x\n", pThis->mixer_nreg, val));
1681 break;
1682 }
1683
1684 pThis->mixer_regs[pThis->mixer_nreg] = val;
1685
1686 /* Update the master (mixer) volume. */
1687 if (fUpdateMaster)
1688 sb16SetMasterVolume(pThis);
1689
1690 /* Update the stream (PCM) volume. */
1691 if (fUpdateStream)
1692 sb16SetPcmOutVolume(pThis);
1693
1694 return VINF_SUCCESS;
1695}
1696
1697static IO_WRITE_PROTO(mixer_write)
1698{
1699 PSB16STATE pThis = (PSB16STATE)opaque;
1700 int iport = nport - pThis->port;
1701 switch (cb)
1702 {
1703 case 1:
1704 switch (iport)
1705 {
1706 case 4:
1707 mixer_write_indexb (pDevIns, opaque, nport, val, 1);
1708 break;
1709 case 5:
1710 mixer_write_datab (pDevIns, opaque, nport, val, 1);
1711 break;
1712 }
1713 break;
1714 case 2:
1715 mixer_write_indexb (pDevIns, opaque, nport, val & 0xff, 1);
1716 mixer_write_datab (pDevIns, opaque, nport, (val >> 8) & 0xff, 1);
1717 break;
1718 default:
1719 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", nport, cb, val));
1720 break;
1721 }
1722 return VINF_SUCCESS;
1723}
1724
1725static IO_READ_PROTO(mixer_read)
1726{
1727 RT_NOREF(pDevIns, cb);
1728 PSB16STATE pThis = (PSB16STATE)opaque;
1729
1730 (void) nport;
1731#ifndef DEBUG_SB16_MOST
1732 if (pThis->mixer_nreg != 0x82) {
1733 LogFlowFunc(("mixer_read[%#x] -> %#x\n",
1734 pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
1735 }
1736#else
1737 LogFlowFunc(("mixer_read[%#x] -> %#x\n",
1738 pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
1739#endif
1740 *pu32 = pThis->mixer_regs[pThis->mixer_nreg];
1741 return VINF_SUCCESS;
1742}
1743
1744static int sb16WriteAudio(PSB16STATE pThis, int nchan, uint32_t dma_pos,
1745 uint32_t dma_len, int len)
1746{
1747 uint8_t tmpbuf[_4K]; /** @todo Have a buffer on the heap. */
1748 uint32_t cbToWrite = len;
1749 uint32_t cbWrittenTotal = 0;
1750
1751 while (cbToWrite)
1752 {
1753 uint32_t cbToRead = RT_MIN(dma_len - dma_pos, cbToWrite);
1754 if (cbToRead > sizeof(tmpbuf))
1755 cbToRead = sizeof(tmpbuf);
1756
1757 uint32_t cbRead = 0;
1758 int rc2 = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, nchan, tmpbuf, dma_pos, cbToRead, &cbRead);
1759 AssertMsgRC(rc2, (" from DMA failed: %Rrc\n", rc2));
1760
1761#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
1762 if (cbRead)
1763 {
1764 RTFILE fh;
1765 RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "sb16WriteAudio.pcm",
1766 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1767 RTFileWrite(fh, tmpbuf, cbRead, NULL);
1768 RTFileClose(fh);
1769 }
1770#endif
1771 /*
1772 * Write data to the backends.
1773 */
1774 uint32_t cbWritten = 0;
1775
1776 /* Just multiplex the output to the connected backends.
1777 * No need to utilize the virtual mixer here (yet). */
1778 PSB16DRIVER pDrv;
1779 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1780 {
1781 uint32_t cbWrittenToStream = 0;
1782 rc2 = pDrv->pConnector->pfnStreamWrite(pDrv->pConnector, pDrv->Out.pStream, tmpbuf, cbRead, &cbWrittenToStream);
1783
1784 LogFlowFunc(("\tLUN#%RU8: rc=%Rrc, cbWrittenToStream=%RU32\n", pDrv->uLUN, rc2, cbWrittenToStream));
1785
1786 /* The primary driver sets the overall pace. */
1787 if (pDrv->fFlags & PDMAUDIODRVFLAGS_PRIMARY)
1788 {
1789 cbWritten = cbWrittenToStream;
1790
1791 if (RT_FAILURE(rc2))
1792 break;
1793 }
1794 }
1795
1796 LogFlowFunc(("\tcbToRead=%RU32, cbToWrite=%RU32, cbWritten=%RU32, cbLeft=%RU32\n",
1797 cbToRead, cbToWrite, cbWritten, cbToWrite - cbWrittenTotal));
1798
1799 Assert(cbToWrite >= cbWritten);
1800 cbToWrite -= cbWritten;
1801 dma_pos = (dma_pos + cbWritten) % dma_len;
1802 cbWrittenTotal += cbWritten;
1803
1804 if (!cbWritten)
1805 break;
1806 }
1807
1808 return cbWrittenTotal;
1809}
1810
1811static DECLCALLBACK(uint32_t) sb16DMARead(PPDMDEVINS pDevIns, void *opaque, unsigned nchan, uint32_t dma_pos, uint32_t dma_len)
1812{
1813 RT_NOREF(pDevIns);
1814 PSB16STATE pThis = (PSB16STATE)opaque;
1815 int till, copy, written, free;
1816
1817 if (pThis->block_size <= 0)
1818 {
1819 LogFlowFunc(("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
1820 pThis->block_size, nchan, dma_pos, dma_len));
1821 return dma_pos;
1822 }
1823
1824 if (pThis->left_till_irq < 0)
1825 pThis->left_till_irq = pThis->block_size;
1826
1827 uint32_t cbOutMin = UINT32_MAX;
1828
1829 PSB16DRIVER pDrv;
1830 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1831 {
1832 uint32_t cbOut = pDrv->pConnector->pfnStreamGetWritable(pDrv->pConnector, pDrv->Out.pStream);
1833
1834 if (cbOut < cbOutMin)
1835 cbOutMin = cbOut;
1836 }
1837
1838 LogFlowFunc(("cbOutMin=%RU32\n", cbOutMin));
1839 if (cbOutMin == UINT32_MAX)
1840 {
1841 free = dma_len;
1842 }
1843 else
1844 {
1845 free = cbOutMin & ~pThis->align; /** @todo int vs. uint32. */
1846 if ((free <= 0) || !dma_len)
1847 return dma_pos;
1848 }
1849
1850 copy = free;
1851 till = pThis->left_till_irq;
1852
1853#ifdef DEBUG_SB16_MOST
1854 LogFlowFunc(("pos:%06d %d till:%d len:%d\n", dma_pos, free, till, dma_len));
1855#endif
1856
1857 if (copy >= till)
1858 {
1859 if (0 == pThis->dma_auto)
1860 {
1861 copy = till;
1862 }
1863 else
1864 {
1865 if (copy >= till + pThis->block_size)
1866 copy = till; /* Make sure we won't skip IRQs. */
1867 }
1868 }
1869
1870 written = sb16WriteAudio(pThis, nchan, dma_pos, dma_len, copy);
1871 dma_pos = (dma_pos + written) % dma_len;
1872 pThis->left_till_irq -= written;
1873
1874 if (pThis->left_till_irq <= 0)
1875 {
1876 pThis->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
1877 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
1878 if (0 == pThis->dma_auto)
1879 {
1880 sb16Control(pThis, 0);
1881 sb16SpeakerControl(pThis, 0);
1882 }
1883 }
1884
1885 Log3Func(("pos %d/%d free %5d till %5d copy %5d written %5d block_size %5d\n",
1886 dma_pos, dma_len, free, pThis->left_till_irq, copy, written,
1887 pThis->block_size));
1888
1889 while (pThis->left_till_irq <= 0)
1890 pThis->left_till_irq += pThis->block_size;
1891
1892 return dma_pos;
1893}
1894
1895#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
1896static void sb16TimerMaybeStart(PSB16STATE pThis)
1897{
1898 LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive));
1899
1900 if (pThis->cStreamsActive == 0) /* Only start the timer if there are no active streams. */
1901 return;
1902
1903 if (!pThis->pTimerIO)
1904 return;
1905
1906 /* Set timer flag. */
1907 ASMAtomicXchgBool(&pThis->fTimerActive, true);
1908
1909 /* Update current time timestamp. */
1910 pThis->uTimerTSIO = TMTimerGet(pThis->pTimerIO);
1911
1912 /* Fire off timer. */
1913 TMTimerSet(pThis->pTimerIO, TMTimerGet(pThis->pTimerIO) + pThis->cTimerTicksIO);
1914}
1915
1916static void sb16TimerMaybeStop(PSB16STATE pThis)
1917{
1918 LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive));
1919
1920 if (pThis->cStreamsActive) /* Some streams still active? Bail out. */
1921 return;
1922
1923 if (!pThis->pTimerIO)
1924 return;
1925
1926 /* Set timer flag. */
1927 ASMAtomicXchgBool(&pThis->fTimerActive, false);
1928}
1929
1930static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1931{
1932 RT_NOREF(pDevIns);
1933 PSB16STATE pThis = (PSB16STATE)pvUser;
1934 Assert(pThis == PDMINS_2_DATA(pDevIns, PSB16STATE));
1935 AssertPtr(pThis);
1936
1937 uint64_t cTicksNow = TMTimerGet(pTimer);
1938 bool fIsPlaying = false; /* Whether one or more streams are still playing. */
1939 bool fDoTransfer = false;
1940
1941 pThis->uTimerTSIO = cTicksNow;
1942
1943 PSB16DRIVER pDrv;
1944 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1945 {
1946 PPDMAUDIOSTREAM pStream = pDrv->Out.pStream;
1947 if (!pStream)
1948 continue;
1949
1950#ifdef DEBUG
1951 PSB16DRIVER pDrvPrev = RTListNodeGetPrev(&pDrv->Node, SB16DRIVER, Node);
1952 if ( pDrvPrev
1953 && !RTListNodeIsDummy(&pThis->lstDrv, pDrvPrev, SB16DRIVER, Node))
1954 {
1955 PPDMAUDIOSTREAM pStreamPrev = pDrvPrev->Out.pStream;
1956 AssertPtr(pStreamPrev);
1957
1958 /*
1959 * Sanity. Make sure that all streams have the same configuration
1960 * to get SB16's DMA transfers right.
1961 *
1962 * SB16 only allows one output configuration per serial data out,
1963 * so check if all streams have the same configuration.
1964 */
1965 AssertMsg(pStream->Cfg.Props.uHz == pStreamPrev->Cfg.Props.uHz,
1966 ("%RU32Hz vs. %RU32Hz\n", pStream->Cfg.Props.uHz, pStreamPrev->Cfg.Props.uHz));
1967 AssertMsg(pStream->Cfg.Props.cChannels == pStreamPrev->Cfg.Props.cChannels,
1968 ("%RU8 vs. %RU8 channels\n", pStream->Cfg.Props.cChannels, pStreamPrev->Cfg.Props.cChannels));
1969 AssertMsg(pStream->Cfg.Props.cBits == pStreamPrev->Cfg.Props.cBits,
1970 ("%d vs. %d bits\n", pStream->Cfg.Props.cBits, pStreamPrev->Cfg.Props.cBits));
1971 AssertMsg(pStream->Cfg.Props.fSigned == pStreamPrev->Cfg.Props.fSigned,
1972 ("%RTbool vs. %RTbool signed\n", pStream->Cfg.Props.fSigned, pStreamPrev->Cfg.Props.fSigned));
1973 }
1974#endif
1975 PPDMIAUDIOCONNECTOR pConn = pDrv->pConnector;
1976 if (!pConn)
1977 continue;
1978
1979 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1980 if (RT_SUCCESS(rc2))
1981 {
1982 if (pStream->enmDir == PDMAUDIODIR_IN)
1983 {
1984 /** @todo Implement recording! */
1985 }
1986 else
1987 {
1988 rc2 = pConn->pfnStreamPlay(pConn, pStream, NULL /* cPlayed */);
1989 if (RT_FAILURE(rc2))
1990 {
1991 LogFlowFunc(("%s: Failed playing stream, rc=%Rrc\n", pStream->szName, rc2));
1992 continue;
1993 }
1994 }
1995
1996 if (pDrv->fFlags & PDMAUDIODRVFLAGS_PRIMARY)
1997 {
1998 /* Only do the next DMA transfer if we're able to write the remaining data block. */
1999 fDoTransfer = pConn->pfnStreamGetWritable(pConn, pStream) > (unsigned)pThis->left_till_irq;
2000 }
2001 }
2002
2003 PDMAUDIOSTREAMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pStream);
2004 fIsPlaying |= ( (strmSts & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
2005 || (strmSts & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE));
2006 }
2007
2008 bool fTimerActive = ASMAtomicReadBool(&pThis->fTimerActive);
2009 bool fKickTimer = fTimerActive || fIsPlaying;
2010
2011 LogFlowFunc(("fTimerActive=%RTbool, fIsPlaying=%RTbool\n", fTimerActive, fIsPlaying));
2012
2013 if (fDoTransfer)
2014 {
2015 /* Schedule the next transfer. */
2016 PDMDevHlpDMASchedule(pThis->pDevInsR3);
2017
2018 /* Kick the timer at least one more time. */
2019 fKickTimer = true;
2020 }
2021
2022 /*
2023 * Recording.
2024 */
2025 /** @todo Implement recording. */
2026
2027 if (fKickTimer)
2028 {
2029 /* Kick the timer again. */
2030 uint64_t cTicks = pThis->cTimerTicksIO;
2031 /** @todo adjust cTicks down by now much cbOutMin represents. */
2032 TMTimerSet(pThis->pTimerIO, cTicksNow + cTicks);
2033 }
2034}
2035#endif /* !VBOX_WITH_AUDIO_SB16_CALLBACKS */
2036
2037static void sb16Save(PSSMHANDLE pSSM, PSB16STATE pThis)
2038{
2039 SSMR3PutS32(pSSM, pThis->irq);
2040 SSMR3PutS32(pSSM, pThis->dma);
2041 SSMR3PutS32(pSSM, pThis->hdma);
2042 SSMR3PutS32(pSSM, pThis->port);
2043 SSMR3PutS32(pSSM, pThis->ver);
2044 SSMR3PutS32(pSSM, pThis->in_index);
2045 SSMR3PutS32(pSSM, pThis->out_data_len);
2046 SSMR3PutS32(pSSM, pThis->fmt_stereo);
2047 SSMR3PutS32(pSSM, pThis->fmt_signed);
2048 SSMR3PutS32(pSSM, pThis->fmt_bits);
2049
2050 SSMR3PutU32(pSSM, pThis->fmt);
2051
2052 SSMR3PutS32(pSSM, pThis->dma_auto);
2053 SSMR3PutS32(pSSM, pThis->block_size);
2054 SSMR3PutS32(pSSM, pThis->fifo);
2055 SSMR3PutS32(pSSM, pThis->freq);
2056 SSMR3PutS32(pSSM, pThis->time_const);
2057 SSMR3PutS32(pSSM, pThis->speaker);
2058 SSMR3PutS32(pSSM, pThis->needed_bytes);
2059 SSMR3PutS32(pSSM, pThis->cmd);
2060 SSMR3PutS32(pSSM, pThis->use_hdma);
2061 SSMR3PutS32(pSSM, pThis->highspeed);
2062 SSMR3PutS32(pSSM, pThis->can_write);
2063 SSMR3PutS32(pSSM, pThis->v2x6);
2064
2065 SSMR3PutU8 (pSSM, pThis->csp_param);
2066 SSMR3PutU8 (pSSM, pThis->csp_value);
2067 SSMR3PutU8 (pSSM, pThis->csp_mode);
2068 SSMR3PutU8 (pSSM, pThis->csp_param); /* Bug compatible! */
2069 SSMR3PutMem(pSSM, pThis->csp_regs, 256);
2070 SSMR3PutU8 (pSSM, pThis->csp_index);
2071 SSMR3PutMem(pSSM, pThis->csp_reg83, 4);
2072 SSMR3PutS32(pSSM, pThis->csp_reg83r);
2073 SSMR3PutS32(pSSM, pThis->csp_reg83w);
2074
2075 SSMR3PutMem(pSSM, pThis->in2_data, sizeof (pThis->in2_data));
2076 SSMR3PutMem(pSSM, pThis->out_data, sizeof (pThis->out_data));
2077 SSMR3PutU8 (pSSM, pThis->test_reg);
2078 SSMR3PutU8 (pSSM, pThis->last_read_byte);
2079
2080 SSMR3PutS32(pSSM, pThis->nzero);
2081 SSMR3PutS32(pSSM, pThis->left_till_irq);
2082 SSMR3PutS32(pSSM, pThis->dma_running);
2083 SSMR3PutS32(pSSM, pThis->bytes_per_second);
2084 SSMR3PutS32(pSSM, pThis->align);
2085
2086 SSMR3PutS32(pSSM, pThis->mixer_nreg);
2087 SSMR3PutMem(pSSM, pThis->mixer_regs, 256);
2088
2089}
2090
2091static int sb16Load(PSSMHANDLE pSSM, PSB16STATE pThis)
2092{
2093 SSMR3GetS32(pSSM, &pThis->irq);
2094 SSMR3GetS32(pSSM, &pThis->dma);
2095 SSMR3GetS32(pSSM, &pThis->hdma);
2096 SSMR3GetS32(pSSM, &pThis->port);
2097 SSMR3GetS32(pSSM, &pThis->ver);
2098 SSMR3GetS32(pSSM, &pThis->in_index);
2099 SSMR3GetS32(pSSM, &pThis->out_data_len);
2100 SSMR3GetS32(pSSM, &pThis->fmt_stereo);
2101 SSMR3GetS32(pSSM, &pThis->fmt_signed);
2102 SSMR3GetS32(pSSM, &pThis->fmt_bits);
2103
2104 SSMR3GetU32(pSSM, (uint32_t *)&pThis->fmt);
2105
2106 SSMR3GetS32(pSSM, &pThis->dma_auto);
2107 SSMR3GetS32(pSSM, &pThis->block_size);
2108 SSMR3GetS32(pSSM, &pThis->fifo);
2109 SSMR3GetS32(pSSM, &pThis->freq);
2110 SSMR3GetS32(pSSM, &pThis->time_const);
2111 SSMR3GetS32(pSSM, &pThis->speaker);
2112 SSMR3GetS32(pSSM, &pThis->needed_bytes);
2113 SSMR3GetS32(pSSM, &pThis->cmd);
2114 SSMR3GetS32(pSSM, &pThis->use_hdma);
2115 SSMR3GetS32(pSSM, &pThis->highspeed);
2116 SSMR3GetS32(pSSM, &pThis->can_write);
2117 SSMR3GetS32(pSSM, &pThis->v2x6);
2118
2119 SSMR3GetU8 (pSSM, &pThis->csp_param);
2120 SSMR3GetU8 (pSSM, &pThis->csp_value);
2121 SSMR3GetU8 (pSSM, &pThis->csp_mode);
2122 SSMR3GetU8 (pSSM, &pThis->csp_param); /* Bug compatible! */
2123 SSMR3GetMem(pSSM, pThis->csp_regs, 256);
2124 SSMR3GetU8 (pSSM, &pThis->csp_index);
2125 SSMR3GetMem(pSSM, pThis->csp_reg83, 4);
2126 SSMR3GetS32(pSSM, &pThis->csp_reg83r);
2127 SSMR3GetS32(pSSM, &pThis->csp_reg83w);
2128
2129 SSMR3GetMem(pSSM, pThis->in2_data, sizeof (pThis->in2_data));
2130 SSMR3GetMem(pSSM, pThis->out_data, sizeof (pThis->out_data));
2131 SSMR3GetU8 (pSSM, &pThis->test_reg);
2132 SSMR3GetU8 (pSSM, &pThis->last_read_byte);
2133
2134 SSMR3GetS32(pSSM, &pThis->nzero);
2135 SSMR3GetS32(pSSM, &pThis->left_till_irq);
2136 SSMR3GetS32(pSSM, &pThis->dma_running);
2137 SSMR3GetS32(pSSM, &pThis->bytes_per_second);
2138 SSMR3GetS32(pSSM, &pThis->align);
2139
2140 SSMR3GetS32(pSSM, &pThis->mixer_nreg);
2141 SSMR3GetMem(pSSM, pThis->mixer_regs, 256);
2142
2143#if 0
2144 PSB16DRIVER pDrv;
2145 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2146 {
2147 if (pDrv->Out.pStream)
2148 {
2149 pDrv->pConnector->pfnCloseOut(pThis->pDrv, pDrv->Out.pStream);
2150 pDrv->Out.pStream = NULL;
2151 }
2152 }
2153#endif
2154
2155 if (pThis->dma_running)
2156 {
2157 if (pThis->freq)
2158 {
2159 /* At the moment we only have one stream, the output stream. */
2160 PPDMAUDIOSTREAMCFG pCfg = &pThis->Out.Cfg;
2161
2162 pCfg->enmDir = PDMAUDIODIR_OUT;
2163 pCfg->DestSource.Dest = PDMAUDIOPLAYBACKDEST_FRONT;
2164 pCfg->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
2165
2166 pCfg->Props.uHz = pThis->freq;
2167 pCfg->Props.cChannels = 1 << pThis->fmt_stereo;
2168 pCfg->Props.cBits = pThis->fmt_bits;
2169 pCfg->Props.fSigned = RT_BOOL(pThis->fmt_signed);
2170 pCfg->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfg->Props.cBits, pCfg->Props.cChannels);
2171
2172 RTStrPrintf(pCfg->szName, sizeof(pCfg->szName), "Output");
2173
2174 sb16CloseOut(pThis);
2175
2176 int rc = sb16OpenOut(pThis, pCfg);
2177 AssertRC(rc);
2178 }
2179
2180 sb16Control(pThis, 1);
2181 sb16SpeakerControl(pThis, pThis->speaker);
2182 }
2183
2184 /* Update the master (mixer) and PCM out volumes. */
2185 sb16SetMasterVolume(pThis);
2186 sb16SetPcmOutVolume(pThis);
2187
2188 return VINF_SUCCESS;
2189}
2190
2191static DECLCALLBACK(int) sb16LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
2192{
2193 RT_NOREF(uPass);
2194 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2195
2196 SSMR3PutS32(pSSM, pThis->irqCfg);
2197 SSMR3PutS32(pSSM, pThis->dmaCfg);
2198 SSMR3PutS32(pSSM, pThis->hdmaCfg);
2199 SSMR3PutS32(pSSM, pThis->portCfg);
2200 SSMR3PutS32(pSSM, pThis->verCfg);
2201 return VINF_SSM_DONT_CALL_AGAIN;
2202}
2203
2204static DECLCALLBACK(int) sb16SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2205{
2206 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2207
2208 sb16LiveExec(pDevIns, pSSM, 0);
2209 sb16Save(pSSM, pThis);
2210 return VINF_SUCCESS;
2211}
2212
2213static DECLCALLBACK(int) sb16LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2214{
2215 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2216
2217 AssertMsgReturn( uVersion == SB16_SAVE_STATE_VERSION
2218 || uVersion == SB16_SAVE_STATE_VERSION_VBOX_30,
2219 ("%u\n", uVersion),
2220 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2221 if (uVersion > SB16_SAVE_STATE_VERSION_VBOX_30)
2222 {
2223 int32_t irq;
2224 SSMR3GetS32 (pSSM, &irq);
2225 int32_t dma;
2226 SSMR3GetS32 (pSSM, &dma);
2227 int32_t hdma;
2228 SSMR3GetS32 (pSSM, &hdma);
2229 int32_t port;
2230 SSMR3GetS32 (pSSM, &port);
2231 int32_t ver;
2232 int rc = SSMR3GetS32 (pSSM, &ver);
2233 AssertRCReturn (rc, rc);
2234
2235 if ( irq != pThis->irqCfg
2236 || dma != pThis->dmaCfg
2237 || hdma != pThis->hdmaCfg
2238 || port != pThis->portCfg
2239 || ver != pThis->verCfg)
2240 {
2241 return SSMR3SetCfgError(pSSM, RT_SRC_POS,
2242 N_("config changed: irq=%x/%x dma=%x/%x hdma=%x/%x port=%x/%x ver=%x/%x (saved/config)"),
2243 irq, pThis->irqCfg,
2244 dma, pThis->dmaCfg,
2245 hdma, pThis->hdmaCfg,
2246 port, pThis->portCfg,
2247 ver, pThis->verCfg);
2248 }
2249 }
2250
2251 if (uPass != SSM_PASS_FINAL)
2252 return VINF_SUCCESS;
2253
2254 sb16Load(pSSM, pThis);
2255 return VINF_SUCCESS;
2256}
2257
2258/**
2259 * Creates a PDM audio stream for a specific driver.
2260 *
2261 * @returns IPRT status code.
2262 * @param pThis SB16 state.
2263 * @param pCfg Stream configuration to use.
2264 * @param pDrv Driver stream to create PDM stream for.
2265 */
2266static int sb16CreateDrvStream(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg, PSB16DRIVER pDrv)
2267{
2268 RT_NOREF(pThis);
2269
2270 AssertReturn(pCfg->enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
2271 Assert(DrvAudioHlpStreamCfgIsValid(pCfg));
2272
2273 PPDMAUDIOSTREAMCFG pCfgHost = DrvAudioHlpStreamCfgDup(pCfg);
2274 if (!pCfgHost)
2275 return VERR_NO_MEMORY;
2276
2277 if (!RTStrPrintf(pCfgHost->szName, sizeof(pCfgHost->szName), "%s", pCfg->szName))
2278 {
2279 RTMemFree(pCfgHost);
2280 return VERR_BUFFER_OVERFLOW;
2281 }
2282
2283 LogFunc(("[LUN#%RU8] %s\n", pDrv->uLUN, pCfgHost->szName));
2284
2285 AssertMsg(pDrv->Out.pStream == NULL, ("[LUN#%RU8] Driver stream already present when it must not\n", pDrv->uLUN));
2286
2287 int rc = pDrv->pConnector->pfnStreamCreate(pDrv->pConnector, pCfgHost, pCfg /* pCfgGuest */, &pDrv->Out.pStream);
2288 if (RT_SUCCESS(rc))
2289 {
2290 pDrv->pConnector->pfnStreamRetain(pDrv->pConnector, pDrv->Out.pStream);
2291 LogFlowFunc(("LUN#%RU8: Created output \"%s\", rc=%Rrc\n", pDrv->uLUN, pCfg->szName, rc));
2292 }
2293
2294 if (pCfgHost)
2295 {
2296 RTMemFree(pCfgHost);
2297 pCfgHost = NULL;
2298 }
2299
2300 return rc;
2301}
2302
2303/**
2304 * Destroys a PDM audio stream of a specific driver.
2305 *
2306 * @param pThis SB16 state.
2307 * @param pDrv Driver stream to destroy PDM stream for.
2308 */
2309static void sb16DestroyDrvStream(PSB16STATE pThis, PSB16DRIVER pDrv)
2310{
2311 AssertPtrReturnVoid(pThis);
2312 AssertPtrReturnVoid(pDrv);
2313
2314 if (pDrv->Out.pStream)
2315 {
2316 pDrv->pConnector->pfnStreamRelease(pDrv->pConnector, pDrv->Out.pStream);
2317
2318 int rc2 = pDrv->pConnector->pfnStreamDestroy(pDrv->pConnector, pDrv->Out.pStream);
2319 if (RT_SUCCESS(rc2))
2320 pDrv->Out.pStream = NULL;
2321 }
2322}
2323
2324static int sb16OpenOut(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg)
2325{
2326 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2327 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2328
2329 LogFlowFuncEnter();
2330
2331 if (!DrvAudioHlpStreamCfgIsValid(pCfg))
2332 return VERR_INVALID_PARAMETER;
2333
2334 int rc = VINF_SUCCESS;
2335
2336 PSB16DRIVER pDrv;
2337 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2338 {
2339 int rc2 = sb16CreateDrvStream(pThis, pCfg, pDrv);
2340 if (RT_SUCCESS(rc))
2341 rc = rc2;
2342 }
2343
2344 LogFlowFuncLeaveRC(rc);
2345 return rc;
2346}
2347
2348static void sb16CloseOut(PSB16STATE pThis)
2349{
2350 AssertPtrReturnVoid(pThis);
2351
2352 LogFlowFuncEnter();
2353
2354 PSB16DRIVER pDrv;
2355 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2356 sb16DestroyDrvStream(pThis, pDrv);
2357
2358 LogFlowFuncLeave();
2359}
2360
2361/**
2362 * @interface_method_impl{PDMDEVREG,pfnReset}
2363 */
2364static DECLCALLBACK(void) sb16DevReset(PPDMDEVINS pDevIns)
2365{
2366 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2367
2368 /* Bring back the device to initial state, and especially make
2369 * sure there's no interrupt or DMA activity.
2370 */
2371 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
2372
2373 pThis->mixer_regs[0x82] = 0;
2374 pThis->csp_regs[5] = 1;
2375 pThis->csp_regs[9] = 0xf8;
2376
2377 pThis->dma_auto = 0;
2378 pThis->in_index = 0;
2379 pThis->out_data_len = 0;
2380 pThis->left_till_irq = 0;
2381 pThis->needed_bytes = 0;
2382 pThis->block_size = -1;
2383 pThis->nzero = 0;
2384 pThis->highspeed = 0;
2385 pThis->v2x6 = 0;
2386 pThis->cmd = -1;
2387
2388 sb16MixerReset(pThis);
2389 sb16SpeakerControl(pThis, 0);
2390 sb16Control(pThis, 0);
2391 sb16ResetLegacy(pThis);
2392}
2393
2394/**
2395 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2396 */
2397static DECLCALLBACK(void *) sb16QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2398{
2399 PSB16STATE pThis = RT_FROM_MEMBER(pInterface, SB16STATE, IBase);
2400 Assert(&pThis->IBase == pInterface);
2401
2402 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2403 return NULL;
2404}
2405
2406/**
2407 * Powers off the device.
2408 *
2409 * @param pDevIns Device instance to power off.
2410 */
2411static DECLCALLBACK(void) sb16PowerOff(PPDMDEVINS pDevIns)
2412{
2413 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2414
2415 LogRel2(("SB16: Powering off ...\n"));
2416
2417 PSB16DRIVER pDrv;
2418 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2419 {
2420 if (pDrv->Out.pStream)
2421 {
2422 pDrv->pConnector->pfnStreamRelease(pDrv->pConnector, pDrv->Out.pStream);
2423
2424 int rc2 = pDrv->pConnector->pfnStreamDestroy(pDrv->pConnector, pDrv->Out.pStream);
2425 if (RT_SUCCESS(rc2))
2426 pDrv->Out.pStream = NULL;
2427 }
2428 }
2429}
2430
2431/**
2432 * @interface_method_impl{PDMDEVREG,pfnDestruct}
2433 */
2434static DECLCALLBACK(int) sb16Destruct(PPDMDEVINS pDevIns)
2435{
2436 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2437 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2438
2439 LogFlowFuncEnter();
2440
2441 PSB16DRIVER pDrv;
2442 while (!RTListIsEmpty(&pThis->lstDrv))
2443 {
2444 pDrv = RTListGetFirst(&pThis->lstDrv, SB16DRIVER, Node);
2445
2446 RTListNodeRemove(&pDrv->Node);
2447 RTMemFree(pDrv);
2448 }
2449
2450 return VINF_SUCCESS;
2451}
2452
2453static DECLCALLBACK(int) sb16Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2454{
2455 RT_NOREF(iInstance);
2456 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2457 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2458
2459 /*
2460 * Validations.
2461 */
2462 Assert(iInstance == 0);
2463 if (!CFGMR3AreValuesValid(pCfg,
2464 "IRQ\0"
2465 "DMA\0"
2466 "DMA16\0"
2467 "Port\0"
2468 "Version\0"
2469 "TimerHz\0"))
2470 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2471 N_("Invalid configuration for SB16 device"));
2472
2473 /*
2474 * Read config data.
2475 */
2476 int rc = CFGMR3QuerySIntDef(pCfg, "IRQ", &pThis->irq, 5);
2477 if (RT_FAILURE(rc))
2478 return PDMDEV_SET_ERROR(pDevIns, rc,
2479 N_("SB16 configuration error: Failed to get the \"IRQ\" value"));
2480 pThis->irqCfg = pThis->irq;
2481
2482 rc = CFGMR3QuerySIntDef(pCfg, "DMA", &pThis->dma, 1);
2483 if (RT_FAILURE(rc))
2484 return PDMDEV_SET_ERROR(pDevIns, rc,
2485 N_("SB16 configuration error: Failed to get the \"DMA\" value"));
2486 pThis->dmaCfg = pThis->dma;
2487
2488 rc = CFGMR3QuerySIntDef(pCfg, "DMA16", &pThis->hdma, 5);
2489 if (RT_FAILURE(rc))
2490 return PDMDEV_SET_ERROR(pDevIns, rc,
2491 N_("SB16 configuration error: Failed to get the \"DMA16\" value"));
2492 pThis->hdmaCfg = pThis->hdma;
2493
2494 RTIOPORT Port;
2495 rc = CFGMR3QueryPortDef(pCfg, "Port", &Port, 0x220);
2496 if (RT_FAILURE(rc))
2497 return PDMDEV_SET_ERROR(pDevIns, rc,
2498 N_("SB16 configuration error: Failed to get the \"Port\" value"));
2499 pThis->port = Port;
2500 pThis->portCfg = Port;
2501
2502 uint16_t u16Version;
2503 rc = CFGMR3QueryU16Def(pCfg, "Version", &u16Version, 0x0405);
2504 if (RT_FAILURE(rc))
2505 return PDMDEV_SET_ERROR(pDevIns, rc,
2506 N_("SB16 configuration error: Failed to get the \"Version\" value"));
2507
2508#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
2509 uint16_t uTimerHz;
2510 rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, 100 /* Hz */);
2511 if (RT_FAILURE(rc))
2512 return PDMDEV_SET_ERROR(pDevIns, rc,
2513 N_("SB16 configuration error: failed to read Hertz (Hz) rate as unsigned integer"));
2514#endif
2515
2516 pThis->ver = u16Version;
2517 pThis->verCfg = u16Version;
2518
2519 /*
2520 * Init instance data.
2521 */
2522 pThis->pDevInsR3 = pDevIns;
2523 pThis->IBase.pfnQueryInterface = sb16QueryInterface;
2524 pThis->cmd = -1;
2525
2526 pThis->mixer_regs[0x80] = magic_of_irq (pThis->irq);
2527 pThis->mixer_regs[0x81] = (1 << pThis->dma) | (1 << pThis->hdma);
2528 pThis->mixer_regs[0x82] = 2 << 5;
2529
2530 pThis->csp_regs[5] = 1;
2531 pThis->csp_regs[9] = 0xf8;
2532
2533 RTListInit(&pThis->lstDrv);
2534
2535 sb16MixerReset(pThis);
2536
2537 /*
2538 * Create timer(s), register & attach stuff.
2539 */
2540 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIRQ, pThis,
2541 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "SB16 IRQ timer", &pThis->pTimerIRQ);
2542 if (RT_FAILURE(rc))
2543 AssertMsgFailedReturn(("Error creating IRQ timer, rc=%Rrc\n", rc), rc);
2544
2545 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->port + 0x04, 2, pThis,
2546 mixer_write, mixer_read, NULL, NULL, "SB16");
2547 if (RT_FAILURE(rc))
2548 return rc;
2549 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->port + 0x06, 10, pThis,
2550 dsp_write, dsp_read, NULL, NULL, "SB16");
2551 if (RT_FAILURE(rc))
2552 return rc;
2553
2554 rc = PDMDevHlpDMARegister(pDevIns, pThis->hdma, sb16DMARead, pThis);
2555 if (RT_FAILURE(rc))
2556 return rc;
2557 rc = PDMDevHlpDMARegister(pDevIns, pThis->dma, sb16DMARead, pThis);
2558 if (RT_FAILURE(rc))
2559 return rc;
2560
2561 pThis->can_write = 1;
2562
2563 rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec);
2564 if (RT_FAILURE(rc))
2565 return rc;
2566
2567 /*
2568 * Attach driver.
2569 */
2570 uint8_t uLUN;
2571 for (uLUN = 0; uLUN < UINT8_MAX; ++uLUN)
2572 {
2573 LogFunc(("Trying to attach driver for LUN #%RU8 ...\n", uLUN));
2574 rc = sb16AttachInternal(pThis, uLUN, 0 /* fFlags */, NULL /* ppDrv */);
2575 if (RT_FAILURE(rc))
2576 {
2577 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2578 rc = VINF_SUCCESS;
2579 else if (rc == VERR_AUDIO_BACKEND_INIT_FAILED)
2580 {
2581 sb16Reattach(pThis, NULL /* pDrv */, uLUN, "NullAudio");
2582 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2583 N_("Host audio backend initialization has failed. Selecting the NULL audio backend "
2584 "with the consequence that no sound is audible"));
2585 /* Attaching to the NULL audio backend will never fail. */
2586 rc = VINF_SUCCESS;
2587 }
2588 break;
2589 }
2590 }
2591
2592 LogFunc(("cLUNs=%RU8, rc=%Rrc\n", uLUN, rc));
2593
2594 sb16ResetLegacy(pThis);
2595
2596#ifdef VBOX_WITH_AUDIO_SB16_ONETIME_INIT
2597 PSB16DRIVER pDrv;
2598 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2599 {
2600 /*
2601 * Only primary drivers are critical for the VM to run. Everything else
2602 * might not worth showing an own error message box in the GUI.
2603 */
2604 if (!(pDrv->fFlags & PDMAUDIODRVFLAGS_PRIMARY))
2605 continue;
2606
2607 PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
2608 AssertPtr(pCon);
2609
2610 /** @todo No input streams available for SB16 yet. */
2611 bool fValidOut = pCon->pfnStreamGetStatus(pCon, pDrv->Out.pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
2612 if (!fValidOut)
2613 {
2614 LogRel(("SB16: Falling back to NULL backend (no sound audible)\n"));
2615
2616 sb16ResetLegacy(pThis);
2617 sb16Reattach(pThis, pDrv, pDrv->uLUN, "NullAudio");
2618
2619 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2620 N_("No audio devices could be opened. Selecting the NULL audio backend "
2621 "with the consequence that no sound is audible"));
2622 }
2623 }
2624#endif
2625
2626#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
2627 if (RT_SUCCESS(rc))
2628 {
2629 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, pThis,
2630 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "SB16 IO timer", &pThis->pTimerIO);
2631 if (RT_SUCCESS(rc))
2632 {
2633 pThis->cTimerTicksIO = TMTimerGetFreq(pThis->pTimerIO) / uTimerHz;
2634 pThis->uTimerTSIO = TMTimerGet(pThis->pTimerIO);
2635 LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTimerTicksIO, uTimerHz));
2636 }
2637 else
2638 AssertMsgFailedReturn(("Error creating I/O timer, rc=%Rrc\n", rc), rc);
2639 }
2640#else /* !VBOX_WITH_AUDIO_SB16_CALLBACKS */
2641 if (RT_SUCCESS(rc))
2642 {
2643 /** @todo Merge this callback registration with the validation block above once
2644 * this becomes the standard. */
2645 PSB16DRIVER pDrv;
2646 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2647 {
2648 /* Only register primary driver.
2649 * The device emulation does the output multiplexing then. */
2650 if (pDrv->fFlags != PDMAUDIODRVFLAGS_PRIMARY)
2651 continue;
2652
2653 PDMAUDIOCBRECORD AudioCallbacks[2];
2654
2655 SB16CALLBACKCTX Ctx = { pThis, pDrv };
2656
2657 AudioCallbacks[0].enmType = PDMAUDIOCALLBACKTYPE_INPUT;
2658 AudioCallbacks[0].pfnCallback = sb16CallbackInput;
2659 AudioCallbacks[0].pvCtx = &Ctx;
2660 AudioCallbacks[0].cbCtx = sizeof(SB16CALLBACKCTX);
2661
2662 AudioCallbacks[1].enmType = PDMAUDIOCALLBACKTYPE_OUTPUT;
2663 AudioCallbacks[1].pfnCallback = sb16CallbackOutput;
2664 AudioCallbacks[1].pvCtx = &Ctx;
2665 AudioCallbacks[1].cbCtx = sizeof(SB16CALLBACKCTX);
2666
2667 rc = pDrv->pConnector->pfnRegisterCallbacks(pDrv->pConnector, AudioCallbacks, RT_ELEMENTS(AudioCallbacks));
2668 if (RT_FAILURE(rc))
2669 break;
2670 }
2671 }
2672#endif /* VBOX_WITH_AUDIO_SB16_CALLBACKS */
2673
2674#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
2675 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "sb16WriteAudio.pcm");
2676#endif
2677
2678 return VINF_SUCCESS;
2679}
2680
2681const PDMDEVREG g_DeviceSB16 =
2682{
2683 /* u32Version */
2684 PDM_DEVREG_VERSION,
2685 /* szName */
2686 "sb16",
2687 /* szRCMod */
2688 "",
2689 /* szR0Mod */
2690 "",
2691 /* pszDescription */
2692 "Sound Blaster 16 Controller",
2693 /* fFlags */
2694 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2695 /* fClass */
2696 PDM_DEVREG_CLASS_AUDIO,
2697 /* cMaxInstances */
2698 1,
2699 /* cbInstance */
2700 sizeof(SB16STATE),
2701 /* pfnConstruct */
2702 sb16Construct,
2703 /* pfnDestruct */
2704 sb16Destruct,
2705 /* pfnRelocate */
2706 NULL,
2707 /* pfnMemSetup */
2708 NULL,
2709 /* pfnPowerOn */
2710 NULL,
2711 /* pfnReset */
2712 sb16DevReset,
2713 /* pfnSuspend */
2714 NULL,
2715 /* pfnResume */
2716 NULL,
2717 /* pfnAttach */
2718 sb16Attach,
2719 /* pfnDetach */
2720 sb16Detach,
2721 /* pfnQueryInterface */
2722 NULL,
2723 /* pfnInitComplete */
2724 NULL,
2725 /* pfnPowerOff */
2726 sb16PowerOff,
2727 /* pfnSoftReset */
2728 NULL,
2729 /* u32VersionEnd */
2730 PDM_DEVREG_VERSION
2731};
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