VirtualBox

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

Last change on this file since 78135 was 76847, checked in by vboxsync, 6 years ago

Audio/SB16: Disable pre-buffering for SB16; not needed for that bit of data.

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