VirtualBox

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

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

Audio: Renamed PDMAUDIOSTRMSTS* -> PDMAUDIOSTREAMSTS*. No actual code changes.

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