VirtualBox

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

Last change on this file since 86009 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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