VirtualBox

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

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

Audio/SB16: Now also is using our internal software mixer [SCM fix].

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