VirtualBox

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

Last change on this file since 88561 was 88561, checked in by vboxsync, 4 years ago

Audio: Moved the HostAudioNotResponding runtime error reporting during driver attching and initialization into DrvAudio instead of having it duplicated in every audio device. Also simplified the NULL driver replacing by skipping the CFGM + PDM work and just use the NULL driver vtable directly. bugref:9890

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