VirtualBox

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

Last change on this file since 67717 was 67717, checked in by vboxsync, 7 years ago

Audio/DevSB16.cpp: Also set the stream layout in sb16OpenOut().

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