VirtualBox

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

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

DevSB16: drop confusing if (pCfgHost) check in sb16CreateDrvStream.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 84.5 KB
Line 
1/* $Id: DevSB16.cpp 71766 2018-04-09 10:56:56Z 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 strcpy(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 strcpy(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 sb16CmdResetLegacy(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 AssertCompile(sizeof(pCfg->szName) > sizeof("Output"));
1088 strcpy(pCfg->szName, "Output");
1089
1090 sb16CloseOut(pThis);
1091}
1092
1093static void sb16CmdReset(PSB16STATE pThis)
1094{
1095 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1096 if (pThis->dma_auto)
1097 {
1098 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
1099 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1100 }
1101
1102 pThis->mixer_regs[0x82] = 0;
1103 pThis->dma_auto = 0;
1104 pThis->in_index = 0;
1105 pThis->out_data_len = 0;
1106 pThis->left_till_irq = 0;
1107 pThis->needed_bytes = 0;
1108 pThis->block_size = -1;
1109 pThis->nzero = 0;
1110 pThis->highspeed = 0;
1111 pThis->v2x6 = 0;
1112 pThis->cmd = -1;
1113
1114 dsp_out_data(pThis, 0xaa);
1115 sb16SpeakerControl(pThis, 0);
1116
1117 sb16Control(pThis, 0);
1118 sb16CmdResetLegacy(pThis);
1119}
1120
1121/**
1122 * @callback_method_impl{PFNIOMIOPORTOUT}
1123 */
1124static DECLCALLBACK(int) dsp_write(PPDMDEVINS pDevIns, void *opaque, RTIOPORT nport, uint32_t val, unsigned cb)
1125{
1126 RT_NOREF(pDevIns, cb);
1127 PSB16STATE pThis = (PSB16STATE)opaque;
1128 int iport = nport - pThis->port;
1129
1130 LogFlowFunc(("write %#x <- %#x\n", nport, val));
1131 switch (iport)
1132 {
1133 case 0x06:
1134 switch (val)
1135 {
1136 case 0x00:
1137 {
1138 if (pThis->v2x6 == 1)
1139 {
1140 if (0 && pThis->highspeed)
1141 {
1142 pThis->highspeed = 0;
1143 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1144 sb16Control(pThis, 0);
1145 }
1146 else
1147 sb16CmdReset(pThis);
1148 }
1149 pThis->v2x6 = 0;
1150 break;
1151 }
1152
1153 case 0x01:
1154 case 0x03: /* FreeBSD kludge */
1155 pThis->v2x6 = 1;
1156 break;
1157
1158 case 0xc6:
1159 pThis->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */
1160 break;
1161
1162 case 0xb8: /* Panic */
1163 sb16CmdReset(pThis);
1164 break;
1165
1166 case 0x39:
1167 dsp_out_data(pThis, 0x38);
1168 sb16CmdReset(pThis);
1169 pThis->v2x6 = 0x39;
1170 break;
1171
1172 default:
1173 pThis->v2x6 = val;
1174 break;
1175 }
1176 break;
1177
1178 case 0x0c: /* Write data or command | write status */
1179#if 0
1180 if (pThis->highspeed)
1181 break;
1182#endif
1183 if (0 == pThis->needed_bytes)
1184 {
1185 sb16HandleCommand(pThis, val);
1186#if 0
1187 if (0 == pThis->needed_bytes) {
1188 log_dsp (pThis);
1189 }
1190#endif
1191 }
1192 else
1193 {
1194 if (pThis->in_index == sizeof (pThis->in2_data))
1195 {
1196 LogFlowFunc(("in data overrun\n"));
1197 }
1198 else
1199 {
1200 pThis->in2_data[pThis->in_index++] = val;
1201 if (pThis->in_index == pThis->needed_bytes)
1202 {
1203 pThis->needed_bytes = 0;
1204 complete (pThis);
1205#if 0
1206 log_dsp (pThis);
1207#endif
1208 }
1209 }
1210 }
1211 break;
1212
1213 default:
1214 LogFlowFunc(("nport=%#x, val=%#x)\n", nport, val));
1215 break;
1216 }
1217
1218 return VINF_SUCCESS;
1219}
1220
1221
1222/**
1223 * @callback_method_impl{PFNIOMIOPORTIN}
1224 */
1225static DECLCALLBACK(int) dsp_read(PPDMDEVINS pDevIns, void *opaque, RTIOPORT nport, uint32_t *pu32, unsigned cb)
1226{
1227 RT_NOREF(pDevIns, cb);
1228 PSB16STATE pThis = (PSB16STATE)opaque;
1229 int iport, retval, ack = 0;
1230
1231 iport = nport - pThis->port;
1232
1233 /** @todo reject non-byte access?
1234 * The spec does not mention a non-byte access so we should check how real hardware behaves. */
1235
1236 switch (iport)
1237 {
1238 case 0x06: /* reset */
1239 retval = 0xff;
1240 break;
1241
1242 case 0x0a: /* read data */
1243 if (pThis->out_data_len)
1244 {
1245 retval = pThis->out_data[--pThis->out_data_len];
1246 pThis->last_read_byte = retval;
1247 }
1248 else
1249 {
1250 if (pThis->cmd != -1)
1251 LogFlowFunc(("empty output buffer for command %#x\n", pThis->cmd));
1252 retval = pThis->last_read_byte;
1253 /* goto error; */
1254 }
1255 break;
1256
1257 case 0x0c: /* 0 can write */
1258 retval = pThis->can_write ? 0 : 0x80;
1259 break;
1260
1261 case 0x0d: /* timer interrupt clear */
1262 /* LogFlowFunc(("timer interrupt clear\n")); */
1263 retval = 0;
1264 break;
1265
1266 case 0x0e: /* data available status | irq 8 ack */
1267 retval = (!pThis->out_data_len || pThis->highspeed) ? 0 : 0x80;
1268 if (pThis->mixer_regs[0x82] & 1)
1269 {
1270 ack = 1;
1271 pThis->mixer_regs[0x82] &= ~1;
1272 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1273 }
1274 break;
1275
1276 case 0x0f: /* irq 16 ack */
1277 retval = 0xff;
1278 if (pThis->mixer_regs[0x82] & 2)
1279 {
1280 ack = 1;
1281 pThis->mixer_regs[0x82] &= ~2;
1282 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1283 }
1284 break;
1285
1286 default:
1287 goto error;
1288 }
1289
1290 if (!ack)
1291 LogFlowFunc(("read %#x -> %#x\n", nport, retval));
1292
1293 *pu32 = retval;
1294 return VINF_SUCCESS;
1295
1296 error:
1297 LogFlowFunc(("warning: dsp_read %#x error\n", nport));
1298 return VERR_IOM_IOPORT_UNUSED;
1299}
1300
1301static void sb16MixerReset(PSB16STATE pThis)
1302{
1303 memset(pThis->mixer_regs, 0xff, 0x7f);
1304 memset(pThis->mixer_regs + 0x83, 0xff, sizeof (pThis->mixer_regs) - 0x83);
1305
1306 pThis->mixer_regs[0x02] = 4; /* master volume 3bits */
1307 pThis->mixer_regs[0x06] = 4; /* MIDI volume 3bits */
1308 pThis->mixer_regs[0x08] = 0; /* CD volume 3bits */
1309 pThis->mixer_regs[0x0a] = 0; /* voice volume 2bits */
1310
1311 /* d5=input filt, d3=lowpass filt, d1,d2=input source */
1312 pThis->mixer_regs[0x0c] = 0;
1313
1314 /* d5=output filt, d1=stereo switch */
1315 pThis->mixer_regs[0x0e] = 0;
1316
1317 /* voice volume L d5,d7, R d1,d3 */
1318 pThis->mixer_regs[0x04] = (12 << 4) | 12;
1319 /* master ... */
1320 pThis->mixer_regs[0x22] = (12 << 4) | 12;
1321 /* MIDI ... */
1322 pThis->mixer_regs[0x26] = (12 << 4) | 12;
1323
1324 /* master/voice/MIDI L/R volume */
1325 for (int i = 0x30; i < 0x36; i++)
1326 pThis->mixer_regs[i] = 24 << 3; /* -14 dB */
1327
1328 /* treble/bass */
1329 for (int i = 0x44; i < 0x48; i++)
1330 pThis->mixer_regs[i] = 0x80;
1331
1332 /* Update the master (mixer) and PCM out volumes. */
1333 sb16UpdateVolume(pThis);
1334}
1335
1336static int mixer_write_indexb(PSB16STATE pThis, uint8_t val)
1337{
1338 pThis->mixer_nreg = val;
1339 return VINF_SUCCESS;
1340}
1341
1342uint32_t popcount(uint32_t u) /** @todo r=andy WTF? */
1343{
1344 u = ((u&0x55555555) + ((u>>1)&0x55555555));
1345 u = ((u&0x33333333) + ((u>>2)&0x33333333));
1346 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
1347 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
1348 u = ( u&0x0000ffff) + (u>>16);
1349 return u;
1350}
1351
1352uint32_t lsbindex(uint32_t u)
1353{
1354 return popcount((u & -(int32_t)u) - 1);
1355}
1356
1357/* Convert SB16 to SB Pro mixer volume (left). */
1358static inline void sb16ConvVolumeL(PSB16STATE pThis, unsigned reg, uint8_t val)
1359{
1360 /* High nibble in SBP mixer. */
1361 pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0x0f) | (val & 0xf0);
1362}
1363
1364/* Convert SB16 to SB Pro mixer volume (right). */
1365static inline void sb16ConvVolumeR(PSB16STATE pThis, unsigned reg, uint8_t val)
1366{
1367 /* Low nibble in SBP mixer. */
1368 pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0xf0) | (val >> 4);
1369}
1370
1371/* Convert SB Pro to SB16 mixer volume (left + right). */
1372static inline void sb16ConvVolumeOldToNew(PSB16STATE pThis, unsigned reg, uint8_t val)
1373{
1374 /* Left channel. */
1375 pThis->mixer_regs[reg + 0] = (val & 0xf0) | RT_BIT(3);
1376 /* Right channel (the register immediately following). */
1377 pThis->mixer_regs[reg + 1] = (val << 4) | RT_BIT(3);
1378}
1379
1380
1381static int mixer_write_datab(PSB16STATE pThis, uint8_t val)
1382{
1383 bool fUpdateMaster = false;
1384 bool fUpdateStream = false;
1385
1386 LogFlowFunc(("mixer_write [%#x] <- %#x\n", pThis->mixer_nreg, val));
1387
1388 switch (pThis->mixer_nreg)
1389 {
1390 case 0x00:
1391 sb16MixerReset(pThis);
1392 /* And update the actual volume, too. */
1393 fUpdateMaster = true;
1394 fUpdateStream = true;
1395 break;
1396
1397 case 0x04: /* Translate from old style voice volume (L/R). */
1398 sb16ConvVolumeOldToNew(pThis, 0x32, val);
1399 fUpdateStream = true;
1400 break;
1401
1402 case 0x22: /* Translate from old style master volume (L/R). */
1403 sb16ConvVolumeOldToNew(pThis, 0x30, val);
1404 fUpdateMaster = true;
1405 break;
1406
1407 case 0x26: /* Translate from old style MIDI volume (L/R). */
1408 sb16ConvVolumeOldToNew(pThis, 0x34, val);
1409 break;
1410
1411 case 0x28: /* Translate from old style CD volume (L/R). */
1412 sb16ConvVolumeOldToNew(pThis, 0x36, val);
1413 break;
1414
1415 case 0x2E: /* Translate from old style line volume (L/R). */
1416 sb16ConvVolumeOldToNew(pThis, 0x38, val);
1417 break;
1418
1419 case 0x30: /* Translate to old style master volume (L). */
1420 sb16ConvVolumeL(pThis, 0x22, val);
1421 fUpdateMaster = true;
1422 break;
1423
1424 case 0x31: /* Translate to old style master volume (R). */
1425 sb16ConvVolumeR(pThis, 0x22, val);
1426 fUpdateMaster = true;
1427 break;
1428
1429 case 0x32: /* Translate to old style voice volume (L). */
1430 sb16ConvVolumeL(pThis, 0x04, val);
1431 fUpdateStream = true;
1432 break;
1433
1434 case 0x33: /* Translate to old style voice volume (R). */
1435 sb16ConvVolumeR(pThis, 0x04, val);
1436 fUpdateStream = true;
1437 break;
1438
1439 case 0x34: /* Translate to old style MIDI volume (L). */
1440 sb16ConvVolumeL(pThis, 0x26, val);
1441 break;
1442
1443 case 0x35: /* Translate to old style MIDI volume (R). */
1444 sb16ConvVolumeR(pThis, 0x26, val);
1445 break;
1446
1447 case 0x36: /* Translate to old style CD volume (L). */
1448 sb16ConvVolumeL(pThis, 0x28, val);
1449 break;
1450
1451 case 0x37: /* Translate to old style CD volume (R). */
1452 sb16ConvVolumeR(pThis, 0x28, val);
1453 break;
1454
1455 case 0x38: /* Translate to old style line volume (L). */
1456 sb16ConvVolumeL(pThis, 0x2E, val);
1457 break;
1458
1459 case 0x39: /* Translate to old style line volume (R). */
1460 sb16ConvVolumeR(pThis, 0x2E, val);
1461 break;
1462
1463 case 0x80:
1464 {
1465 int irq = irq_of_magic(val);
1466 LogFlowFunc(("setting irq to %d (val=%#x)\n", irq, val));
1467 if (irq > 0)
1468 pThis->irq = irq;
1469 break;
1470 }
1471
1472 case 0x81:
1473 {
1474 int dma, hdma;
1475
1476 dma = lsbindex (val & 0xf);
1477 hdma = lsbindex (val & 0xf0);
1478 if (dma != pThis->dma || hdma != pThis->hdma)
1479 LogFlow(("SB16: attempt to change DMA 8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
1480 dma, pThis->dma, hdma, pThis->hdma, val));
1481#if 0
1482 pThis->dma = dma;
1483 pThis->hdma = hdma;
1484#endif
1485 break;
1486 }
1487
1488 case 0x82:
1489 LogFlowFunc(("attempt to write into IRQ status register (val=%#x)\n", val));
1490 return VINF_SUCCESS;
1491
1492 default:
1493 if (pThis->mixer_nreg >= 0x80)
1494 LogFlowFunc(("attempt to write mixer[%#x] <- %#x\n", pThis->mixer_nreg, val));
1495 break;
1496 }
1497
1498 pThis->mixer_regs[pThis->mixer_nreg] = val;
1499
1500 /* Update the master (mixer) volume. */
1501 if ( fUpdateMaster
1502 || fUpdateStream)
1503 {
1504 sb16UpdateVolume(pThis);
1505 }
1506
1507 return VINF_SUCCESS;
1508}
1509
1510/**
1511 * @callback_method_impl{PFNIOMIOPORTOUT}
1512 */
1513static DECLCALLBACK(int) mixer_write(PPDMDEVINS pDevIns, void *opaque, RTIOPORT nport, uint32_t val, unsigned cb)
1514{
1515 RT_NOREF(pDevIns);
1516 PSB16STATE pThis = (PSB16STATE)opaque;
1517 int iport = nport - pThis->port;
1518 switch (cb)
1519 {
1520 case 1:
1521 switch (iport)
1522 {
1523 case 4:
1524 mixer_write_indexb(pThis, val);
1525 break;
1526 case 5:
1527 mixer_write_datab(pThis, val);
1528 break;
1529 }
1530 break;
1531 case 2:
1532 mixer_write_indexb(pThis, val & 0xff);
1533 mixer_write_datab(pThis, (val >> 8) & 0xff);
1534 break;
1535 default:
1536 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", nport, cb, val));
1537 break;
1538 }
1539 return VINF_SUCCESS;
1540}
1541
1542/**
1543 * @callback_method_impl{PFNIOMIOPORTIN}
1544 */
1545static DECLCALLBACK(int) mixer_read(PPDMDEVINS pDevIns, void *opaque, RTIOPORT nport, uint32_t *pu32, unsigned cb)
1546{
1547 RT_NOREF(pDevIns, cb, nport);
1548 PSB16STATE pThis = (PSB16STATE)opaque;
1549
1550#ifndef DEBUG_SB16_MOST
1551 if (pThis->mixer_nreg != 0x82)
1552 LogFlowFunc(("mixer_read[%#x] -> %#x\n", pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
1553#else
1554 LogFlowFunc(("mixer_read[%#x] -> %#x\n", pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
1555#endif
1556 *pu32 = pThis->mixer_regs[pThis->mixer_nreg];
1557 return VINF_SUCCESS;
1558}
1559
1560/**
1561 * Called by sb16DMARead.
1562 */
1563static int sb16WriteAudio(PSB16STATE pThis, int nchan, uint32_t dma_pos, uint32_t dma_len, int len)
1564{
1565 uint8_t tmpbuf[_4K]; /** @todo Have a buffer on the heap. */
1566 uint32_t cbToWrite = len;
1567 uint32_t cbWrittenTotal = 0;
1568
1569 while (cbToWrite)
1570 {
1571 uint32_t cbToRead = RT_MIN(dma_len - dma_pos, cbToWrite);
1572 if (cbToRead > sizeof(tmpbuf))
1573 cbToRead = sizeof(tmpbuf);
1574
1575 uint32_t cbRead = 0;
1576 int rc2 = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, nchan, tmpbuf, dma_pos, cbToRead, &cbRead);
1577 AssertMsgRC(rc2, (" from DMA failed: %Rrc\n", rc2));
1578
1579#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
1580 if (cbRead)
1581 {
1582 RTFILE fh;
1583 RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "sb16WriteAudio.pcm",
1584 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1585 RTFileWrite(fh, tmpbuf, cbRead, NULL);
1586 RTFileClose(fh);
1587 }
1588#endif
1589 /*
1590 * Write data to the backends.
1591 */
1592 uint32_t cbWritten = 0;
1593
1594 /* Just multiplex the output to the connected backends.
1595 * No need to utilize the virtual mixer here (yet). */
1596 PSB16DRIVER pDrv;
1597 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1598 {
1599 if (!pDrv->Out.pStream)
1600 continue;
1601
1602 uint32_t cbWrittenToStream = 0;
1603 rc2 = pDrv->pConnector->pfnStreamWrite(pDrv->pConnector, pDrv->Out.pStream, tmpbuf, cbRead, &cbWrittenToStream);
1604
1605 LogFlowFunc(("\tLUN#%RU8: rc=%Rrc, cbWrittenToStream=%RU32\n", pDrv->uLUN, rc2, cbWrittenToStream));
1606
1607 /* The primary driver sets the overall pace. */
1608 if (pDrv->fFlags & PDMAUDIODRVFLAGS_PRIMARY)
1609 {
1610 cbWritten = cbWrittenToStream;
1611
1612 if (RT_FAILURE(rc2))
1613 break;
1614 }
1615 }
1616
1617 LogFlowFunc(("\tcbToRead=%RU32, cbToWrite=%RU32, cbWritten=%RU32, cbLeft=%RU32\n",
1618 cbToRead, cbToWrite, cbWritten, cbToWrite - cbWrittenTotal));
1619
1620 Assert(cbToWrite >= cbWritten);
1621 cbToWrite -= cbWritten;
1622 dma_pos = (dma_pos + cbWritten) % dma_len;
1623 cbWrittenTotal += cbWritten;
1624
1625 if (!cbWritten)
1626 break;
1627 }
1628
1629 return cbWrittenTotal;
1630}
1631
1632/**
1633 * @callback_method_impl{FNDMATRANSFERHANDLER,
1634 * Worker callback for both DMA channels.}
1635 */
1636static DECLCALLBACK(uint32_t) sb16DMARead(PPDMDEVINS pDevIns, void *opaque, unsigned nchan, uint32_t dma_pos, uint32_t dma_len)
1637{
1638 RT_NOREF(pDevIns);
1639 PSB16STATE pThis = (PSB16STATE)opaque;
1640 int till, copy, written, free;
1641
1642 if (pThis->block_size <= 0)
1643 {
1644 LogFlowFunc(("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
1645 pThis->block_size, nchan, dma_pos, dma_len));
1646 return dma_pos;
1647 }
1648
1649 if (pThis->left_till_irq < 0)
1650 pThis->left_till_irq = pThis->block_size;
1651
1652 uint32_t cbOutMin = UINT32_MAX;
1653
1654 PSB16DRIVER pDrv;
1655 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1656 {
1657 if (!pDrv->Out.pStream)
1658 continue;
1659
1660 uint32_t cbOut = pDrv->pConnector->pfnStreamGetWritable(pDrv->pConnector, pDrv->Out.pStream);
1661
1662 if (cbOut < cbOutMin)
1663 cbOutMin = cbOut;
1664 }
1665
1666 LogFlowFunc(("cbOutMin=%RU32\n", cbOutMin));
1667 if (cbOutMin == UINT32_MAX)
1668 {
1669 free = dma_len;
1670 }
1671 else
1672 {
1673 free = cbOutMin & ~pThis->align; /** @todo int vs. uint32. */
1674 if ((free <= 0) || !dma_len)
1675 return dma_pos;
1676 }
1677
1678 copy = free;
1679 till = pThis->left_till_irq;
1680
1681#ifdef DEBUG_SB16_MOST
1682 LogFlowFunc(("pos:%06d %d till:%d len:%d\n", dma_pos, free, till, dma_len));
1683#endif
1684
1685 if (copy >= till)
1686 {
1687 if (0 == pThis->dma_auto)
1688 {
1689 copy = till;
1690 }
1691 else
1692 {
1693 if (copy >= till + pThis->block_size)
1694 copy = till; /* Make sure we won't skip IRQs. */
1695 }
1696 }
1697
1698 written = sb16WriteAudio(pThis, nchan, dma_pos, dma_len, copy);
1699 dma_pos = (dma_pos + written) % dma_len;
1700 pThis->left_till_irq -= written;
1701
1702 if (pThis->left_till_irq <= 0)
1703 {
1704 pThis->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
1705 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
1706 if (0 == pThis->dma_auto)
1707 {
1708 sb16Control(pThis, 0);
1709 sb16SpeakerControl(pThis, 0);
1710 }
1711 }
1712
1713 Log3Func(("pos %d/%d free %5d till %5d copy %5d written %5d block_size %5d\n",
1714 dma_pos, dma_len, free, pThis->left_till_irq, copy, written,
1715 pThis->block_size));
1716
1717 while (pThis->left_till_irq <= 0)
1718 pThis->left_till_irq += pThis->block_size;
1719
1720 return dma_pos;
1721}
1722
1723#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
1724
1725static void sb16TimerMaybeStart(PSB16STATE pThis)
1726{
1727 LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive));
1728
1729 if (pThis->cStreamsActive == 0) /* Only start the timer if there are no active streams. */
1730 return;
1731
1732 if (!pThis->pTimerIO)
1733 return;
1734
1735 /* Set timer flag. */
1736 ASMAtomicXchgBool(&pThis->fTimerActive, true);
1737
1738 /* Update current time timestamp. */
1739 pThis->uTimerTSIO = TMTimerGet(pThis->pTimerIO);
1740
1741 /* Fire off timer. */
1742 TMTimerSet(pThis->pTimerIO, TMTimerGet(pThis->pTimerIO) + pThis->cTimerTicksIO);
1743}
1744
1745static void sb16TimerMaybeStop(PSB16STATE pThis)
1746{
1747 LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive));
1748
1749 if (pThis->cStreamsActive) /* Some streams still active? Bail out. */
1750 return;
1751
1752 if (!pThis->pTimerIO)
1753 return;
1754
1755 /* Set timer flag. */
1756 ASMAtomicXchgBool(&pThis->fTimerActive, false);
1757}
1758
1759/**
1760 * @callback_method_impl{FNTMTIMERDEV}
1761 */
1762static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1763{
1764 RT_NOREF(pDevIns);
1765 PSB16STATE pThis = (PSB16STATE)pvUser;
1766 Assert(pThis == PDMINS_2_DATA(pDevIns, PSB16STATE));
1767 AssertPtr(pThis);
1768
1769 uint64_t cTicksNow = TMTimerGet(pTimer);
1770 bool fIsPlaying = false; /* Whether one or more streams are still playing. */
1771 bool fDoTransfer = false;
1772
1773 pThis->uTimerTSIO = cTicksNow;
1774
1775 PSB16DRIVER pDrv;
1776 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1777 {
1778 PPDMAUDIOSTREAM pStream = pDrv->Out.pStream;
1779 if (!pStream)
1780 continue;
1781
1782# ifdef VBOX_STRICT
1783 /*
1784 * Sanity. Make sure that all streams have the same configuration
1785 * to get SB16's DMA transfers right.
1786 *
1787 * SB16 only allows one output configuration per serial data out,
1788 * so check if all streams have the same configuration.
1789 */
1790 PSB16DRIVER pDrvPrev = RTListNodeGetPrev(&pDrv->Node, SB16DRIVER, Node);
1791 if ( pDrvPrev
1792 && !RTListNodeIsDummy(&pThis->lstDrv, pDrvPrev, SB16DRIVER, Node))
1793 {
1794 PPDMAUDIOSTREAM pStreamPrev = pDrvPrev->Out.pStream;
1795 if (pStreamPrev)
1796 {
1797 AssertMsg(pStream->Cfg.Props.uHz == pStreamPrev->Cfg.Props.uHz,
1798 ("%RU32Hz vs. %RU32Hz\n", pStream->Cfg.Props.uHz, pStreamPrev->Cfg.Props.uHz));
1799 AssertMsg(pStream->Cfg.Props.cChannels == pStreamPrev->Cfg.Props.cChannels,
1800 ("%RU8 vs. %RU8 channels\n", pStream->Cfg.Props.cChannels, pStreamPrev->Cfg.Props.cChannels));
1801 AssertMsg(pStream->Cfg.Props.cBits == pStreamPrev->Cfg.Props.cBits,
1802 ("%d vs. %d bits\n", pStream->Cfg.Props.cBits, pStreamPrev->Cfg.Props.cBits));
1803 AssertMsg(pStream->Cfg.Props.fSigned == pStreamPrev->Cfg.Props.fSigned,
1804 ("%RTbool vs. %RTbool signed\n", pStream->Cfg.Props.fSigned, pStreamPrev->Cfg.Props.fSigned));
1805 }
1806 }
1807# endif
1808 PPDMIAUDIOCONNECTOR pConn = pDrv->pConnector;
1809 if (!pConn)
1810 continue;
1811
1812 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1813 if (RT_SUCCESS(rc2))
1814 {
1815 if (pStream->enmDir == PDMAUDIODIR_IN)
1816 {
1817 /** @todo Implement recording! */
1818 }
1819 else
1820 {
1821 rc2 = pConn->pfnStreamPlay(pConn, pStream, NULL /* cPlayed */);
1822 if (RT_FAILURE(rc2))
1823 {
1824 LogFlowFunc(("%s: Failed playing stream, rc=%Rrc\n", pStream->szName, rc2));
1825 continue;
1826 }
1827 }
1828
1829 if (pDrv->fFlags & PDMAUDIODRVFLAGS_PRIMARY)
1830 {
1831 /* Only do the next DMA transfer if we're able to write the remaining data block. */
1832 fDoTransfer = pConn->pfnStreamGetWritable(pConn, pStream) > (unsigned)pThis->left_till_irq;
1833 }
1834 }
1835
1836 PDMAUDIOSTREAMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pStream);
1837 fIsPlaying |= ( (strmSts & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1838 || (strmSts & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE));
1839 }
1840
1841 bool fTimerActive = ASMAtomicReadBool(&pThis->fTimerActive);
1842 bool fKickTimer = fTimerActive || fIsPlaying;
1843
1844 LogFlowFunc(("fTimerActive=%RTbool, fIsPlaying=%RTbool\n", fTimerActive, fIsPlaying));
1845
1846 if (fDoTransfer)
1847 {
1848 /* Schedule the next transfer. */
1849 PDMDevHlpDMASchedule(pThis->pDevInsR3);
1850
1851 /* Kick the timer at least one more time. */
1852 fKickTimer = true;
1853 }
1854
1855 /*
1856 * Recording.
1857 */
1858 /** @todo Implement recording. */
1859
1860 if (fKickTimer)
1861 {
1862 /* Kick the timer again. */
1863 uint64_t cTicks = pThis->cTimerTicksIO;
1864 /** @todo adjust cTicks down by now much cbOutMin represents. */
1865 TMTimerSet(pThis->pTimerIO, cTicksNow + cTicks);
1866 }
1867}
1868
1869#endif /* !VBOX_WITH_AUDIO_SB16_CALLBACKS */
1870
1871
1872/**
1873 * @callback_method_impl{FNSSMDEVLIVEEXEC}
1874 */
1875static DECLCALLBACK(int) sb16LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1876{
1877 RT_NOREF(uPass);
1878 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
1879
1880 SSMR3PutS32(pSSM, pThis->irqCfg);
1881 SSMR3PutS32(pSSM, pThis->dmaCfg);
1882 SSMR3PutS32(pSSM, pThis->hdmaCfg);
1883 SSMR3PutS32(pSSM, pThis->portCfg);
1884 SSMR3PutS32(pSSM, pThis->verCfg);
1885 return VINF_SSM_DONT_CALL_AGAIN;
1886}
1887
1888/**
1889 * Worker for sb16SaveExec.
1890 */
1891static int sb16Save(PSSMHANDLE pSSM, PSB16STATE pThis)
1892{
1893 SSMR3PutS32(pSSM, pThis->irq);
1894 SSMR3PutS32(pSSM, pThis->dma);
1895 SSMR3PutS32(pSSM, pThis->hdma);
1896 SSMR3PutS32(pSSM, pThis->port);
1897 SSMR3PutS32(pSSM, pThis->ver);
1898 SSMR3PutS32(pSSM, pThis->in_index);
1899 SSMR3PutS32(pSSM, pThis->out_data_len);
1900 SSMR3PutS32(pSSM, pThis->fmt_stereo);
1901 SSMR3PutS32(pSSM, pThis->fmt_signed);
1902 SSMR3PutS32(pSSM, pThis->fmt_bits);
1903
1904 SSMR3PutU32(pSSM, pThis->fmt);
1905
1906 SSMR3PutS32(pSSM, pThis->dma_auto);
1907 SSMR3PutS32(pSSM, pThis->block_size);
1908 SSMR3PutS32(pSSM, pThis->fifo);
1909 SSMR3PutS32(pSSM, pThis->freq);
1910 SSMR3PutS32(pSSM, pThis->time_const);
1911 SSMR3PutS32(pSSM, pThis->speaker);
1912 SSMR3PutS32(pSSM, pThis->needed_bytes);
1913 SSMR3PutS32(pSSM, pThis->cmd);
1914 SSMR3PutS32(pSSM, pThis->use_hdma);
1915 SSMR3PutS32(pSSM, pThis->highspeed);
1916 SSMR3PutS32(pSSM, pThis->can_write);
1917 SSMR3PutS32(pSSM, pThis->v2x6);
1918
1919 SSMR3PutU8 (pSSM, pThis->csp_param);
1920 SSMR3PutU8 (pSSM, pThis->csp_value);
1921 SSMR3PutU8 (pSSM, pThis->csp_mode);
1922 SSMR3PutU8 (pSSM, pThis->csp_param); /* Bug compatible! */
1923 SSMR3PutMem(pSSM, pThis->csp_regs, 256);
1924 SSMR3PutU8 (pSSM, pThis->csp_index);
1925 SSMR3PutMem(pSSM, pThis->csp_reg83, 4);
1926 SSMR3PutS32(pSSM, pThis->csp_reg83r);
1927 SSMR3PutS32(pSSM, pThis->csp_reg83w);
1928
1929 SSMR3PutMem(pSSM, pThis->in2_data, sizeof(pThis->in2_data));
1930 SSMR3PutMem(pSSM, pThis->out_data, sizeof(pThis->out_data));
1931 SSMR3PutU8 (pSSM, pThis->test_reg);
1932 SSMR3PutU8 (pSSM, pThis->last_read_byte);
1933
1934 SSMR3PutS32(pSSM, pThis->nzero);
1935 SSMR3PutS32(pSSM, pThis->left_till_irq);
1936 SSMR3PutS32(pSSM, pThis->dma_running);
1937 SSMR3PutS32(pSSM, pThis->bytes_per_second);
1938 SSMR3PutS32(pSSM, pThis->align);
1939
1940 SSMR3PutS32(pSSM, pThis->mixer_nreg);
1941 return SSMR3PutMem(pSSM, pThis->mixer_regs, 256);
1942}
1943
1944/**
1945 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1946 */
1947static DECLCALLBACK(int) sb16SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1948{
1949 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
1950
1951 sb16LiveExec(pDevIns, pSSM, 0);
1952 return sb16Save(pSSM, pThis);
1953}
1954
1955/**
1956 * Worker for sb16LoadExec.
1957 */
1958static int sb16Load(PSSMHANDLE pSSM, PSB16STATE pThis)
1959{
1960 SSMR3GetS32(pSSM, &pThis->irq);
1961 SSMR3GetS32(pSSM, &pThis->dma);
1962 SSMR3GetS32(pSSM, &pThis->hdma);
1963 SSMR3GetS32(pSSM, &pThis->port);
1964 SSMR3GetS32(pSSM, &pThis->ver);
1965 SSMR3GetS32(pSSM, &pThis->in_index);
1966 SSMR3GetS32(pSSM, &pThis->out_data_len);
1967 SSMR3GetS32(pSSM, &pThis->fmt_stereo);
1968 SSMR3GetS32(pSSM, &pThis->fmt_signed);
1969 SSMR3GetS32(pSSM, &pThis->fmt_bits);
1970
1971 SSMR3GetU32(pSSM, (uint32_t *)&pThis->fmt);
1972
1973 SSMR3GetS32(pSSM, &pThis->dma_auto);
1974 SSMR3GetS32(pSSM, &pThis->block_size);
1975 SSMR3GetS32(pSSM, &pThis->fifo);
1976 SSMR3GetS32(pSSM, &pThis->freq);
1977 SSMR3GetS32(pSSM, &pThis->time_const);
1978 SSMR3GetS32(pSSM, &pThis->speaker);
1979 SSMR3GetS32(pSSM, &pThis->needed_bytes);
1980 SSMR3GetS32(pSSM, &pThis->cmd);
1981 SSMR3GetS32(pSSM, &pThis->use_hdma);
1982 SSMR3GetS32(pSSM, &pThis->highspeed);
1983 SSMR3GetS32(pSSM, &pThis->can_write);
1984 SSMR3GetS32(pSSM, &pThis->v2x6);
1985
1986 SSMR3GetU8 (pSSM, &pThis->csp_param);
1987 SSMR3GetU8 (pSSM, &pThis->csp_value);
1988 SSMR3GetU8 (pSSM, &pThis->csp_mode);
1989 SSMR3GetU8 (pSSM, &pThis->csp_param); /* Bug compatible! */
1990 SSMR3GetMem(pSSM, pThis->csp_regs, 256);
1991 SSMR3GetU8 (pSSM, &pThis->csp_index);
1992 SSMR3GetMem(pSSM, pThis->csp_reg83, 4);
1993 SSMR3GetS32(pSSM, &pThis->csp_reg83r);
1994 SSMR3GetS32(pSSM, &pThis->csp_reg83w);
1995
1996 SSMR3GetMem(pSSM, pThis->in2_data, sizeof(pThis->in2_data));
1997 SSMR3GetMem(pSSM, pThis->out_data, sizeof(pThis->out_data));
1998 SSMR3GetU8 (pSSM, &pThis->test_reg);
1999 SSMR3GetU8 (pSSM, &pThis->last_read_byte);
2000
2001 SSMR3GetS32(pSSM, &pThis->nzero);
2002 SSMR3GetS32(pSSM, &pThis->left_till_irq);
2003 SSMR3GetS32(pSSM, &pThis->dma_running);
2004 SSMR3GetS32(pSSM, &pThis->bytes_per_second);
2005 SSMR3GetS32(pSSM, &pThis->align);
2006
2007 int32_t mixer_nreg = 0;
2008 int rc = SSMR3GetS32(pSSM, &mixer_nreg);
2009 AssertRCReturn(rc, rc);
2010 pThis->mixer_nreg = (uint8_t)mixer_nreg;
2011 rc = SSMR3GetMem(pSSM, pThis->mixer_regs, 256);
2012 AssertRCReturn(rc, rc);
2013
2014#if 0
2015 PSB16DRIVER pDrv;
2016 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2017 {
2018 if (pDrv->Out.pStream)
2019 {
2020 pDrv->pConnector->pfnCloseOut(pThis->pDrv, pDrv->Out.pStream);
2021 pDrv->Out.pStream = NULL;
2022 }
2023 }
2024#endif
2025
2026 if (pThis->dma_running)
2027 {
2028 if (pThis->freq)
2029 {
2030 /* At the moment we only have one stream, the output stream. */
2031 PPDMAUDIOSTREAMCFG pCfg = &pThis->Out.Cfg;
2032
2033 pCfg->enmDir = PDMAUDIODIR_OUT;
2034 pCfg->DestSource.Dest = PDMAUDIOPLAYBACKDEST_FRONT;
2035 pCfg->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
2036
2037 pCfg->Props.uHz = pThis->freq;
2038 pCfg->Props.cChannels = 1 << pThis->fmt_stereo;
2039 pCfg->Props.cBits = pThis->fmt_bits;
2040 pCfg->Props.fSigned = RT_BOOL(pThis->fmt_signed);
2041 pCfg->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfg->Props.cBits, pCfg->Props.cChannels);
2042
2043 strcpy(pCfg->szName, "Output");
2044
2045 sb16CloseOut(pThis);
2046
2047 rc = sb16OpenOut(pThis, pCfg);
2048 AssertRC(rc);
2049 }
2050
2051 sb16Control(pThis, 1);
2052 sb16SpeakerControl(pThis, pThis->speaker);
2053 }
2054
2055 /* Update the master (mixer) and PCM out volumes. */
2056 sb16UpdateVolume(pThis);
2057
2058 return VINF_SUCCESS;
2059}
2060
2061/**
2062 * @callback_method_impl{FNSSMDEVLOADEXEC}
2063 */
2064static DECLCALLBACK(int) sb16LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2065{
2066 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2067
2068 AssertMsgReturn( uVersion == SB16_SAVE_STATE_VERSION
2069 || uVersion == SB16_SAVE_STATE_VERSION_VBOX_30,
2070 ("%u\n", uVersion),
2071 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2072 if (uVersion > SB16_SAVE_STATE_VERSION_VBOX_30)
2073 {
2074 int32_t irq;
2075 SSMR3GetS32 (pSSM, &irq);
2076 int32_t dma;
2077 SSMR3GetS32 (pSSM, &dma);
2078 int32_t hdma;
2079 SSMR3GetS32 (pSSM, &hdma);
2080 int32_t port;
2081 SSMR3GetS32 (pSSM, &port);
2082 int32_t ver;
2083 int rc = SSMR3GetS32 (pSSM, &ver);
2084 AssertRCReturn (rc, rc);
2085
2086 if ( irq != pThis->irqCfg
2087 || dma != pThis->dmaCfg
2088 || hdma != pThis->hdmaCfg
2089 || port != pThis->portCfg
2090 || ver != pThis->verCfg)
2091 {
2092 return SSMR3SetCfgError(pSSM, RT_SRC_POS,
2093 N_("config changed: irq=%x/%x dma=%x/%x hdma=%x/%x port=%x/%x ver=%x/%x (saved/config)"),
2094 irq, pThis->irqCfg,
2095 dma, pThis->dmaCfg,
2096 hdma, pThis->hdmaCfg,
2097 port, pThis->portCfg,
2098 ver, pThis->verCfg);
2099 }
2100 }
2101
2102 if (uPass != SSM_PASS_FINAL)
2103 return VINF_SUCCESS;
2104
2105 return sb16Load(pSSM, pThis);
2106}
2107
2108/**
2109 * Creates a PDM audio stream for a specific driver.
2110 *
2111 * @returns IPRT status code.
2112 * @param pThis SB16 state.
2113 * @param pCfg Stream configuration to use.
2114 * @param pDrv Driver stream to create PDM stream for.
2115 */
2116static int sb16CreateDrvStream(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg, PSB16DRIVER pDrv)
2117{
2118 RT_NOREF(pThis);
2119
2120 AssertReturn(pCfg->enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
2121 Assert(DrvAudioHlpStreamCfgIsValid(pCfg));
2122
2123 PPDMAUDIOSTREAMCFG pCfgHost = DrvAudioHlpStreamCfgDup(pCfg);
2124 if (!pCfgHost)
2125 return VERR_NO_MEMORY;
2126
2127 LogFunc(("[LUN#%RU8] %s\n", pDrv->uLUN, pCfgHost->szName));
2128
2129 AssertMsg(pDrv->Out.pStream == NULL, ("[LUN#%RU8] Driver stream already present when it must not\n", pDrv->uLUN));
2130
2131 int rc = pDrv->pConnector->pfnStreamCreate(pDrv->pConnector, pCfgHost, pCfg /* pCfgGuest */, &pDrv->Out.pStream);
2132 if (RT_SUCCESS(rc))
2133 {
2134 pDrv->pConnector->pfnStreamRetain(pDrv->pConnector, pDrv->Out.pStream);
2135 LogFlowFunc(("LUN#%RU8: Created output \"%s\", rc=%Rrc\n", pDrv->uLUN, pCfg->szName, rc));
2136 }
2137
2138 RTMemFree(pCfgHost);
2139 return rc;
2140}
2141
2142/**
2143 * Destroys a PDM audio stream of a specific driver.
2144 *
2145 * @param pThis SB16 state.
2146 * @param pDrv Driver stream to destroy PDM stream for.
2147 */
2148static void sb16DestroyDrvStream(PSB16STATE pThis, PSB16DRIVER pDrv)
2149{
2150 AssertPtrReturnVoid(pThis);
2151 AssertPtrReturnVoid(pDrv);
2152
2153 if (pDrv->Out.pStream)
2154 {
2155 pDrv->pConnector->pfnStreamRelease(pDrv->pConnector, pDrv->Out.pStream);
2156
2157 int rc2 = pDrv->pConnector->pfnStreamControl(pDrv->pConnector, pDrv->Out.pStream, PDMAUDIOSTREAMCMD_DISABLE);
2158 AssertRC(rc2);
2159
2160 rc2 = pDrv->pConnector->pfnStreamDestroy(pDrv->pConnector, pDrv->Out.pStream);
2161 AssertRC(rc2);
2162
2163 pDrv->Out.pStream = NULL;
2164 }
2165}
2166
2167static int sb16OpenOut(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg)
2168{
2169 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2170 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2171
2172 LogFlowFuncEnter();
2173
2174 if (!DrvAudioHlpStreamCfgIsValid(pCfg))
2175 return VERR_INVALID_PARAMETER;
2176
2177 int rc = VINF_SUCCESS;
2178
2179 PSB16DRIVER pDrv;
2180 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2181 {
2182 int rc2 = sb16CreateDrvStream(pThis, pCfg, pDrv);
2183 if (RT_FAILURE(rc2))
2184 LogFunc(("Attaching stream failed with %Rrc\n", rc2));
2185
2186 /* Do not pass failure to rc here, as there might be drivers which aren't
2187 * configured / ready yet. */
2188 }
2189
2190 sb16UpdateVolume(pThis);
2191
2192 LogFlowFuncLeaveRC(rc);
2193 return rc;
2194}
2195
2196static void sb16CloseOut(PSB16STATE pThis)
2197{
2198 AssertPtrReturnVoid(pThis);
2199
2200 LogFlowFuncEnter();
2201
2202 PSB16DRIVER pDrv;
2203 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2204 sb16DestroyDrvStream(pThis, pDrv);
2205
2206 LogFlowFuncLeave();
2207}
2208
2209
2210/**
2211 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2212 */
2213static DECLCALLBACK(void *) sb16QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2214{
2215 PSB16STATE pThis = RT_FROM_MEMBER(pInterface, SB16STATE, IBase);
2216 Assert(&pThis->IBase == pInterface);
2217
2218 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2219 return NULL;
2220}
2221
2222
2223/**
2224 * Attach command, internal version.
2225 *
2226 * This is called to let the device attach to a driver for a specified LUN
2227 * during runtime. This is not called during VM construction, the device
2228 * constructor has to attach to all the available drivers.
2229 *
2230 * @returns VBox status code.
2231 * @param pThis SB16 state.
2232 * @param uLUN The logical unit which is being detached.
2233 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2234 * @param ppDrv Attached driver instance on success. Optional.
2235 */
2236static int sb16AttachInternal(PSB16STATE pThis, unsigned uLUN, uint32_t fFlags, PSB16DRIVER *ppDrv)
2237{
2238 RT_NOREF(fFlags);
2239
2240 /*
2241 * Attach driver.
2242 */
2243 char *pszDesc;
2244 if (RTStrAPrintf(&pszDesc, "Audio driver port (SB16) for LUN #%u", uLUN) <= 0)
2245 AssertLogRelFailedReturn(VERR_NO_MEMORY);
2246
2247 PPDMIBASE pDrvBase;
2248 int rc = PDMDevHlpDriverAttach(pThis->pDevInsR3, uLUN,
2249 &pThis->IBase, &pDrvBase, pszDesc);
2250 if (RT_SUCCESS(rc))
2251 {
2252 PSB16DRIVER pDrv = (PSB16DRIVER)RTMemAllocZ(sizeof(SB16DRIVER));
2253 if (pDrv)
2254 {
2255 pDrv->pDrvBase = pDrvBase;
2256 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
2257 AssertMsg(pDrv->pConnector != NULL, ("Configuration error: LUN #%u has no host audio interface, rc=%Rrc\n", uLUN, rc));
2258 pDrv->pSB16State = pThis;
2259 pDrv->uLUN = uLUN;
2260
2261 /*
2262 * For now we always set the driver at LUN 0 as our primary
2263 * host backend. This might change in the future.
2264 */
2265 if (pDrv->uLUN == 0)
2266 pDrv->fFlags |= PDMAUDIODRVFLAGS_PRIMARY;
2267
2268 LogFunc(("LUN#%RU8: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->fFlags));
2269
2270 /* Attach to driver list if not attached yet. */
2271 if (!pDrv->fAttached)
2272 {
2273 RTListAppend(&pThis->lstDrv, &pDrv->Node);
2274 pDrv->fAttached = true;
2275 }
2276
2277 if (ppDrv)
2278 *ppDrv = pDrv;
2279 }
2280 else
2281 rc = VERR_NO_MEMORY;
2282 }
2283 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2284 LogFunc(("No attached driver for LUN #%u\n", uLUN));
2285
2286 if (RT_FAILURE(rc))
2287 {
2288 /* Only free this string on failure;
2289 * must remain valid for the live of the driver instance. */
2290 RTStrFree(pszDesc);
2291 }
2292
2293 LogFunc(("iLUN=%u, fFlags=0x%x, rc=%Rrc\n", uLUN, fFlags, rc));
2294 return rc;
2295}
2296
2297/**
2298 * Detach command, internal version.
2299 *
2300 * This is called to let the device detach from a driver for a specified LUN
2301 * during runtime.
2302 *
2303 * @returns VBox status code.
2304 * @param pThis SB16 state.
2305 * @param pDrv Driver to detach device from.
2306 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2307 */
2308static int sb16DetachInternal(PSB16STATE pThis, PSB16DRIVER pDrv, uint32_t fFlags)
2309{
2310 RT_NOREF(fFlags);
2311
2312 sb16DestroyDrvStream(pThis, pDrv);
2313
2314 RTListNodeRemove(&pDrv->Node);
2315
2316 LogFunc(("uLUN=%u, fFlags=0x%x\n", pDrv->uLUN, fFlags));
2317 return VINF_SUCCESS;
2318}
2319
2320/**
2321 * @interface_method_impl{PDMDEVREG,pfnAttach}
2322 */
2323static DECLCALLBACK(int) sb16Attach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
2324{
2325 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2326
2327 LogFunc(("uLUN=%u, fFlags=0x%x\n", uLUN, fFlags));
2328
2329 PSB16DRIVER pDrv;
2330 int rc2 = sb16AttachInternal(pThis, uLUN, fFlags, &pDrv);
2331 if (RT_SUCCESS(rc2))
2332 rc2 = sb16CreateDrvStream(pThis, &pThis->Out.Cfg, pDrv);
2333
2334 return VINF_SUCCESS;
2335}
2336
2337/**
2338 * @interface_method_impl{PDMDEVREG,pfnDetach}
2339 */
2340static DECLCALLBACK(void) sb16Detach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
2341{
2342 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2343
2344 LogFunc(("uLUN=%u, fFlags=0x%x\n", uLUN, fFlags));
2345
2346 PSB16DRIVER pDrv, pDrvNext;
2347 RTListForEachSafe(&pThis->lstDrv, pDrv, pDrvNext, SB16DRIVER, Node)
2348 {
2349 if (pDrv->uLUN == uLUN)
2350 {
2351 int rc2 = sb16DetachInternal(pThis, pDrv, fFlags);
2352 if (RT_SUCCESS(rc2))
2353 {
2354 RTMemFree(pDrv);
2355 pDrv = NULL;
2356 }
2357
2358 break;
2359 }
2360 }
2361}
2362
2363/**
2364 * Re-attaches (replaces) a driver with a new driver.
2365 *
2366 * @returns VBox status code.
2367 * @param pThis Device instance.
2368 * @param pDrv Driver instance used for attaching to.
2369 * If NULL is specified, a new driver will be created and appended
2370 * to the driver list.
2371 * @param uLUN The logical unit which is being re-detached.
2372 * @param pszDriver New driver name to attach.
2373 */
2374static int sb16Reattach(PSB16STATE pThis, PSB16DRIVER pDrv, uint8_t uLUN, const char *pszDriver)
2375{
2376 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2377 AssertPtrReturn(pszDriver, VERR_INVALID_POINTER);
2378
2379 int rc;
2380
2381 if (pDrv)
2382 {
2383 rc = sb16DetachInternal(pThis, pDrv, 0 /* fFlags */);
2384 if (RT_SUCCESS(rc))
2385 rc = PDMDevHlpDriverDetach(pThis->pDevInsR3, PDMIBASE_2_PDMDRV(pDrv->pDrvBase), 0 /* fFlags */);
2386
2387 if (RT_FAILURE(rc))
2388 return rc;
2389
2390 pDrv = NULL;
2391 }
2392
2393 PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
2394 PCFGMNODE pRoot = CFGMR3GetRoot(pVM);
2395 PCFGMNODE pDev0 = CFGMR3GetChild(pRoot, "Devices/sb16/0/");
2396
2397 /* Remove LUN branch. */
2398 CFGMR3RemoveNode(CFGMR3GetChildF(pDev0, "LUN#%u/", uLUN));
2399
2400 if (pDrv)
2401 {
2402 /* Re-use the driver instance so detach it before. */
2403 rc = PDMDevHlpDriverDetach(pThis->pDevInsR3, PDMIBASE_2_PDMDRV(pDrv->pDrvBase), 0 /* fFlags */);
2404 if (RT_FAILURE(rc))
2405 return rc;
2406 }
2407
2408#define RC_CHECK() if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; }
2409
2410 do
2411 {
2412 PCFGMNODE pLunL0;
2413 rc = CFGMR3InsertNodeF(pDev0, &pLunL0, "LUN#%u/", uLUN); RC_CHECK();
2414 rc = CFGMR3InsertString(pLunL0, "Driver", "AUDIO"); RC_CHECK();
2415 rc = CFGMR3InsertNode(pLunL0, "Config/", NULL); RC_CHECK();
2416
2417 PCFGMNODE pLunL1, pLunL2;
2418 rc = CFGMR3InsertNode (pLunL0, "AttachedDriver/", &pLunL1); RC_CHECK();
2419 rc = CFGMR3InsertNode (pLunL1, "Config/", &pLunL2); RC_CHECK();
2420 rc = CFGMR3InsertString(pLunL1, "Driver", pszDriver); RC_CHECK();
2421
2422 rc = CFGMR3InsertString(pLunL2, "AudioDriver", pszDriver); RC_CHECK();
2423
2424 } while (0);
2425
2426 if (RT_SUCCESS(rc))
2427 rc = sb16AttachInternal(pThis, uLUN, 0 /* fFlags */, NULL /* ppDrv */);
2428
2429 LogFunc(("pThis=%p, uLUN=%u, pszDriver=%s, rc=%Rrc\n", pThis, uLUN, pszDriver, rc));
2430
2431#undef RC_CHECK
2432
2433 return rc;
2434}
2435
2436/**
2437 * @interface_method_impl{PDMDEVREG,pfnReset}
2438 */
2439static DECLCALLBACK(void) sb16DevReset(PPDMDEVINS pDevIns)
2440{
2441 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2442
2443 /* Bring back the device to initial state, and especially make
2444 * sure there's no interrupt or DMA activity.
2445 */
2446 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
2447
2448 pThis->mixer_regs[0x82] = 0;
2449 pThis->csp_regs[5] = 1;
2450 pThis->csp_regs[9] = 0xf8;
2451
2452 pThis->dma_auto = 0;
2453 pThis->in_index = 0;
2454 pThis->out_data_len = 0;
2455 pThis->left_till_irq = 0;
2456 pThis->needed_bytes = 0;
2457 pThis->block_size = -1;
2458 pThis->nzero = 0;
2459 pThis->highspeed = 0;
2460 pThis->v2x6 = 0;
2461 pThis->cmd = -1;
2462
2463 sb16MixerReset(pThis);
2464 sb16SpeakerControl(pThis, 0);
2465 sb16Control(pThis, 0);
2466 sb16CmdResetLegacy(pThis);
2467}
2468
2469/**
2470 * Powers off the device.
2471 *
2472 * @param pDevIns Device instance to power off.
2473 */
2474static DECLCALLBACK(void) sb16PowerOff(PPDMDEVINS pDevIns)
2475{
2476 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2477
2478 LogRel2(("SB16: Powering off ...\n"));
2479
2480 PSB16DRIVER pDrv;
2481 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2482 sb16DestroyDrvStream(pThis, pDrv);
2483}
2484
2485/**
2486 * @interface_method_impl{PDMDEVREG,pfnDestruct}
2487 */
2488static DECLCALLBACK(int) sb16Destruct(PPDMDEVINS pDevIns)
2489{
2490 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); /* this shall come first */
2491 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2492
2493 LogFlowFuncEnter();
2494
2495 PSB16DRIVER pDrv;
2496 while (!RTListIsEmpty(&pThis->lstDrv))
2497 {
2498 pDrv = RTListGetFirst(&pThis->lstDrv, SB16DRIVER, Node);
2499
2500 RTListNodeRemove(&pDrv->Node);
2501 RTMemFree(pDrv);
2502 }
2503
2504 return VINF_SUCCESS;
2505}
2506
2507static DECLCALLBACK(int) sb16Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2508{
2509 RT_NOREF(iInstance);
2510 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* this shall come first */
2511 PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
2512
2513 /*
2514 * Initialize the data so sb16Destruct runs without a hitch if we return early.
2515 */
2516 pThis->pDevInsR3 = pDevIns;
2517 pThis->IBase.pfnQueryInterface = sb16QueryInterface;
2518 pThis->cmd = -1;
2519
2520 pThis->csp_regs[5] = 1;
2521 pThis->csp_regs[9] = 0xf8;
2522
2523 RTListInit(&pThis->lstDrv);
2524
2525 /*
2526 * Validations.
2527 */
2528 Assert(iInstance == 0);
2529 if (!CFGMR3AreValuesValid(pCfg,
2530 "IRQ\0"
2531 "DMA\0"
2532 "DMA16\0"
2533 "Port\0"
2534 "Version\0"
2535 "TimerHz\0"))
2536 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2537 N_("Invalid configuration for SB16 device"));
2538
2539 /*
2540 * Read config data.
2541 */
2542 int rc = CFGMR3QuerySIntDef(pCfg, "IRQ", &pThis->irq, 5);
2543 if (RT_FAILURE(rc))
2544 return PDMDEV_SET_ERROR(pDevIns, rc,
2545 N_("SB16 configuration error: Failed to get the \"IRQ\" value"));
2546 pThis->irqCfg = pThis->irq;
2547
2548 rc = CFGMR3QuerySIntDef(pCfg, "DMA", &pThis->dma, 1);
2549 if (RT_FAILURE(rc))
2550 return PDMDEV_SET_ERROR(pDevIns, rc,
2551 N_("SB16 configuration error: Failed to get the \"DMA\" value"));
2552 pThis->dmaCfg = pThis->dma;
2553
2554 rc = CFGMR3QuerySIntDef(pCfg, "DMA16", &pThis->hdma, 5);
2555 if (RT_FAILURE(rc))
2556 return PDMDEV_SET_ERROR(pDevIns, rc,
2557 N_("SB16 configuration error: Failed to get the \"DMA16\" value"));
2558 pThis->hdmaCfg = pThis->hdma;
2559
2560 RTIOPORT Port;
2561 rc = CFGMR3QueryPortDef(pCfg, "Port", &Port, 0x220);
2562 if (RT_FAILURE(rc))
2563 return PDMDEV_SET_ERROR(pDevIns, rc,
2564 N_("SB16 configuration error: Failed to get the \"Port\" value"));
2565 pThis->port = Port;
2566 pThis->portCfg = Port;
2567
2568 uint16_t u16Version;
2569 rc = CFGMR3QueryU16Def(pCfg, "Version", &u16Version, 0x0405);
2570 if (RT_FAILURE(rc))
2571 return PDMDEV_SET_ERROR(pDevIns, rc,
2572 N_("SB16 configuration error: Failed to get the \"Version\" value"));
2573 pThis->ver = u16Version;
2574 pThis->verCfg = u16Version;
2575
2576#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
2577 uint16_t uTimerHz;
2578 rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, 100 /* Hz */);
2579 if (RT_FAILURE(rc))
2580 return PDMDEV_SET_ERROR(pDevIns, rc,
2581 N_("SB16 configuration error: failed to read Hertz (Hz) rate as unsigned integer"));
2582#endif
2583
2584 /*
2585 * Setup the mixer now that we've got the irq and dma channel numbers.
2586 */
2587 pThis->mixer_regs[0x80] = magic_of_irq(pThis->irq);
2588 pThis->mixer_regs[0x81] = (1 << pThis->dma) | (1 << pThis->hdma);
2589 pThis->mixer_regs[0x82] = 2 << 5;
2590
2591 sb16MixerReset(pThis);
2592
2593 /*
2594 * Create timer(s), register & attach stuff.
2595 */
2596 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIRQ, pThis,
2597 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "SB16 IRQ timer", &pThis->pTimerIRQ);
2598 if (RT_FAILURE(rc))
2599 AssertMsgFailedReturn(("Error creating IRQ timer, rc=%Rrc\n", rc), rc);
2600
2601 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->port + 0x04, 2, pThis, mixer_write, mixer_read, NULL, NULL, "SB16");
2602 if (RT_FAILURE(rc))
2603 return rc;
2604 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->port + 0x06, 10, pThis, dsp_write, dsp_read, NULL, NULL, "SB16");
2605 if (RT_FAILURE(rc))
2606 return rc;
2607
2608 rc = PDMDevHlpDMARegister(pDevIns, pThis->hdma, sb16DMARead, pThis);
2609 if (RT_FAILURE(rc))
2610 return rc;
2611 rc = PDMDevHlpDMARegister(pDevIns, pThis->dma, sb16DMARead, pThis);
2612 if (RT_FAILURE(rc))
2613 return rc;
2614
2615 pThis->can_write = 1;
2616
2617 rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec);
2618 if (RT_FAILURE(rc))
2619 return rc;
2620
2621 /*
2622 * Attach driver.
2623 */
2624 uint8_t uLUN;
2625 for (uLUN = 0; uLUN < UINT8_MAX; ++uLUN)
2626 {
2627 LogFunc(("Trying to attach driver for LUN #%RU8 ...\n", uLUN));
2628 rc = sb16AttachInternal(pThis, uLUN, 0 /* fFlags */, NULL /* ppDrv */);
2629 if (RT_FAILURE(rc))
2630 {
2631 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2632 rc = VINF_SUCCESS;
2633 else if (rc == VERR_AUDIO_BACKEND_INIT_FAILED)
2634 {
2635 sb16Reattach(pThis, NULL /* pDrv */, uLUN, "NullAudio");
2636 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2637 N_("Host audio backend initialization has failed. Selecting the NULL audio backend "
2638 "with the consequence that no sound is audible"));
2639 /* Attaching to the NULL audio backend will never fail. */
2640 rc = VINF_SUCCESS;
2641 }
2642 break;
2643 }
2644 }
2645
2646 LogFunc(("cLUNs=%RU8, rc=%Rrc\n", uLUN, rc));
2647
2648 sb16CmdResetLegacy(pThis);
2649
2650#ifdef VBOX_WITH_AUDIO_SB16_ONETIME_INIT
2651 PSB16DRIVER pDrv;
2652 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2653 {
2654 /*
2655 * Only primary drivers are critical for the VM to run. Everything else
2656 * might not worth showing an own error message box in the GUI.
2657 */
2658 if (!(pDrv->fFlags & PDMAUDIODRVFLAGS_PRIMARY))
2659 continue;
2660
2661 PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
2662 AssertPtr(pCon);
2663
2664 /** @todo No input streams available for SB16 yet. */
2665
2666 if (!pDrv->Out.pStream)
2667 continue;
2668
2669 bool fValidOut = pCon->pfnStreamGetStatus(pCon, pDrv->Out.pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
2670 if (!fValidOut)
2671 {
2672 LogRel(("SB16: Falling back to NULL backend (no sound audible)\n"));
2673
2674 sb16CmdResetLegacy(pThis);
2675 sb16Reattach(pThis, pDrv, pDrv->uLUN, "NullAudio");
2676
2677 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2678 N_("No audio devices could be opened. Selecting the NULL audio backend "
2679 "with the consequence that no sound is audible"));
2680 }
2681 }
2682#endif
2683
2684#ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS
2685 if (RT_SUCCESS(rc))
2686 {
2687 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, pThis,
2688 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "SB16 IO timer", &pThis->pTimerIO);
2689 if (RT_SUCCESS(rc))
2690 {
2691 pThis->cTimerTicksIO = TMTimerGetFreq(pThis->pTimerIO) / uTimerHz;
2692 pThis->uTimerTSIO = TMTimerGet(pThis->pTimerIO);
2693 LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTimerTicksIO, uTimerHz));
2694 }
2695 else
2696 AssertMsgFailedReturn(("Error creating I/O timer, rc=%Rrc\n", rc), rc);
2697 }
2698#else /* !VBOX_WITH_AUDIO_SB16_CALLBACKS */
2699 if (RT_SUCCESS(rc))
2700 {
2701 /** @todo Merge this callback registration with the validation block above once
2702 * this becomes the standard. */
2703 PSB16DRIVER pDrv;
2704 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2705 {
2706 /* Only register primary driver.
2707 * The device emulation does the output multiplexing then. */
2708 if (pDrv->fFlags != PDMAUDIODRVFLAGS_PRIMARY)
2709 continue;
2710
2711 PDMAUDIOCBRECORD AudioCallbacks[2];
2712
2713 SB16CALLBACKCTX Ctx = { pThis, pDrv };
2714
2715 AudioCallbacks[0].enmType = PDMAUDIOCALLBACKTYPE_INPUT;
2716 AudioCallbacks[0].pfnCallback = sb16CallbackInput;
2717 AudioCallbacks[0].pvCtx = &Ctx;
2718 AudioCallbacks[0].cbCtx = sizeof(SB16CALLBACKCTX);
2719
2720 AudioCallbacks[1].enmType = PDMAUDIOCALLBACKTYPE_OUTPUT;
2721 AudioCallbacks[1].pfnCallback = sb16CallbackOutput;
2722 AudioCallbacks[1].pvCtx = &Ctx;
2723 AudioCallbacks[1].cbCtx = sizeof(SB16CALLBACKCTX);
2724
2725 rc = pDrv->pConnector->pfnRegisterCallbacks(pDrv->pConnector, AudioCallbacks, RT_ELEMENTS(AudioCallbacks));
2726 if (RT_FAILURE(rc))
2727 break;
2728 }
2729 }
2730#endif /* VBOX_WITH_AUDIO_SB16_CALLBACKS */
2731
2732#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
2733 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "sb16WriteAudio.pcm");
2734#endif
2735
2736 return VINF_SUCCESS;
2737}
2738
2739const PDMDEVREG g_DeviceSB16 =
2740{
2741 /* u32Version */
2742 PDM_DEVREG_VERSION,
2743 /* szName */
2744 "sb16",
2745 /* szRCMod */
2746 "",
2747 /* szR0Mod */
2748 "",
2749 /* pszDescription */
2750 "Sound Blaster 16 Controller",
2751 /* fFlags */
2752 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2753 /* fClass */
2754 PDM_DEVREG_CLASS_AUDIO,
2755 /* cMaxInstances */
2756 1,
2757 /* cbInstance */
2758 sizeof(SB16STATE),
2759 /* pfnConstruct */
2760 sb16Construct,
2761 /* pfnDestruct */
2762 sb16Destruct,
2763 /* pfnRelocate */
2764 NULL,
2765 /* pfnMemSetup */
2766 NULL,
2767 /* pfnPowerOn */
2768 NULL,
2769 /* pfnReset */
2770 sb16DevReset,
2771 /* pfnSuspend */
2772 NULL,
2773 /* pfnResume */
2774 NULL,
2775 /* pfnAttach */
2776 sb16Attach,
2777 /* pfnDetach */
2778 sb16Detach,
2779 /* pfnQueryInterface */
2780 NULL,
2781 /* pfnInitComplete */
2782 NULL,
2783 /* pfnPowerOff */
2784 sb16PowerOff,
2785 /* pfnSoftReset */
2786 NULL,
2787 /* u32VersionEnd */
2788 PDM_DEVREG_VERSION
2789};
2790
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