VirtualBox

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

最後變更 在這個檔案從52052是 50686,由 vboxsync 提交於 11 年 前

src/VBox/Devices/Audio, src/VBox/Main/src-client, include/VBox/vmm:

src/VBox/Devices/Audio: part of restructuring of audio code. Devices files correspondin to Hda, AC97 and SB16 audio. The structure of files have been modifed as per PDM specs. The modified code is under #ifdef VBOX_WITH_PDM_AUDIO_DRIVER

src/VBox/Main/src-client: Driver for the VRDE that interacts with DrvAudio. Enhancement of the CFGM tree for audio.

Config.kmk : addition of one configuration parameter that will control whether new audio code is disabled or enabled. "VBOX_WITH_PDM_AUDIO_DRIVER"

pdmaudioifs.h: common header file between Device , Intermediate audio driver and Backends specific to audio.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 54.0 KB
 
1/* $Id: DevSB16.cpp 50686 2014-03-04 19:21:18Z vboxsync $ */
2/** @file
3 * DevSB16 - VBox SB16 Audio Controller.
4 *
5 * (r3917 sb16.c)
6 *
7 * @todo hiccups on NT4 and Win98.
8 */
9
10/*
11 * QEMU Soundblaster 16 emulation
12 *
13 * Copyright (c) 2003-2005 Vassili Karpov (malc)
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a copy
16 * of this software and associated documentation files (the "Software"), to deal
17 * in the Software without restriction, including without limitation the rights
18 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 * copies of the Software, and to permit persons to whom the Software is
20 * furnished to do so, subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 * THE SOFTWARE.
32 */
33
34#define LOG_GROUP LOG_GROUP_DEV_AUDIO
35#include <VBox/vmm/pdmdev.h>
36#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
37#include <VBox/vmm/pdmaudioifs.h>
38#endif
39#include <iprt/assert.h>
40#include <iprt/string.h>
41#include <iprt/uuid.h>
42#include "vl_vbox.h"
43
44#ifndef VBOX_WITH_PDM_AUDIO_DRIVER
45extern "C" {
46#include "audio.h"
47}
48#endif
49
50#ifndef VBOX
51#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0])))
52#else /* VBOX */
53/** Current saved state version. */
54#define SB16_SAVE_STATE_VERSION 2
55/** The version used in VirtualBox version 3.0 and earlier. This didn't include
56 * the config dump. */
57#define SB16_SAVE_STATE_VERSION_VBOX_30 1
58#endif /* VBOX */
59
60#ifndef VBOX
61#define IO_READ_PROTO(name) \
62 uint32_t name (void *opaque, uint32_t nport)
63#define IO_WRITE_PROTO(name) \
64 void name (void *opaque, uint32_t nport, uint32_t val)
65#else /* VBOX */
66#define IO_READ_PROTO(name) \
67 DECLCALLBACK(int) name (PPDMDEVINS pDevIns, void *opaque, \
68 RTIOPORT nport, uint32_t *pu32, unsigned cb)
69
70#define IO_WRITE_PROTO(name) \
71 DECLCALLBACK(int) name (PPDMDEVINS pDevIns, void *opaque, \
72 RTIOPORT nport, uint32_t val, unsigned cb)
73#endif /* VBOX */
74
75static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
76
77#ifndef VBOX
78static struct {
79 int ver_lo;
80 int ver_hi;
81 int irq;
82 int dma;
83 int hdma;
84 int port;
85} conf = {5, 4, 5, 1, 5, 0x220};
86#endif /* !VBOX */
87
88typedef struct SB16State {
89#ifdef VBOX
90 /** Pointer to the device instance. */
91 PPDMDEVINSR3 pDevIns;
92# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
93 /** Pointer to the connector of the attached audio driver. */
94 PPDMIAUDIOCONNECTOR pDrv;
95# endif
96#endif
97#ifndef VBOX
98 qemu_irq *pic;
99#endif
100#ifndef VBOX_WITH_PDM_AUDIO_DRIVER
101 QEMUSoundCard card;
102#endif
103#ifdef VBOX /* lazy bird */
104 int irqCfg;
105 int dmaCfg;
106 int hdmaCfg;
107 int portCfg;
108 int verCfg;
109#endif
110 int irq;
111 int dma;
112 int hdma;
113 int port;
114 int ver;
115
116 int in_index;
117 int out_data_len;
118 int fmt_stereo;
119 int fmt_signed;
120 int fmt_bits;
121 audfmt_e fmt;
122 int dma_auto;
123 int block_size;
124 int fifo;
125 int freq;
126 int time_const;
127 int speaker;
128 int needed_bytes;
129 int cmd;
130 int use_hdma;
131 int highspeed;
132 int can_write;
133
134 int v2x6;
135
136 uint8_t csp_param;
137 uint8_t csp_value;
138 uint8_t csp_mode;
139 uint8_t csp_regs[256];
140 uint8_t csp_index;
141 uint8_t csp_reg83[4];
142 int csp_reg83r;
143 int csp_reg83w;
144
145 uint8_t in2_data[10];
146 uint8_t out_data[50];
147 uint8_t test_reg;
148 uint8_t last_read_byte;
149 int nzero;
150
151 int left_till_irq;
152
153 int dma_running;
154 int bytes_per_second;
155 int align;
156 int audio_free;
157#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
158 PPDMGSTVOICEOUT voice;
159#else
160 SWVoiceOut *voice;
161#endif
162
163#ifndef VBOX
164 QEMUTimer *aux_ts;
165#else
166 PTMTIMER pTimer;
167 PPDMIBASE pDrvBase;
168 /** LUN\#0: Base interface. */
169 PDMIBASE IBase;
170#endif
171 /* mixer state */
172 int mixer_nreg;
173 uint8_t mixer_regs[256];
174} SB16State;
175
176static void SB_audio_callback (void *opaque, int free);
177
178static int magic_of_irq (int irq)
179{
180 switch (irq) {
181 case 5:
182 return 2;
183 case 7:
184 return 4;
185 case 9:
186 return 1;
187 case 10:
188 return 8;
189 default:
190 LogFlow(("SB16: bad irq %d\n", irq));
191 return 2;
192 }
193}
194
195static int irq_of_magic (int magic)
196{
197 switch (magic) {
198 case 1:
199 return 9;
200 case 2:
201 return 5;
202 case 4:
203 return 7;
204 case 8:
205 return 10;
206 default:
207 LogFlow(("SB16: bad irq magic %d\n", magic));
208 return -1;
209 }
210}
211
212#if 0
213static void log_dsp (SB16State *dsp)
214{
215 ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
216 dsp->fmt_stereo ? "Stereo" : "Mono",
217 dsp->fmt_signed ? "Signed" : "Unsigned",
218 dsp->fmt_bits,
219 dsp->dma_auto ? "Auto" : "Single",
220 dsp->block_size,
221 dsp->freq,
222 dsp->time_const,
223 dsp->speaker);
224}
225#endif
226
227static void speaker (SB16State *s, int on)
228{
229 s->speaker = on;
230 /* AUD_enable (s->voice, on); */
231}
232
233static void control (SB16State *s, int hold)
234{
235 int dma = s->use_hdma ? s->hdma : s->dma;
236 s->dma_running = hold;
237
238 LogFlow(("SB16: hold %d high %d dma %d\n", hold, s->use_hdma, dma));
239
240#ifndef VBOX
241 if (hold) {
242 DMA_hold_DREQ (dma);
243 AUD_set_active_out (s->voice, 1);
244 }
245 else {
246 DMA_release_DREQ (dma);
247 AUD_set_active_out (s->voice, 0);
248 }
249#else /* VBOX */
250 if (hold)
251 {
252 PDMDevHlpDMASetDREQ (s->pDevIns, dma, 1);
253 PDMDevHlpDMASchedule (s->pDevIns);
254#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
255 s->pDrv->pfnEnableOut(s->pDrv, s->voice, 1);
256#else
257 AUD_set_active_out (s->voice, 1);
258#endif
259 }
260 else
261 {
262 PDMDevHlpDMASetDREQ (s->pDevIns, dma, 0);
263#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
264 s->pDrv->pfnEnableOut(s->pDrv, s->voice, 0);
265#else
266 AUD_set_active_out (s->voice, 0);
267#endif
268 }
269#endif /* VBOX */
270}
271
272#ifndef VBOX
273static void aux_timer (void *opaque)
274{
275 SB16State *s = opaque;
276 s->can_write = 1;
277 qemu_irq_raise (s->pic[s->irq]);
278}
279#else /* VBOX */
280static DECLCALLBACK(void) sb16Timer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvThis)
281{
282 SB16State *s = (SB16State *)pvThis;
283 s->can_write = 1;
284 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 1);
285}
286#endif /* VBOX */
287
288#define DMA8_AUTO 1
289#define DMA8_HIGH 2
290
291static void continue_dma8 (SB16State *s)
292{
293#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
294 int rc;
295#endif
296 if (s->freq > 0) {
297
298 s->audio_free = 0;
299
300
301#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
302 rc = s->pDrv->pfnOpenOut(s->pDrv, &s->voice, "sb16", s, SB_audio_callback, s->freq, 1 << s->fmt_stereo,
303 s->fmt, 0);
304#else
305 audsettings_t as;
306 as.freq = s->freq;
307 as.nchannels = 1 << s->fmt_stereo;
308 as.fmt = s->fmt;
309 as.endianness = 0;
310 s->voice = AUD_open_out (
311 &s->card,
312 s->voice,
313 "sb16",
314 s,
315 SB_audio_callback,
316 &as
317 );
318#endif
319 }
320
321 control (s, 1);
322}
323
324static void dma_cmd8 (SB16State *s, int mask, int dma_len)
325{
326 s->fmt = AUD_FMT_U8;
327 s->use_hdma = 0;
328 s->fmt_bits = 8;
329 s->fmt_signed = 0;
330 s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0;
331 if (-1 == s->time_const) {
332 if (s->freq <= 0)
333 s->freq = 11025;
334 }
335 else {
336 int tmp = (256 - s->time_const);
337 s->freq = (1000000 + (tmp / 2)) / tmp;
338 }
339
340 if (dma_len != -1) {
341 s->block_size = dma_len << s->fmt_stereo;
342 }
343 else {
344 /* This is apparently the only way to make both Act1/PL
345 and SecondReality/FC work
346
347 Act1 sets block size via command 0x48 and it's an odd number
348 SR does the same with even number
349 Both use stereo, and Creatives own documentation states that
350 0x48 sets block size in bytes less one.. go figure */
351 s->block_size &= ~s->fmt_stereo;
352 }
353
354 s->freq >>= s->fmt_stereo;
355 s->left_till_irq = s->block_size;
356 s->bytes_per_second = (s->freq << s->fmt_stereo);
357 /* s->highspeed = (mask & DMA8_HIGH) != 0; */
358 s->dma_auto = (mask & DMA8_AUTO) != 0;
359 s->align = (1 << s->fmt_stereo) - 1;
360
361 if (s->block_size & s->align) {
362 LogFlow(("SB16: warning: misaligned block size %d, alignment %d\n",
363 s->block_size, s->align + 1));
364 }
365
366 LogFlow(("SB16: freq %d, stereo %d, sign %d, bits %d, "
367 "dma %d, auto %d, fifo %d, high %d\n",
368 s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
369 s->block_size, s->dma_auto, s->fifo, s->highspeed));
370
371 continue_dma8 (s);
372 speaker (s, 1);
373}
374
375static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
376{
377 s->use_hdma = cmd < 0xc0;
378 s->fifo = (cmd >> 1) & 1;
379 s->dma_auto = (cmd >> 2) & 1;
380 s->fmt_signed = (d0 >> 4) & 1;
381 s->fmt_stereo = (d0 >> 5) & 1;
382
383 switch (cmd >> 4) {
384 case 11:
385 s->fmt_bits = 16;
386 break;
387
388 case 12:
389 s->fmt_bits = 8;
390 break;
391 }
392
393 if (-1 != s->time_const) {
394#if 1
395 int tmp = 256 - s->time_const;
396 s->freq = (1000000 + (tmp / 2)) / tmp;
397#else
398 /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */
399 s->freq = 1000000 / ((255 - s->time_const));
400#endif
401 s->time_const = -1;
402 }
403
404 s->block_size = dma_len + 1;
405 s->block_size <<= ((s->fmt_bits == 16) ? 1 : 0);
406 if (!s->dma_auto) {
407 /* It is clear that for DOOM and auto-init this value
408 shouldn't take stereo into account, while Miles Sound Systems
409 setsound.exe with single transfer mode wouldn't work without it
410 wonders of SB16 yet again */
411 s->block_size <<= s->fmt_stereo;
412 }
413
414 LogFlow(("SB16: freq %d, stereo %d, sign %d, bits %d, "
415 "dma %d, auto %d, fifo %d, high %d\n",
416 s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
417 s->block_size, s->dma_auto, s->fifo, s->highspeed));
418
419 if (16 == s->fmt_bits) {
420 if (s->fmt_signed) {
421 s->fmt = AUD_FMT_S16;
422 }
423 else {
424 s->fmt = AUD_FMT_U16;
425 }
426 }
427 else {
428 if (s->fmt_signed) {
429 s->fmt = AUD_FMT_S8;
430 }
431 else {
432 s->fmt = AUD_FMT_U8;
433 }
434 }
435
436 s->left_till_irq = s->block_size;
437
438 s->bytes_per_second = (s->freq << s->fmt_stereo) << ((s->fmt_bits == 16) ? 1 : 0);
439 s->highspeed = 0;
440 s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
441 if (s->block_size & s->align) {
442 LogFlow(("SB16: warning: misaligned block size %d, alignment %d\n",
443 s->block_size, s->align + 1));
444 }
445
446 if (s->freq) {
447
448 s->audio_free = 0;
449
450
451#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
452 int rc;
453 rc = s->pDrv->pfnOpenOut(s->pDrv, &s->voice, "sb16", s,SB_audio_callback, s->freq, 1 << s->fmt_stereo, s->fmt, 0);
454#else
455 audsettings_t as;
456 as.freq = s->freq;
457 as.nchannels = 1 << s->fmt_stereo;
458 as.fmt = s->fmt;
459 as.endianness = 0;
460 s->voice = AUD_open_out (
461 &s->card,
462 s->voice,
463 "sb16",
464 s,
465 SB_audio_callback,
466 &as
467 );
468#endif
469 }
470
471 control (s, 1);
472 speaker (s, 1);
473}
474
475static inline void dsp_out_data (SB16State *s, uint8_t val)
476{
477 LogFlow(("SB16: outdata %#x\n", val));
478 if ((size_t) s->out_data_len < sizeof (s->out_data)) {
479 s->out_data[s->out_data_len++] = val;
480 }
481}
482
483static inline uint8_t dsp_get_data (SB16State *s)
484{
485 if (s->in_index) {
486 return s->in2_data[--s->in_index];
487 }
488 else {
489 LogFlow(("SB16: buffer underflow\n"));
490 return 0;
491 }
492}
493
494static void command (SB16State *s, uint8_t cmd)
495{
496 LogFlow(("SB16: command %#x\n", cmd));
497
498 if (cmd > 0xaf && cmd < 0xd0) {
499 if (cmd & 8) {
500 LogFlow(("SB16: ADC not yet supported (command %#x)\n", cmd));
501 }
502
503 switch (cmd >> 4) {
504 case 11:
505 case 12:
506 break;
507 default:
508 LogFlow(("SB16: %#x wrong bits\n", cmd));
509 }
510 s->needed_bytes = 3;
511 }
512 else {
513 s->needed_bytes = 0;
514
515 switch (cmd) {
516 case 0x03:
517 dsp_out_data (s, 0x10); /* s->csp_param); */
518 goto warn;
519
520 case 0x04:
521 s->needed_bytes = 1;
522 goto warn;
523
524 case 0x05:
525 s->needed_bytes = 2;
526 goto warn;
527
528 case 0x08:
529 /* __asm__ ("int3"); */
530 goto warn;
531
532 case 0x0e:
533 s->needed_bytes = 2;
534 goto warn;
535
536 case 0x09:
537 dsp_out_data (s, 0xf8);
538 goto warn;
539
540 case 0x0f:
541 s->needed_bytes = 1;
542 goto warn;
543
544 case 0x10:
545 s->needed_bytes = 1;
546 goto warn;
547
548 case 0x14:
549 s->needed_bytes = 2;
550 s->block_size = 0;
551 break;
552
553 case 0x1c: /* Auto-Initialize DMA DAC, 8-bit */
554 dma_cmd8 (s, DMA8_AUTO, -1);
555 break;
556
557 case 0x20: /* Direct ADC, Juice/PL */
558 dsp_out_data (s, 0xff);
559 goto warn;
560
561 case 0x35:
562 LogFlow(("SB16: 0x35 - MIDI command not implemented\n"));
563 break;
564
565 case 0x40:
566 s->freq = -1;
567 s->time_const = -1;
568 s->needed_bytes = 1;
569 break;
570
571 case 0x41:
572 s->freq = -1;
573 s->time_const = -1;
574 s->needed_bytes = 2;
575 break;
576
577 case 0x42:
578 s->freq = -1;
579 s->time_const = -1;
580 s->needed_bytes = 2;
581 goto warn;
582
583 case 0x45:
584 dsp_out_data (s, 0xaa);
585 goto warn;
586
587 case 0x47: /* Continue Auto-Initialize DMA 16bit */
588 break;
589
590 case 0x48:
591 s->needed_bytes = 2;
592 break;
593
594 case 0x74:
595 s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
596 LogFlow(("SB16: 0x75 - DMA DAC, 4-bit ADPCM not implemented\n"));
597 break;
598
599 case 0x75: /* DMA DAC, 4-bit ADPCM Reference */
600 s->needed_bytes = 2;
601 LogFlow(("SB16: 0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n"));
602 break;
603
604 case 0x76: /* DMA DAC, 2.6-bit ADPCM */
605 s->needed_bytes = 2;
606 LogFlow(("SB16: 0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n"));
607 break;
608
609 case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */
610 s->needed_bytes = 2;
611 LogFlow(("SB16: 0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n"));
612 break;
613
614 case 0x7d:
615 LogFlow(("SB16: 0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n"));
616 LogFlow(("SB16: not implemented\n"));
617 break;
618
619 case 0x7f:
620 LogFlow(("SB16: 0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"));
621 LogFlow(("SB16: not implemented\n"));
622 break;
623
624 case 0x80:
625 s->needed_bytes = 2;
626 break;
627
628 case 0x90:
629 case 0x91:
630 dma_cmd8 (s, (((cmd & 1) == 0) ? 1 : 0) | DMA8_HIGH, -1);
631 break;
632
633 case 0xd0: /* halt DMA operation. 8bit */
634 control (s, 0);
635 break;
636
637 case 0xd1: /* speaker on */
638 speaker (s, 1);
639 break;
640
641 case 0xd3: /* speaker off */
642 speaker (s, 0);
643 break;
644
645 case 0xd4: /* continue DMA operation. 8bit */
646 /* KQ6 (or maybe Sierras audblst.drv in general) resets
647 the frequency between halt/continue */
648 continue_dma8 (s);
649 break;
650
651 case 0xd5: /* halt DMA operation. 16bit */
652 control (s, 0);
653 break;
654
655 case 0xd6: /* continue DMA operation. 16bit */
656 control (s, 1);
657 break;
658
659 case 0xd9: /* exit auto-init DMA after this block. 16bit */
660 s->dma_auto = 0;
661 break;
662
663 case 0xda: /* exit auto-init DMA after this block. 8bit */
664 s->dma_auto = 0;
665 break;
666
667 case 0xe0: /* DSP identification */
668 s->needed_bytes = 1;
669 break;
670
671 case 0xe1:
672 dsp_out_data (s, s->ver & 0xff);
673 dsp_out_data (s, s->ver >> 8);
674 break;
675
676 case 0xe2:
677 s->needed_bytes = 1;
678 goto warn;
679
680 case 0xe3:
681 {
682 int i;
683 for (i = sizeof (e3) - 1; i >= 0; --i)
684 dsp_out_data (s, e3[i]);
685 }
686 break;
687
688 case 0xe4: /* write test reg */
689 s->needed_bytes = 1;
690 break;
691
692 case 0xe7:
693 LogFlow(("SB16: Attempt to probe for ESS (0xe7)?\n"));
694 break;
695
696 case 0xe8: /* read test reg */
697 dsp_out_data (s, s->test_reg);
698 break;
699
700 case 0xf2:
701 case 0xf3:
702 dsp_out_data (s, 0xaa);
703 s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
704#ifndef VBOX
705 qemu_irq_raise (s->pic[s->irq]);
706#else
707 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 1);
708#endif
709 break;
710
711 case 0xf9:
712 s->needed_bytes = 1;
713 goto warn;
714
715 case 0xfa:
716 dsp_out_data (s, 0);
717 goto warn;
718
719 case 0xfc: /* FIXME */
720 dsp_out_data (s, 0);
721 goto warn;
722
723 default:
724 LogFlow(("SB16: Unrecognized command %#x\n", cmd));
725 break;
726 }
727 }
728
729 if (!s->needed_bytes) {
730 LogFlow(("\n"));
731 }
732
733 exit:
734 if (!s->needed_bytes) {
735 s->cmd = -1;
736 }
737 else {
738 s->cmd = cmd;
739 }
740 return;
741
742 warn:
743 LogFlow(("SB16: warning: command %#x,%d is not truly understood yet\n",
744 cmd, s->needed_bytes));
745 goto exit;
746
747}
748
749static uint16_t dsp_get_lohi (SB16State *s)
750{
751 uint8_t hi = dsp_get_data (s);
752 uint8_t lo = dsp_get_data (s);
753 return (hi << 8) | lo;
754}
755
756static uint16_t dsp_get_hilo (SB16State *s)
757{
758 uint8_t lo = dsp_get_data (s);
759 uint8_t hi = dsp_get_data (s);
760 return (hi << 8) | lo;
761}
762
763static void complete (SB16State *s)
764{
765 int d0, d1, d2;
766 LogFlow(("SB16: complete command %#x, in_index %d, needed_bytes %d\n",
767 s->cmd, s->in_index, s->needed_bytes));
768
769 if (s->cmd > 0xaf && s->cmd < 0xd0) {
770 d2 = dsp_get_data (s);
771 d1 = dsp_get_data (s);
772 d0 = dsp_get_data (s);
773
774 if (s->cmd & 8) {
775 LogFlow(("SB16: ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
776 s->cmd, d0, d1, d2));
777 }
778 else {
779 LogFlow(("SB16: cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
780 s->cmd, d0, d1, d2));
781 dma_cmd (s, s->cmd, d0, d1 + (d2 << 8));
782 }
783 }
784 else {
785 switch (s->cmd) {
786 case 0x04:
787 s->csp_mode = dsp_get_data (s);
788 s->csp_reg83r = 0;
789 s->csp_reg83w = 0;
790 LogFlow(("SB16: CSP command 0x04: mode=%#x\n", s->csp_mode));
791 break;
792
793 case 0x05:
794 s->csp_param = dsp_get_data (s);
795 s->csp_value = dsp_get_data (s);
796 LogFlow(("SB16: CSP command 0x05: param=%#x value=%#x\n",
797 s->csp_param,
798 s->csp_value));
799 break;
800
801 case 0x0e:
802 d0 = dsp_get_data (s);
803 d1 = dsp_get_data (s);
804 LogFlow(("SB16: write CSP register %d <- %#x\n", d1, d0));
805 if (d1 == 0x83) {
806 LogFlow(("SB16: 0x83[%d] <- %#x\n", s->csp_reg83r, d0));
807 s->csp_reg83[s->csp_reg83r % 4] = d0;
808 s->csp_reg83r += 1;
809 }
810 else {
811 s->csp_regs[d1] = d0;
812 }
813 break;
814
815 case 0x0f:
816 d0 = dsp_get_data (s);
817 LogFlow(("SB16: read CSP register %#x -> %#x, mode=%#x\n",
818 d0, s->csp_regs[d0], s->csp_mode));
819 if (d0 == 0x83) {
820 LogFlow(("SB16: 0x83[%d] -> %#x\n",
821 s->csp_reg83w,
822 s->csp_reg83[s->csp_reg83w % 4]));
823 dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
824 s->csp_reg83w += 1;
825 }
826 else {
827 dsp_out_data (s, s->csp_regs[d0]);
828 }
829 break;
830
831 case 0x10:
832 d0 = dsp_get_data (s);
833 LogFlow(("SB16: cmd 0x10 d0=%#x\n", d0));
834 break;
835
836 case 0x14:
837 dma_cmd8 (s, 0, dsp_get_lohi (s) + 1);
838 break;
839
840 case 0x40:
841 s->time_const = dsp_get_data (s);
842 LogFlow(("SB16: set time const %d\n", s->time_const));
843 break;
844
845 case 0x42: /* FT2 sets output freq with this, go figure */
846#if 0
847 dolog ("cmd 0x42 might not do what it think it should\n");
848#endif
849 case 0x41:
850 s->freq = dsp_get_hilo (s);
851 LogFlow(("SB16: set freq %d\n", s->freq));
852 break;
853
854 case 0x48:
855 s->block_size = dsp_get_lohi (s) + 1;
856 LogFlow(("SB16: set dma block len %d\n", s->block_size));
857 break;
858
859 case 0x74:
860 case 0x75:
861 case 0x76:
862 case 0x77:
863 /* ADPCM stuff, ignore */
864 break;
865
866 case 0x80:
867 {
868 int freq, samples, bytes;
869 uint64_t ticks;
870
871 freq = s->freq > 0 ? s->freq : 11025;
872 samples = dsp_get_lohi (s) + 1;
873 bytes = samples << s->fmt_stereo << ((s->fmt_bits == 16) ? 1 : 0);
874#ifndef VBOX
875 ticks = (bytes * ticks_per_sec) / freq;
876 if (ticks < ticks_per_sec / 1024) {
877 qemu_irq_raise (s->pic[s->irq]);
878 }
879 else {
880 if (s->aux_ts) {
881 qemu_mod_timer (
882 s->aux_ts,
883 qemu_get_clock (vm_clock) + ticks
884 );
885 }
886 }
887 LogFlow(("SB16: mix silence %d %d %" PRId64 "\n", samples, bytes, ticks));
888#else /* VBOX */
889 ticks = (bytes * TMTimerGetFreq(s->pTimer)) / freq;
890 if (ticks < TMTimerGetFreq(s->pTimer) / 1024)
891 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 1);
892 else
893 TMTimerSet(s->pTimer, TMTimerGet(s->pTimer) + ticks);
894 LogFlow(("SB16: mix silence %d %d % %RU64\n", samples, bytes, ticks));
895#endif /* VBOX */
896 }
897 break;
898
899 case 0xe0:
900 d0 = dsp_get_data (s);
901 s->out_data_len = 0;
902 LogFlow(("SB16: E0 data = %#x\n", d0));
903 dsp_out_data (s, ~d0);
904 break;
905
906 case 0xe2:
907 d0 = dsp_get_data (s);
908 LogFlow(("SB16:E2 = %#x\n", d0));
909 break;
910
911 case 0xe4:
912 s->test_reg = dsp_get_data (s);
913 break;
914
915 case 0xf9:
916 d0 = dsp_get_data (s);
917 LogFlow(("SB16: command 0xf9 with %#x\n", d0));
918 switch (d0) {
919 case 0x0e:
920 dsp_out_data (s, 0xff);
921 break;
922
923 case 0x0f:
924 dsp_out_data (s, 0x07);
925 break;
926
927 case 0x37:
928 dsp_out_data (s, 0x38);
929 break;
930
931 default:
932 dsp_out_data (s, 0x00);
933 break;
934 }
935 break;
936
937 default:
938 LogFlow(("SB16: complete: unrecognized command %#x\n", s->cmd));
939 return;
940 }
941 }
942
943 LogFlow(("\n"));
944 s->cmd = -1;
945 return;
946}
947
948static void legacy_reset (SB16State *s)
949{
950
951 s->freq = 11025;
952 s->fmt_signed = 0;
953 s->fmt_bits = 8;
954 s->fmt_stereo = 0;
955
956#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
957 int rc;
958 rc = s->pDrv->pfnOpenOut(s->pDrv, &s->voice, "sb16", s, SB_audio_callback, 11025, 1, AUD_FMT_U8, 0);
959#else
960 audsettings_t as;
961 as.freq = s->freq;
962 as.nchannels = 1;
963 as.fmt = AUD_FMT_U8;
964 as.endianness = 0;
965 s->voice = AUD_open_out (
966 &s->card,
967 s->voice,
968 "sb16",
969 s,
970 SB_audio_callback,
971 &as
972 );
973
974#endif
975 /* Not sure about that... */
976 /* AUD_set_active_out (s->voice, 1); */
977}
978
979static void reset (SB16State *s)
980{
981#ifndef VBOX
982 qemu_irq_lower (s->pic[s->irq]);
983 if (s->dma_auto) {
984 qemu_irq_raise (s->pic[s->irq]);
985 qemu_irq_lower (s->pic[s->irq]);
986 }
987#else /* VBOX */
988 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 0);
989 if (s->dma_auto) {
990 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 1);
991 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 0);
992 }
993#endif /* VBOX */
994
995 s->mixer_regs[0x82] = 0;
996 s->dma_auto = 0;
997 s->in_index = 0;
998 s->out_data_len = 0;
999 s->left_till_irq = 0;
1000 s->needed_bytes = 0;
1001 s->block_size = -1;
1002 s->nzero = 0;
1003 s->highspeed = 0;
1004 s->v2x6 = 0;
1005 s->cmd = -1;
1006
1007 dsp_out_data(s, 0xaa);
1008 speaker (s, 0);
1009 control (s, 0);
1010 legacy_reset (s);
1011}
1012
1013static IO_WRITE_PROTO (dsp_write)
1014{
1015 SB16State *s = (SB16State*)opaque;
1016 int iport = nport - s->port;
1017
1018 LogFlow(("SB16: write %#x <- %#x\n", nport, val));
1019 switch (iport) {
1020 case 0x06:
1021 switch (val) {
1022 case 0x00:
1023 if (s->v2x6 == 1) {
1024 if (0 && s->highspeed) {
1025 s->highspeed = 0;
1026#ifndef VBOX
1027 qemu_irq_lower (s->pic[s->irq]);
1028#else
1029 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 0);
1030#endif
1031 control (s, 0);
1032 }
1033 else {
1034 reset (s);
1035 }
1036 }
1037 s->v2x6 = 0;
1038 break;
1039
1040 case 0x01:
1041 case 0x03: /* FreeBSD kludge */
1042 s->v2x6 = 1;
1043 break;
1044
1045 case 0xc6:
1046 s->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */
1047 break;
1048
1049 case 0xb8: /* Panic */
1050 reset (s);
1051 break;
1052
1053 case 0x39:
1054 dsp_out_data (s, 0x38);
1055 reset (s);
1056 s->v2x6 = 0x39;
1057 break;
1058
1059 default:
1060 s->v2x6 = val;
1061 break;
1062 }
1063 break;
1064
1065 case 0x0c: /* write data or command | write status */
1066/* if (s->highspeed) */
1067/* break; */
1068
1069 if (0 == s->needed_bytes) {
1070 command (s, val);
1071#if 0
1072 if (0 == s->needed_bytes) {
1073 log_dsp (s);
1074 }
1075#endif
1076 }
1077 else {
1078 if (s->in_index == sizeof (s->in2_data)) {
1079 LogFlow(("SB16: in data overrun\n"));
1080 }
1081 else {
1082 s->in2_data[s->in_index++] = val;
1083 if (s->in_index == s->needed_bytes) {
1084 s->needed_bytes = 0;
1085 complete (s);
1086#if 0
1087 log_dsp (s);
1088#endif
1089 }
1090 }
1091 }
1092 break;
1093
1094 default:
1095 LogFlow(("SB16: nport=%#x, val=%#x)\n", nport, val));
1096 break;
1097 }
1098
1099#ifdef VBOX
1100 return VINF_SUCCESS;
1101#endif
1102}
1103
1104static IO_READ_PROTO (dsp_read)
1105{
1106 SB16State *s = (SB16State*)opaque;
1107 int iport, retval, ack = 0;
1108
1109 iport = nport - s->port;
1110#ifdef VBOX
1111 /** @todo reject non-byte access?
1112 * The spec does not mention a non-byte access so we should check how real hardware behaves. */
1113#endif
1114
1115 switch (iport) {
1116 case 0x06: /* reset */
1117 retval = 0xff;
1118 break;
1119
1120 case 0x0a: /* read data */
1121 if (s->out_data_len) {
1122 retval = s->out_data[--s->out_data_len];
1123 s->last_read_byte = retval;
1124 }
1125 else {
1126 if (s->cmd != -1) {
1127 LogFlow(("SB16: empty output buffer for command %#x\n",
1128 s->cmd));
1129 }
1130 retval = s->last_read_byte;
1131 /* goto error; */
1132 }
1133 break;
1134
1135 case 0x0c: /* 0 can write */
1136 retval = s->can_write ? 0 : 0x80;
1137 break;
1138
1139 case 0x0d: /* timer interrupt clear */
1140 /* dolog ("timer interrupt clear\n"); */
1141 retval = 0;
1142 break;
1143
1144 case 0x0e: /* data available status | irq 8 ack */
1145 retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80;
1146 if (s->mixer_regs[0x82] & 1) {
1147 ack = 1;
1148 s->mixer_regs[0x82] &= 1;
1149#ifndef VBOX
1150 qemu_irq_lower (s->pic[s->irq]);
1151#else
1152 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 0);
1153#endif
1154 }
1155 break;
1156
1157 case 0x0f: /* irq 16 ack */
1158 retval = 0xff;
1159 if (s->mixer_regs[0x82] & 2) {
1160 ack = 1;
1161 s->mixer_regs[0x82] &= 2;
1162#ifndef VBOX
1163 qemu_irq_lower (s->pic[s->irq]);
1164#else
1165 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 0);
1166#endif
1167 }
1168 break;
1169
1170 default:
1171 goto error;
1172 }
1173
1174 if (!ack) {
1175 LogFlow(("SB16: read %#x -> %#x\n", nport, retval));
1176 }
1177
1178#ifndef VBOX
1179 return retval;
1180#else
1181 *pu32 = retval;
1182 return VINF_SUCCESS;
1183#endif
1184
1185 error:
1186 LogFlow(("SB16: warning: dsp_read %#x error\n", nport));
1187#ifndef VBOX
1188 return 0xff;
1189#else
1190 return VERR_IOM_IOPORT_UNUSED;
1191#endif
1192}
1193
1194static void reset_mixer (SB16State *s)
1195{
1196 int i;
1197
1198 memset (s->mixer_regs, 0xff, 0x7f);
1199 memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83);
1200
1201 s->mixer_regs[0x02] = 4; /* master volume 3bits */
1202 s->mixer_regs[0x06] = 4; /* MIDI volume 3bits */
1203 s->mixer_regs[0x08] = 0; /* CD volume 3bits */
1204 s->mixer_regs[0x0a] = 0; /* voice volume 2bits */
1205
1206 /* d5=input filt, d3=lowpass filt, d1,d2=input source */
1207 s->mixer_regs[0x0c] = 0;
1208
1209 /* d5=output filt, d1=stereo switch */
1210 s->mixer_regs[0x0e] = 0;
1211
1212 /* voice volume L d5,d7, R d1,d3 */
1213 s->mixer_regs[0x04] = (4 << 5) | (4 << 1);
1214 /* master ... */
1215 s->mixer_regs[0x22] = (4 << 5) | (4 << 1);
1216 /* MIDI ... */
1217 s->mixer_regs[0x26] = (4 << 5) | (4 << 1);
1218
1219 for (i = 0x30; i < 0x48; i++) {
1220 s->mixer_regs[i] = 0x20;
1221 }
1222}
1223
1224static IO_WRITE_PROTO(mixer_write_indexb)
1225{
1226 SB16State *s = (SB16State*)opaque;
1227 (void) nport;
1228 s->mixer_nreg = val;
1229
1230#ifdef VBOX
1231 return VINF_SUCCESS;
1232#endif
1233}
1234#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
1235uint32_t popcount (uint32_t u)
1236{
1237 u = ((u&0x55555555) + ((u>>1)&0x55555555));
1238 u = ((u&0x33333333) + ((u>>2)&0x33333333));
1239 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
1240 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
1241 u = ( u&0x0000ffff) + (u>>16);
1242 return u;
1243}
1244
1245uint32_t lsbindex (uint32_t u)
1246{
1247 return popcount ((u&-u)-1);
1248}
1249#endif
1250
1251static IO_WRITE_PROTO(mixer_write_datab)
1252{
1253 SB16State *s = (SB16State*)opaque;
1254 bool update_master = false;
1255 bool update_voice = false;
1256
1257 (void) nport;
1258 LogFlow(("SB16: mixer_write [%#x] <- %#x\n", s->mixer_nreg, val));
1259
1260 switch (s->mixer_nreg) {
1261 case 0x00:
1262 reset_mixer(s);
1263 /* And update the actual volume, too. */
1264 update_master = true;
1265 update_voice = true;
1266 break;
1267
1268 case 0x04:
1269 /* Translate from old style voice volume (L/R). */
1270 s->mixer_regs[0x32] = val & 0xff;
1271 s->mixer_regs[0x33] = val << 4;
1272 update_voice = true;
1273 break;
1274
1275 case 0x22:
1276 /* Translate from old style master volume (L/R). */
1277 s->mixer_regs[0x30] = val & 0xff;
1278 s->mixer_regs[0x31] = val << 4;
1279 update_master = true;
1280 break;
1281
1282 case 0x30:
1283 /* Translate to old style master volume (L). */
1284 s->mixer_regs[0x22] = (s->mixer_regs[0x22] & 0x0f) | val;
1285 update_master = true;
1286 break;
1287
1288 case 0x31:
1289 /* Translate to old style master volume (R). */
1290 s->mixer_regs[0x22] = (s->mixer_regs[0x22] & 0xf0) | (val >> 4);
1291 update_master = true;
1292 break;
1293
1294 case 0x32:
1295 /* Translate to old style voice volume (L). */
1296 s->mixer_regs[0x04] = (s->mixer_regs[0x04] & 0x0f) | val;
1297 update_voice = true;
1298 break;
1299
1300 case 0x33:
1301 /* Translate to old style voice volume (R). */
1302 s->mixer_regs[0x04] = (s->mixer_regs[0x04] & 0xf0) | (val >> 4);
1303 update_voice = true;
1304 break;
1305
1306 case 0x80:
1307 {
1308 int irq = irq_of_magic (val);
1309 LogFlow(("SB16: setting irq to %d (val=%#x)\n", irq, val));
1310 if (irq > 0) {
1311 s->irq = irq;
1312 }
1313 }
1314 break;
1315
1316 case 0x81:
1317 {
1318 int dma, hdma;
1319
1320 dma = lsbindex (val & 0xf);
1321 hdma = lsbindex (val & 0xf0);
1322 if (dma != s->dma || hdma != s->hdma) {
1323 LogFlow((
1324 "SB16: attempt to change DMA "
1325 "8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
1326 dma, s->dma, hdma, s->hdma, val));
1327 }
1328#if 0
1329 s->dma = dma;
1330 s->hdma = hdma;
1331#endif
1332 }
1333 break;
1334
1335 case 0x82:
1336 LogFlow(("SB16: attempt to write into IRQ status register (val=%#x)\n",
1337 val));
1338#ifdef VBOX
1339 return VINF_SUCCESS;
1340#endif
1341
1342 default:
1343 if (s->mixer_nreg >= 0x80) {
1344 LogFlow(("SB16: attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val));
1345 }
1346 break;
1347 }
1348
1349 s->mixer_regs[s->mixer_nreg] = val;
1350
1351#ifdef VBOX
1352 /* Update the master (mixer) volume. */
1353 if (update_master)
1354 {
1355 int mute = 0;
1356 uint8_t lvol = s->mixer_regs[0x30];
1357 uint8_t rvol = s->mixer_regs[0x31];
1358#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
1359 /*@todo not passinga audmixer_Ctl values as its not used in DrvAudio.c */
1360 s->pDrv->pfnSetVolume(s->pDrv, &mute, &lvol, &rvol);
1361#else
1362 AUD_set_volume(AUD_MIXER_VOLUME, &mute, &lvol, &rvol);
1363#endif
1364 }
1365 /* Update the voice (PCM) volume. */
1366 if (update_voice)
1367 {
1368 int mute = 0;
1369 uint8_t lvol = s->mixer_regs[0x32];
1370 uint8_t rvol = s->mixer_regs[0x33];
1371#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
1372 s->pDrv->pfnSetVolume(s->pDrv, &mute, &lvol, &rvol);
1373#else
1374 AUD_set_volume(AUD_MIXER_PCM, &mute, &lvol, &rvol);
1375#endif
1376 }
1377#endif /* VBOX */
1378
1379#ifdef VBOX
1380 return VINF_SUCCESS;
1381#endif
1382}
1383
1384static IO_WRITE_PROTO(mixer_write)
1385{
1386#ifndef VBOX
1387 mixer_write_indexb (opaque, nport, val & 0xff);
1388 mixer_write_datab (opaque, nport, (val >> 8) & 0xff);
1389#else /* VBOX */
1390 SB16State *s = (SB16State*)opaque;
1391 int iport = nport - s->port;
1392 switch (cb)
1393 {
1394 case 1:
1395 switch (iport)
1396 {
1397 case 4:
1398 mixer_write_indexb (pDevIns, opaque, nport, val, 1);
1399 break;
1400 case 5:
1401 mixer_write_datab (pDevIns, opaque, nport, val, 1);
1402 break;
1403 }
1404 break;
1405 case 2:
1406 mixer_write_indexb (pDevIns, opaque, nport, val & 0xff, 1);
1407 mixer_write_datab (pDevIns, opaque, nport, (val >> 8) & 0xff, 1);
1408 break;
1409 default:
1410 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", nport, cb, val));
1411 break;
1412 }
1413 return VINF_SUCCESS;
1414#endif /* VBOX */
1415}
1416
1417static IO_READ_PROTO(mixer_read)
1418{
1419 SB16State *s = (SB16State*)opaque;
1420
1421 (void) nport;
1422#ifndef DEBUG_SB16_MOST
1423 if (s->mixer_nreg != 0x82) {
1424 LogFlow(("SB16: mixer_read[%#x] -> %#x\n",
1425 s->mixer_nreg, s->mixer_regs[s->mixer_nreg]));
1426 }
1427#else
1428 LogFlow(("SB16: mixer_read[%#x] -> %#x\n",
1429 s->mixer_nreg, s->mixer_regs[s->mixer_nreg]));
1430#endif
1431#ifndef VBOX
1432 return s->mixer_regs[s->mixer_nreg];
1433#else
1434 *pu32 = s->mixer_regs[s->mixer_nreg];
1435 return VINF_SUCCESS;
1436#endif
1437}
1438
1439static int write_audio (SB16State *s, int nchan, int dma_pos,
1440 int dma_len, int len)
1441{
1442 int temp, net;
1443 uint8_t tmpbuf[4096];
1444
1445 temp = len;
1446 net = 0;
1447
1448 while (temp) {
1449 int left = dma_len - dma_pos;
1450#ifndef VBOX
1451 int copied;
1452 size_t to_copy;
1453#else
1454 uint32_t copied;
1455 uint32_t to_copy;
1456#endif
1457
1458 to_copy = audio_MIN (temp, left);
1459 if (to_copy > sizeof (tmpbuf)) {
1460 to_copy = sizeof (tmpbuf);
1461 }
1462
1463#ifndef VBOX
1464 copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
1465#else
1466 int rc = PDMDevHlpDMAReadMemory(s->pDevIns, nchan, tmpbuf, dma_pos,
1467 to_copy, &copied);
1468 AssertMsgRC (rc, ("DMAReadMemory -> %Rrc\n", rc));
1469#endif
1470
1471#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
1472 copied = s->pDrv->pfnWrite(s->pDrv, s->voice, tmpbuf, copied);
1473#else
1474 copied = AUD_write (s->voice, tmpbuf, copied);
1475#endif
1476
1477 temp -= copied;
1478 dma_pos = (dma_pos + copied) % dma_len;
1479 net += copied;
1480
1481 if (!copied) {
1482 break;
1483 }
1484 }
1485
1486 return net;
1487}
1488
1489#ifndef VBOX
1490static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
1491#else
1492static DECLCALLBACK(uint32_t) SB_read_DMA (PPDMDEVINS pDevIns, void *opaque, unsigned nchan, uint32_t dma_pos, uint32_t dma_len)
1493#endif
1494{
1495 SB16State *s = (SB16State*)opaque;
1496 int till, copy, written, free;
1497
1498 if (s->block_size <= 0) {
1499 LogFlow(("SB16: invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
1500 s->block_size, nchan, dma_pos, dma_len));
1501 return dma_pos;
1502 }
1503
1504 if (s->left_till_irq < 0) {
1505 s->left_till_irq = s->block_size;
1506 }
1507
1508 if (s->voice) {
1509 free = s->audio_free & ~s->align;
1510 if ((free <= 0) || !dma_len) {
1511 return dma_pos;
1512 }
1513 }
1514 else {
1515 free = dma_len;
1516 }
1517
1518 copy = free;
1519 till = s->left_till_irq;
1520
1521#ifdef DEBUG_SB16_MOST
1522 LogFlow(("SB16: pos:%06d %d till:%d len:%d\n",
1523 dma_pos, free, till, dma_len));
1524#endif
1525
1526 if (copy >= till) {
1527 if (0 == s->dma_auto) {
1528 copy = till;
1529 } else {
1530 if( copy >= till + s->block_size ) {
1531 copy = till; /* Make sure we won't skip IRQs. */
1532 }
1533 }
1534 }
1535
1536 written = write_audio (s, nchan, dma_pos, dma_len, copy);
1537 dma_pos = (dma_pos + written) % dma_len;
1538 s->left_till_irq -= written;
1539
1540 if (s->left_till_irq <= 0) {
1541 s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
1542#ifndef VBOX
1543 qemu_irq_raise (s->pic[s->irq]);
1544#else
1545 PDMDevHlpISASetIrq(s->pDevIns, s->irq, 1);
1546#endif
1547 if (0 == s->dma_auto) {
1548 control (s, 0);
1549 speaker (s, 0);
1550 }
1551 }
1552
1553#ifdef DEBUG_SB16_MOST
1554 LogFlow(("SB16: pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
1555 dma_pos, free, dma_len, s->left_till_irq, copy, written,
1556 s->block_size));
1557#endif
1558
1559 while (s->left_till_irq <= 0) {
1560 s->left_till_irq = s->block_size + s->left_till_irq;
1561 }
1562
1563 return dma_pos;
1564}
1565
1566static void SB_audio_callback (void *opaque, int free)
1567{
1568 SB16State *s = (SB16State*)opaque;
1569 s->audio_free = free;
1570#ifdef VBOX
1571 /* New space available, see if we can transfer more. There is no cyclic DMA timer in VBox. */
1572 PDMDevHlpDMASchedule (s->pDevIns);
1573#endif
1574}
1575
1576static void SB_save (QEMUFile *f, void *opaque)
1577{
1578#ifndef VBOX
1579 SB16State *s = opaque;
1580#else
1581 SB16State *s = (SB16State *)opaque;
1582#endif
1583
1584 qemu_put_be32 (f, s->irq);
1585 qemu_put_be32 (f, s->dma);
1586 qemu_put_be32 (f, s->hdma);
1587 qemu_put_be32 (f, s->port);
1588 qemu_put_be32 (f, s->ver);
1589 qemu_put_be32 (f, s->in_index);
1590 qemu_put_be32 (f, s->out_data_len);
1591 qemu_put_be32 (f, s->fmt_stereo);
1592 qemu_put_be32 (f, s->fmt_signed);
1593 qemu_put_be32 (f, s->fmt_bits);
1594 qemu_put_be32s (f, &s->fmt);
1595 qemu_put_be32 (f, s->dma_auto);
1596 qemu_put_be32 (f, s->block_size);
1597 qemu_put_be32 (f, s->fifo);
1598 qemu_put_be32 (f, s->freq);
1599 qemu_put_be32 (f, s->time_const);
1600 qemu_put_be32 (f, s->speaker);
1601 qemu_put_be32 (f, s->needed_bytes);
1602 qemu_put_be32 (f, s->cmd);
1603 qemu_put_be32 (f, s->use_hdma);
1604 qemu_put_be32 (f, s->highspeed);
1605 qemu_put_be32 (f, s->can_write);
1606 qemu_put_be32 (f, s->v2x6);
1607
1608 qemu_put_8s (f, &s->csp_param);
1609 qemu_put_8s (f, &s->csp_value);
1610 qemu_put_8s (f, &s->csp_mode);
1611 qemu_put_8s (f, &s->csp_param);
1612 qemu_put_buffer (f, s->csp_regs, 256);
1613 qemu_put_8s (f, &s->csp_index);
1614 qemu_put_buffer (f, s->csp_reg83, 4);
1615 qemu_put_be32 (f, s->csp_reg83r);
1616 qemu_put_be32 (f, s->csp_reg83w);
1617
1618 qemu_put_buffer (f, s->in2_data, sizeof (s->in2_data));
1619 qemu_put_buffer (f, s->out_data, sizeof (s->out_data));
1620 qemu_put_8s (f, &s->test_reg);
1621 qemu_put_8s (f, &s->last_read_byte);
1622
1623 qemu_put_be32 (f, s->nzero);
1624 qemu_put_be32 (f, s->left_till_irq);
1625 qemu_put_be32 (f, s->dma_running);
1626 qemu_put_be32 (f, s->bytes_per_second);
1627 qemu_put_be32 (f, s->align);
1628
1629 qemu_put_be32 (f, s->mixer_nreg);
1630 qemu_put_buffer (f, s->mixer_regs, 256);
1631}
1632
1633static int SB_load (QEMUFile *f, void *opaque, int version_id)
1634{
1635#ifndef VBOX
1636 SB16State *s = opaque;
1637
1638 if (version_id != 1) {
1639 return -EINVAL;
1640 }
1641#else
1642 SB16State *s = (SB16State *)opaque;
1643#endif
1644
1645 s->irq=qemu_get_be32 (f);
1646 s->dma=qemu_get_be32 (f);
1647 s->hdma=qemu_get_be32 (f);
1648 s->port=qemu_get_be32 (f);
1649 s->ver=qemu_get_be32 (f);
1650 s->in_index=qemu_get_be32 (f);
1651 s->out_data_len=qemu_get_be32 (f);
1652 s->fmt_stereo=qemu_get_be32 (f);
1653 s->fmt_signed=qemu_get_be32 (f);
1654 s->fmt_bits=qemu_get_be32 (f);
1655 qemu_get_be32s (f, (uint32_t*)&s->fmt);
1656 s->dma_auto=qemu_get_be32 (f);
1657 s->block_size=qemu_get_be32 (f);
1658 s->fifo=qemu_get_be32 (f);
1659 s->freq=qemu_get_be32 (f);
1660 s->time_const=qemu_get_be32 (f);
1661 s->speaker=qemu_get_be32 (f);
1662 s->needed_bytes=qemu_get_be32 (f);
1663 s->cmd=qemu_get_be32 (f);
1664 s->use_hdma=qemu_get_be32 (f);
1665 s->highspeed=qemu_get_be32 (f);
1666 s->can_write=qemu_get_be32 (f);
1667 s->v2x6=qemu_get_be32 (f);
1668
1669 qemu_get_8s (f, &s->csp_param);
1670 qemu_get_8s (f, &s->csp_value);
1671 qemu_get_8s (f, &s->csp_mode);
1672 qemu_get_8s (f, &s->csp_param);
1673 qemu_get_buffer (f, s->csp_regs, 256);
1674 qemu_get_8s (f, &s->csp_index);
1675 qemu_get_buffer (f, s->csp_reg83, 4);
1676 s->csp_reg83r=qemu_get_be32 (f);
1677 s->csp_reg83w=qemu_get_be32 (f);
1678
1679 qemu_get_buffer (f, s->in2_data, sizeof (s->in2_data));
1680 qemu_get_buffer (f, s->out_data, sizeof (s->out_data));
1681 qemu_get_8s (f, &s->test_reg);
1682 qemu_get_8s (f, &s->last_read_byte);
1683
1684 s->nzero=qemu_get_be32 (f);
1685 s->left_till_irq=qemu_get_be32 (f);
1686 s->dma_running=qemu_get_be32 (f);
1687 s->bytes_per_second=qemu_get_be32 (f);
1688 s->align=qemu_get_be32 (f);
1689
1690 s->mixer_nreg=qemu_get_be32 (f);
1691 qemu_get_buffer (f, s->mixer_regs, 256);
1692
1693 if (s->voice) {
1694#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
1695 s->pDrv->pfnCloseOut(s->pDrv, s->voice);
1696#else
1697 AUD_close_out (&s->card, s->voice);
1698#endif
1699 s->voice = NULL;
1700 }
1701
1702 if (s->dma_running) {
1703 if (s->freq) {
1704
1705 s->audio_free = 0;
1706
1707#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
1708 int rc;
1709 rc = s->pDrv->pfnOpenOut(s->pDrv, &s->voice, "sb16", s, SB_audio_callback, s->freq, 1 << s->fmt_stereo, s->fmt, 0);
1710#else
1711 audsettings_t as;
1712 as.freq = s->freq;
1713 as.nchannels = 1 << s->fmt_stereo;
1714 as.fmt = s->fmt;
1715 as.endianness = 0;
1716 s->voice = AUD_open_out (
1717 &s->card,
1718 s->voice,
1719 "sb16",
1720 s,
1721 SB_audio_callback,
1722 &as
1723 );
1724#endif
1725 }
1726
1727 control (s, 1);
1728 speaker (s, s->speaker);
1729 }
1730
1731#ifdef VBOX
1732 return VINF_SUCCESS;
1733#endif
1734}
1735
1736#ifndef VBOX
1737int SB16_init (AudioState *audio, qemu_irq *pic)
1738{
1739 SB16State *s;
1740 int i;
1741 static const uint8_t dsp_write_ports[] = {0x6, 0xc};
1742 static const uint8_t dsp_read_ports[] = {0x6, 0xa, 0xc, 0xd, 0xe, 0xf};
1743
1744 if (!audio) {
1745 LogFlow(("SB16: No audio state\n"));
1746 return -1;
1747 }
1748
1749 s = qemu_mallocz (sizeof (*s));
1750 if (!s) {
1751 LogFlow(("SB16: Could not allocate memory for SB16 (%zu bytes)\n",
1752 sizeof (*s)));
1753 return -1;
1754 }
1755
1756 s->cmd = -1;
1757 s->pic = pic;
1758 s->irq = conf.irq;
1759 s->dma = conf.dma;
1760 s->hdma = conf.hdma;
1761 s->port = conf.port;
1762 s->ver = conf.ver_lo | (conf.ver_hi << 8);
1763
1764 s->mixer_regs[0x80] = magic_of_irq (s->irq);
1765 s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
1766 s->mixer_regs[0x82] = 2 << 5;
1767
1768 s->csp_regs[5] = 1;
1769 s->csp_regs[9] = 0xf8;
1770
1771 reset_mixer (s);
1772 s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s);
1773 if (!s->aux_ts) {
1774 LogFlow(("SB16: warning: Could not create auxiliary timer\n"));
1775 }
1776
1777 for (i = 0; i < LENOFA (dsp_write_ports); i++) {
1778 register_ioport_write (s->port + dsp_write_ports[i], 1, 1, dsp_write, s);
1779 }
1780
1781 for (i = 0; i < LENOFA (dsp_read_ports); i++) {
1782 register_ioport_read (s->port + dsp_read_ports[i], 1, 1, dsp_read, s);
1783 }
1784
1785 register_ioport_write (s->port + 0x4, 1, 1, mixer_write_indexb, s);
1786 register_ioport_write (s->port + 0x4, 1, 2, mixer_write_indexw, s);
1787 register_ioport_read (s->port + 0x5, 1, 1, mixer_read, s);
1788 register_ioport_write (s->port + 0x5, 1, 1, mixer_write_datab, s);
1789
1790 DMA_register_channel (s->hdma, SB_read_DMA, s);
1791 DMA_register_channel (s->dma, SB_read_DMA, s);
1792 s->can_write = 1;
1793
1794 register_savevm ("sb16", 0, 1, SB_save, SB_load, s);
1795 AUD_register_card (audio, "sb16");
1796 return 0;
1797}
1798
1799#else /* VBOX */
1800
1801
1802static DECLCALLBACK(int) sb16LiveExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1803{
1804 SB16State *pThis = PDMINS_2_DATA (pDevIns, SB16State *);
1805
1806 SSMR3PutS32(pSSM, pThis->irqCfg);
1807 SSMR3PutS32(pSSM, pThis->dmaCfg);
1808 SSMR3PutS32(pSSM, pThis->hdmaCfg);
1809 SSMR3PutS32(pSSM, pThis->portCfg);
1810 SSMR3PutS32(pSSM, pThis->verCfg);
1811 return VINF_SSM_DONT_CALL_AGAIN;
1812}
1813
1814static DECLCALLBACK(int) sb16SaveExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1815{
1816 SB16State *pThis = PDMINS_2_DATA (pDevIns, SB16State *);
1817
1818 sb16LiveExec (pDevIns, pSSM, 0);
1819 SB_save (pSSM, pThis);
1820 return VINF_SUCCESS;
1821}
1822
1823static DECLCALLBACK(int) sb16LoadExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSM,
1824 uint32_t uVersion, uint32_t uPass)
1825{
1826 SB16State *pThis = PDMINS_2_DATA (pDevIns, SB16State *);
1827
1828 AssertMsgReturn( uVersion == SB16_SAVE_STATE_VERSION
1829 || uVersion == SB16_SAVE_STATE_VERSION_VBOX_30,
1830 ("%u\n", uVersion),
1831 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
1832 if (uVersion > SB16_SAVE_STATE_VERSION_VBOX_30)
1833 {
1834 int32_t irq;
1835 SSMR3GetS32 (pSSM, &irq);
1836 int32_t dma;
1837 SSMR3GetS32 (pSSM, &dma);
1838 int32_t hdma;
1839 SSMR3GetS32 (pSSM, &hdma);
1840 int32_t port;
1841 SSMR3GetS32 (pSSM, &port);
1842 int32_t ver;
1843 int rc = SSMR3GetS32 (pSSM, &ver);
1844 AssertRCReturn (rc, rc);
1845
1846 if ( irq != pThis->irqCfg
1847 || dma != pThis->dmaCfg
1848 || hdma != pThis->hdmaCfg
1849 || port != pThis->portCfg
1850 || ver != pThis->verCfg )
1851 return SSMR3SetCfgError(pSSM, RT_SRC_POS,
1852 N_("config changed: irq=%x/%x dma=%x/%x hdma=%x/%x port=%x/%x ver=%x/%x (saved/config)"),
1853 irq, pThis->irqCfg,
1854 dma, pThis->dmaCfg,
1855 hdma, pThis->hdmaCfg,
1856 port, pThis->portCfg,
1857 ver, pThis->verCfg);
1858 }
1859 if (uPass != SSM_PASS_FINAL)
1860 return VINF_SUCCESS;
1861
1862 SB_load(pSSM, pThis, uVersion);
1863 return VINF_SUCCESS;
1864}
1865
1866/**
1867 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1868 */
1869static DECLCALLBACK(void *) sb16QueryInterface (struct PDMIBASE *pInterface,
1870 const char *pszIID)
1871{
1872 SB16State *pThis = RT_FROM_MEMBER(pInterface, SB16State, IBase);
1873 Assert(&pThis->IBase == pInterface);
1874
1875 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1876 return NULL;
1877}
1878
1879static DECLCALLBACK(int) sb16Construct (PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1880{
1881 SB16State *s = PDMINS_2_DATA(pDevIns, SB16State *);
1882 int rc;
1883
1884 /*
1885 * Validations.
1886 */
1887 Assert(iInstance == 0);
1888 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1889 if (!CFGMR3AreValuesValid(pCfgHandle,
1890 "IRQ\0"
1891 "DMA\0"
1892 "DMA16\0"
1893 "Port\0"
1894 "Version\0"))
1895 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1896 N_("Invalid configuration for sb16 device"));
1897
1898 /*
1899 * Read config data.
1900 */
1901 rc = CFGMR3QuerySIntDef(pCfgHandle, "IRQ", &s->irq, 5);
1902 if (RT_FAILURE(rc))
1903 return PDMDEV_SET_ERROR(pDevIns, rc,
1904 N_("Configuration error: Failed to get the \"IRQ\" value"));
1905 s->irqCfg = s->irq;
1906
1907 rc = CFGMR3QuerySIntDef(pCfgHandle, "DMA", &s->dma, 1);
1908 if (RT_FAILURE(rc))
1909 return PDMDEV_SET_ERROR(pDevIns, rc,
1910 N_("Configuration error: Failed to get the \"DMA\" value"));
1911 s->dmaCfg = s->dma;
1912
1913 rc = CFGMR3QuerySIntDef(pCfgHandle, "DMA16", &s->hdma, 5);
1914 if (RT_FAILURE(rc))
1915 return PDMDEV_SET_ERROR(pDevIns, rc,
1916 N_("Configuration error: Failed to get the \"DMA16\" value"));
1917 s->hdmaCfg = s->hdma;
1918
1919 RTIOPORT Port;
1920 rc = CFGMR3QueryPortDef(pCfgHandle, "Port", &Port, 0x220);
1921 if (RT_FAILURE(rc))
1922 return PDMDEV_SET_ERROR(pDevIns, rc,
1923 N_("Configuration error: Failed to get the \"Port\" value"));
1924 s->port = Port;
1925 s->portCfg = Port;
1926
1927 uint16_t u16Version;
1928 rc = CFGMR3QueryU16Def(pCfgHandle, "Version", &u16Version, 0x0405);
1929 if (RT_FAILURE(rc))
1930 return PDMDEV_SET_ERROR(pDevIns, rc,
1931 N_("Configuration error: Failed to get the \"Version\" value"));
1932 s->ver = u16Version;
1933 s->verCfg = u16Version;
1934
1935 /*
1936 * Init instance data.
1937 */
1938 s->pDevIns = pDevIns;
1939 s->IBase.pfnQueryInterface = sb16QueryInterface;
1940 s->cmd = -1;
1941
1942 s->mixer_regs[0x80] = magic_of_irq (s->irq);
1943 s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
1944 s->mixer_regs[0x82] = 2 << 5;
1945
1946 s->csp_regs[5] = 1;
1947 s->csp_regs[9] = 0xf8;
1948
1949 reset_mixer(s);
1950
1951 /*
1952 * Create timer, register & attach stuff.
1953 */
1954 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16Timer, s,
1955 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "SB16 timer", &s->pTimer);
1956 if (RT_FAILURE(rc))
1957 AssertMsgFailedReturn(("pfnTMTimerCreate -> %Rrc\n", rc), rc);
1958
1959 rc = PDMDevHlpIOPortRegister(pDevIns, s->port + 0x04, 2, s,
1960 mixer_write, mixer_read, NULL, NULL, "SB16");
1961 if (RT_FAILURE(rc))
1962 return rc;
1963 rc = PDMDevHlpIOPortRegister(pDevIns, s->port + 0x06, 10, s,
1964 dsp_write, dsp_read, NULL, NULL, "SB16");
1965 if (RT_FAILURE(rc))
1966 return rc;
1967
1968 rc = PDMDevHlpDMARegister(pDevIns, s->hdma, SB_read_DMA, s);
1969 if (RT_FAILURE(rc))
1970 return rc;
1971 rc = PDMDevHlpDMARegister(pDevIns, s->dma, SB_read_DMA, s);
1972 if (RT_FAILURE(rc))
1973 return rc;
1974
1975 s->can_write = 1;
1976
1977 rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(*s), sb16LiveExec, sb16SaveExec, sb16LoadExec);
1978 if (RT_FAILURE(rc))
1979 return rc;
1980
1981 rc = PDMDevHlpDriverAttach(pDevIns, 0, &s->IBase, &s->pDrvBase, "Audio Driver Port");
1982#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
1983 if(RT_SUCCESS(rc))
1984 {
1985 s->pDrv = PDMIBASE_QUERY_INTERFACE(s->pDrvBase, PDMIAUDIOCONNECTOR);
1986 AssertMsgReturn(s->pDrv,
1987 ("Configuration error: instance %d has no host audio interface!\n", iInstance),
1988 VERR_PDM_MISSING_INTERFACE);
1989 }
1990 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1991#else
1992 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1993#endif
1994 Log(("ac97: No attached driver!\n"));
1995 else if (RT_FAILURE(rc))
1996 {
1997 AssertMsgFailed(("Failed to attach AC97 LUN #0! rc=%Rrc\n", rc));
1998 return rc;
1999 }
2000
2001#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
2002 s->pDrv->pfnRegisterCard(s->pDrv, "sb16");
2003#else
2004 AUD_register_card("sb16", &s->card);
2005#endif
2006 legacy_reset(s);
2007
2008#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
2009 if (!s->pDrv->pfnIsHostVoiceOutOK(s->pDrv,s->voice))
2010#else
2011 if (!AUD_is_host_voice_out_ok(s->voice))
2012#endif
2013 {
2014 LogRel (("SB16: WARNING: Unable to open PCM OUT!\n"));
2015#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
2016 s->pDrv->pfnCloseOut(s->pDrv, s->voice );
2017#else
2018 AUD_close_out (&s->card, s->voice);
2019#endif
2020 s->voice = NULL;
2021#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
2022 s->pDrv->pfnInitNull(s->pDrv);
2023#else
2024 AUD_init_null();
2025#endif
2026 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2027 N_("No audio devices could be opened. Selecting the NULL audio backend "
2028 "with the consequence that no sound is audible"));
2029 }
2030 return VINF_SUCCESS;
2031}
2032
2033const PDMDEVREG g_DeviceSB16 =
2034{
2035 /* u32Version */
2036 PDM_DEVREG_VERSION,
2037 /* szName */
2038 "sb16",
2039 /* szRCMod */
2040 "",
2041 /* szR0Mod */
2042 "",
2043 /* pszDescription */
2044 "Sound Blaster 16 Controller",
2045 /* fFlags */
2046 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2047 /* fClass */
2048 PDM_DEVREG_CLASS_AUDIO,
2049 /* cMaxInstances */
2050 1,
2051 /* cbInstance */
2052 sizeof(SB16State),
2053 /* pfnConstruct */
2054 sb16Construct,
2055 /* pfnDestruct */
2056 NULL,
2057 /* pfnRelocate */
2058 NULL,
2059 /* pfnMemSetup */
2060 NULL,
2061 /* pfnPowerOn */
2062 NULL,
2063 /* pfnReset */
2064 NULL,
2065 /* pfnSuspend */
2066 NULL,
2067 /* pfnResume */
2068 NULL,
2069 /* pfnAttach */
2070 NULL,
2071 /* pfnDetach */
2072 NULL,
2073 /* pfnQueryInterface */
2074 NULL,
2075 /* pfnInitComplete */
2076 NULL,
2077 /* pfnPowerOff */
2078 NULL,
2079 /* pfnSoftReset */
2080 NULL,
2081 /* u32VersionEnd */
2082 PDM_DEVREG_VERSION
2083};
2084
2085#endif /* VBOX */
2086
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette