VirtualBox

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

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

DevSB16: Moved functions around a little to get a more usual grouping.

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