VirtualBox

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

Last change on this file since 66259 was 65624, checked in by vboxsync, 8 years ago

Audio: More abstraction for the backends: Now the backend stream's data is completely separate from the audio connector interface. That way the backends cannot mess with the audio connector's data (e.g. mixing buffers and friends) anymore, and those are forced to use the audio connector API as meant now. Needs more testing, partly work in progress.

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