VirtualBox

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

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

vmm/pdmaudioifs.h: The prefix 'cf' reads 'count-of-flags', so if you want 'count-of-frames' in a non-confusing way you have to spell it out. The prefix 'cx' means 'count-of-x-coordinates', i.e. width in pixels, and is not in any way suited to combine 'cb' and 'cFrames' in this context (PDM). bugref:9218

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