VirtualBox

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

最後變更 在這個檔案從88061是 88028,由 vboxsync 提交於 4 年 前

Audio: Moving some of the DrvAudio.h stuff into PDM - VBox/vmm/pdmaudioinline.h. bugref:9890

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 83.7 KB
 
1/* $Id: DevSB16.cpp 88028 2021-03-08 19:31:22Z 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.alldomusa.eu.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 "DrvAudio.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
913 pCfg->Props.uHz = pThis->freq;
914 pCfg->Props.cChannels = 1; /* Mono */
915 pCfg->Props.cbSample = 1 /* 8-bit */;
916 pCfg->Props.fSigned = false;
917 pCfg->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfg->Props.cbSample, pCfg->Props.cChannels);
918
919 AssertCompile(sizeof(pCfg->szName) >= sizeof("Output"));
920 memcpy(pCfg->szName, "Output", sizeof("Output"));
921
922 sb16CloseOut(pThis);
923}
924
925static void sb16CmdReset(PPDMDEVINS pDevIns, PSB16STATE pThis)
926{
927 PDMDevHlpISASetIrq(pDevIns, pThis->irq, 0);
928 if (pThis->dma_auto)
929 {
930 PDMDevHlpISASetIrq(pDevIns, pThis->irq, 1);
931 PDMDevHlpISASetIrq(pDevIns, pThis->irq, 0);
932 }
933
934 pThis->mixer_regs[0x82] = 0;
935 pThis->dma_auto = 0;
936 pThis->in_index = 0;
937 pThis->out_data_len = 0;
938 pThis->left_till_irq = 0;
939 pThis->needed_bytes = 0;
940 pThis->block_size = -1;
941 pThis->nzero = 0;
942 pThis->highspeed = 0;
943 pThis->v2x6 = 0;
944 pThis->cmd = -1;
945
946 dsp_out_data(pThis, 0xaa);
947 sb16SpeakerControl(pThis, 0);
948
949 sb16Control(pDevIns, pThis, 0);
950 sb16CmdResetLegacy(pThis);
951}
952
953/**
954 * @callback_method_impl{PFNIOMIOPORTNEWOUT}
955 */
956static DECLCALLBACK(VBOXSTRICTRC) sb16IoPortDspWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
957{
958 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
959 RT_NOREF(pvUser, cb);
960
961 LogFlowFunc(("write %#x <- %#x\n", offPort, u32));
962 switch (offPort)
963 {
964 case 0:
965 switch (u32)
966 {
967 case 0x00:
968 {
969 if (pThis->v2x6 == 1)
970 {
971 if (0 && pThis->highspeed)
972 {
973 pThis->highspeed = 0;
974 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
975 sb16Control(pDevIns, pThis, 0);
976 }
977 else
978 sb16CmdReset(pDevIns, pThis);
979 }
980 pThis->v2x6 = 0;
981 break;
982 }
983
984 case 0x01:
985 case 0x03: /* FreeBSD kludge */
986 pThis->v2x6 = 1;
987 break;
988
989 case 0xc6:
990 pThis->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */
991 break;
992
993 case 0xb8: /* Panic */
994 sb16CmdReset(pDevIns, pThis);
995 break;
996
997 case 0x39:
998 dsp_out_data(pThis, 0x38);
999 sb16CmdReset(pDevIns, pThis);
1000 pThis->v2x6 = 0x39;
1001 break;
1002
1003 default:
1004 pThis->v2x6 = u32;
1005 break;
1006 }
1007 break;
1008
1009 case 6: /* Write data or command | write status */
1010#if 0
1011 if (pThis->highspeed)
1012 break;
1013#endif
1014 if (0 == pThis->needed_bytes)
1015 {
1016 sb16HandleCommand(pDevIns, pThis, u32);
1017#if 0
1018 if (0 == pThis->needed_bytes) {
1019 log_dsp (pThis);
1020 }
1021#endif
1022 }
1023 else
1024 {
1025 if (pThis->in_index == sizeof (pThis->in2_data))
1026 {
1027 LogFlowFunc(("in data overrun\n"));
1028 }
1029 else
1030 {
1031 pThis->in2_data[pThis->in_index++] = u32;
1032 if (pThis->in_index == pThis->needed_bytes)
1033 {
1034 pThis->needed_bytes = 0;
1035 complete(pDevIns, pThis);
1036#if 0
1037 log_dsp (pThis);
1038#endif
1039 }
1040 }
1041 }
1042 break;
1043
1044 default:
1045 LogFlowFunc(("offPort=%#x, u32=%#x)\n", offPort, u32));
1046 break;
1047 }
1048
1049 return VINF_SUCCESS;
1050}
1051
1052
1053/**
1054 * @callback_method_impl{PFNIOMIOPORTNEWIN}
1055 */
1056static DECLCALLBACK(VBOXSTRICTRC) sb16IoPortDspRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1057{
1058 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1059 uint32_t retval;
1060 int ack = 0;
1061 RT_NOREF(pvUser, cb);
1062
1063
1064 /** @todo reject non-byte access?
1065 * The spec does not mention a non-byte access so we should check how real hardware behaves. */
1066
1067 switch (offPort)
1068 {
1069 case 0: /* reset */
1070 retval = 0xff;
1071 break;
1072
1073 case 4: /* read data */
1074 if (pThis->out_data_len)
1075 {
1076 retval = pThis->out_data[--pThis->out_data_len];
1077 pThis->last_read_byte = retval;
1078 }
1079 else
1080 {
1081 if (pThis->cmd != -1)
1082 LogFlowFunc(("empty output buffer for command %#x\n", pThis->cmd));
1083 retval = pThis->last_read_byte;
1084 /* goto error; */
1085 }
1086 break;
1087
1088 case 6: /* 0 can write */
1089 retval = pThis->can_write ? 0 : 0x80;
1090 break;
1091
1092 case 7: /* timer interrupt clear */
1093 /* LogFlowFunc(("timer interrupt clear\n")); */
1094 retval = 0;
1095 break;
1096
1097 case 8: /* data available status | irq 8 ack */
1098 retval = (!pThis->out_data_len || pThis->highspeed) ? 0 : 0x80;
1099 if (pThis->mixer_regs[0x82] & 1)
1100 {
1101 ack = 1;
1102 pThis->mixer_regs[0x82] &= ~1;
1103 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1104 }
1105 break;
1106
1107 case 9: /* irq 16 ack */
1108 retval = 0xff;
1109 if (pThis->mixer_regs[0x82] & 2)
1110 {
1111 ack = 1;
1112 pThis->mixer_regs[0x82] &= ~2;
1113 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
1114 }
1115 break;
1116
1117 default:
1118 LogFlowFunc(("warning: sb16IoPortDspRead %#x error\n", offPort));
1119 return VERR_IOM_IOPORT_UNUSED;
1120 }
1121
1122 if (!ack)
1123 LogFlowFunc(("read %#x -> %#x\n", offPort, retval));
1124
1125 *pu32 = retval;
1126 return VINF_SUCCESS;
1127}
1128
1129
1130/* -=-=-=-=-=- Mixer -=-=-=-=-=- */
1131
1132static uint8_t sb16MixRegToVol(PSB16STATE pThis, int reg)
1133{
1134 /* The SB16 mixer has a 0 to -62dB range in 32 levels (2dB each step).
1135 * We use a 0 to -96dB range in 256 levels (0.375dB each step).
1136 * Only the top 5 bits of a mixer register are used.
1137 */
1138 uint8_t steps = 31 - (pThis->mixer_regs[reg] >> 3);
1139 uint8_t vol = 255 - steps * 16 / 3; /* (2dB*8) / (0.375dB*8) */
1140 return vol;
1141}
1142
1143/**
1144 * Returns the device's current master volume.
1145 *
1146 * @param pThis SB16 state.
1147 * @param pVol Where to store the master volume information.
1148 */
1149static void sb16GetMasterVolume(PSB16STATE pThis, PPDMAUDIOVOLUME pVol)
1150{
1151 /* There's no mute switch, only volume controls. */
1152 uint8_t lvol = sb16MixRegToVol(pThis, 0x30);
1153 uint8_t rvol = sb16MixRegToVol(pThis, 0x31);
1154
1155 pVol->fMuted = false;
1156 pVol->uLeft = lvol;
1157 pVol->uRight = rvol;
1158}
1159
1160/**
1161 * Returns the device's current output stream volume.
1162 *
1163 * @param pThis SB16 state.
1164 * @param pVol Where to store the output stream volume information.
1165 */
1166static void sb16GetPcmOutVolume(PSB16STATE pThis, PPDMAUDIOVOLUME pVol)
1167{
1168 /* There's no mute switch, only volume controls. */
1169 uint8_t lvol = sb16MixRegToVol(pThis, 0x32);
1170 uint8_t rvol = sb16MixRegToVol(pThis, 0x33);
1171
1172 pVol->fMuted = false;
1173 pVol->uLeft = lvol;
1174 pVol->uRight = rvol;
1175}
1176
1177static void sb16UpdateVolume(PSB16STATE pThis)
1178{
1179 PDMAUDIOVOLUME VolMaster;
1180 sb16GetMasterVolume(pThis, &VolMaster);
1181
1182 PDMAUDIOVOLUME VolOut;
1183 sb16GetPcmOutVolume(pThis, &VolOut);
1184
1185 /* Combine the master + output stream volume. */
1186 PDMAUDIOVOLUME VolCombined;
1187 RT_ZERO(VolCombined);
1188
1189 VolCombined.fMuted = VolMaster.fMuted || VolOut.fMuted;
1190 if (!VolCombined.fMuted)
1191 {
1192 VolCombined.uLeft = ( (VolOut.uLeft ? VolOut.uLeft : 1)
1193 * (VolMaster.uLeft ? VolMaster.uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1194
1195 VolCombined.uRight = ( (VolOut.uRight ? VolOut.uRight : 1)
1196 * (VolMaster.uRight ? VolMaster.uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1197 }
1198
1199 PSB16DRIVER pDrv;
1200 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1201 {
1202 PPDMAUDIOSTREAM pStream = pDrv->Out.pStream;
1203 if (pStream)
1204 {
1205 int rc2 = pDrv->pConnector->pfnStreamSetVolume(pDrv->pConnector, pStream, &VolCombined);
1206 AssertRC(rc2);
1207 }
1208 }
1209}
1210
1211static void sb16MixerReset(PSB16STATE pThis)
1212{
1213 memset(pThis->mixer_regs, 0xff, 0x7f);
1214 memset(pThis->mixer_regs + 0x83, 0xff, sizeof (pThis->mixer_regs) - 0x83);
1215
1216 pThis->mixer_regs[0x02] = 4; /* master volume 3bits */
1217 pThis->mixer_regs[0x06] = 4; /* MIDI volume 3bits */
1218 pThis->mixer_regs[0x08] = 0; /* CD volume 3bits */
1219 pThis->mixer_regs[0x0a] = 0; /* voice volume 2bits */
1220
1221 /* d5=input filt, d3=lowpass filt, d1,d2=input source */
1222 pThis->mixer_regs[0x0c] = 0;
1223
1224 /* d5=output filt, d1=stereo switch */
1225 pThis->mixer_regs[0x0e] = 0;
1226
1227 /* voice volume L d5,d7, R d1,d3 */
1228 pThis->mixer_regs[0x04] = (12 << 4) | 12;
1229 /* master ... */
1230 pThis->mixer_regs[0x22] = (12 << 4) | 12;
1231 /* MIDI ... */
1232 pThis->mixer_regs[0x26] = (12 << 4) | 12;
1233
1234 /* master/voice/MIDI L/R volume */
1235 for (int i = 0x30; i < 0x36; i++)
1236 pThis->mixer_regs[i] = 24 << 3; /* -14 dB */
1237
1238 /* treble/bass */
1239 for (int i = 0x44; i < 0x48; i++)
1240 pThis->mixer_regs[i] = 0x80;
1241
1242 /* Update the master (mixer) and PCM out volumes. */
1243 sb16UpdateVolume(pThis);
1244}
1245
1246static int magic_of_irq(int irq)
1247{
1248 switch (irq)
1249 {
1250 case 5:
1251 return 2;
1252 case 7:
1253 return 4;
1254 case 9:
1255 return 1;
1256 case 10:
1257 return 8;
1258 default:
1259 break;
1260 }
1261
1262 LogFlowFunc(("bad irq %d\n", irq));
1263 return 2;
1264}
1265
1266static int irq_of_magic(int magic)
1267{
1268 switch (magic)
1269 {
1270 case 1:
1271 return 9;
1272 case 2:
1273 return 5;
1274 case 4:
1275 return 7;
1276 case 8:
1277 return 10;
1278 default:
1279 break;
1280 }
1281
1282 LogFlowFunc(("bad irq magic %d\n", magic));
1283 return -1;
1284}
1285
1286static int mixer_write_indexb(PSB16STATE pThis, uint8_t val)
1287{
1288 pThis->mixer_nreg = val;
1289 return VINF_SUCCESS;
1290}
1291
1292#ifndef VBOX
1293static uint32_t popcount(uint32_t u)
1294{
1295 u = ((u&0x55555555) + ((u>>1)&0x55555555));
1296 u = ((u&0x33333333) + ((u>>2)&0x33333333));
1297 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
1298 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
1299 u = ( u&0x0000ffff) + (u>>16);
1300 return u;
1301}
1302#endif
1303
1304static uint32_t lsbindex(uint32_t u)
1305{
1306#ifdef VBOX
1307 return u ? ASMBitFirstSetU32(u) - 1 : 32;
1308#else
1309 return popcount((u & -(int32_t)u) - 1);
1310#endif
1311}
1312
1313/* Convert SB16 to SB Pro mixer volume (left). */
1314static inline void sb16ConvVolumeL(PSB16STATE pThis, unsigned reg, uint8_t val)
1315{
1316 /* High nibble in SBP mixer. */
1317 pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0x0f) | (val & 0xf0);
1318}
1319
1320/* Convert SB16 to SB Pro mixer volume (right). */
1321static inline void sb16ConvVolumeR(PSB16STATE pThis, unsigned reg, uint8_t val)
1322{
1323 /* Low nibble in SBP mixer. */
1324 pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0xf0) | (val >> 4);
1325}
1326
1327/* Convert SB Pro to SB16 mixer volume (left + right). */
1328static inline void sb16ConvVolumeOldToNew(PSB16STATE pThis, unsigned reg, uint8_t val)
1329{
1330 /* Left channel. */
1331 pThis->mixer_regs[reg + 0] = (val & 0xf0) | RT_BIT(3);
1332 /* Right channel (the register immediately following). */
1333 pThis->mixer_regs[reg + 1] = (val << 4) | RT_BIT(3);
1334}
1335
1336
1337static int mixer_write_datab(PSB16STATE pThis, uint8_t val)
1338{
1339 bool fUpdateMaster = false;
1340 bool fUpdateStream = false;
1341
1342 LogFlowFunc(("sb16IoPortMixerWrite [%#x] <- %#x\n", pThis->mixer_nreg, val));
1343
1344 switch (pThis->mixer_nreg)
1345 {
1346 case 0x00:
1347 sb16MixerReset(pThis);
1348 /* And update the actual volume, too. */
1349 fUpdateMaster = true;
1350 fUpdateStream = true;
1351 break;
1352
1353 case 0x04: /* Translate from old style voice volume (L/R). */
1354 sb16ConvVolumeOldToNew(pThis, 0x32, val);
1355 fUpdateStream = true;
1356 break;
1357
1358 case 0x22: /* Translate from old style master volume (L/R). */
1359 sb16ConvVolumeOldToNew(pThis, 0x30, val);
1360 fUpdateMaster = true;
1361 break;
1362
1363 case 0x26: /* Translate from old style MIDI volume (L/R). */
1364 sb16ConvVolumeOldToNew(pThis, 0x34, val);
1365 break;
1366
1367 case 0x28: /* Translate from old style CD volume (L/R). */
1368 sb16ConvVolumeOldToNew(pThis, 0x36, val);
1369 break;
1370
1371 case 0x2E: /* Translate from old style line volume (L/R). */
1372 sb16ConvVolumeOldToNew(pThis, 0x38, val);
1373 break;
1374
1375 case 0x30: /* Translate to old style master volume (L). */
1376 sb16ConvVolumeL(pThis, 0x22, val);
1377 fUpdateMaster = true;
1378 break;
1379
1380 case 0x31: /* Translate to old style master volume (R). */
1381 sb16ConvVolumeR(pThis, 0x22, val);
1382 fUpdateMaster = true;
1383 break;
1384
1385 case 0x32: /* Translate to old style voice volume (L). */
1386 sb16ConvVolumeL(pThis, 0x04, val);
1387 fUpdateStream = true;
1388 break;
1389
1390 case 0x33: /* Translate to old style voice volume (R). */
1391 sb16ConvVolumeR(pThis, 0x04, val);
1392 fUpdateStream = true;
1393 break;
1394
1395 case 0x34: /* Translate to old style MIDI volume (L). */
1396 sb16ConvVolumeL(pThis, 0x26, val);
1397 break;
1398
1399 case 0x35: /* Translate to old style MIDI volume (R). */
1400 sb16ConvVolumeR(pThis, 0x26, val);
1401 break;
1402
1403 case 0x36: /* Translate to old style CD volume (L). */
1404 sb16ConvVolumeL(pThis, 0x28, val);
1405 break;
1406
1407 case 0x37: /* Translate to old style CD volume (R). */
1408 sb16ConvVolumeR(pThis, 0x28, val);
1409 break;
1410
1411 case 0x38: /* Translate to old style line volume (L). */
1412 sb16ConvVolumeL(pThis, 0x2E, val);
1413 break;
1414
1415 case 0x39: /* Translate to old style line volume (R). */
1416 sb16ConvVolumeR(pThis, 0x2E, val);
1417 break;
1418
1419 case 0x80:
1420 {
1421 int irq = irq_of_magic(val);
1422 LogFlowFunc(("setting irq to %d (val=%#x)\n", irq, val));
1423 if (irq > 0)
1424 pThis->irq = irq;
1425 break;
1426 }
1427
1428 case 0x81:
1429 {
1430 int dma, hdma;
1431
1432 dma = lsbindex (val & 0xf);
1433 hdma = lsbindex (val & 0xf0);
1434 if (dma != pThis->dma || hdma != pThis->hdma)
1435 LogFlow(("SB16: attempt to change DMA 8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
1436 dma, pThis->dma, hdma, pThis->hdma, val));
1437#if 0
1438 pThis->dma = dma;
1439 pThis->hdma = hdma;
1440#endif
1441 break;
1442 }
1443
1444 case 0x82:
1445 LogFlowFunc(("attempt to write into IRQ status register (val=%#x)\n", val));
1446 return VINF_SUCCESS;
1447
1448 default:
1449 if (pThis->mixer_nreg >= 0x80)
1450 LogFlowFunc(("attempt to write mixer[%#x] <- %#x\n", pThis->mixer_nreg, val));
1451 break;
1452 }
1453
1454 pThis->mixer_regs[pThis->mixer_nreg] = val;
1455
1456 /* Update the master (mixer) volume. */
1457 if ( fUpdateMaster
1458 || fUpdateStream)
1459 {
1460 sb16UpdateVolume(pThis);
1461 }
1462
1463 return VINF_SUCCESS;
1464}
1465
1466/**
1467 * @callback_method_impl{PFNIOMIOPORTNEWOUT}
1468 */
1469static DECLCALLBACK(VBOXSTRICTRC) sb16IoPortMixerWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
1470{
1471 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1472 RT_NOREF(pvUser);
1473
1474 switch (cb)
1475 {
1476 case 1:
1477 switch (offPort)
1478 {
1479 case 0:
1480 mixer_write_indexb(pThis, u32);
1481 break;
1482 case 1:
1483 mixer_write_datab(pThis, u32);
1484 break;
1485 default:
1486 AssertFailed();
1487 }
1488 break;
1489 case 2:
1490 mixer_write_indexb(pThis, u32 & 0xff);
1491 mixer_write_datab(pThis, (u32 >> 8) & 0xff);
1492 break;
1493 default:
1494 ASSERT_GUEST_MSG_FAILED(("offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
1495 break;
1496 }
1497 return VINF_SUCCESS;
1498}
1499
1500/**
1501 * @callback_method_impl{PFNIOMIOPORTNEWIN}
1502 */
1503static DECLCALLBACK(VBOXSTRICTRC) sb16IoPortMixerRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1504{
1505 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1506 RT_NOREF(pvUser, cb, offPort);
1507
1508#ifndef DEBUG_SB16_MOST
1509 if (pThis->mixer_nreg != 0x82)
1510 LogFlowFunc(("sb16IoPortMixerRead[%#x] -> %#x\n", pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
1511#else
1512 LogFlowFunc(("sb16IoPortMixerRead[%#x] -> %#x\n", pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
1513#endif
1514 *pu32 = pThis->mixer_regs[pThis->mixer_nreg];
1515 return VINF_SUCCESS;
1516}
1517
1518
1519/* -=-=-=-=-=- DMA -=-=-=-=-=- */
1520
1521/**
1522 * Worker for sb16DMARead.
1523 */
1524static int sb16WriteAudio(PSB16STATE pThis, int nchan, uint32_t dma_pos, uint32_t dma_len, int len)
1525{
1526 uint8_t abBuf[_4K]; /** @todo Have a buffer on the heap. */
1527 uint32_t cbToWrite = len;
1528 uint32_t cbWrittenTotal = 0;
1529
1530 while (cbToWrite)
1531 {
1532 uint32_t cbToRead = RT_MIN(dma_len - dma_pos, cbToWrite);
1533 if (cbToRead > sizeof(abBuf))
1534 cbToRead = sizeof(abBuf);
1535
1536 uint32_t cbRead = 0;
1537 int rc2 = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, nchan, abBuf, dma_pos, cbToRead, &cbRead);
1538 AssertMsgRC(rc2, (" from DMA failed: %Rrc\n", rc2));
1539
1540#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
1541 if (cbRead)
1542 {
1543 RTFILE fh;
1544 RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "sb16WriteAudio.pcm",
1545 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1546 RTFileWrite(fh, abBuf, cbRead, NULL);
1547 RTFileClose(fh);
1548 }
1549#endif
1550 /*
1551 * Write data to the backends.
1552 */
1553 uint32_t cbWritten = 0;
1554
1555 /* Just multiplex the output to the connected backends.
1556 * No need to utilize the virtual mixer here (yet). */
1557 PSB16DRIVER pDrv;
1558 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1559 {
1560 if ( pDrv->Out.pStream
1561 && PDMAudioStrmStatusCanWrite(pDrv->pConnector->pfnStreamGetStatus(pDrv->pConnector, pDrv->Out.pStream)))
1562 {
1563 uint32_t cbWrittenToStream = 0;
1564 rc2 = pDrv->pConnector->pfnStreamWrite(pDrv->pConnector, pDrv->Out.pStream, abBuf, cbRead, &cbWrittenToStream);
1565
1566 LogFlowFunc(("\tLUN#%RU8: rc=%Rrc, cbWrittenToStream=%RU32\n", pDrv->uLUN, rc2, cbWrittenToStream));
1567 }
1568 }
1569
1570 cbWritten = cbRead; /* Always report everything written, as the backends need to keep up themselves. */
1571
1572 LogFlowFunc(("\tcbToRead=%RU32, cbToWrite=%RU32, cbWritten=%RU32, cbLeft=%RU32\n",
1573 cbToRead, cbToWrite, cbWritten, cbToWrite - cbWrittenTotal));
1574
1575 if (!cbWritten)
1576 break;
1577
1578 Assert(cbToWrite >= cbWritten);
1579 cbToWrite -= cbWritten;
1580 dma_pos = (dma_pos + cbWritten) % dma_len;
1581 cbWrittenTotal += cbWritten;
1582 }
1583
1584 return cbWrittenTotal;
1585}
1586
1587/**
1588 * @callback_method_impl{FNDMATRANSFERHANDLER,
1589 * Worker callback for both DMA channels.}
1590 */
1591static DECLCALLBACK(uint32_t) sb16DMARead(PPDMDEVINS pDevIns, void *pvUser, unsigned uChannel, uint32_t off, uint32_t cb)
1592
1593{
1594 RT_NOREF(pDevIns);
1595 PSB16STATE pThis = (PSB16STATE)pvUser;
1596 int till, copy, written, free;
1597
1598 if (pThis->block_size <= 0)
1599 {
1600 LogFlowFunc(("invalid block size=%d uChannel=%d off=%d cb=%d\n", pThis->block_size, uChannel, off, cb));
1601 return off;
1602 }
1603
1604 if (pThis->left_till_irq < 0)
1605 pThis->left_till_irq = pThis->block_size;
1606
1607 free = cb;
1608
1609 copy = free;
1610 till = pThis->left_till_irq;
1611
1612#ifdef DEBUG_SB16_MOST
1613 LogFlowFunc(("pos:%06d %d till:%d len:%d\n", off, free, till, cb));
1614#endif
1615
1616 if (copy >= till)
1617 {
1618 if (0 == pThis->dma_auto)
1619 {
1620 copy = till;
1621 }
1622 else
1623 {
1624 if (copy >= till + pThis->block_size)
1625 copy = till; /* Make sure we won't skip IRQs. */
1626 }
1627 }
1628
1629 written = sb16WriteAudio(pThis, uChannel, off, cb, copy);
1630 off = (off + written) % cb;
1631 pThis->left_till_irq -= written;
1632
1633 if (pThis->left_till_irq <= 0)
1634 {
1635 pThis->mixer_regs[0x82] |= (uChannel & 4) ? 2 : 1;
1636 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
1637 if (0 == pThis->dma_auto)
1638 {
1639 sb16Control(pDevIns, pThis, 0);
1640 sb16SpeakerControl(pThis, 0);
1641 }
1642 }
1643
1644 Log3Func(("pos %d/%d free %5d till %5d copy %5d written %5d block_size %5d\n",
1645 off, cb, free, pThis->left_till_irq, copy, written, pThis->block_size));
1646
1647 while (pThis->left_till_irq <= 0)
1648 pThis->left_till_irq += pThis->block_size;
1649
1650 return off;
1651}
1652
1653
1654/* -=-=-=-=-=- I/O timer -=-=-=-=-=- */
1655
1656static void sb16TimerMaybeStart(PPDMDEVINS pDevIns, PSB16STATE pThis)
1657{
1658 LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive));
1659
1660 if (pThis->cStreamsActive == 0) /* Only start the timer if there are no active streams. */
1661 return;
1662
1663 /* Set timer flag. */
1664 ASMAtomicWriteBool(&pThis->fTimerActive, true);
1665
1666 /* Update current time timestamp. */
1667 uint64_t tsNow = PDMDevHlpTimerGet(pDevIns, pThis->hTimerIO);
1668 pThis->tsTimerIO = tsNow;
1669
1670 /* Arm the timer. */
1671 PDMDevHlpTimerSet(pDevIns, pThis->hTimerIO, tsNow + pThis->cTicksTimerIOInterval);
1672}
1673
1674/**
1675 * This clears fTimerActive if no streams are active, so that the timer won't be
1676 * rearmed then next time it fires.
1677 */
1678static void sb16TimerMaybeStop(PSB16STATE pThis)
1679{
1680 LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive));
1681
1682 if (pThis->cStreamsActive) /* Some streams still active? Bail out. */
1683 return;
1684
1685 /* Clear the timer flag. */
1686 ASMAtomicWriteBool(&pThis->fTimerActive, false);
1687}
1688
1689/**
1690 * @callback_method_impl{FNTMTIMERDEV}
1691 */
1692static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1693{
1694 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1695 Assert(hTimer == pThis->hTimerIO); RT_NOREF(pvUser);
1696
1697 uint64_t cTicksNow = PDMDevHlpTimerGet(pDevIns, hTimer);
1698 bool fIsPlaying = false; /* Whether one or more streams are still playing. */
1699 bool fDoTransfer = false;
1700
1701 pThis->tsTimerIO = cTicksNow;
1702
1703 PSB16DRIVER pDrv;
1704 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1705 {
1706 PPDMAUDIOSTREAM const pStream = pDrv->Out.pStream;
1707 PPDMIAUDIOCONNECTOR const pConn = pDrv->pConnector;
1708 if (pStream && pConn)
1709 {
1710 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1711 if (RT_SUCCESS(rc2))
1712 {
1713 rc2 = pConn->pfnStreamPlay(pConn, pStream, NULL /* cPlayed */);
1714 if (RT_FAILURE(rc2))
1715 {
1716 LogFlowFunc(("%s: Failed playing stream, rc=%Rrc\n", pStream->szName, rc2));
1717 continue;
1718 }
1719
1720 /* Only do the next DMA transfer if we're able to write the remaining data block. */
1721 fDoTransfer = pConn->pfnStreamGetWritable(pConn, pStream) > (unsigned)pThis->left_till_irq;
1722 }
1723
1724 PDMAUDIOSTREAMSTS fStrmSts = pConn->pfnStreamGetStatus(pConn, pStream);
1725 fIsPlaying |= RT_BOOL(fStrmSts & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE));
1726 }
1727 }
1728
1729 bool fTimerActive = ASMAtomicReadBool(&pThis->fTimerActive);
1730 bool fArmTimer = fTimerActive || fIsPlaying;
1731 LogFlowFunc(("fTimerActive=%RTbool, fIsPlaying=%RTbool\n", fTimerActive, fIsPlaying));
1732
1733 if (fDoTransfer)
1734 {
1735 /* Schedule the next transfer. */
1736 PDMDevHlpDMASchedule(pDevIns);
1737
1738 /* Arm the timer at least one more time. */
1739 fArmTimer = true;
1740 }
1741
1742 /*
1743 * Recording.
1744 */
1745 /** @todo Implement recording. */
1746
1747 if (fArmTimer)
1748 {
1749 /* Arm the timer again. */
1750 uint64_t cTicks = pThis->cTicksTimerIOInterval;
1751 /** @todo adjust cTicks down by now much cbOutMin represents. */
1752 PDMDevHlpTimerSet(pDevIns, hTimer, cTicksNow + cTicks);
1753 }
1754}
1755
1756
1757/* -=-=-=-=-=- Streams? -=-=-=-=-=- */
1758
1759/**
1760 * Creates the output PDM audio stream for a specific driver.
1761 *
1762 * @returns IPRT status code.
1763 * @param pCfg Stream configuration to use.
1764 * @param pDrv Driver stream to create PDM stream for.
1765 */
1766static int sb16CreateDrvStream(PPDMAUDIOSTREAMCFG pCfg, PSB16DRIVER pDrv)
1767{
1768 AssertReturn(pCfg->enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
1769 Assert(DrvAudioHlpStreamCfgIsValid(pCfg));
1770
1771 PPDMAUDIOSTREAMCFG pCfgHost = PDMAudioStrmCfgDup(pCfg);
1772 if (!pCfgHost)
1773 return VERR_NO_MEMORY;
1774
1775 LogFunc(("[LUN#%RU8] %s\n", pDrv->uLUN, pCfgHost->szName));
1776
1777 AssertMsg(pDrv->Out.pStream == NULL, ("[LUN#%RU8] Driver stream already present when it must not\n", pDrv->uLUN));
1778
1779 /* Disable pre-buffering for SB16; not needed for that bit of data. */
1780 pCfgHost->Backend.cFramesPreBuffering = 0;
1781
1782 int rc = pDrv->pConnector->pfnStreamCreate(pDrv->pConnector, pCfgHost, pCfg /* pCfgGuest */, &pDrv->Out.pStream);
1783 if (RT_SUCCESS(rc))
1784 {
1785 pDrv->pConnector->pfnStreamRetain(pDrv->pConnector, pDrv->Out.pStream);
1786 LogFlowFunc(("LUN#%RU8: Created output \"%s\", rc=%Rrc\n", pDrv->uLUN, pCfg->szName, rc));
1787 }
1788
1789 PDMAudioStrmCfgFree(pCfgHost);
1790
1791 return rc;
1792}
1793
1794/**
1795 * Destroys the output PDM audio stream of a specific driver.
1796 *
1797 * @param pDrv Driver stream to destroy PDM stream for.
1798 */
1799static void sb16DestroyDrvStreamOut(PSB16DRIVER pDrv)
1800{
1801 AssertPtr(pDrv);
1802
1803 if (pDrv->Out.pStream)
1804 {
1805 pDrv->pConnector->pfnStreamRelease(pDrv->pConnector, pDrv->Out.pStream);
1806
1807 int rc2 = pDrv->pConnector->pfnStreamControl(pDrv->pConnector, pDrv->Out.pStream, PDMAUDIOSTREAMCMD_DISABLE);
1808 AssertRC(rc2);
1809
1810 rc2 = pDrv->pConnector->pfnStreamDestroy(pDrv->pConnector, pDrv->Out.pStream);
1811 AssertRC(rc2);
1812
1813 pDrv->Out.pStream = NULL;
1814 }
1815}
1816
1817/**
1818 * Checks if the output stream needs to be (re-)created and does so if needed.
1819 *
1820 * @return VBox status code.
1821 * @param pDevIns The device instance.
1822 * @param pThis SB16 state.
1823 */
1824static int sb16CheckAndReOpenOut(PPDMDEVINS pDevIns, PSB16STATE pThis)
1825{
1826 AssertPtr(pThis);
1827
1828 int rc = VINF_SUCCESS;
1829
1830 if (pThis->freq > 0)
1831 {
1832 /* At the moment we only have one stream, the output stream. */
1833 PDMAUDIOSTREAMCFG Cfg;
1834 RT_ZERO(Cfg);
1835
1836 Cfg.Props.uHz = pThis->freq;
1837 Cfg.Props.cChannels = 1 << pThis->fmt_stereo;
1838 Cfg.Props.cbSample = pThis->fmt_bits / 8;
1839 Cfg.Props.fSigned = RT_BOOL(pThis->fmt_signed);
1840 Cfg.Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(Cfg.Props.cbSample, Cfg.Props.cChannels);
1841
1842 if (!PDMAudioStrmCfgMatchesProps(&Cfg, &pThis->Out.Cfg.Props))
1843 {
1844 Cfg.enmDir = PDMAUDIODIR_OUT;
1845 Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
1846 Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
1847
1848 strcpy(Cfg.szName, "Output");
1849
1850 sb16CloseOut(pThis);
1851
1852 rc = sb16OpenOut(pDevIns, pThis, &Cfg);
1853 AssertRC(rc);
1854 }
1855 }
1856 else
1857 sb16CloseOut(pThis);
1858
1859 LogFlowFuncLeaveRC(rc);
1860 return rc;
1861}
1862
1863static int sb16OpenOut(PPDMDEVINS pDevIns, PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg)
1864{
1865 LogFlowFuncEnter();
1866 AssertPtr(pThis);
1867 AssertPtr(pCfg);
1868
1869 if (!DrvAudioHlpStreamCfgIsValid(pCfg))
1870 return VERR_INVALID_PARAMETER;
1871
1872 int rc = PDMAudioStrmCfgCopy(&pThis->Out.Cfg, pCfg);
1873 if (RT_SUCCESS(rc))
1874 {
1875 /* Set scheduling hint (if available). */
1876 if (pThis->cTicksTimerIOInterval)
1877 pThis->Out.Cfg.Device.cMsSchedulingHint = 1000 /* ms */
1878 / ( PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerIO)
1879 / RT_MIN(pThis->cTicksTimerIOInterval, 1));
1880
1881 PSB16DRIVER pDrv;
1882 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1883 {
1884 int rc2 = sb16CreateDrvStream(&pThis->Out.Cfg, pDrv);
1885 if (RT_FAILURE(rc2))
1886 LogFunc(("Attaching stream failed with %Rrc\n", rc2));
1887
1888 /* Do not pass failure to rc here, as there might be drivers which aren't
1889 * configured / ready yet. */
1890 }
1891
1892 sb16UpdateVolume(pThis);
1893 }
1894
1895 LogFlowFuncLeaveRC(rc);
1896 return rc;
1897}
1898
1899static void sb16CloseOut(PSB16STATE pThis)
1900{
1901 LogFlowFuncEnter();
1902 AssertPtr(pThis);
1903
1904 PSB16DRIVER pDrv;
1905 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
1906 {
1907 sb16DestroyDrvStreamOut(pDrv);
1908 }
1909
1910 LogFlowFuncLeave();
1911}
1912
1913
1914/* -=-=-=-=-=- Saved state -=-=-=-=-=- */
1915
1916/**
1917 * @callback_method_impl{FNSSMDEVLIVEEXEC}
1918 */
1919static DECLCALLBACK(int) sb16LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1920{
1921 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1922 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1923 RT_NOREF(uPass);
1924
1925 pHlp->pfnSSMPutS32(pSSM, pThis->irqCfg);
1926 pHlp->pfnSSMPutS32(pSSM, pThis->dmaCfg);
1927 pHlp->pfnSSMPutS32(pSSM, pThis->hdmaCfg);
1928 pHlp->pfnSSMPutS32(pSSM, pThis->portCfg);
1929 pHlp->pfnSSMPutS32(pSSM, pThis->verCfg);
1930 return VINF_SSM_DONT_CALL_AGAIN;
1931}
1932
1933/**
1934 * Worker for sb16SaveExec.
1935 */
1936static int sb16Save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PSB16STATE pThis)
1937{
1938 pHlp->pfnSSMPutS32(pSSM, pThis->irq);
1939 pHlp->pfnSSMPutS32(pSSM, pThis->dma);
1940 pHlp->pfnSSMPutS32(pSSM, pThis->hdma);
1941 pHlp->pfnSSMPutS32(pSSM, pThis->port);
1942 pHlp->pfnSSMPutS32(pSSM, pThis->ver);
1943 pHlp->pfnSSMPutS32(pSSM, pThis->in_index);
1944 pHlp->pfnSSMPutS32(pSSM, pThis->out_data_len);
1945 pHlp->pfnSSMPutS32(pSSM, pThis->fmt_stereo);
1946 pHlp->pfnSSMPutS32(pSSM, pThis->fmt_signed);
1947 pHlp->pfnSSMPutS32(pSSM, pThis->fmt_bits);
1948
1949 pHlp->pfnSSMPutU32(pSSM, pThis->fmt);
1950
1951 pHlp->pfnSSMPutS32(pSSM, pThis->dma_auto);
1952 pHlp->pfnSSMPutS32(pSSM, pThis->block_size);
1953 pHlp->pfnSSMPutS32(pSSM, pThis->fifo);
1954 pHlp->pfnSSMPutS32(pSSM, pThis->freq);
1955 pHlp->pfnSSMPutS32(pSSM, pThis->time_const);
1956 pHlp->pfnSSMPutS32(pSSM, pThis->speaker);
1957 pHlp->pfnSSMPutS32(pSSM, pThis->needed_bytes);
1958 pHlp->pfnSSMPutS32(pSSM, pThis->cmd);
1959 pHlp->pfnSSMPutS32(pSSM, pThis->use_hdma);
1960 pHlp->pfnSSMPutS32(pSSM, pThis->highspeed);
1961 pHlp->pfnSSMPutS32(pSSM, pThis->can_write);
1962 pHlp->pfnSSMPutS32(pSSM, pThis->v2x6);
1963
1964 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_param);
1965 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_value);
1966 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_mode);
1967 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_param); /* Bug compatible! */
1968 pHlp->pfnSSMPutMem(pSSM, pThis->csp_regs, 256);
1969 pHlp->pfnSSMPutU8 (pSSM, pThis->csp_index);
1970 pHlp->pfnSSMPutMem(pSSM, pThis->csp_reg83, 4);
1971 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83r);
1972 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83w);
1973
1974 pHlp->pfnSSMPutMem(pSSM, pThis->in2_data, sizeof(pThis->in2_data));
1975 pHlp->pfnSSMPutMem(pSSM, pThis->out_data, sizeof(pThis->out_data));
1976 pHlp->pfnSSMPutU8 (pSSM, pThis->test_reg);
1977 pHlp->pfnSSMPutU8 (pSSM, pThis->last_read_byte);
1978
1979 pHlp->pfnSSMPutS32(pSSM, pThis->nzero);
1980 pHlp->pfnSSMPutS32(pSSM, pThis->left_till_irq);
1981 pHlp->pfnSSMPutS32(pSSM, pThis->dma_running);
1982 pHlp->pfnSSMPutS32(pSSM, pThis->bytes_per_second);
1983 pHlp->pfnSSMPutS32(pSSM, pThis->align);
1984
1985 pHlp->pfnSSMPutS32(pSSM, pThis->mixer_nreg);
1986 return pHlp->pfnSSMPutMem(pSSM, pThis->mixer_regs, 256);
1987}
1988
1989/**
1990 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1991 */
1992static DECLCALLBACK(int) sb16SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1993{
1994 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
1995 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1996
1997 sb16LiveExec(pDevIns, pSSM, 0);
1998 return sb16Save(pHlp, pSSM, pThis);
1999}
2000
2001/**
2002 * Worker for sb16LoadExec.
2003 */
2004static int sb16Load(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PSB16STATE pThis)
2005{
2006 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2007
2008 pHlp->pfnSSMGetS32(pSSM, &pThis->irq);
2009 pHlp->pfnSSMGetS32(pSSM, &pThis->dma);
2010 pHlp->pfnSSMGetS32(pSSM, &pThis->hdma);
2011 pHlp->pfnSSMGetS32(pSSM, &pThis->port);
2012 pHlp->pfnSSMGetS32(pSSM, &pThis->ver);
2013 pHlp->pfnSSMGetS32(pSSM, &pThis->in_index);
2014 pHlp->pfnSSMGetS32(pSSM, &pThis->out_data_len);
2015 pHlp->pfnSSMGetS32(pSSM, &pThis->fmt_stereo);
2016 pHlp->pfnSSMGetS32(pSSM, &pThis->fmt_signed);
2017 pHlp->pfnSSMGetS32(pSSM, &pThis->fmt_bits);
2018
2019 PDMDEVHLP_SSM_GET_ENUM32_RET(pHlp, pSSM, pThis->fmt, PDMAUDIOFMT);
2020
2021 pHlp->pfnSSMGetS32(pSSM, &pThis->dma_auto);
2022 pHlp->pfnSSMGetS32(pSSM, &pThis->block_size);
2023 pHlp->pfnSSMGetS32(pSSM, &pThis->fifo);
2024 pHlp->pfnSSMGetS32(pSSM, &pThis->freq);
2025 pHlp->pfnSSMGetS32(pSSM, &pThis->time_const);
2026 pHlp->pfnSSMGetS32(pSSM, &pThis->speaker);
2027 pHlp->pfnSSMGetS32(pSSM, &pThis->needed_bytes);
2028 pHlp->pfnSSMGetS32(pSSM, &pThis->cmd);
2029 pHlp->pfnSSMGetS32(pSSM, &pThis->use_hdma);
2030 pHlp->pfnSSMGetS32(pSSM, &pThis->highspeed);
2031 pHlp->pfnSSMGetS32(pSSM, &pThis->can_write);
2032 pHlp->pfnSSMGetS32(pSSM, &pThis->v2x6);
2033
2034 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_param);
2035 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_value);
2036 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_mode);
2037 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_param); /* Bug compatible! */
2038 pHlp->pfnSSMGetMem(pSSM, pThis->csp_regs, 256);
2039 pHlp->pfnSSMGetU8 (pSSM, &pThis->csp_index);
2040 pHlp->pfnSSMGetMem(pSSM, pThis->csp_reg83, 4);
2041 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83r);
2042 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83w);
2043
2044 pHlp->pfnSSMGetMem(pSSM, pThis->in2_data, sizeof(pThis->in2_data));
2045 pHlp->pfnSSMGetMem(pSSM, pThis->out_data, sizeof(pThis->out_data));
2046 pHlp->pfnSSMGetU8 (pSSM, &pThis->test_reg);
2047 pHlp->pfnSSMGetU8 (pSSM, &pThis->last_read_byte);
2048
2049 pHlp->pfnSSMGetS32(pSSM, &pThis->nzero);
2050 pHlp->pfnSSMGetS32(pSSM, &pThis->left_till_irq);
2051 pHlp->pfnSSMGetS32(pSSM, &pThis->dma_running);
2052 pHlp->pfnSSMGetS32(pSSM, &pThis->bytes_per_second);
2053 pHlp->pfnSSMGetS32(pSSM, &pThis->align);
2054
2055 int32_t mixer_nreg = 0;
2056 int rc = pHlp->pfnSSMGetS32(pSSM, &mixer_nreg);
2057 AssertRCReturn(rc, rc);
2058 pThis->mixer_nreg = (uint8_t)mixer_nreg;
2059 rc = pHlp->pfnSSMGetMem(pSSM, pThis->mixer_regs, 256);
2060 AssertRCReturn(rc, rc);
2061
2062 if (pThis->dma_running)
2063 {
2064 sb16CheckAndReOpenOut(pDevIns, pThis);
2065 sb16Control(pDevIns, pThis, 1);
2066 sb16SpeakerControl(pThis, pThis->speaker);
2067 }
2068
2069 /* Update the master (mixer) and PCM out volumes. */
2070 sb16UpdateVolume(pThis);
2071
2072 return VINF_SUCCESS;
2073}
2074
2075/**
2076 * @callback_method_impl{FNSSMDEVLOADEXEC}
2077 */
2078static DECLCALLBACK(int) sb16LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2079{
2080 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2081 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2082
2083 AssertMsgReturn( uVersion == SB16_SAVE_STATE_VERSION
2084 || uVersion == SB16_SAVE_STATE_VERSION_VBOX_30,
2085 ("%u\n", uVersion),
2086 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2087 if (uVersion > SB16_SAVE_STATE_VERSION_VBOX_30)
2088 {
2089 int32_t irq;
2090 pHlp->pfnSSMGetS32(pSSM, &irq);
2091 int32_t dma;
2092 pHlp->pfnSSMGetS32(pSSM, &dma);
2093 int32_t hdma;
2094 pHlp->pfnSSMGetS32(pSSM, &hdma);
2095 int32_t port;
2096 pHlp->pfnSSMGetS32(pSSM, &port);
2097 int32_t ver;
2098 int rc = pHlp->pfnSSMGetS32(pSSM, &ver);
2099 AssertRCReturn (rc, rc);
2100
2101 if ( irq != pThis->irqCfg
2102 || dma != pThis->dmaCfg
2103 || hdma != pThis->hdmaCfg
2104 || port != pThis->portCfg
2105 || ver != pThis->verCfg)
2106 {
2107 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
2108 N_("config changed: irq=%x/%x dma=%x/%x hdma=%x/%x port=%x/%x ver=%x/%x (saved/config)"),
2109 irq, pThis->irqCfg,
2110 dma, pThis->dmaCfg,
2111 hdma, pThis->hdmaCfg,
2112 port, pThis->portCfg,
2113 ver, pThis->verCfg);
2114 }
2115 }
2116
2117 if (uPass != SSM_PASS_FINAL)
2118 return VINF_SUCCESS;
2119
2120 return sb16Load(pDevIns, pSSM, pThis);
2121}
2122
2123
2124/* -=-=-=-=-=- IBase -=-=-=-=-=- */
2125
2126/**
2127 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2128 */
2129static DECLCALLBACK(void *) sb16QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2130{
2131 PSB16STATE pThis = RT_FROM_MEMBER(pInterface, SB16STATE, IBase);
2132 Assert(&pThis->IBase == pInterface);
2133
2134 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2135 return NULL;
2136}
2137
2138
2139/* -=-=-=-=-=- Device -=-=-=-=-=- */
2140
2141/**
2142 * Attach command, internal version.
2143 *
2144 * This is called to let the device attach to a driver for a specified LUN
2145 * during runtime. This is not called during VM construction, the device
2146 * constructor has to attach to all the available drivers.
2147 *
2148 * @returns VBox status code.
2149 * @param pThis SB16 state.
2150 * @param uLUN The logical unit which is being detached.
2151 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2152 * @param ppDrv Attached driver instance on success. Optional.
2153 */
2154static int sb16AttachInternal(PSB16STATE pThis, unsigned uLUN, uint32_t fFlags, PSB16DRIVER *ppDrv)
2155{
2156 RT_NOREF(fFlags);
2157
2158 /*
2159 * Attach driver.
2160 */
2161 PSB16DRIVER pDrv = (PSB16DRIVER)RTMemAllocZ(sizeof(SB16DRIVER));
2162 AssertReturn(pDrv, VERR_NO_MEMORY);
2163 RTStrPrintf(pDrv->szDesc, sizeof(pDrv->szDesc), "Audio driver port (SB16) for LUN #%u", uLUN);
2164
2165 PPDMIBASE pDrvBase;
2166 int rc = PDMDevHlpDriverAttach(pThis->pDevInsR3, uLUN, &pThis->IBase, &pDrvBase, pDrv->szDesc);
2167 if (RT_SUCCESS(rc))
2168 {
2169 pDrv->pDrvBase = pDrvBase;
2170 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
2171 AssertMsg(pDrv->pConnector != NULL, ("Configuration error: LUN #%u has no host audio interface, rc=%Rrc\n", uLUN, rc));
2172 pDrv->pSB16State = pThis;
2173 pDrv->uLUN = uLUN;
2174
2175 /*
2176 * For now we always set the driver at LUN 0 as our primary
2177 * host backend. This might change in the future.
2178 */
2179 if (pDrv->uLUN == 0)
2180 pDrv->fFlags |= PDMAUDIODRVFLAGS_PRIMARY;
2181
2182 LogFunc(("LUN#%u: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->fFlags));
2183
2184 /* Attach to driver list if not attached yet. */
2185 if (!pDrv->fAttached)
2186 {
2187 RTListAppend(&pThis->lstDrv, &pDrv->Node);
2188 pDrv->fAttached = true;
2189 }
2190
2191 if (ppDrv)
2192 *ppDrv = pDrv;
2193 }
2194 else
2195 {
2196 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2197 LogFunc(("No attached driver for LUN #%u\n", uLUN));
2198 RTMemFree(pDrv);
2199 }
2200
2201 LogFunc(("iLUN=%u, fFlags=0x%x, rc=%Rrc\n", uLUN, fFlags, rc));
2202 return rc;
2203}
2204
2205/**
2206 * Detach command, internal version.
2207 *
2208 * This is called to let the device detach from a driver for a specified LUN
2209 * during runtime.
2210 *
2211 * @returns VBox status code.
2212 * @param pDrv Driver to detach device from.
2213 */
2214static int sb16DetachInternal(PSB16DRIVER pDrv)
2215{
2216 sb16DestroyDrvStreamOut(pDrv);
2217 RTListNodeRemove(&pDrv->Node);
2218 LogFunc(("uLUN=%u\n", pDrv->uLUN));
2219 return VINF_SUCCESS;
2220}
2221
2222/**
2223 * @interface_method_impl{PDMDEVREG,pfnAttach}
2224 */
2225static DECLCALLBACK(int) sb16Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2226{
2227 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2228
2229 LogFunc(("iLUN=%u, fFlags=0x%x\n", iLUN, fFlags));
2230
2231 PSB16DRIVER pDrv;
2232 int rc2 = sb16AttachInternal(pThis, iLUN, fFlags, &pDrv);
2233 if (RT_SUCCESS(rc2))
2234 rc2 = sb16CreateDrvStream(&pThis->Out.Cfg, pDrv);
2235
2236 return VINF_SUCCESS;
2237}
2238
2239/**
2240 * @interface_method_impl{PDMDEVREG,pfnDetach}
2241 */
2242static DECLCALLBACK(void) sb16Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2243{
2244 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2245 RT_NOREF(fFlags);
2246
2247 LogFunc(("iLUN=%u, fFlags=0x%x\n", iLUN, fFlags));
2248
2249 PSB16DRIVER pDrv, pDrvNext;
2250 RTListForEachSafe(&pThis->lstDrv, pDrv, pDrvNext, SB16DRIVER, Node)
2251 {
2252 if (pDrv->uLUN == iLUN)
2253 {
2254 int rc2 = sb16DetachInternal(pDrv);
2255 if (RT_SUCCESS(rc2))
2256 {
2257 RTMemFree(pDrv);
2258 pDrv = NULL;
2259 }
2260 break;
2261 }
2262 }
2263}
2264
2265/**
2266 * Replaces a driver with a the NullAudio drivers.
2267 *
2268 * @returns VBox status code.
2269 * @param pThis Device instance.
2270 * @param iLun The logical unit which is being replaced.
2271 */
2272static int sb16ReconfigLunWithNullAudio(PSB16STATE pThis, unsigned iLun)
2273{
2274 int rc = PDMDevHlpDriverReconfigure2(pThis->pDevInsR3, iLun, "AUDIO", "NullAudio");
2275 if (RT_SUCCESS(rc))
2276 rc = sb16AttachInternal(pThis, iLun, 0 /* fFlags */, NULL /* ppDrv */);
2277 LogFunc(("pThis=%p, iLun=%u, rc=%Rrc\n", pThis, iLun, rc));
2278 return rc;
2279}
2280
2281/**
2282 * @interface_method_impl{PDMDEVREG,pfnReset}
2283 */
2284static DECLCALLBACK(void) sb16DevReset(PPDMDEVINS pDevIns)
2285{
2286 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2287
2288 /* Bring back the device to initial state, and especially make
2289 * sure there's no interrupt or DMA activity.
2290 */
2291 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
2292
2293 pThis->mixer_regs[0x82] = 0;
2294 pThis->csp_regs[5] = 1;
2295 pThis->csp_regs[9] = 0xf8;
2296
2297 pThis->dma_auto = 0;
2298 pThis->in_index = 0;
2299 pThis->out_data_len = 0;
2300 pThis->left_till_irq = 0;
2301 pThis->needed_bytes = 0;
2302 pThis->block_size = -1;
2303 pThis->nzero = 0;
2304 pThis->highspeed = 0;
2305 pThis->v2x6 = 0;
2306 pThis->cmd = -1;
2307
2308 sb16MixerReset(pThis);
2309 sb16SpeakerControl(pThis, 0);
2310 sb16Control(pDevIns, pThis, 0);
2311 sb16CmdResetLegacy(pThis);
2312}
2313
2314/**
2315 * Powers off the device.
2316 *
2317 * @param pDevIns Device instance to power off.
2318 */
2319static DECLCALLBACK(void) sb16PowerOff(PPDMDEVINS pDevIns)
2320{
2321 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2322
2323 LogRel2(("SB16: Powering off ...\n"));
2324
2325 PSB16DRIVER pDrv;
2326 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
2327 {
2328 sb16DestroyDrvStreamOut(pDrv);
2329 }
2330}
2331
2332/**
2333 * @interface_method_impl{PDMDEVREG,pfnDestruct}
2334 */
2335static DECLCALLBACK(int) sb16Destruct(PPDMDEVINS pDevIns)
2336{
2337 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); /* this shall come first */
2338 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2339
2340 LogFlowFuncEnter();
2341
2342 PSB16DRIVER pDrv;
2343 while (!RTListIsEmpty(&pThis->lstDrv))
2344 {
2345 pDrv = RTListGetFirst(&pThis->lstDrv, SB16DRIVER, Node);
2346
2347 RTListNodeRemove(&pDrv->Node);
2348 RTMemFree(pDrv);
2349 }
2350
2351 return VINF_SUCCESS;
2352}
2353
2354/**
2355 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
2356 */
2357static DECLCALLBACK(int) sb16Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2358{
2359 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* this shall come first */
2360 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
2361 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2362 RT_NOREF(iInstance);
2363
2364 Assert(iInstance == 0);
2365
2366 /*
2367 * Initialize the data so sb16Destruct runs without a hitch if we return early.
2368 */
2369 pThis->pDevInsR3 = pDevIns;
2370 pThis->IBase.pfnQueryInterface = sb16QueryInterface;
2371 pThis->cmd = -1;
2372
2373 pThis->csp_regs[5] = 1;
2374 pThis->csp_regs[9] = 0xf8;
2375
2376 RTListInit(&pThis->lstDrv);
2377
2378 /*
2379 * Validate and read config data.
2380 */
2381 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "IRQ|DMA|DMA16|Port|Version|TimerHz", "");
2382 int rc = pHlp->pfnCFGMQuerySIntDef(pCfg, "IRQ", &pThis->irq, 5);
2383 if (RT_FAILURE(rc))
2384 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"IRQ\" value"));
2385 pThis->irqCfg = pThis->irq;
2386
2387 rc = pHlp->pfnCFGMQuerySIntDef(pCfg, "DMA", &pThis->dma, 1);
2388 if (RT_FAILURE(rc))
2389 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"DMA\" value"));
2390 pThis->dmaCfg = pThis->dma;
2391
2392 rc = pHlp->pfnCFGMQuerySIntDef(pCfg, "DMA16", &pThis->hdma, 5);
2393 if (RT_FAILURE(rc))
2394 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"DMA16\" value"));
2395 pThis->hdmaCfg = pThis->hdma;
2396
2397 RTIOPORT Port;
2398 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &Port, 0x220);
2399 if (RT_FAILURE(rc))
2400 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"Port\" value"));
2401 pThis->port = Port;
2402 pThis->portCfg = Port;
2403
2404 uint16_t u16Version;
2405 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "Version", &u16Version, 0x0405);
2406 if (RT_FAILURE(rc))
2407 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Failed to get the \"Version\" value"));
2408 pThis->ver = u16Version;
2409 pThis->verCfg = u16Version;
2410
2411 uint16_t uTimerHz;
2412 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "TimerHz", &uTimerHz, 100 /* Hz */);
2413 if (RT_FAILURE(rc))
2414 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: failed to read Hertz (Hz) rate as unsigned integer"));
2415 if (uTimerHz == 0)
2416 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: TimerHz is zero"));
2417 if (uTimerHz > 2048)
2418 return PDMDEV_SET_ERROR(pDevIns, rc, N_("SB16 configuration error: Max TimerHz value is 2048."));
2419
2420 /*
2421 * Setup the mixer now that we've got the irq and dma channel numbers.
2422 */
2423 pThis->mixer_regs[0x80] = magic_of_irq(pThis->irq);
2424 pThis->mixer_regs[0x81] = (1 << pThis->dma) | (1 << pThis->hdma);
2425 pThis->mixer_regs[0x82] = 2 << 5;
2426
2427 sb16MixerReset(pThis);
2428
2429 /*
2430 * Create timers.
2431 */
2432 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIRQ, pThis,
2433 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IRQ", &pThis->hTimerIRQ);
2434 AssertRCReturn(rc, rc);
2435 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, pThis,
2436 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IO", &pThis->hTimerIO);
2437 AssertRCReturn(rc, rc);
2438 pThis->cTicksTimerIOInterval = PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerIO) / uTimerHz;
2439 pThis->tsTimerIO = PDMDevHlpTimerGet(pDevIns, pThis->hTimerIO);
2440 LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTicksTimerIOInterval, uTimerHz));
2441
2442 /*
2443 * Register I/O and DMA.
2444 */
2445 static const IOMIOPORTDESC s_aAllDescs[] =
2446 {
2447 { "FM Music Status Port", "FM Music Register Address Port", NULL, NULL }, // 00h
2448 { NULL, "FM Music Data Port", NULL, NULL }, // 01h
2449 { "Advanced FM Music Status Port", "Advanced FM Music Register Address Port", NULL, NULL }, // 02h
2450 { NULL, "Advanced FM Music Data Port", NULL, NULL }, // 03h
2451 { NULL, "Mixer chip Register Address Port", NULL, NULL }, // 04h
2452 { "Mixer chip Data Port", NULL, NULL, NULL }, // 05h
2453 { NULL, "DSP Reset", NULL, NULL }, // 06h
2454 { "Unused7", "Unused7", NULL, NULL }, // 07h
2455 { "FM Music Status Port", "FM Music Register Port", NULL, NULL }, // 08h
2456 { NULL, "FM Music Data Port", NULL, NULL }, // 09h
2457 { "DSP Read Data Port", NULL, NULL, NULL }, // 0Ah
2458 { "UnusedB", "UnusedB", NULL, NULL }, // 0Bh
2459 { "DSP Write-Buffer Status", "DSP Write Command/Data", NULL, NULL }, // 0Ch
2460 { "UnusedD", "UnusedD", NULL, NULL }, // 0Dh
2461 { "DSP Read-Buffer Status", NULL, NULL, NULL }, // 0Eh
2462 { "IRQ16ACK", NULL, NULL, NULL }, // 0Fh
2463 { "CD-ROM Data Register", "CD-ROM Command Register", NULL, NULL }, // 10h
2464 { "CD-ROM Status Register", NULL, NULL, NULL }, // 11h
2465 { NULL, "CD-ROM Reset Register", NULL, NULL }, // 12h
2466 { NULL, "CD-ROM Enable Register", NULL, NULL }, // 13h
2467 { NULL, NULL, NULL, NULL },
2468 };
2469
2470 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->port + 0x04 /*uPort*/, 2 /*cPorts*/,
2471 sb16IoPortMixerWrite, sb16IoPortMixerRead,
2472 "SB16 - Mixer", &s_aAllDescs[4], &pThis->hIoPortsMixer);
2473 AssertRCReturn(rc, rc);
2474 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->port + 0x06 /*uPort*/, 10 /*cPorts*/,
2475 sb16IoPortDspWrite, sb16IoPortDspRead,
2476 "SB16 - DSP", &s_aAllDescs[6], &pThis->hIoPortsDsp);
2477 AssertRCReturn(rc, rc);
2478
2479 rc = PDMDevHlpDMARegister(pDevIns, pThis->hdma, sb16DMARead, pThis);
2480 AssertRCReturn(rc, rc);
2481 rc = PDMDevHlpDMARegister(pDevIns, pThis->dma, sb16DMARead, pThis);
2482 AssertRCReturn(rc, rc);
2483
2484 pThis->can_write = 1;
2485
2486 /*
2487 * Register Saved state.
2488 */
2489 rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec);
2490 AssertRCReturn(rc, rc);
2491
2492 /*
2493 * Attach drivers. We ASSUME they are configured consecutively without any
2494 * gaps, so we stop when we hit the first LUN w/o a driver configured.
2495 */
2496 for (unsigned iLun = 0; ; iLun++)
2497 {
2498 AssertBreak(iLun < UINT8_MAX);
2499 LogFunc(("Trying to attach driver for LUN#%u ...\n", iLun));
2500 rc = sb16AttachInternal(pThis, iLun, 0 /* fFlags */, NULL /* ppDrv */);
2501 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2502 {
2503 LogFunc(("cLUNs=%u\n", iLun));
2504 break;
2505 }
2506 if (rc == VERR_AUDIO_BACKEND_INIT_FAILED)
2507 {
2508 sb16ReconfigLunWithNullAudio(pThis, iLun); /* Pretend attaching to the NULL audio backend will never fail. */
2509 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2510 N_("Host audio backend initialization has failed. "
2511 "Selecting the NULL audio backend with the consequence that no sound is audible"));
2512 }
2513 else
2514 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("LUN#%u: rc=%Rrc\n", iLun, rc), rc);
2515 }
2516
2517 sb16CmdResetLegacy(pThis);
2518
2519#ifdef VBOX_WITH_AUDIO_SB16_ONETIME_INIT
2520 PSB16DRIVER pDrv, pNext;
2521 RTListForEachSafe(&pThis->lstDrv, pDrv, pNext, SB16DRIVER, Node)
2522 {
2523 /*
2524 * Only primary drivers are critical for the VM to run. Everything else
2525 * might not worth showing an own error message box in the GUI.
2526 */
2527 if (!(pDrv->fFlags & PDMAUDIODRVFLAGS_PRIMARY))
2528 continue;
2529
2530 /** @todo No input streams available for SB16 yet. */
2531 if (!pDrv->Out.pStream)
2532 continue;
2533
2534 PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
2535 AssertPtr(pCon);
2536 if ( pCon == NULL /* paranoia */
2537 || !(pCon->pfnStreamGetStatus(pCon, pDrv->Out.pStream) & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED))
2538 {
2539 LogRel(("SB16: Falling back to NULL backend (no sound audible)\n"));
2540
2541 sb16CmdResetLegacy(pThis);
2542 sb16ReconfigLunWithNullAudio(pThis, pDrv->uLUN);
2543 pDrv = NULL; /* no longer valid */
2544
2545 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2546 N_("No audio devices could be opened. "
2547 "Selecting the NULL audio backend with the consequence that no sound is audible"));
2548 }
2549 }
2550#endif
2551
2552 /*
2553 * Delete debug file.
2554 */
2555#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
2556 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "sb16WriteAudio.pcm");
2557#endif
2558
2559 return VINF_SUCCESS;
2560}
2561
2562const PDMDEVREG g_DeviceSB16 =
2563{
2564 /* .u32Version = */ PDM_DEVREG_VERSION,
2565 /* .uReserved0 = */ 0,
2566 /* .szName = */ "sb16",
2567 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
2568 /* .fClass = */ PDM_DEVREG_CLASS_AUDIO,
2569 /* .cMaxInstances = */ 1,
2570 /* .uSharedVersion = */ 42,
2571 /* .cbInstanceShared = */ sizeof(SB16STATE),
2572 /* .cbInstanceCC = */ 0,
2573 /* .cbInstanceRC = */ 0,
2574 /* .cMaxPciDevices = */ 0,
2575 /* .cMaxMsixVectors = */ 0,
2576 /* .pszDescription = */ "Sound Blaster 16 Controller",
2577#if defined(IN_RING3)
2578 /* .pszRCMod = */ "",
2579 /* .pszR0Mod = */ "",
2580 /* .pfnConstruct = */ sb16Construct,
2581 /* .pfnDestruct = */ sb16Destruct,
2582 /* .pfnRelocate = */ NULL,
2583 /* .pfnMemSetup = */ NULL,
2584 /* .pfnPowerOn = */ NULL,
2585 /* .pfnReset = */ sb16DevReset,
2586 /* .pfnSuspend = */ NULL,
2587 /* .pfnResume = */ NULL,
2588 /* .pfnAttach = */ sb16Attach,
2589 /* .pfnDetach = */ sb16Detach,
2590 /* .pfnQueryInterface = */ NULL,
2591 /* .pfnInitComplete = */ NULL,
2592 /* .pfnPowerOff = */ sb16PowerOff,
2593 /* .pfnSoftReset = */ 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_RING0)
2603 /* .pfnEarlyConstruct = */ NULL,
2604 /* .pfnConstruct = */ NULL,
2605 /* .pfnDestruct = */ NULL,
2606 /* .pfnFinalDestruct = */ NULL,
2607 /* .pfnRequest = */ NULL,
2608 /* .pfnReserved0 = */ NULL,
2609 /* .pfnReserved1 = */ NULL,
2610 /* .pfnReserved2 = */ NULL,
2611 /* .pfnReserved3 = */ NULL,
2612 /* .pfnReserved4 = */ NULL,
2613 /* .pfnReserved5 = */ NULL,
2614 /* .pfnReserved6 = */ NULL,
2615 /* .pfnReserved7 = */ NULL,
2616#elif defined(IN_RC)
2617 /* .pfnConstruct = */ NULL,
2618 /* .pfnReserved0 = */ NULL,
2619 /* .pfnReserved1 = */ NULL,
2620 /* .pfnReserved2 = */ NULL,
2621 /* .pfnReserved3 = */ NULL,
2622 /* .pfnReserved4 = */ NULL,
2623 /* .pfnReserved5 = */ NULL,
2624 /* .pfnReserved6 = */ NULL,
2625 /* .pfnReserved7 = */ NULL,
2626#else
2627# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2628#endif
2629 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2630};
2631
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette