VirtualBox

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

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

DevSB16: Converted timers. bugref:9218

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