VirtualBox

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

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

Audio/DevSB16.cpp: Added missing stream configuration parameters.

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