windows-nt/Source/XPSP1/NT/base/mvdm/vdd/vsndblst/vsb.c
2020-09-26 16:20:57 +08:00

473 lines
10 KiB
C

/***************************************************************************
*
* vsb.c
*
* Copyright (c) 1991-1996 Microsoft Corporation. All Rights Reserved.
*
* This code provides VDD support for SB 2.0 sound output, specifically:
* DSP 2.01+ (excluding SB-MIDI port)
* Mixer Chip CT1335 (not strictly part of SB 2.0, but apps seem to like it)
* FM Chip OPL2 (a.k.a. Adlib)
*
***************************************************************************/
/*****************************************************************************
*
* #includes
*
*****************************************************************************/
#include <windows.h> // The VDD is a win32 DLL
#include <mmsystem.h> // Multi-media APIs
#include <vddsvc.h> // Definition of VDD calls
#include <vsb.h>
#include <dsp.h>
#include <mixer.h>
#include <fm.h>
/*****************************************************************************
*
* Globals
*
*****************************************************************************/
//
// Definitions for MM api entry points. The functions will be linked
// dynamically to avoid bringing winmm.dll in before wow32.
//
SETVOLUMEPROC SetVolumeProc;
GETNUMDEVSPROC GetNumDevsProc;
GETDEVCAPSPROC GetDevCapsProc;
OPENPROC OpenProc;
RESETPROC ResetProc;
CLOSEPROC CloseProc;
GETPOSITIONPROC GetPositionProc;
WRITEPROC WriteProc;
PREPAREHEADERPROC PrepareHeaderProc;
UNPREPAREHEADERPROC UnprepareHeaderProc;
BOOL bWaveOutActive = FALSE;
BOOL bWinmmLoaded = FALSE;
BOOL LoadWinmm(VOID);
BOOL InitDevices(VOID);
HINSTANCE hWinmm;
/*
* General globals
*/
HINSTANCE GlobalHInstance; // handle passed to dll entry point
WORD BasePort; // Where the card is mapped
/*****************************************************************************
*
* General Functions
*
*****************************************************************************/
/*
* DLL entry point routine.
* Returns TRUE on success.
*/
BOOL WINAPI
DllEntryPoint(
HINSTANCE hInstance,
DWORD reason,
LPVOID reserved
)
{
switch (reason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInstance);
// save instance
GlobalHInstance = hInstance;
// Hook i/o ports
if (!InstallIoHook(hInstance)) {
dprintf1(("VDD failed to load"));
#if 0
MessageBoxA(NULL, "Unable to load wave out device",
"Sound Blaster VDD", MB_OK | MB_ICONEXCLAMATION);
#endif
return FALSE;
}
if (!DspProcessAttach()) {
DeInstallIoHook(hInstance);
return FALSE;
}
#if 0
{
char buf[256];
wsprintfA(buf, "Sound blaster VDD loaded at port %3X, IRQ %d, DMA Channel %d, %s OPL2/Adlib support.",
BasePort, SB_INTERRUPT, SB_DMA_CHANNEL,
(FMActive ? "with" : "without"));
MessageBoxA(NULL, buf, "Sound Blaster VDD", MB_OK | MB_ICONINFORMATION);
}
#endif // DBG
return TRUE;
case DLL_PROCESS_DETACH:
DspProcessDetach();
DeInstallIoHook(hInstance);
if (bWaveOutActive) {
CloseFMDevice();
SetSpeaker(TRUE);
}
if (bWinmmLoaded) {
FreeLibrary(hWinmm);
}
return TRUE;
default:
return TRUE;
}
}
/***************************************************************************/
//
// LoadWinmm()
//
// This function dynamically loads the "waveOutxxx" entry points. This
// is done because there is code in WINMM which does certain things in a
// WOW vdm. If we do static links, then winmm may get loaded way before
// WOW32, in which case it can't do the things it should.
//
BOOL
LoadWinmm(
VOID
)
{
if (!(hWinmm = LoadLibrary(L"WINMM.DLL"))) {
return FALSE;
}
//BUGBUG: Should check for error return from getprocaddress
//
SetVolumeProc = (SETVOLUMEPROC) GetProcAddress(hWinmm, "waveOutSetVolume");
GetNumDevsProc = (GETNUMDEVSPROC) GetProcAddress(hWinmm, "waveOutGetNumDevs");
GetDevCapsProc = (GETDEVCAPSPROC) GetProcAddress(hWinmm, "waveOutGetDevCapsW");
OpenProc = (OPENPROC) GetProcAddress(hWinmm, "waveOutOpen");
ResetProc = (RESETPROC) GetProcAddress(hWinmm, "waveOutReset");
CloseProc = (CLOSEPROC) GetProcAddress(hWinmm, "waveOutClose");
GetPositionProc = (GETPOSITIONPROC) GetProcAddress(hWinmm, "waveOutGetPosition");
WriteProc = (WRITEPROC) GetProcAddress(hWinmm, "waveOutWrite");
PrepareHeaderProc = (PREPAREHEADERPROC) GetProcAddress(hWinmm, "waveOutPrepareHeader");
UnprepareHeaderProc = (UNPREPAREHEADERPROC) GetProcAddress(hWinmm, "waveOutUnprepareHeader");
return TRUE;
}
/***************************************************************************/
//
// InitDevices()
//
// This function tries to get handles to the waveout and FM devices.
//
BOOL
InitDevices(
VOID
)
{
static BOOL bTriedLoadAndFailed = FALSE;
if (bWaveOutActive) {
return TRUE;
}
if (bTriedLoadAndFailed) {
return FALSE;
}
if (!bWinmmLoaded) {
if (!LoadWinmm()) {
bTriedLoadAndFailed = TRUE;
return FALSE;
}
bWinmmLoaded = TRUE;
}
if (!FindWaveDevice()) {
return FALSE;
}
bWaveOutActive = TRUE;
OpenFMDevice();
return TRUE;
}
/***************************************************************************/
/*
* Hooks i/o ports with i/o handlers.
* Sets BasePort and returns TRUE if successful.
*/
BOOL
InstallIoHook(
HINSTANCE hInstance
)
{
int i;
WORD ports[] = { 0x220, 0x210, 0x230, 0x240, 0x250, 0x260, 0x270 };
VDD_IO_HANDLERS handlers = {
VsbByteIn,
NULL,
NULL,
NULL,
VsbByteOut,
NULL,
NULL,
NULL};
// try each base port until success is achieved
for (i = 0; i < sizeof(ports) / sizeof(ports[0]); i++ ) {
VDD_IO_PORTRANGE PortRange[5];
PortRange[0].First = ports[i] + 0x04;
PortRange[0].Last = ports[i] + 0x06;
PortRange[1].First = ports[i] + 0x08;
PortRange[1].Last = ports[i] + 0x0A;
PortRange[2].First = ports[i] + 0x0C;
PortRange[2].Last = ports[i] + 0x0C;
PortRange[3].First = ports[i] + 0x0E;
PortRange[3].Last = ports[i] + 0x0E;
PortRange[4].First = 0x388;
PortRange[4].Last = 0x389;
if (VDDInstallIOHook((HANDLE)hInstance, 5, PortRange, &handlers)) {
dprintf2(("Device installed at %X", ports[i]));
BasePort = ports[i];
return TRUE;
}
}
return FALSE;
}
/***************************************************************************/
/*
* Remove our i/o hook.
*/
VOID
DeInstallIoHook(
HINSTANCE hInstance
)
{
VDD_IO_PORTRANGE PortRange[5];
PortRange[0].First = BasePort + 0x04;
PortRange[0].Last = BasePort + 0x06;
PortRange[1].First = BasePort + 0x08;
PortRange[1].Last = BasePort + 0x0A;
PortRange[2].First = BasePort + 0x0C;
PortRange[2].Last = BasePort + 0x0C;
PortRange[3].First = BasePort + 0x0E;
PortRange[3].Last = BasePort + 0x0E;
PortRange[4].First = 0x388;
PortRange[4].Last = 0x389;
VDDDeInstallIOHook((HANDLE)hInstance, 5, PortRange);
}
/***************************************************************************/
/*
* Gets called when the application reads from port.
* Returns results to application in data.
*/
VOID
VsbByteIn(
WORD port,
BYTE * data
)
{
// If we fail simulate nothing at the port
*data = 0xFF;
//
// make sure we are linked in with winmm
//
if (!bWaveOutActive) {
if (!InitDevices()) {
// no wave device, forget it
return;
}
}
switch (port - BasePort) {
case READ_STATUS:
DspReadStatus(data);
break;
case READ_DATA:
DspReadData(data);
break;
case WRITE_STATUS:
// Can always write
*data = 0x7F;
break;
case MIXER_ADDRESS:
// apps sometimes read from this port??
break;
case MIXER_DATA:
MixerDataRead(data);
break;
case 0x8:
// remap to ADLIB_STATUS_PORT
port = ADLIB_STATUS_PORT;
break;
}
switch(port) {
case ADLIB_STATUS_PORT:
FMStatusRead(data);
break;
}
dprintf4(("Read %4X, <= %2X", port, *data));
}
/***************************************************************************/
/*
* Gets called when an application writes data to port.
*/
VOID
VsbByteOut(
WORD port,
BYTE data
)
{
//
// make sure we are linked in with winmm
//
if (!bWaveOutActive) {
if (!InitDevices()) {
// no wave device, forget it
return;
}
}
dprintf4(("Write %4X, => %2X", port, data));
switch (port - BasePort) {
case RESET_PORT:
DspResetWrite(data);
break;
case WRITE_PORT:
DspWrite(data);
break;
case MIXER_ADDRESS:
MixerAddrWrite(data);
break;
case MIXER_DATA:
MixerDataWrite(data);
break;
case 0x8:
// remap to ADLIB_REGISTER_SELECT_PORT
port = ADLIB_REGISTER_SELECT_PORT;
break;
case 0x9:
// remap to ADLIB_DATA_PORT
port = ADLIB_DATA_PORT;
break;
}
switch(port) {
case ADLIB_REGISTER_SELECT_PORT:
FMRegisterSelect(data);
break;
case ADLIB_DATA_PORT:
FMDataWrite(data);
break;
}
}
/***************************************************************************/
/*
* Reset all devices
*/
VOID
ResetAll(
VOID
)
{
dprintf2(("Resetting"));
ResetDSP();
ResetFM();
ResetMixer();
}
/***************************************************************************/
/*
* Debug
*/
#if DBG
int VddDebugLevel = 3;
int VddDebugCount = 0;
#define DEBUG_START 0
/*
* Generate debug output in printf type format.
*/
void VddDbgOut(LPSTR lpszFormat, ...)
{
char buf[256];
char buf2[300] = "VSBD: ";
va_list va;
if (++VddDebugCount < DEBUG_START) {
return;
}
va_start(va, lpszFormat);
wvsprintfA(buf, lpszFormat, va);
va_end(va);
strcat(buf2, buf);
strcat(buf2, "\r\n");
OutputDebugStringA(buf2);
}
#endif