VirtualBox

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

Last change on this file since 63214 was 62975, checked in by vboxsync, 8 years ago

enums defaults to int, so use '%d' and not '%ld' to prinft them!

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