VirtualBox

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

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

DevSB16: cleanups. bugref:9218

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