Changeset 88767 in vbox for trunk/src/VBox/Devices
- Timestamp:
- Apr 29, 2021 9:37:55 AM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DevSB16.cpp
r88676 r88767 5 5 6 6 /* 7 * Copyright (C) 2015-202 0Oracle Corporation7 * Copyright (C) 2015-2021 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 69 69 * Defined Constants And Macros * 70 70 *********************************************************************************************************************************/ 71 /** The maximum number of separate streams we currently implement. 72 * Currently we only support one stream only, namely the output stream. */ 73 #define SB16_MAX_STREAMS 1 74 /** The (zero-based) index of the output stream in \a aStreams. */ 75 #define SB16_IDX_OUT 0 76 71 77 /** Current saved state version. */ 72 78 #define SB16_SAVE_STATE_VERSION 2 … … 87 93 /** Pointer to the SB16 state. */ 88 94 typedef struct SB16STATE *PSB16STATE; 95 96 #ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 97 /** 98 * Asynchronous I/O state for an SB16 stream. 99 */ 100 typedef struct SB16STREAMSTATEAIO 101 { 102 PPDMTHREAD pThread; 103 /** Event for letting the thread know there is some data to process. */ 104 SUPSEMEVENT hEvtProcess; 105 /** Critical section for synchronizing access. */ 106 RTCRITSECT CritSect; 107 /** Started indicator. */ 108 volatile bool fStarted; 109 /** Shutdown indicator. */ 110 volatile bool fShutdown; 111 /** Whether the thread should do any data processing or not. */ 112 volatile bool fEnabled; 113 bool afPadding[5]; 114 } SB16STREAMSTATEAIO; 115 /** Pointer to the async I/O state for a SB16 stream. */ 116 typedef SB16STREAMSTATEAIO *PSB16STREAMSTATEAIO; 117 #endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */ 118 119 /** 120 * The internal state of a SB16 stream. 121 */ 122 typedef struct SB16STREAMSTATE 123 { 124 /** Flag indicating whether this stream is in enabled state or not. */ 125 bool fEnabled; 126 #ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 127 /** Asynchronous I/O state members. */ 128 SB16STREAMSTATEAIO AIO; 129 #endif 130 /** DMA cache to read data from / write data to. */ 131 PRTCIRCBUF pCircBuf; 132 } SB16STREAMSTATE; 133 /** Pointer to internal state of an SB16 stream. */ 134 typedef SB16STREAMSTATE *PSB16STREAMSTATE; 89 135 90 136 /** … … 155 201 typedef struct SB16STREAM 156 202 { 203 /** The stream's own index in \a aStreams of SB16STATE. 204 * Set to UINT8_MAX if not set (yet). */ 205 uint8_t uIdx; 206 /** The timer for pumping data thru the attached LUN drivers. */ 207 TMTIMERHANDLE hTimerIO; 208 /** The timer interval for pumping data thru the LUN drivers in timer ticks. */ 209 uint64_t cTicksTimerIOInterval; 210 /** Timestamp of the last timer callback (sb16TimerIO). 211 * Used to calculate the time actually elapsed between two timer callbacks. 212 * This currently ASSMUMES that we only have one single (output) stream. */ 213 uint64_t tsTimerIO; /** @todo Make this a per-stream value. */ 157 214 /** The stream's current configuration. */ 158 215 PDMAUDIOSTREAMCFG Cfg; 216 /** Internal state of this stream. */ 217 SB16STREAMSTATE State; 159 218 /** Debug stuff. */ 160 219 SB16STREAMDEBUG Dbg; … … 162 221 /** Pointer to a SB16 stream */ 163 222 typedef SB16STREAM *PSB16STREAM; 223 224 #ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 225 /** 226 * Asynchronous I/O thread context (arguments). 227 */ 228 typedef struct SB16STREAMTHREADCTX 229 { 230 /** The SB16 device state. */ 231 PSB16STATE pThis; 232 /** The SB16 stream state. */ 233 PSB16STREAM pStream; 234 } SB16STREAMTHREADCTX; 235 /** Pointer to the context for an async I/O thread. */ 236 typedef SB16STREAMTHREADCTX *PSB16STREAMTHREADCTX; 237 #endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */ 164 238 165 239 /** … … 198 272 int ver; 199 273 200 int in_index;201 int out_data_len;274 int dsp_in_idx; 275 int dsp_out_data_len; 202 276 int fmt_stereo; 203 277 int fmt_signed; … … 210 284 int time_const; 211 285 int speaker; 212 int needed_bytes;286 int dsp_in_needed_bytes; 213 287 int cmd; 214 288 int use_hdma; 215 289 int highspeed; 216 int can_write; /** @todo Value never gets set to 0! */290 int can_write; /** @todo r=andy BUGBUG Value never gets set to 0! */ 217 291 218 292 int v2x6; … … 227 301 int csp_reg83w; 228 302 229 uint8_t in2_data[10];230 uint8_t out_data[50];303 uint8_t dsp_in_data[10]; 304 uint8_t dsp_out_data[50]; 231 305 uint8_t test_reg; 232 306 uint8_t last_read_byte; … … 245 319 PDMIBASE IBase; 246 320 247 /** Outputstream. */248 SB16STREAM StreamOut;321 /** Array of all SB16 hardware audio stream. */ 322 SB16STREAM aStreams[SB16_MAX_STREAMS]; 249 323 /** The device's software mixer. */ 250 324 R3PTRTYPE(PAUDIOMIXER) pMixer; 251 325 /** Audio sink for PCM output. */ 252 326 R3PTRTYPE(PAUDMIXSINK) pSinkOut; 253 254 /** The timer for pumping data thru the attached LUN drivers. */255 TMTIMERHANDLE hTimerIO;256 /** The timer interval for pumping data thru the LUN drivers in timer ticks. */257 uint64_t cTicksTimerIOInterval;258 /** Timestamp of the last timer callback (sb16TimerIO).259 * Used to calculate the time actually elapsed between two timer callbacks. */260 uint64_t tsTimerIO;261 /** Number of active (running) SDn streams. */262 uint8_t cStreamsActive;263 /** Flag indicating whether the timer is active or not. */264 bool volatile fTimerActive;265 uint8_t u8Padding1[5];266 327 267 328 /** The two mixer I/O ports (port + 4). */ … … 287 348 * Internal Functions * 288 349 *********************************************************************************************************************************/ 289 static int sb16CheckAndReOpenOut(PPDMDEVINS pDevIns, PSB16STATE pThis); 290 static int sb16StreamEnable(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream, bool fEnable); 350 DECLINLINE(PDMAUDIODIR) sb16GetDirFromIndex(uint8_t uIdx); 351 352 static int sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable, bool fForce); 353 static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream); 291 354 static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream); 292 static void sb16StreamClose(PSB16STATE pThis, PSB16STREAM pStream); 293 DECLINLINE(PAUDMIXSINK) sb16StreamToSink(PSB16STATE pThis, PSB16STREAM pStream); 294 static void sb16TimerMaybeStart(PPDMDEVINS pDevIns, PSB16STATE pThis); 295 static void sb16TimerMaybeStop(PSB16STATE pThis); 296 355 static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream); 356 DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx); 357 static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cSamples); 358 static void sb16StreamUpdate(PSB16STREAM pStream, PAUDMIXSINK pSink); 359 static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDmaChan, uint32_t offDma, uint32_t cbDma, uint32_t cbToRead, uint32_t *pcbRead); 360 361 static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser); 362 static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser); 363 DECLINLINE(void) sb16TimerSet(PPDMDEVINS pDevIns, PSB16STREAM pStream, uint64_t cTicksToDeadline); 364 365 #ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 366 static int sb16StreamAsyncIOCreate(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream); 367 static int sb16StreamAsyncIODestroy(PPDMDEVINS pDevIns, PSB16STREAM pStream); 368 static int sb16StreamAsyncIONotify(PPDMDEVINS pDevIns, PSB16STREAM pStream); 369 #endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */ 370 371 static void sb16SpeakerControl(PSB16STATE pThis, int on); 372 static void sb16UpdateVolume(PSB16STATE pThis); 297 373 298 374 #if 0 // unused // def DEBUG … … 311 387 #endif 312 388 389 #ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 390 /** 391 * @callback_method_impl{FNPDMTHREADDEV} 392 */ 393 static DECLCALLBACK(int) sb16StreamAsyncIOThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread) 394 { 395 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) 396 return VINF_SUCCESS; 397 398 PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)pThread->pvUser; 399 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 400 401 PSB16STATE pThis = pCtx->pThis; 402 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 403 404 PSB16STREAM pStream = pCtx->pStream; 405 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 406 407 AssertReturn(pThread == pStream->State.AIO.pThread, VERR_INVALID_PARAMETER); 408 409 PAUDMIXSINK pSink = sb16StreamIndexToSink(pThis, pStream->uIdx); 410 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 411 412 while ( pThread->enmState != PDMTHREADSTATE_TERMINATING 413 && pThread->enmState != PDMTHREADSTATE_TERMINATED) 414 { 415 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pStream->State.AIO.hEvtProcess, RT_INDEFINITE_WAIT); 416 if (pStream->State.AIO.fShutdown) 417 break; 418 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc); 419 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING)) 420 return VINF_SUCCESS; 421 if (rc == VERR_INTERRUPTED) 422 continue; 423 424 sb16StreamUpdate(pStream, pSink); 425 } 426 427 return VINF_SUCCESS; 428 } 429 430 /** 431 * @callback_method_impl{FNPDMTHREADWAKEUPDEV} 432 */ 433 static DECLCALLBACK(int) sb16StreamAsyncIOWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread) 434 { 435 PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)pThread->pvUser; 436 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 437 438 PSB16STREAM pStream = pCtx->pStream; 439 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 440 441 sb16StreamAsyncIONotify(pDevIns, pStream); 442 443 return VINF_SUCCESS; 444 } 445 446 /** 447 * Creates the async I/O thread for a specific SB16 audio stream. 448 * 449 * @returns VBox status code. 450 * @param pDevIns The device instance. 451 * @param pThis The shared SB16 state. 452 * @param pStream SB16 audio stream to create the async I/O thread for. 453 */ 454 static int sb16StreamAsyncIOCreate(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream) 455 { 456 PSB16STREAMSTATEAIO pAIO = &pStream->State.AIO; 457 458 int rc; 459 460 if (!ASMAtomicReadBool(&pAIO->fStarted)) 461 { 462 pAIO->fShutdown = false; 463 pAIO->fEnabled = true; /* Enabled by default. */ 464 465 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pAIO->hEvtProcess); 466 if (RT_SUCCESS(rc)) 467 { 468 rc = RTCritSectInit(&pAIO->CritSect); 469 if (RT_SUCCESS(rc)) 470 { 471 /** @todo Make the device thread naming more specific once we have more streams here. */ 472 473 char szDevTag[20]; 474 RTStrPrintf(szDevTag, sizeof(szDevTag), "SB16-%u", pDevIns->iInstance); 475 476 PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)RTMemAllocZ(sizeof(SB16STREAMTHREADCTX)); 477 if (pCtx) 478 { 479 pCtx->pStream = pStream; 480 pCtx->pThis = pThis; 481 482 rc = PDMDevHlpThreadCreate(pDevIns, &pAIO->pThread, pCtx, sb16StreamAsyncIOThread, 483 sb16StreamAsyncIOWakeUp, 0, RTTHREADTYPE_IO, szDevTag); 484 if (RT_FAILURE(rc)) 485 RTMemFree(pCtx); 486 } 487 else 488 rc = VERR_NO_MEMORY; 489 } 490 } 491 } 492 else 493 rc = VINF_SUCCESS; 494 495 return rc; 496 } 497 498 /** 499 * Lets the stream's async I/O thread know that there is some data to process. 500 * 501 * @returns VBox status code. 502 * @param pDevIns The device instance. 503 * @param pStream The SB16 stream to notify async I/O thread. 504 */ 505 static int sb16StreamAsyncIONotify(PPDMDEVINS pDevIns, PSB16STREAM pStream) 506 { 507 return PDMDevHlpSUPSemEventSignal(pDevIns, pStream->State.AIO.hEvtProcess); 508 } 509 510 /** 511 * Destroys the async I/O thread of a specific SB16 audio stream. 512 * 513 * @returns VBox status code. 514 * @param pDevIns The device instance. 515 * @param pStream SB16 audio stream to destroy the async I/O thread for. 516 */ 517 static int sb16StreamAsyncIODestroy(PPDMDEVINS pDevIns, PSB16STREAM pStream) 518 { 519 PSB16STREAMSTATEAIO pAIO = &pStream->State.AIO; 520 521 if (!ASMAtomicReadBool(&pAIO->fStarted)) 522 return VINF_SUCCESS; 523 524 ASMAtomicWriteBool(&pAIO->fShutdown, true); 525 526 int rc = sb16StreamAsyncIONotify(pDevIns, pStream); 527 AssertRC(rc); 528 529 rc = PDMDevHlpThreadDestroy(pDevIns, pAIO->pThread, NULL); 530 AssertRC(rc); 531 532 rc = RTCritSectDelete(&pAIO->CritSect); 533 AssertRC(rc); 534 535 if (pStream->State.AIO.hEvtProcess != NIL_SUPSEMEVENT) 536 { 537 PDMDevHlpSUPSemEventSignal(pDevIns, pStream->State.AIO.hEvtProcess); 538 PDMDevHlpSUPSemEventClose(pDevIns, pStream->State.AIO.hEvtProcess); 539 pStream->State.AIO.hEvtProcess = NIL_SUPSEMEVENT; 540 } 541 542 pAIO->fShutdown = false; 543 return rc; 544 } 545 #endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */ 546 313 547 static void sb16SpeakerControl(PSB16STATE pThis, int on) 314 548 { 315 549 pThis->speaker = on; 316 /* AUD_enable (pThis->voice, on); */317 550 } 318 551 … … 326 559 PDMDevHlpDMASetDREQ(pThis->pDevInsR3, dma, hold); 327 560 328 /* We only support one output stream at the moment, so keep things easy here for now. */ 329 PSB16STREAM pStream = &pThis->StreamOut; 330 sb16StreamEnable(pDevIns, pThis, pStream, RT_BOOL(hold)); 561 /* We only support one output stream at the moment, so keep things simple here for now. */ 562 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT]; 331 563 332 564 if (hold) 333 565 { 334 pThis->cStreamsActive++; 335 sb16TimerMaybeStart(pDevIns, pThis); 336 PDMDevHlpDMASchedule(pThis->pDevInsR3); 566 int rc = VINF_SUCCESS; 567 568 if (pThis->freq > 0) 569 { 570 rc = sb16StreamOpen(pDevIns, pThis, pStream); 571 if (RT_SUCCESS(rc)) 572 sb16UpdateVolume(pThis); 573 } 574 575 if (RT_SUCCESS(rc)) 576 { 577 rc = sb16StreamEnable(pThis, pStream, true /* fEnable */, false /* fForce */); 578 if (RT_SUCCESS(rc)) 579 { 580 sb16TimerSet(pDevIns, pStream, pStream->cTicksTimerIOInterval); 581 582 PDMDevHlpDMASchedule(pThis->pDevInsR3); 583 } 584 } 337 585 } 338 586 else 339 587 { 340 if (pThis->cStreamsActive) 341 pThis->cStreamsActive--; 342 sb16TimerMaybeStop(pThis); 343 } 344 } 345 346 /** 347 * @callback_method_impl{PFNTMTIMERDEV} 348 */ 349 static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser) 350 { 351 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE); 352 RT_NOREF(pvUser, hTimer); 353 354 pThis->can_write = 1; 355 PDMDevHlpISASetIrq(pDevIns, pThis->irq, 1); 588 sb16StreamEnable(pThis, pStream, false /* fEnable */, false /* fForce */); 589 } 356 590 } 357 591 … … 361 595 static void continue_dma8(PPDMDEVINS pDevIns, PSB16STATE pThis) 362 596 { 363 sb16CheckAndReOpenOut(pDevIns, pThis);364 597 sb16Control(pDevIns, pThis, 1); 365 598 } … … 484 717 } 485 718 486 sb16CheckAndReOpenOut(pDevIns, pThis);487 719 sb16Control(pDevIns, pThis, 1); 488 720 sb16SpeakerControl(pThis, 1); 489 721 } 490 722 491 static inline void dsp_out_data (PSB16STATE pThis, uint8_t val) 492 { 493 LogFlowFunc(("outdata %#x\n", val)); 494 if ((size_t) pThis->out_data_len < sizeof (pThis->out_data)) { 495 pThis->out_data[pThis->out_data_len++] = val; 496 } 497 } 498 499 static inline uint8_t dsp_get_data (PSB16STATE pThis) 500 { 501 if (pThis->in_index) { 502 return pThis->in2_data[--pThis->in_index]; 503 } 504 LogFlowFunc(("buffer underflow\n")); 723 static inline void dsp_set_data(PSB16STATE pThis, uint8_t val) 724 { 725 LogFlowFunc(("%#x\n", val)); 726 if ((size_t) pThis->dsp_out_data_len < sizeof (pThis->dsp_out_data)) 727 pThis->dsp_out_data[pThis->dsp_out_data_len++] = val; 728 } 729 730 static inline uint8_t dsp_get_data(PSB16STATE pThis) 731 { 732 if (pThis->dsp_in_idx) 733 return pThis->dsp_in_data[--pThis->dsp_in_idx]; 734 AssertMsgFailed(("DSP input buffer underflow\n")); 505 735 return 0; 506 736 } … … 524 754 } 525 755 526 pThis-> needed_bytes = 3;756 pThis->dsp_in_needed_bytes = 3; 527 757 } 528 758 else 529 759 { 530 pThis-> needed_bytes = 0;760 pThis->dsp_in_needed_bytes = 0; 531 761 532 762 switch (cmd) 533 763 { 534 764 case 0x03: 535 dsp_ out_data(pThis, 0x10); /* pThis->csp_param); */765 dsp_set_data(pThis, 0x10); /* pThis->csp_param); */ 536 766 goto warn; 537 767 538 768 case 0x04: 539 pThis-> needed_bytes = 1;769 pThis->dsp_in_needed_bytes = 1; 540 770 goto warn; 541 771 542 772 case 0x05: 543 pThis-> needed_bytes = 2;773 pThis->dsp_in_needed_bytes = 2; 544 774 goto warn; 545 775 … … 549 779 550 780 case 0x0e: 551 pThis-> needed_bytes = 2;781 pThis->dsp_in_needed_bytes = 2; 552 782 goto warn; 553 783 554 784 case 0x09: 555 dsp_ out_data(pThis, 0xf8);785 dsp_set_data(pThis, 0xf8); 556 786 goto warn; 557 787 558 788 case 0x0f: 559 pThis-> needed_bytes = 1;789 pThis->dsp_in_needed_bytes = 1; 560 790 goto warn; 561 791 562 792 case 0x10: 563 pThis-> needed_bytes = 1;793 pThis->dsp_in_needed_bytes = 1; 564 794 goto warn; 565 795 566 796 case 0x14: 567 pThis-> needed_bytes = 2;797 pThis->dsp_in_needed_bytes = 2; 568 798 pThis->block_size = 0; 569 799 break; … … 574 804 575 805 case 0x20: /* Direct ADC, Juice/PL */ 576 dsp_ out_data(pThis, 0xff);806 dsp_set_data(pThis, 0xff); 577 807 goto warn; 578 808 … … 584 814 pThis->freq = -1; 585 815 pThis->time_const = -1; 586 pThis-> needed_bytes = 1;816 pThis->dsp_in_needed_bytes = 1; 587 817 break; 588 818 … … 590 820 pThis->freq = -1; 591 821 pThis->time_const = -1; 592 pThis-> needed_bytes = 2;822 pThis->dsp_in_needed_bytes = 2; 593 823 break; 594 824 … … 596 826 pThis->freq = -1; 597 827 pThis->time_const = -1; 598 pThis-> needed_bytes = 2;828 pThis->dsp_in_needed_bytes = 2; 599 829 goto warn; 600 830 601 831 case 0x45: 602 dsp_ out_data(pThis, 0xaa);832 dsp_set_data(pThis, 0xaa); 603 833 goto warn; 604 834 … … 607 837 608 838 case 0x48: 609 pThis-> needed_bytes = 2;839 pThis->dsp_in_needed_bytes = 2; 610 840 break; 611 841 612 842 case 0x74: 613 pThis-> needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */843 pThis->dsp_in_needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */ 614 844 LogFlowFunc(("0x75 - DMA DAC, 4-bit ADPCM not implemented\n")); 615 845 break; 616 846 617 847 case 0x75: /* DMA DAC, 4-bit ADPCM Reference */ 618 pThis-> needed_bytes = 2;848 pThis->dsp_in_needed_bytes = 2; 619 849 LogFlowFunc(("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n")); 620 850 break; 621 851 622 852 case 0x76: /* DMA DAC, 2.6-bit ADPCM */ 623 pThis-> needed_bytes = 2;853 pThis->dsp_in_needed_bytes = 2; 624 854 LogFlowFunc(("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n")); 625 855 break; 626 856 627 857 case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */ 628 pThis-> needed_bytes = 2;858 pThis->dsp_in_needed_bytes = 2; 629 859 LogFlowFunc(("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n")); 630 860 break; … … 641 871 642 872 case 0x80: 643 pThis-> needed_bytes = 2;873 pThis->dsp_in_needed_bytes = 2; 644 874 break; 645 875 … … 684 914 685 915 case 0xe0: /* DSP identification */ 686 pThis-> needed_bytes = 1;916 pThis->dsp_in_needed_bytes = 1; 687 917 break; 688 918 689 919 case 0xe1: 690 dsp_ out_data(pThis, pThis->ver & 0xff);691 dsp_ out_data(pThis, pThis->ver >> 8);920 dsp_set_data(pThis, pThis->ver & 0xff); 921 dsp_set_data(pThis, pThis->ver >> 8); 692 922 break; 693 923 694 924 case 0xe2: 695 pThis-> needed_bytes = 1;925 pThis->dsp_in_needed_bytes = 1; 696 926 goto warn; 697 927 … … 699 929 { 700 930 for (int i = sizeof (e3) - 1; i >= 0; --i) 701 dsp_ out_data(pThis, e3[i]);931 dsp_set_data(pThis, e3[i]); 702 932 703 933 break; … … 705 935 706 936 case 0xe4: /* write test reg */ 707 pThis-> needed_bytes = 1;937 pThis->dsp_in_needed_bytes = 1; 708 938 break; 709 939 … … 713 943 714 944 case 0xe8: /* read test reg */ 715 dsp_ out_data(pThis, pThis->test_reg);945 dsp_set_data(pThis, pThis->test_reg); 716 946 break; 717 947 718 948 case 0xf2: 719 949 case 0xf3: 720 dsp_ out_data(pThis, 0xaa);950 dsp_set_data(pThis, 0xaa); 721 951 pThis->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2; 722 952 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1); … … 725 955 case 0xf8: 726 956 /* Undocumented, used by old Creative diagnostic programs. */ 727 dsp_ out_data(pThis, 0);957 dsp_set_data(pThis, 0); 728 958 goto warn; 729 959 730 960 case 0xf9: 731 pThis-> needed_bytes = 1;961 pThis->dsp_in_needed_bytes = 1; 732 962 goto warn; 733 963 734 964 case 0xfa: 735 dsp_ out_data(pThis, 0);965 dsp_set_data(pThis, 0); 736 966 goto warn; 737 967 738 968 case 0xfc: /* FIXME */ 739 dsp_ out_data(pThis, 0);969 dsp_set_data(pThis, 0); 740 970 goto warn; 741 971 … … 746 976 } 747 977 748 if (!pThis-> needed_bytes)978 if (!pThis->dsp_in_needed_bytes) 749 979 LogFlow(("\n")); 750 980 751 981 exit: 752 982 753 if (!pThis-> needed_bytes)983 if (!pThis->dsp_in_needed_bytes) 754 984 pThis->cmd = -1; 755 985 else … … 759 989 760 990 warn: 761 LogFlowFunc(("warning: command %#x,%d is not truly understood yet\n", cmd, pThis-> needed_bytes));991 LogFlowFunc(("warning: command %#x,%d is not truly understood yet\n", cmd, pThis->dsp_in_needed_bytes)); 762 992 goto exit; 763 993 } 764 994 765 static uint16_t dsp_get_lohi (PSB16STATE pThis) 766 { 767 uint8_t hi = dsp_get_data (pThis); 768 uint8_t lo = dsp_get_data (pThis); 769 return (hi << 8) | lo; 770 } 771 772 static uint16_t dsp_get_hilo (PSB16STATE pThis) 773 { 774 uint8_t lo = dsp_get_data (pThis); 775 uint8_t hi = dsp_get_data (pThis); 776 return (hi << 8) | lo; 777 } 778 779 static void complete(PPDMDEVINS pDevIns, PSB16STATE pThis) 780 { 781 int d0, d1, d2; 782 LogFlowFunc(("complete command %#x, in_index %d, needed_bytes %d\n", pThis->cmd, pThis->in_index, pThis->needed_bytes)); 995 DECLINLINE(uint16_t) dsp_get_lohi(PSB16STATE pThis) 996 { 997 const uint8_t hi = dsp_get_data(pThis); 998 const uint8_t lo = dsp_get_data(pThis); 999 return RT_MAKE_U16(lo, hi); 1000 } 1001 1002 DECLINLINE(uint16_t) dsp_get_hilo(PSB16STATE pThis) 1003 { 1004 const uint8_t lo = dsp_get_data(pThis); 1005 const uint8_t hi = dsp_get_data(pThis); 1006 return RT_MAKE_U16(lo, hi); 1007 } 1008 1009 static void dsp_cmd_complete(PPDMDEVINS pDevIns, PSB16STATE pThis) 1010 { 1011 LogFlowFunc(("Command %#x, in_index %d, needed_bytes %d\n", pThis->cmd, pThis->dsp_in_idx, pThis->dsp_in_needed_bytes)); 1012 1013 int v0, v1, v2; 1014 1015 PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT]; /** @ŧodo Improve this. */ 783 1016 784 1017 if (pThis->cmd > 0xaf && pThis->cmd < 0xd0) 785 1018 { 786 d2 = dsp_get_data(pThis);787 d1 = dsp_get_data(pThis);788 d0 = dsp_get_data(pThis);1019 v2 = dsp_get_data(pThis); 1020 v1 = dsp_get_data(pThis); 1021 v0 = dsp_get_data(pThis); 789 1022 790 1023 if (pThis->cmd & 8) 791 LogFlowFunc(("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, d0, d1, d2));1024 LogFlowFunc(("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, v0, v1, v2)); 792 1025 else 793 1026 { 794 LogFlowFunc(("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, d0, d1, d2));795 dma_cmd(pDevIns, pThis, pThis->cmd, d0, d1 + (d2 << 8));1027 LogFlowFunc(("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, v0, v1, v2)); 1028 dma_cmd(pDevIns, pThis, pThis->cmd, v0, v1 + (v2 << 8)); 796 1029 } 797 1030 } … … 800 1033 switch (pThis->cmd) 801 1034 { 802 case 0x04: 803 pThis->csp_mode = dsp_get_data (pThis); 804 pThis->csp_reg83r = 0; 805 pThis->csp_reg83w = 0; 806 LogFlowFunc(("CSP command 0x04: mode=%#x\n", pThis->csp_mode)); 807 break; 808 809 case 0x05: 810 pThis->csp_param = dsp_get_data (pThis); 811 pThis->csp_value = dsp_get_data (pThis); 812 LogFlowFunc(("CSP command 0x05: param=%#x value=%#x\n", pThis->csp_param, pThis->csp_value)); 813 break; 814 815 case 0x0e: 816 { 817 d0 = dsp_get_data(pThis); 818 d1 = dsp_get_data(pThis); 819 LogFlowFunc(("write CSP register %d <- %#x\n", d1, d0)); 820 if (d1 == 0x83) 1035 case 0x04: 821 1036 { 822 LogFlowFunc(("0x83[%d] <- %#x\n", pThis->csp_reg83r, d0)); 823 pThis->csp_reg83[pThis->csp_reg83r % 4] = d0; 824 pThis->csp_reg83r += 1; 1037 pThis->csp_mode = dsp_get_data(pThis); 1038 pThis->csp_reg83r = 0; 1039 pThis->csp_reg83w = 0; 1040 LogFlowFunc(("CSP command 0x04: mode=%#x\n", pThis->csp_mode)); 1041 break; 825 1042 } 826 else 827 pThis->csp_regs[d1] = d0; 828 break; 1043 1044 case 0x05: 1045 { 1046 pThis->csp_param = dsp_get_data(pThis); 1047 pThis->csp_value = dsp_get_data(pThis); 1048 LogFlowFunc(("CSP command 0x05: param=%#x value=%#x\n", pThis->csp_param, pThis->csp_value)); 1049 break; 1050 } 1051 1052 case 0x0e: 1053 { 1054 v0 = dsp_get_data(pThis); 1055 v1 = dsp_get_data(pThis); 1056 LogFlowFunc(("write CSP register %d <- %#x\n", v1, v0)); 1057 if (v1 == 0x83) 1058 { 1059 LogFlowFunc(("0x83[%d] <- %#x\n", pThis->csp_reg83r, v0)); 1060 pThis->csp_reg83[pThis->csp_reg83r % 4] = v0; 1061 pThis->csp_reg83r += 1; 1062 } 1063 else 1064 pThis->csp_regs[v1] = v0; 1065 break; 1066 } 1067 1068 case 0x0f: 1069 { 1070 v0 = dsp_get_data(pThis); 1071 LogFlowFunc(("read CSP register %#x -> %#x, mode=%#x\n", v0, pThis->csp_regs[v0], pThis->csp_mode)); 1072 if (v0 == 0x83) 1073 { 1074 LogFlowFunc(("0x83[%d] -> %#x\n", pThis->csp_reg83w, pThis->csp_reg83[pThis->csp_reg83w % 4])); 1075 dsp_set_data(pThis, pThis->csp_reg83[pThis->csp_reg83w % 4]); 1076 pThis->csp_reg83w += 1; 1077 } 1078 else 1079 dsp_set_data(pThis, pThis->csp_regs[v0]); 1080 break; 1081 } 1082 1083 case 0x10: 1084 v0 = dsp_get_data(pThis); 1085 LogFlowFunc(("cmd 0x10 d0=%#x\n", v0)); 1086 break; 1087 1088 case 0x14: 1089 dma_cmd8(pDevIns, pThis, 0, dsp_get_lohi(pThis) + 1); 1090 break; 1091 1092 case 0x22: /* Sets the master volume. */ 1093 /** @todo Setting the master volume is not implemented yet. */ 1094 break; 1095 1096 case 0x40: /* Sets the timer constant; SB16 is able to use sample rates via 0x41 instead. */ 1097 pThis->time_const = dsp_get_data(pThis); 1098 LogFlowFunc(("set time const %d\n", pThis->time_const)); 1099 break; 1100 1101 case 0x42: /* Sets the input rate (in Hz). */ 1102 #if 0 1103 LogFlowFunc(("cmd 0x42 might not do what it think it should\n")); 1104 #endif 1105 RT_FALL_THROUGH(); /** @todo BUGBUG FT2 sets output freq with this, go figure. */ 1106 1107 case 0x41: /* Sets the output rate (in Hz). */ 1108 { 1109 pThis->freq = dsp_get_hilo(pThis); 1110 LogFlowFunc(("set freq %d\n", pThis->freq)); 1111 break; 1112 } 1113 1114 case 0x48: 1115 { 1116 pThis->block_size = dsp_get_lohi(pThis) + 1; 1117 LogFlowFunc(("set dma block len %d\n", pThis->block_size)); 1118 break; 1119 } 1120 1121 case 0x74: 1122 RT_FALL_THROUGH(); 1123 case 0x75: 1124 RT_FALL_THROUGH(); 1125 case 0x76: 1126 RT_FALL_THROUGH(); 1127 case 0x77: 1128 /* ADPCM stuff, ignore. */ 1129 break; 1130 1131 case 0x80: /* Sets the IRQ. */ 1132 { 1133 sb16StreamTransferScheduleNext(pThis, pStream, dsp_get_lohi(pThis) + 1); 1134 break; 1135 } 1136 1137 case 0xe0: 1138 { 1139 v0 = dsp_get_data(pThis); 1140 pThis->dsp_out_data_len = 0; 1141 LogFlowFunc(("E0=%#x\n", v0)); 1142 dsp_set_data(pThis, ~v0); 1143 break; 1144 } 1145 1146 case 0xe2: 1147 { 1148 v0 = dsp_get_data(pThis); 1149 LogFlowFunc(("E2=%#x\n", v0)); 1150 break; 1151 } 1152 1153 case 0xe4: 1154 pThis->test_reg = dsp_get_data(pThis); 1155 break; 1156 1157 case 0xf9: 1158 v0 = dsp_get_data(pThis); 1159 switch (v0) 1160 { 1161 case 0x0e: 1162 dsp_set_data(pThis, 0xff); 1163 break; 1164 1165 case 0x0f: 1166 dsp_set_data(pThis, 0x07); 1167 break; 1168 1169 case 0x37: 1170 dsp_set_data(pThis, 0x38); 1171 break; 1172 1173 default: 1174 dsp_set_data(pThis, 0x00); 1175 break; 1176 } 1177 break; 1178 1179 default: 1180 LogRel2(("SB16: Unrecognized command %#x, skipping\n", pThis->cmd)); 1181 return; 829 1182 } 830 831 case 0x0f: 832 d0 = dsp_get_data(pThis); 833 LogFlowFunc(("read CSP register %#x -> %#x, mode=%#x\n", d0, pThis->csp_regs[d0], pThis->csp_mode)); 834 if (d0 == 0x83) 835 { 836 LogFlowFunc(("0x83[%d] -> %#x\n", pThis->csp_reg83w, pThis->csp_reg83[pThis->csp_reg83w % 4])); 837 dsp_out_data(pThis, pThis->csp_reg83[pThis->csp_reg83w % 4]); 838 pThis->csp_reg83w += 1; 839 } 840 else 841 dsp_out_data(pThis, pThis->csp_regs[d0]); 842 break; 843 844 case 0x10: 845 d0 = dsp_get_data(pThis); 846 LogFlowFunc(("cmd 0x10 d0=%#x\n", d0)); 847 break; 848 849 case 0x14: 850 dma_cmd8(pDevIns, pThis, 0, dsp_get_lohi (pThis) + 1); 851 break; 852 853 case 0x40: 854 pThis->time_const = dsp_get_data(pThis); 855 LogFlowFunc(("set time const %d\n", pThis->time_const)); 856 break; 857 858 case 0x42: /* FT2 sets output freq with this, go figure */ 859 #if 0 860 LogFlowFunc(("cmd 0x42 might not do what it think it should\n")); 861 #endif 862 case 0x41: 863 pThis->freq = dsp_get_hilo(pThis); 864 LogFlowFunc(("set freq %d\n", pThis->freq)); 865 break; 866 867 case 0x48: 868 pThis->block_size = dsp_get_lohi(pThis) + 1; 869 LogFlowFunc(("set dma block len %d\n", pThis->block_size)); 870 break; 871 872 case 0x74: 873 case 0x75: 874 case 0x76: 875 case 0x77: 876 /* ADPCM stuff, ignore */ 877 break; 878 879 case 0x80: 880 { 881 uint32_t const freq = pThis->freq > 0 ? pThis->freq : 11025; 882 uint32_t const samples = dsp_get_lohi(pThis) + 1; 883 uint32_t const bytes = samples << pThis->fmt_stereo << (pThis->fmt_bits == 16 ? 1 : 0); 884 uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerIRQ); 885 uint64_t const cTicks = (bytes * uTimerHz) / freq; 886 if (cTicks < uTimerHz / 1024) 887 PDMDevHlpISASetIrq(pDevIns, pThis->irq, 1); 888 else 889 PDMDevHlpTimerSetRelative(pDevIns, pThis->hTimerIRQ, cTicks, NULL); 890 LogFlowFunc(("mix silence: %d samples, %d bytes, %RU64 ticks\n", samples, bytes, cTicks)); 891 break; 892 } 893 894 case 0xe0: 895 d0 = dsp_get_data(pThis); 896 pThis->out_data_len = 0; 897 LogFlowFunc(("E0 data = %#x\n", d0)); 898 dsp_out_data(pThis, ~d0); 899 break; 900 901 case 0xe2: 902 d0 = dsp_get_data(pThis); 903 LogFlow(("SB16:E2 = %#x\n", d0)); 904 break; 905 906 case 0xe4: 907 pThis->test_reg = dsp_get_data(pThis); 908 break; 909 910 case 0xf9: 911 d0 = dsp_get_data(pThis); 912 LogFlowFunc(("command 0xf9 with %#x\n", d0)); 913 switch (d0) { 914 case 0x0e: 915 dsp_out_data(pThis, 0xff); 916 break; 917 918 case 0x0f: 919 dsp_out_data(pThis, 0x07); 920 break; 921 922 case 0x37: 923 dsp_out_data(pThis, 0x38); 924 break; 925 926 default: 927 dsp_out_data(pThis, 0x00); 928 break; 929 } 930 break; 931 932 default: 933 LogFlowFunc(("complete: unrecognized command %#x\n", pThis->cmd)); 934 return; 935 } 936 } 937 938 LogFlow(("\n")); 1183 } 1184 939 1185 pThis->cmd = -1; 940 1186 return; … … 950 1196 pThis->fmt_stereo = 0; 951 1197 952 /* At the moment we only have one stream, the output stream. */ 953 PSB16STREAM pStream = &pThis->StreamOut; 954 955 pStream->Cfg.enmDir = PDMAUDIODIR_OUT; 956 pStream->Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT; 957 pStream->Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; 958 959 PDMAudioPropsInit(&pStream->Cfg.Props, 1 /* 8-bit */, false /* fSigned */, 1 /* Mono */, pThis->freq); 960 RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output"); 961 962 sb16StreamClose(pThis, pStream); 1198 /* 1199 * Reset all streams. 1200 */ 1201 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++) 1202 { 1203 sb16StreamEnable(pThis, &pThis->aStreams[i], false /* fEnable */, false /* fForce */); 1204 sb16StreamReset(pThis, &pThis->aStreams[i]); 1205 } 963 1206 } 964 1207 … … 974 1217 pThis->mixer_regs[0x82] = 0; 975 1218 pThis->dma_auto = 0; 976 pThis-> in_index = 0;977 pThis-> out_data_len = 0;1219 pThis->dsp_in_idx = 0; 1220 pThis->dsp_out_data_len = 0; 978 1221 pThis->left_till_irq = 0; 979 pThis-> needed_bytes = 0;1222 pThis->dsp_in_needed_bytes = 0; 980 1223 pThis->block_size = -1; 981 1224 pThis->nzero = 0; … … 984 1227 pThis->cmd = -1; 985 1228 986 dsp_ out_data(pThis, 0xaa);1229 dsp_set_data(pThis, 0xaa); 987 1230 sb16SpeakerControl(pThis, 0); 988 1231 … … 1036 1279 1037 1280 case 0x39: 1038 dsp_ out_data(pThis, 0x38);1281 dsp_set_data(pThis, 0x38); 1039 1282 sb16CmdReset(pDevIns, pThis); 1040 1283 pThis->v2x6 = 0x39; … … 1052 1295 break; 1053 1296 #endif 1054 if (0 == pThis-> needed_bytes)1297 if (0 == pThis->dsp_in_needed_bytes) 1055 1298 { 1056 1299 sb16HandleCommand(pDevIns, pThis, u32); … … 1063 1306 else 1064 1307 { 1065 if (pThis-> in_index == sizeof (pThis->in2_data))1308 if (pThis->dsp_in_idx == sizeof (pThis->dsp_in_data)) 1066 1309 { 1067 LogFlowFunc(("indata overrun\n"));1310 AssertMsgFailed(("DSP input data overrun\n")); 1068 1311 } 1069 1312 else 1070 1313 { 1071 pThis-> in2_data[pThis->in_index++] = u32;1072 if (pThis-> in_index == pThis->needed_bytes)1314 pThis->dsp_in_data[pThis->dsp_in_idx++] = u32; 1315 if (pThis->dsp_in_idx == pThis->dsp_in_needed_bytes) 1073 1316 { 1074 pThis-> needed_bytes = 0;1075 complete(pDevIns, pThis);1317 pThis->dsp_in_needed_bytes = 0; 1318 dsp_cmd_complete(pDevIns, pThis); 1076 1319 #if 0 1077 1320 log_dsp (pThis); … … 1112 1355 1113 1356 case 4: /* read data */ 1114 if (pThis-> out_data_len)1357 if (pThis->dsp_out_data_len) 1115 1358 { 1116 retval = pThis-> out_data[--pThis->out_data_len];1359 retval = pThis->dsp_out_data[--pThis->dsp_out_data_len]; 1117 1360 pThis->last_read_byte = retval; 1118 1361 } … … 1136 1379 1137 1380 case 8: /* data available status | irq 8 ack */ 1138 retval = (!pThis-> out_data_len || pThis->highspeed) ? 0 : 0x80;1381 retval = (!pThis->dsp_out_data_len || pThis->highspeed) ? 0 : 0x80; 1139 1382 if (pThis->mixer_regs[0x82] & 1) 1140 1383 { … … 1168 1411 1169 1412 1170 /* -=-=-=-=-=- Mixer -=-=-=-=-=- */ 1413 /********************************************************************************************************************************* 1414 * Mixer functions * 1415 *********************************************************************************************************************************/ 1171 1416 1172 1417 static uint8_t sb16MixRegToVol(PSB16STATE pThis, int reg) … … 1274 1519 /* Update the master (mixer) and PCM out volumes. */ 1275 1520 sb16UpdateVolume(pThis); 1521 1522 /* 1523 * Reset mixer sinks. 1524 * 1525 * Do the reset here instead of in sb16StreamReset(); 1526 * the mixer sink(s) might still have data to be processed when an audio stream gets reset. 1527 */ 1528 if (pThis->pSinkOut) 1529 AudioMixerSinkReset(pThis->pSinkOut); 1276 1530 } 1277 1531 … … 1549 1803 1550 1804 1551 /* -=-=-=-=-=- DMA -=-=-=-=-=- */ 1805 /********************************************************************************************************************************* 1806 * DMA handling * 1807 *********************************************************************************************************************************/ 1552 1808 1553 1809 /** 1554 1810 * Worker for sb16DMARead. 1555 1811 */ 1556 static int sb16WriteAudio(PSB16STATE pThis,1557 PSB16STREAM pStream, int nchan, uint32_t dma_pos, uint32_t dma_len, uint32_t len, uint32_t *pcbWritten)1558 {1559 uint8_t abBuf[_4K]; /** @todo Have a buffer on the heap. */1560 1561 uint32_t cbToWrite = len;1562 uint32_t cbWrittenTotal = 0;1563 1564 PAUDMIXSINK pDstMixSink = pThis->pSinkOut;1565 AssertPtrReturn(pDstMixSink, VERR_INVALID_POINTER);1566 1567 int rc = VINF_SUCCESS;1568 1569 while (cbToWrite)1570 {1571 uint32_t cbToRead = RT_MIN(dma_len - dma_pos, cbToWrite);1572 if (cbToRead > (int)sizeof(abBuf)) /** @todo BUGBUG Clean this up. */1573 cbToRead = (int)sizeof(abBuf);1574 1575 uint32_t cbRead = 0;1576 rc = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, nchan, abBuf, dma_pos, cbToRead, &cbRead);1577 AssertMsgRCReturn(rc, ("Reading from DMA failed, rc=%Rrc\n", rc), rc);1578 1579 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))1580 { /* likely */ }1581 else1582 AudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMA, abBuf, cbRead, 0 /* fFlags */);1583 1584 /*1585 * Write data to the backends.1586 */1587 uint32_t cbWritten = 0;1588 rc = AudioMixerSinkWrite(pDstMixSink, AUDMIXOP_COPY, abBuf, cbRead, &cbWritten);1589 if ( !cbWritten /* Nothing written? */1590 || RT_FAILURE(rc))1591 break;1592 1593 Assert(cbToWrite >= cbWritten);1594 cbToWrite -= cbWritten;1595 dma_pos = (dma_pos + cbWritten) % dma_len;1596 cbWrittenTotal += cbWritten;1597 }1598 1599 if (pcbWritten)1600 *pcbWritten = cbWrittenTotal;1601 1602 return rc;1603 }1604 1812 1605 1813 /** … … 1610 1818 1611 1819 { 1612 PSB16STATE pThis 1820 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE); 1613 1821 AssertPtr(pThis); 1614 1822 PSB16STREAM pStream = (PSB16STREAM)pvUser; … … 1631 1839 till = pThis->left_till_irq; 1632 1840 1633 #ifdef DEBUG_SB16_MOST 1634 LogFlowFunc(("pos:%06d %d till:%d len:%d\n", off, free, till, cb)); 1635 #endif 1841 Log4Func(("pos=%d %d, till=%d, len=%d\n", off, free, till, cb)); 1636 1842 1637 1843 if (copy >= till) … … 1650 1856 STAM_COUNTER_ADD(&pThis->StatBytesRead, copy); 1651 1857 1652 uint32_t cbWritten;1653 int rc = sb16 WriteAudio(pThis, pStream, uChannel, off, cb, copy, &cbWritten);1858 uint32_t written = 0; /* Shut up GCC. */ 1859 int rc = sb16StreamDoDmaOutput(pThis, pStream, uChannel, off, cb, copy, &written); 1654 1860 AssertRC(rc); 1655 1861 1656 1862 /** @todo Convert the rest to uin32_t / size_t. */ 1657 off = (off + (int)cbWritten) % cb; 1658 pThis->left_till_irq -= (int)cbWritten; 1863 off = (off + (int)written) % cb; 1864 pThis->left_till_irq -= (int)written; /** @todo r=andy left_till_irq can be < 0. Correct? Revisit this. */ 1865 1866 Log3Func(("pos %d/%d, free=%d, till=%d, copy=%d, written=%RU32, block_size=%d\n", 1867 off, cb, free, pThis->left_till_irq, copy, copy, pThis->block_size)); 1659 1868 1660 1869 if (pThis->left_till_irq <= 0) 1661 1870 { 1662 1871 pThis->mixer_regs[0x82] |= (uChannel & 4) ? 2 : 1; 1872 1663 1873 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1); 1664 if (0 == pThis->dma_auto) 1874 1875 if (0 == pThis->dma_auto) /** @todo r=andy BUGBUG Why do we first assert the IRQ if dma_auto is 0? Revisit this. */ 1665 1876 { 1666 1877 sb16Control(pDevIns, pThis, 0); … … 1669 1880 } 1670 1881 1671 Log3Func(("pos %d/%d, free=%5d, till=%5d, copy=%5d, written=%RU32, block_size=%5d\n",1672 off, cb, free, pThis->left_till_irq, copy, cbWritten, pThis->block_size));1673 1674 1882 while (pThis->left_till_irq <= 0) 1675 1883 pThis->left_till_irq += pThis->block_size; … … 1679 1887 1680 1888 1681 /* -=-=-=-=-=- I/O timer -=-=-=-=-=- */ 1682 1683 static void sb16TimerMaybeStart(PPDMDEVINS pDevIns, PSB16STATE pThis) 1684 { 1685 LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive)); 1686 1687 if (pThis->cStreamsActive == 0) /* Only start the timer if there are no active streams. */ 1688 return; 1689 1690 /* Set timer flag. */ 1691 ASMAtomicWriteBool(&pThis->fTimerActive, true); 1692 1693 /* Update current time timestamp. */ 1694 uint64_t tsNow = PDMDevHlpTimerGet(pDevIns, pThis->hTimerIO); 1695 pThis->tsTimerIO = tsNow; 1696 1697 /* Arm the timer. */ 1698 PDMDevHlpTimerSet(pDevIns, pThis->hTimerIO, tsNow + pThis->cTicksTimerIOInterval); 1699 } 1700 1701 /** 1702 * This clears fTimerActive if no streams are active, so that the timer won't be 1703 * rearmed then next time it fires. 1704 */ 1705 static void sb16TimerMaybeStop(PSB16STATE pThis) 1706 { 1707 LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive)); 1708 1709 if (pThis->cStreamsActive) /* Some streams still active? Bail out. */ 1710 return; 1711 1712 /* Clear the timer flag. */ 1713 ASMAtomicWriteBool(&pThis->fTimerActive, false); 1889 /********************************************************************************************************************************* 1890 * Timer-related code * 1891 *********************************************************************************************************************************/ 1892 1893 /** 1894 * @callback_method_impl{PFNTMTIMERDEV} 1895 */ 1896 static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser) 1897 { 1898 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE); 1899 RT_NOREF(pvUser, hTimer); 1900 1901 LogFlowFuncEnter(); 1902 1903 pThis->can_write = 1; 1904 PDMDevHlpISASetIrq(pDevIns, pThis->irq, 1); 1905 } 1906 1907 /** 1908 * Sets the stream's I/O timer to a new expiration time. 1909 * 1910 * @param pDevIns The device instance. 1911 * @param pStream SB16 stream to set timer for. 1912 * @param cTicksToDeadline The number of ticks to the new deadline. 1913 */ 1914 DECLINLINE(void) sb16TimerSet(PPDMDEVINS pDevIns, PSB16STREAM pStream, uint64_t cTicksToDeadline) 1915 { 1916 int rc = PDMDevHlpTimerSetRelative(pDevIns, pStream->hTimerIO, cTicksToDeadline, NULL /*pu64Now*/); 1917 AssertRC(rc); 1714 1918 } 1715 1919 … … 1719 1923 static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser) 1720 1924 { 1721 RT_NOREF(pvUser); 1925 PSB16STREAM pStream = (PSB16STREAM)pvUser; 1926 AssertPtrReturnVoid(pStream); 1722 1927 1723 1928 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE); 1724 1929 STAM_PROFILE_START(&pThis->StatTimerIO, a); 1725 Assert(hTimer == pThis->hTimerIO); 1726 1727 uint64_t cTicksNow = PDMDevHlpTimerGet(pDevIns, hTimer); 1728 1729 pThis->tsTimerIO = cTicksNow; 1730 1731 int rc2 = AudioMixerSinkUpdate(pThis->pSinkOut); 1732 AssertRC(rc2); 1733 1734 bool fTimerActive = ASMAtomicReadBool(&pThis->fTimerActive); 1735 bool fArmTimer = fTimerActive; 1930 AssertReturnVoid(hTimer == pStream->hTimerIO); 1931 1932 const uint64_t cTicksNow = PDMDevHlpTimerGet(pDevIns, pStream->hTimerIO); 1933 1934 pStream->tsTimerIO = cTicksNow; 1935 1936 PAUDMIXSINK pSink = sb16StreamIndexToSink(pThis, pStream->uIdx); 1937 AssertPtrReturnVoid(pSink); 1938 1939 const bool fSinkActive = AudioMixerSinkIsActive(pSink); 1940 1941 LogFlowFunc(("fSinkActive=%RTbool\n", fSinkActive)); 1942 1943 #ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 1944 sb16StreamAsyncIONotify(pDevIns, pStream); 1945 #else 1946 sb16StreamUpdate(pStream, pSink); 1947 #endif 1736 1948 1737 1949 /* Schedule the next transfer. */ 1738 1950 PDMDevHlpDMASchedule(pDevIns); 1739 1951 1740 /* Arm the timer at least one more time. */ 1741 fArmTimer = true; 1742 1743 /* 1744 * Recording. 1745 */ 1746 /** @todo Implement recording. */ 1747 1748 if (fArmTimer) 1749 { 1750 /* Arm the timer again. */ 1751 uint64_t cTicks = pThis->cTicksTimerIOInterval; 1952 if (fSinkActive) 1953 { 1752 1954 /** @todo adjust cTicks down by now much cbOutMin represents. */ 1753 PDMDevHlpTimerSet(pDevIns, hTimer, cTicksNow + cTicks);1955 sb16TimerSet(pDevIns, pStream, pStream->cTicksTimerIOInterval); 1754 1956 } 1755 1957 … … 1758 1960 1759 1961 1760 /* -=-=-=-=-=- Streams? -=-=-=-=-=- */ 1962 /********************************************************************************************************************************* 1963 * Driver handling * 1964 *********************************************************************************************************************************/ 1761 1965 1762 1966 /** … … 1905 2109 if (pDrvStream->pMixStrm) 1906 2110 { 1907 LogFlowFunc(("[LUN#%RU8]\n", pDrv ));2111 LogFlowFunc(("[LUN#%RU8]\n", pDrv->uLUN)); 1908 2112 1909 2113 AudioMixerSinkRemoveStream(pMixSink, pDrvStream->pMixStrm); … … 1946 2150 int rc = VINF_SUCCESS; 1947 2151 1948 if (AudioHlpStreamCfgIsValid(&pThis->StreamOut.Cfg)) 1949 rc = sb16AddDrvStream(pDevIns, pThis->pSinkOut, &pThis->StreamOut.Cfg, pDrv); 2152 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++) 2153 { 2154 if (AudioHlpStreamCfgIsValid(&pThis->aStreams[i].Cfg)) 2155 { 2156 int rc2 = sb16AddDrvStream(pDevIns, 2157 sb16StreamIndexToSink(pThis, pThis->aStreams[i].uIdx), &pThis->aStreams[i].Cfg, pDrv); 2158 if (RT_SUCCESS(rc)) 2159 rc = rc2; 2160 } 2161 } 1950 2162 1951 2163 return rc; … … 1976 2188 } 1977 2189 1978 /** 1979 * Checks if the output stream needs to be (re-)created and does so if needed. 1980 * 1981 * @return VBox status code. 1982 * @param pDevIns The device instance. 1983 * @param pThis SB16 state. 1984 */ 1985 static int sb16CheckAndReOpenOut(PPDMDEVINS pDevIns, PSB16STATE pThis) 1986 { 1987 AssertPtr(pThis); 2190 2191 /********************************************************************************************************************************* 2192 * Stream handling * 2193 *********************************************************************************************************************************/ 2194 2195 static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, 2196 int uDmaChan, uint32_t offDma, uint32_t cbDma, uint32_t cbToRead, uint32_t *pcbRead) 2197 { 2198 uint32_t cbFree = (uint32_t)RTCircBufFree(pStream->State.pCircBuf); 2199 uint32_t cbReadTotal = 0; 2200 2201 //Assert(cbToRead <= cbFree); /** @todo Add STAM value for overflows. */ 2202 2203 cbToRead = RT_MIN(cbToRead, cbFree); 1988 2204 1989 2205 int rc = VINF_SUCCESS; 1990 2206 1991 PSB16STREAM pStream = &pThis->StreamOut; 1992 1993 if (pThis->freq > 0) 1994 { 1995 sb16StreamEnable(pDevIns, pThis, pStream, false /* fEnable */); 1996 1997 sb16StreamEnable(pDevIns, pThis, pStream, true /* fEnable */); 1998 } 1999 else 2000 sb16StreamEnable(pDevIns, pThis, pStream, false /* fEnable */); 2001 2002 LogFlowFuncLeaveRC(rc); 2207 void *pv; 2208 size_t cb; 2209 2210 while (cbToRead) 2211 { 2212 uint32_t cbChunk = RT_MIN(cbDma - offDma, cbToRead); 2213 2214 RTCircBufAcquireWriteBlock(pStream->State.pCircBuf, cbChunk, &pv, &cb); 2215 2216 uint32_t cbRead; 2217 rc = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, uDmaChan, pv, offDma, cb, &cbRead); 2218 AssertMsgRCReturn(rc, ("Reading from DMA failed, rc=%Rrc\n", rc), rc); 2219 Assert(cbRead == cb); 2220 2221 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled)) 2222 { /* likely */ } 2223 else 2224 AudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMA, pv, cbRead, 0 /* fFlags */); 2225 2226 RTCircBufReleaseWriteBlock(pStream->State.pCircBuf, cbRead); 2227 2228 Assert(cbToRead >= cbRead); 2229 cbToRead -= cbRead; 2230 offDma = (offDma + cbRead) % cbDma; 2231 cbReadTotal += cbRead; 2232 } 2233 2234 if (pcbRead) 2235 *pcbRead = cbReadTotal; 2236 2003 2237 return rc; 2004 2238 } … … 2008 2242 * 2009 2243 * @returns VBox status code. 2010 * @param pDevIns The device instance.2011 2244 * @param pThis The SB16 state. 2012 2245 * @param pStream The SB16 stream to enable or disable. 2013 2246 * @param fEnable Whether to enable or disable the stream. 2014 */ 2015 static int sb16StreamEnable(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream, bool fEnable) 2016 { 2017 int rc = VINF_SUCCESS; 2018 2019 if (fEnable) 2247 * @param fForce Whether to force re-opening the stream or not. 2248 * Otherwise re-opening only will happen if the PCM properties have changed. 2249 */ 2250 static int sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable, bool fForce) 2251 { 2252 if ( !fForce 2253 && fEnable == pStream->State.fEnabled) 2254 return VINF_SUCCESS; 2255 2256 LogFlowFunc(("fEnable=%RTbool, fForce=%RTbool, fStreamEnabled=%RTbool\n", fEnable, fForce, pStream->State.fEnabled)); 2257 2258 /* First, enable or disable the stream and the stream's sink. */ 2259 int rc = AudioMixerSinkCtl(sb16StreamIndexToSink(pThis, pStream->uIdx), 2260 fEnable ? AUDMIXSINKCMD_ENABLE : AUDMIXSINKCMD_DISABLE); 2261 AssertRCReturn(rc, rc); 2262 2263 pStream->State.fEnabled = fEnable; 2264 2265 return rc; 2266 } 2267 2268 /** 2269 * Retrieves the audio mixer sink of a corresponding SB16 stream. 2270 * 2271 * @returns Pointer to audio mixer sink if found, or NULL if not found / invalid. 2272 * @param pThis The SB16 state. 2273 * @param uIdx Stream index to get audio mixer sink for. 2274 */ 2275 DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx) 2276 { 2277 AssertReturn(uIdx <= SB16_MAX_STREAMS, NULL); 2278 2279 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */ 2280 if (uIdx == SB16_IDX_OUT) 2281 return pThis->pSinkOut; /* Can be NULL if not configured / set up yet. */ 2282 2283 AssertMsgFailed(("No sink attached (yet) for index %RU8\n", uIdx)); 2284 return NULL; 2285 } 2286 2287 /** 2288 * Returns the audio direction of a specified stream descriptor. 2289 * 2290 * @returns Audio direction. 2291 * @param uIdx Stream index to get audio direction for. 2292 */ 2293 DECLINLINE(PDMAUDIODIR) sb16GetDirFromIndex(uint8_t uIdx) 2294 { 2295 AssertReturn(uIdx <= SB16_MAX_STREAMS, PDMAUDIODIR_INVALID); 2296 2297 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */ 2298 if (uIdx == SB16_IDX_OUT) 2299 return PDMAUDIODIR_OUT; 2300 2301 return PDMAUDIODIR_INVALID; 2302 } 2303 2304 /** 2305 * Creates a SB16 audio stream. 2306 * 2307 * @returns VBox status code. 2308 * @param pThisCC The SB16 state. 2309 * @param pStream The SB16 stream to create. 2310 * @param uIdx Stream index to assign. 2311 */ 2312 static int sb16StreamCreate(PSB16STATE pThis, PSB16STREAM pStream, uint8_t uIdx) 2313 { 2314 LogFlowFuncEnter(); 2315 2316 pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled; 2317 2318 int rc2; 2319 2320 #ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 2321 rc2 = sb16StreamAsyncIOCreate(pThis->pDevInsR3, pThis, pStream); 2322 AssertRCReturn(rc2, rc2); 2323 #endif 2324 2325 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled)) 2326 { /* likely */ } 2327 else 2328 { 2329 char szFile[64]; 2330 2331 if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_IN) 2332 RTStrPrintf(szFile, sizeof(szFile), "sb16StreamWriteSD%RU8", pStream->uIdx); 2333 else 2334 RTStrPrintf(szFile, sizeof(szFile), "sb16StreamReadSD%RU8", pStream->uIdx); 2335 2336 char szPath[RTPATH_MAX]; 2337 rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThis->Dbg.pszOutPath, szFile, 2338 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE); 2339 AssertRC(rc2); 2340 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStream->Dbg.Runtime.pFileDMA); 2341 AssertRC(rc2); 2342 2343 /* Delete stale debugging files from a former run. */ 2344 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA); 2345 } 2346 2347 pStream->uIdx = uIdx; 2348 2349 return VINF_SUCCESS; 2350 } 2351 2352 /** 2353 * Destroys a SB16 audio stream. 2354 * 2355 * @returns VBox status code. 2356 * @param pDevIns The device instance. 2357 * @param pThis The SB16 state. 2358 * @param pStream The SB16 stream to destroy. 2359 */ 2360 static int sb16StreamDestroy(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream) 2361 { 2362 LogFlowFuncEnter(); 2363 2364 sb16StreamClose(pDevIns, pThis, pStream); 2365 2366 #ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 2367 int rc = sb16StreamAsyncIODestroy(pDevIns, pStream); 2368 AssertRCReturn(rc, rc); 2369 #else 2370 RT_NOREF(pDevIns); 2371 #endif 2372 2373 if (pStream->State.pCircBuf) 2374 { 2375 RTCircBufDestroy(pStream->State.pCircBuf); 2376 pStream->State.pCircBuf = NULL; 2377 } 2378 2379 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled)) 2380 { /* likely */ } 2381 else 2382 { 2383 AudioHlpFileDestroy(pStream->Dbg.Runtime.pFileDMA); 2384 pStream->Dbg.Runtime.pFileDMA = NULL; 2385 } 2386 2387 pStream->uIdx = UINT8_MAX; 2388 2389 return VINF_SUCCESS; 2390 } 2391 2392 /** 2393 * Resets a SB16 stream. 2394 * 2395 * @param pThis The SB16 state. 2396 * @param pStream The SB16 stream to reset. 2397 */ 2398 static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream) 2399 { 2400 LogFlowFuncEnter(); 2401 2402 switch (pStream->uIdx) 2403 { 2404 case SB16_IDX_OUT: 2405 { 2406 pStream->Cfg.enmDir = PDMAUDIODIR_OUT; 2407 pStream->Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT; 2408 pStream->Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; 2409 2410 PDMAudioPropsInit(&pStream->Cfg.Props, 1 /* 8-bit */, false /* fSigned */, 1 /* Mono */, pThis->freq); 2411 RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output"); 2412 2413 break; 2414 } 2415 2416 default: 2417 AssertFailed(); 2418 break; 2419 } 2420 2421 /** @todo Also reset corresponding DSP values here? */ 2422 } 2423 2424 /** 2425 * Opens a SB16 stream with its current mixer settings. 2426 * 2427 * @returns VBox status code. 2428 * @param pDevIns The device instance. 2429 * @param pThis The SB16 device state. 2430 * @param pStream The SB16 stream to open. 2431 * 2432 * @note This currently only supports the one and only output stream. 2433 */ 2434 static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream) 2435 { 2436 LogFlowFuncEnter(); 2437 2438 PDMAUDIOSTREAMCFG Cfg; 2439 RT_ZERO(Cfg); 2440 2441 /** @todo Implement mixer sink selection (and it's in/out + destination mapping) here once we add more streams. */ 2442 PDMAudioPropsInit(&Cfg.Props, pThis->fmt_bits / 8, pThis->fmt_signed != 0, 1 << pThis->fmt_stereo, pThis->freq); 2443 2444 /* Bail out early if the PCM properties did not change. */ 2445 if (PDMAudioPropsAreEqual(&Cfg.Props, &pStream->Cfg.Props)) 2446 return VINF_SUCCESS; 2447 2448 PDMAUDIODSTSRCUNION dstSrc; 2449 PDMAUDIODIR enmDir; 2450 2451 switch (pStream->uIdx) 2452 { 2453 case SB16_IDX_OUT: 2454 { 2455 Cfg.enmDir = PDMAUDIODIR_OUT; 2456 Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT; 2457 Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; 2458 2459 RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Output"); 2460 2461 dstSrc.enmDst = PDMAUDIOPLAYBACKDST_FRONT; 2462 enmDir = PDMAUDIODIR_OUT; 2463 break; 2464 } 2465 2466 default: 2467 AssertFailed(); 2468 break; 2469 } 2470 2471 LogRel2(("SB16: (Re-)Opening stream '%s' (%RU32Hz, %RU8 channels, %s%RU8)\n", Cfg.szName, Cfg.Props.uHz, 2472 PDMAudioPropsChannels(&Cfg.Props), Cfg.Props.fSigned ? "S" : "U", PDMAudioPropsSampleBits(&Cfg.Props))); 2473 2474 int rc = PDMAudioStrmCfgCopy(&pStream->Cfg, &Cfg); 2475 if (RT_FAILURE(rc)) 2476 return rc; 2477 2478 /* (Re-)create the stream's internal ring buffer. */ 2479 if (pStream->State.pCircBuf) 2480 { 2481 RTCircBufDestroy(pStream->State.pCircBuf); 2482 pStream->State.pCircBuf = NULL; 2483 } 2484 2485 const uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, 2 * 10 /* ms */); 2486 2487 rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf); 2488 AssertRCReturn(rc, rc); 2489 2490 /* Set scheduling hint (if available). */ 2491 if (pStream->cTicksTimerIOInterval) 2492 pStream->Cfg.Device.cMsSchedulingHint = 1000 /* ms */ 2493 / ( PDMDevHlpTimerGetFreq(pDevIns, pStream->hTimerIO) 2494 / RT_MIN(pStream->cTicksTimerIOInterval, 1)); 2495 2496 PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx); 2497 AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER); 2498 2499 sb16RemoveDrvStreams(pDevIns, pThis, 2500 sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.u); 2501 2502 rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg); 2503 2504 if (RT_SUCCESS(rc)) 2020 2505 { 2021 2506 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled)) … … 2023 2508 else 2024 2509 { 2025 if (!AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA)) 2510 /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */ 2511 if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA)) 2026 2512 { 2027 int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS, 2028 &pStream->Cfg.Props); 2029 AssertRC(rc2); 2513 AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA); 2514 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA); 2030 2515 } 2516 2517 int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS, 2518 &pStream->Cfg.Props); 2519 AssertRC(rc2); 2031 2520 } 2032 2033 rc = sb16StreamOpen(pDevIns, pThis, pStream); 2034 } 2035 else 2036 sb16StreamClose(pThis, pStream); 2037 2038 if (RT_SUCCESS(rc)) 2039 { 2040 rc = AudioMixerSinkCtl(sb16StreamToSink(pThis, pStream), 2041 fEnable ? AUDMIXSINKCMD_ENABLE : AUDMIXSINKCMD_DISABLE); 2042 } 2043 2044 return rc; 2045 } 2046 2047 /** 2048 * Retrieves the audio mixer sink of a corresponding SB16 stream. 2049 * 2050 * @returns Pointer to audio mixer sink if found, or NULL if not found / invalid. 2051 * @param pThis The SB16 state. 2052 * @param pStream Stream to get audio mixer sink for. 2053 */ 2054 DECLINLINE(PAUDMIXSINK) sb16StreamToSink(PSB16STATE pThis, PSB16STREAM pStream) 2055 { 2056 /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */ 2057 if (pStream->Cfg.enmDir == PDMAUDIODIR_OUT) 2058 return pThis->pSinkOut; /* Can be NULL if not configured / set up yet. */ 2059 2060 AssertFailed(); 2061 return NULL; 2062 } 2063 2064 /** 2065 * Creates a SB16 audio stream. 2066 * 2067 * @returns VBox status code. 2068 * @param pThisCC The SB16 state. 2069 * @param pStream The SB16 stream to create. 2070 * @param fIn Whether this is the input (recording) or output (playback) stream. 2071 */ 2072 static int sb16StreamCreate(PSB16STATE pThis, PSB16STREAM pStream, bool fIn) 2073 { 2074 LogFlowFuncEnter(); 2075 2076 pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled; 2077 2078 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled)) 2079 { /* likely */ } 2080 else 2081 { 2082 char szFile[64]; 2083 RTStrPrintf(szFile, sizeof(szFile), "sb16Stream%s", fIn ? "In" : "Out"); 2084 2085 char szPath[RTPATH_MAX]; 2086 int rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThis->Dbg.pszOutPath, szFile, 2087 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE); 2088 AssertRC(rc2); 2089 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStream->Dbg.Runtime.pFileDMA); 2090 AssertRC(rc2); 2091 2092 /* Delete stale debugging files from a former run. */ 2093 AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA); 2094 } 2095 2096 return VINF_SUCCESS; 2097 } 2098 2099 /** 2100 * Destroys a SB16 audio stream. 2101 * 2102 * @returns VBox status code. 2103 * @param pThis The SB16 state. 2104 * @param pStream The SB16 stream to destroy. 2105 */ 2106 static int sb16StreamDestroy(PSB16STATE pThis, PSB16STREAM pStream) 2107 { 2108 RT_NOREF(pThis); 2109 2110 LogFlowFuncEnter(); 2111 2112 sb16StreamClose(pThis, pStream); 2113 2114 if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled)) 2115 { /* likely */ } 2116 else 2117 { 2118 AudioHlpFileDestroy(pStream->Dbg.Runtime.pFileDMA); 2119 pStream->Dbg.Runtime.pFileDMA = NULL; 2120 } 2121 2122 return VINF_SUCCESS; 2123 } 2124 2125 /** 2126 * Opens a SB16 stream with its current mixer settings. 2127 * 2128 * @returns VBox status code. 2129 * @param pDevIns The device instance. 2130 * @param pThis The SB16 device state. 2131 * @param pStream The SB16 stream to open. 2132 * 2133 * @note This currently only supports the one and only output stream. 2134 */ 2135 static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream) 2136 { 2137 LogFlowFuncEnter(); 2138 2139 PAUDMIXSINK pMixerSink; 2140 PDMAUDIODSTSRCUNION dstSrc; 2141 PDMAUDIODIR enmDir; 2142 2143 int rc = VINF_SUCCESS; 2144 2145 { 2146 /** @todo Implement mixer sink selection (and it's in/out + destination mapping) here once we add more streams. */ 2147 PDMAUDIOSTREAMCFG Cfg; 2148 RT_ZERO(Cfg); 2149 PDMAudioPropsInit(&Cfg.Props, pThis->fmt_bits / 8, pThis->fmt_signed != 0, 1 << pThis->fmt_stereo, pThis->freq); 2150 2151 if (!PDMAudioStrmCfgMatchesProps(&Cfg, &pStream->Cfg.Props)) 2152 { 2153 Cfg.enmDir = PDMAUDIODIR_OUT; 2154 Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT; 2155 Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; 2156 2157 RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Output"); 2158 2159 rc = PDMAudioStrmCfgCopy(&pStream->Cfg, &Cfg); 2160 } 2161 2162 pMixerSink = pThis->pSinkOut; 2163 dstSrc.enmDst = PDMAUDIOPLAYBACKDST_FRONT; 2164 enmDir = PDMAUDIODIR_OUT; 2165 } 2166 2167 if (RT_FAILURE(rc)) 2168 return rc; 2169 2170 /* Set scheduling hint (if available). */ 2171 if (pThis->cTicksTimerIOInterval) 2172 pStream->Cfg.Device.cMsSchedulingHint = 1000 /* ms */ 2173 / ( PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerIO) 2174 / RT_MIN(pThis->cTicksTimerIOInterval, 1)); 2175 2176 sb16RemoveDrvStreams(pDevIns, pThis, pMixerSink, enmDir, dstSrc); 2177 2178 rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg); 2179 if (RT_SUCCESS(rc)) 2180 sb16UpdateVolume(pThis); 2521 } 2181 2522 2182 2523 LogFlowFuncLeaveRC(rc); … … 2187 2528 * Closes a SB16 stream. 2188 2529 * 2530 * @param pDevIns The device instance. 2189 2531 * @param pThis SB16 state. 2190 2532 * @param pStream The SB16 stream to close. 2191 2533 */ 2192 static void sb16StreamClose(P SB16STATE pThis, PSB16STREAM pStream)2193 { 2194 RT_NOREF(p This, pStream);2534 static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream) 2535 { 2536 RT_NOREF(pDevIns, pThis, pStream); 2195 2537 2196 2538 LogFlowFuncEnter(); 2197 2539 2198 LogFlowFuncLeave(); 2199 } 2200 2201 2202 /* -=-=-=-=-=- Saved state -=-=-=-=-=- */ 2540 /* Nothing to do in here right now. */ 2541 } 2542 2543 static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cbBytes) 2544 { 2545 RT_NOREF(pStream); 2546 2547 #if 0 2548 uint32_t const freq = pThis->freq > 0 ? pThis->freq : 11025; 2549 uint32_t const bytes = cSamples << pThis->fmt_stereo << (pThis->fmt_bits == 16 ? 1 : 0); 2550 uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pThis->pDevInsR3, pThis->hTimerIRQ); 2551 uint64_t const cTicks = (bytes * uTimerHz) / freq; 2552 #endif 2553 2554 uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pThis->pDevInsR3, pThis->hTimerIRQ); 2555 2556 const uint64_t usBytes = PDMAudioPropsBytesToMicro(&pStream->Cfg.Props, cbBytes); 2557 const uint64_t cTransferTicks = PDMDevHlpTimerFromMicro(pThis->pDevInsR3, pThis->hTimerIRQ, usBytes); 2558 2559 LogFlowFunc(("%RU32 bytes -> %RU64 ticks\n", cbBytes, cTransferTicks)); 2560 2561 if (cTransferTicks < uTimerHz / 1024) /** @todo Explain this. */ 2562 { 2563 LogFlowFunc(("IRQ\n")); 2564 PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1); 2565 } 2566 else 2567 { 2568 LogFlowFunc(("Scheduled\n")); 2569 PDMDevHlpTimerSetRelative(pThis->pDevInsR3, pThis->hTimerIRQ, cTransferTicks, NULL); 2570 } 2571 } 2572 2573 /** 2574 * Main function for off-DMA data processing by the audio backend(s). 2575 * 2576 * Might be called by a timer callback or by an async I/O worker thread, depending 2577 * on the configuration. 2578 * 2579 * @param pStream The SB16 stream to open. 2580 * @param pSink Mixer sink to use for updating. 2581 */ 2582 static void sb16StreamUpdate(PSB16STREAM pStream, PAUDMIXSINK pSink) 2583 { 2584 if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_OUT) 2585 { 2586 uint32_t const cbSinkWritable = AudioMixerSinkGetWritable(pSink); 2587 uint32_t const cbStreamReadable = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf); 2588 2589 uint32_t cbToReadFromStream = RT_MIN(cbStreamReadable, cbSinkWritable); 2590 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */ 2591 cbToReadFromStream = PDMAudioPropsFloorBytesToFrame(&pStream->Cfg.Props, cbToReadFromStream); 2592 2593 Log3Func(("[SD%RU8] cbSinkWritable=%RU32, cbStreamReadable=%RU32\n", pStream->uIdx, cbSinkWritable, cbStreamReadable)); 2594 2595 void /*const*/ *pvSrcBuf; 2596 size_t cbSrcBuf; 2597 2598 while (cbToReadFromStream > 0) 2599 { 2600 uint32_t cbWritten = 0; 2601 2602 RTCircBufAcquireReadBlock(pStream->State.pCircBuf, cbToReadFromStream, &pvSrcBuf, &cbSrcBuf); 2603 2604 int rc2 = AudioMixerSinkWrite(pSink, AUDMIXOP_COPY, pvSrcBuf, cbSrcBuf, &cbWritten); 2605 AssertRC(rc2); 2606 Assert(cbWritten <= cbSrcBuf); 2607 2608 RTCircBufReleaseReadBlock(pStream->State.pCircBuf, cbWritten); 2609 2610 Assert(cbToReadFromStream >= cbWritten); 2611 cbToReadFromStream -= cbWritten; 2612 } 2613 } 2614 else 2615 AssertFailed(); /** @todo Recording not implemented yet. */ 2616 2617 int rc2 = AudioMixerSinkUpdate(pSink); 2618 AssertRC(rc2); 2619 } 2620 2621 2622 /********************************************************************************************************************************* 2623 * Saved state handling * 2624 *********************************************************************************************************************************/ 2203 2625 2204 2626 /** … … 2229 2651 pHlp->pfnSSMPutS32(pSSM, pThis->port); 2230 2652 pHlp->pfnSSMPutS32(pSSM, pThis->ver); 2231 pHlp->pfnSSMPutS32(pSSM, pThis-> in_index);2232 pHlp->pfnSSMPutS32(pSSM, pThis-> out_data_len);2653 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_idx); 2654 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_out_data_len); 2233 2655 pHlp->pfnSSMPutS32(pSSM, pThis->fmt_stereo); 2234 2656 pHlp->pfnSSMPutS32(pSSM, pThis->fmt_signed); … … 2243 2665 pHlp->pfnSSMPutS32(pSSM, pThis->time_const); 2244 2666 pHlp->pfnSSMPutS32(pSSM, pThis->speaker); 2245 pHlp->pfnSSMPutS32(pSSM, pThis-> needed_bytes);2667 pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_needed_bytes); 2246 2668 pHlp->pfnSSMPutS32(pSSM, pThis->cmd); 2247 2669 pHlp->pfnSSMPutS32(pSSM, pThis->use_hdma); … … 2260 2682 pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83w); 2261 2683 2262 pHlp->pfnSSMPutMem(pSSM, pThis-> in2_data, sizeof(pThis->in2_data));2263 pHlp->pfnSSMPutMem(pSSM, pThis-> out_data, sizeof(pThis->out_data));2684 pHlp->pfnSSMPutMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data)); 2685 pHlp->pfnSSMPutMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data)); 2264 2686 pHlp->pfnSSMPutU8 (pSSM, pThis->test_reg); 2265 2687 pHlp->pfnSSMPutU8 (pSSM, pThis->last_read_byte); … … 2299 2721 pHlp->pfnSSMGetS32(pSSM, &pThis->port); 2300 2722 pHlp->pfnSSMGetS32(pSSM, &pThis->ver); 2301 pHlp->pfnSSMGetS32(pSSM, &pThis-> in_index);2302 pHlp->pfnSSMGetS32(pSSM, &pThis-> out_data_len);2723 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_idx); 2724 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_out_data_len); 2303 2725 pHlp->pfnSSMGetS32(pSSM, &pThis->fmt_stereo); 2304 2726 pHlp->pfnSSMGetS32(pSSM, &pThis->fmt_signed); … … 2313 2735 pHlp->pfnSSMGetS32(pSSM, &pThis->time_const); 2314 2736 pHlp->pfnSSMGetS32(pSSM, &pThis->speaker); 2315 pHlp->pfnSSMGetS32(pSSM, &pThis-> needed_bytes);2737 pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_needed_bytes); 2316 2738 pHlp->pfnSSMGetS32(pSSM, &pThis->cmd); 2317 2739 pHlp->pfnSSMGetS32(pSSM, &pThis->use_hdma); … … 2330 2752 pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83w); 2331 2753 2332 pHlp->pfnSSMGetMem(pSSM, pThis-> in2_data, sizeof(pThis->in2_data));2333 pHlp->pfnSSMGetMem(pSSM, pThis-> out_data, sizeof(pThis->out_data));2754 pHlp->pfnSSMGetMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data)); 2755 pHlp->pfnSSMGetMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data)); 2334 2756 pHlp->pfnSSMGetU8 (pSSM, &pThis->test_reg); 2335 2757 pHlp->pfnSSMGetU8 (pSSM, &pThis->last_read_byte); … … 2350 2772 if (pThis->dma_running) 2351 2773 { 2352 sb16CheckAndReOpenOut(pDevIns, pThis);2353 2774 sb16Control(pDevIns, pThis, 1); 2354 2775 sb16SpeakerControl(pThis, pThis->speaker); … … 2410 2831 2411 2832 2412 /* -=-=-=-=-=- IBase -=-=-=-=-=- */ 2833 /********************************************************************************************************************************* 2834 * IBase implementation * 2835 *********************************************************************************************************************************/ 2413 2836 2414 2837 /** … … 2425 2848 2426 2849 2427 /* -=-=-=-=-=- Device -=-=-=-=-=- */ 2850 /********************************************************************************************************************************* 2851 * Device (PDM) handling * 2852 *********************************************************************************************************************************/ 2428 2853 2429 2854 /** … … 2590 3015 PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE); 2591 3016 3017 LogRel2(("SB16: Reset\n")); 3018 2592 3019 /* Bring back the device to initial state, and especially make 2593 3020 * sure there's no interrupt or DMA activity. … … 2600 3027 2601 3028 pThis->dma_auto = 0; 2602 pThis-> in_index = 0;2603 pThis-> out_data_len = 0;3029 pThis->dsp_in_idx = 0; 3030 pThis->dsp_out_data_len = 0; 2604 3031 pThis->left_till_irq = 0; 2605 pThis-> needed_bytes = 0;3032 pThis->dsp_in_needed_bytes = 0; 2606 3033 pThis->block_size = -1; 2607 3034 pThis->nzero = 0; … … 2630 3057 * Destroy all streams. 2631 3058 */ 2632 sb16StreamClose(pThis, &pThis->StreamOut); 2633 sb16StreamDestroy(pThis, &pThis->StreamOut); 2634 /** @todo Add removal + destruction of other streams here once we support them. */ 3059 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++) 3060 sb16StreamDestroy(pDevIns, pThis, &pThis->aStreams[i]); 2635 3061 2636 3062 /* … … 2773 3199 * For now we have one stream only, namely the output (playback) stream. 2774 3200 */ 2775 rc = sb16StreamCreate(pThis, &pThis->StreamOut, false /* fIn */); 2776 AssertRCReturn(rc, rc); 3201 AssertCompile(RT_ELEMENTS(pThis->aStreams) == SB16_MAX_STREAMS); 3202 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++) 3203 { 3204 rc = sb16StreamCreate(pThis, &pThis->aStreams[i], i /* uIdx */); 3205 AssertRCReturn(rc, rc); 3206 } 2777 3207 2778 3208 /* … … 2791 3221 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IRQ", &pThis->hTimerIRQ); 2792 3222 AssertRCReturn(rc, rc); 2793 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, pThis, 2794 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IO", &pThis->hTimerIO); 2795 AssertRCReturn(rc, rc); 2796 pThis->cTicksTimerIOInterval = PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerIO) / uTimerHz; 2797 pThis->tsTimerIO = PDMDevHlpTimerGet(pDevIns, pThis->hTimerIO); 2798 LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTicksTimerIOInterval, uTimerHz)); 3223 3224 static const char * const s_apszNames[] = { "SB16 OUT" }; 3225 AssertCompile(RT_ELEMENTS(s_apszNames) == SB16_MAX_STREAMS); 3226 for (unsigned i = 0; i < SB16_MAX_STREAMS; i++) 3227 { 3228 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, &pThis->aStreams[i], 3229 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, s_apszNames[i], &pThis->aStreams[i].hTimerIO); 3230 AssertRCReturn(rc, rc); 3231 3232 pThis->aStreams[i].cTicksTimerIOInterval = PDMDevHlpTimerGetFreq(pDevIns, pThis->aStreams[i].hTimerIO) / uTimerHz; 3233 pThis->aStreams[i].tsTimerIO = PDMDevHlpTimerGet(pDevIns, pThis->aStreams[i].hTimerIO); 3234 } 2799 3235 2800 3236 /* … … 2835 3271 AssertRCReturn(rc, rc); 2836 3272 2837 rc = PDMDevHlpDMARegister(pDevIns, pThis->hdma, sb16DMARead, &pThis-> StreamOut/* pvUser */);3273 rc = PDMDevHlpDMARegister(pDevIns, pThis->hdma, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */); 2838 3274 AssertRCReturn(rc, rc); 2839 rc = PDMDevHlpDMARegister(pDevIns, pThis->dma, sb16DMARead, &pThis-> StreamOut/* pvUser */);3275 rc = PDMDevHlpDMARegister(pDevIns, pThis->dma, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */); 2840 3276 AssertRCReturn(rc, rc); 2841 3277 … … 2847 3283 rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec); 2848 3284 AssertRCReturn(rc, rc); 3285 3286 # ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO 3287 LogRel(("SB16: Asynchronous I/O enabled\n")); 3288 # endif 2849 3289 2850 3290 /*
Note:
See TracChangeset
for help on using the changeset viewer.