VirtualBox

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

最後變更 在這個檔案從76841是 76841,由 vboxsync 提交於 6 年 前

Audio/SB16: Removed dead code (VBOX_WITH_AUDIO_SB16_CALLBACKS).

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

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