VirtualBox

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

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

Audio/SB16: Fixed compilation when VBOX_WITH_AUDIO_HDA_ONETIME_INIT is defined.

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