VirtualBox

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

Last change on this file since 62250 was 61609, checked in by vboxsync, 9 years ago

Audio: Update.

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