1317 lines
33 KiB
C
1317 lines
33 KiB
C
|
/*****************************************************************************
|
|||
|
|
|||
|
Copyright (c) 1992-1994 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
init.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains code for the initialization phase of the
|
|||
|
Sound blaster device driver.
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
#include "sound.h"
|
|||
|
#include <stdlib.h>
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Local typedefs
|
|||
|
//
|
|||
|
|
|||
|
typedef struct {
|
|||
|
PGLOBAL_DEVICE_INFO PrevGDI;
|
|||
|
PDRIVER_OBJECT pDriverObject;
|
|||
|
} SOUND_CARD_INSTANCE, *PSOUND_CARD_INSTANCE;
|
|||
|
|
|||
|
//
|
|||
|
// Local definitions
|
|||
|
//
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT pDriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPathName
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
SoundCleanup(
|
|||
|
IN PGLOBAL_DEVICE_INFO pGDI
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
SoundUnload(
|
|||
|
IN PDRIVER_OBJECT pDriverObject
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SoundExcludeRoutine(
|
|||
|
IN OUT PLOCAL_DEVICE_INFO pLDI,
|
|||
|
IN SOUND_EXCLUDE_CODE Code
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SoundShutdown(
|
|||
|
IN PDEVICE_OBJECT pDO,
|
|||
|
IN PIRP pIrp
|
|||
|
);
|
|||
|
SOUND_REGISTRY_CALLBACK_ROUTINE
|
|||
|
SoundCardInstanceInit;
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SBCreateDevice(
|
|||
|
IN OUT PGLOBAL_DEVICE_INFO pGDI,
|
|||
|
IN ULONG DeviceId
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Remove initialization stuff from resident memory
|
|||
|
//
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(INIT,DriverEntry)
|
|||
|
#pragma alloc_text(INIT,SoundCardInstanceInit)
|
|||
|
#pragma alloc_text(INIT,SBCreateDevice)
|
|||
|
|
|||
|
#pragma alloc_text(PAGE,SoundCleanup)
|
|||
|
#pragma alloc_text(PAGE,SoundUnload)
|
|||
|
#pragma alloc_text(PAGE,SoundShutdown)
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Device initialization data
|
|||
|
//
|
|||
|
|
|||
|
CONST SOUND_DEVICE_INIT DeviceInit[NumberOfDevices] =
|
|||
|
{
|
|||
|
{
|
|||
|
NULL, NULL,
|
|||
|
0, // Default Volume
|
|||
|
FILE_DEVICE_WAVE_IN,
|
|||
|
WAVE_IN,
|
|||
|
"LDWi",
|
|||
|
L"\\Device\\SBWaveIn",
|
|||
|
SoundWaveDeferred,
|
|||
|
SoundExcludeRoutine,
|
|||
|
SoundWaveDispatch,
|
|||
|
SoundWaveInGetCaps,
|
|||
|
SoundNoVolume,
|
|||
|
DO_DIRECT_IO
|
|||
|
},
|
|||
|
{
|
|||
|
NULL, NULL,
|
|||
|
0, // Default Volume
|
|||
|
FILE_DEVICE_WAVE_OUT,
|
|||
|
WAVE_OUT,
|
|||
|
"LDWo",
|
|||
|
L"\\Device\\SBWaveOut",
|
|||
|
SoundWaveDeferred,
|
|||
|
SoundExcludeRoutine,
|
|||
|
SoundWaveDispatch,
|
|||
|
SoundWaveOutGetCaps,
|
|||
|
SoundNoVolume,
|
|||
|
DO_DIRECT_IO
|
|||
|
},
|
|||
|
{
|
|||
|
NULL, NULL,
|
|||
|
0,
|
|||
|
FILE_DEVICE_MIDI_OUT,
|
|||
|
MIDI_OUT,
|
|||
|
"LDMo",
|
|||
|
DD_MIDI_OUT_DEVICE_NAME_U,
|
|||
|
NULL,
|
|||
|
SoundExcludeRoutine,
|
|||
|
SoundMidiDispatch,
|
|||
|
SoundMidiOutGetCaps,
|
|||
|
SoundNoVolume,
|
|||
|
DO_DIRECT_IO
|
|||
|
},
|
|||
|
{
|
|||
|
NULL, NULL,
|
|||
|
0,
|
|||
|
FILE_DEVICE_MIDI_IN,
|
|||
|
MIDI_IN,
|
|||
|
"LDMi",
|
|||
|
DD_MIDI_IN_DEVICE_NAME_U,
|
|||
|
SoundMidiInDeferred,
|
|||
|
SoundExcludeRoutine,
|
|||
|
SoundMidiDispatch,
|
|||
|
SoundMidiInGetCaps,
|
|||
|
SoundNoVolume,
|
|||
|
DO_DIRECT_IO
|
|||
|
},
|
|||
|
{
|
|||
|
NULL, NULL,
|
|||
|
0, // Default Volume
|
|||
|
FILE_DEVICE_SOUND,
|
|||
|
AUX_DEVICE,
|
|||
|
"LDLi",
|
|||
|
L"\\Device\\SBAux",
|
|||
|
NULL,
|
|||
|
SoundExcludeRoutine,
|
|||
|
SoundAuxDispatch,
|
|||
|
SoundAuxGetCaps,
|
|||
|
SoundNoVolume,
|
|||
|
DO_BUFFERED_IO
|
|||
|
},
|
|||
|
{
|
|||
|
NULL, NULL,
|
|||
|
DEF_AUX_VOLUME,
|
|||
|
FILE_DEVICE_SOUND,
|
|||
|
AUX_DEVICE,
|
|||
|
"LDCd",
|
|||
|
L"\\Device\\SBAux",
|
|||
|
NULL,
|
|||
|
SoundExcludeRoutine,
|
|||
|
SoundAuxDispatch,
|
|||
|
SoundAuxGetCaps,
|
|||
|
SoundNoVolume,
|
|||
|
DO_BUFFERED_IO
|
|||
|
},
|
|||
|
{
|
|||
|
NULL, NULL,
|
|||
|
0,
|
|||
|
FILE_DEVICE_SOUND,
|
|||
|
MIXER_DEVICE,
|
|||
|
"LDMx",
|
|||
|
L"\\Device\\SBMixer",
|
|||
|
NULL, // No Dpc routine
|
|||
|
SoundExcludeRoutine,
|
|||
|
SoundMixerDispatch,
|
|||
|
SoundMixerDumpConfiguration,
|
|||
|
SoundNoVolume, // No volume setting
|
|||
|
DO_BUFFERED_IO
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SoundShutdown(
|
|||
|
IN PDEVICE_OBJECT pDO,
|
|||
|
IN PIRP pIrp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Save away volume settings when the system is shut down
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDO - the device object we registered for shutdown with
|
|||
|
pIrp - No used
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the final status from the initialization operation.
|
|||
|
Here STATUS_SUCCESS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLOCAL_DEVICE_INFO pLDI;
|
|||
|
PGLOBAL_DEVICE_INFO pGDI;
|
|||
|
|
|||
|
pLDI = pDO->DeviceExtension;
|
|||
|
pGDI = pLDI->pGlobalInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Save mixer settings!
|
|||
|
//
|
|||
|
|
|||
|
if (pGDI->DeviceObject[MixerDevice]) {
|
|||
|
SoundSaveMixerSettings(pGDI);
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SoundExcludeRoutine(
|
|||
|
IN OUT PLOCAL_DEVICE_INFO pLDI,
|
|||
|
IN SOUND_EXCLUDE_CODE Code
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
SoundExcludeRoutine()
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Perform mutual exclusion for our devices
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pLDI - device info for the device being open, closed, entered or left
|
|||
|
Code - Function to perform (see devices.h)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the final status from the initialization operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PGLOBAL_DEVICE_INFO pGDI;
|
|||
|
BOOLEAN ReturnCode;
|
|||
|
|
|||
|
// dprintf3(("SoundExcludeRoutine(): Entry"));
|
|||
|
|
|||
|
pGDI = pLDI->pGlobalInfo;
|
|||
|
|
|||
|
if (Code == SoundExcludeEnter) {
|
|||
|
KeWaitForSingleObject(pLDI->DeviceIndex == MidiOutDevice ?
|
|||
|
&pGDI->MidiMutex : &pGDI->DeviceMutex,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE, // Not alertable
|
|||
|
NULL);
|
|||
|
return TRUE;
|
|||
|
} else {
|
|||
|
if (Code == SoundExcludeLeave) {
|
|||
|
KeReleaseMutex(pLDI->DeviceIndex == MidiOutDevice ?
|
|||
|
&pGDI->MidiMutex : &pGDI->DeviceMutex,
|
|||
|
FALSE);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Synchronize!
|
|||
|
//
|
|||
|
|
|||
|
KeWaitForSingleObject(&pGDI->DeviceMutex,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE, // Not alertable
|
|||
|
NULL);
|
|||
|
|
|||
|
ReturnCode = FALSE;
|
|||
|
|
|||
|
switch (Code) {
|
|||
|
case SoundExcludeOpen:
|
|||
|
// dprintf3((" SoundExcludeRoutine(): case = SoundExcludeOpen"));
|
|||
|
|
|||
|
switch (pLDI->DeviceIndex) {
|
|||
|
case MidiInDevice:
|
|||
|
if (pGDI->Hw.MPU401.PortBase != NULL) {
|
|||
|
if (pGDI->MPU401InputInUse) {
|
|||
|
ReturnCode = FALSE;
|
|||
|
} else {
|
|||
|
ReturnCode = TRUE;
|
|||
|
pGDI->MPU401InputInUse = TRUE;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
// Fall through
|
|||
|
|
|||
|
case WaveInDevice:
|
|||
|
case WaveOutDevice:
|
|||
|
if (pGDI->Usage == 0xFF) {
|
|||
|
pGDI->Usage = pLDI->DeviceIndex;
|
|||
|
ReturnCode = TRUE;
|
|||
|
} else {
|
|||
|
PWAVE_INFO WaveInfo;
|
|||
|
|
|||
|
WaveInfo = pLDI->DeviceSpecificData;
|
|||
|
|
|||
|
//
|
|||
|
// Allow multiple (2) opens for wave input if
|
|||
|
// current is low priority
|
|||
|
//
|
|||
|
|
|||
|
if (pGDI->Usage == WaveInDevice &&
|
|||
|
( WaveInfo->LowPriorityHandle != NULL &&
|
|||
|
!WaveInfo->LowPrioritySaved)) {
|
|||
|
pGDI->Usage = pLDI->DeviceIndex;
|
|||
|
ReturnCode = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case MidiOutDevice:
|
|||
|
//
|
|||
|
// Can't do this if wave if high speed is active
|
|||
|
//
|
|||
|
if ((pGDI->Usage == WaveInDevice ||
|
|||
|
pGDI->Usage == WaveOutDevice) &&
|
|||
|
HwHighSpeed(&pGDI->Hw,
|
|||
|
pGDI->WaveInfo.Channels,
|
|||
|
pGDI->WaveInfo.SamplesPerSec,
|
|||
|
pGDI->WaveInfo.Direction) ||
|
|||
|
pGDI->Hw.HighSpeed) {
|
|||
|
ReturnCode = FALSE;
|
|||
|
} else {
|
|||
|
if (!pGDI->MidiInUse) {
|
|||
|
pGDI->MidiInUse = TRUE;
|
|||
|
ReturnCode = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
//
|
|||
|
// aux devices should not receive this call
|
|||
|
//
|
|||
|
ASSERT(FALSE);
|
|||
|
break;
|
|||
|
} // End SWITCH (pLDI->DeviceIndex)
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case SoundExcludeClose:
|
|||
|
// dprintf3((" SoundExcludeRoutine(): case = SoundExcludeClose"));
|
|||
|
|
|||
|
ReturnCode = TRUE;
|
|||
|
|
|||
|
switch (pLDI->DeviceIndex) {
|
|||
|
case WaveInDevice:
|
|||
|
case WaveOutDevice:
|
|||
|
|
|||
|
if (!pGDI->WaveInfo.LowPrioritySaved) {
|
|||
|
pGDI->Usage = 0xFF;
|
|||
|
} else {
|
|||
|
if (pLDI->DeviceIndex == WaveOutDevice) {
|
|||
|
pGDI->Usage = WaveInDevice;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pGDI->Hw.HighSpeed = FALSE; // reset this
|
|||
|
break;
|
|||
|
|
|||
|
case MidiInDevice:
|
|||
|
|
|||
|
if (pGDI->Hw.MPU401.PortBase) {
|
|||
|
ASSERT(pGDI->MPU401InputInUse);
|
|||
|
pGDI->MPU401InputInUse = FALSE;
|
|||
|
} else {
|
|||
|
ASSERT(pGDI->Usage != 0xFF);
|
|||
|
pGDI->Usage = 0xFF;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case MidiOutDevice:
|
|||
|
ASSERT(pGDI->MidiInUse);
|
|||
|
pGDI->MidiInUse = FALSE;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
//
|
|||
|
// aux devices should not receive this call
|
|||
|
//
|
|||
|
ASSERT(FALSE);
|
|||
|
break;
|
|||
|
} // End SWITCH (pLDI->DeviceIndex)
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case SoundExcludeQueryOpen:
|
|||
|
// dprintf3((" SoundExcludeRoutine(): case = SoundExcludeQueryOpen"));
|
|||
|
|
|||
|
switch (pLDI->DeviceIndex) {
|
|||
|
case WaveInDevice:
|
|||
|
case WaveOutDevice:
|
|||
|
ReturnCode = (BOOLEAN)
|
|||
|
(pGDI->Usage == pLDI->DeviceIndex ||
|
|||
|
pGDI->WaveInfo.LowPrioritySaved &&
|
|||
|
pLDI->DeviceIndex == WaveInDevice);
|
|||
|
break;
|
|||
|
|
|||
|
case MidiInDevice:
|
|||
|
//
|
|||
|
// Guess!
|
|||
|
//
|
|||
|
ReturnCode = (BOOLEAN)(pGDI->Hw.MPU401.PortBase == NULL ?
|
|||
|
(pGDI->Usage == pLDI->DeviceIndex) :
|
|||
|
pGDI->MPU401InputInUse);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case MidiOutDevice:
|
|||
|
ReturnCode = pGDI->MidiInUse;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ASSERT(FALSE);
|
|||
|
break;
|
|||
|
|
|||
|
} // End SWITCH (pLDI->DeviceIndex)
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
} // End SWITCH (Code)
|
|||
|
|
|||
|
|
|||
|
KeReleaseMutex(&pGDI->DeviceMutex, FALSE);
|
|||
|
|
|||
|
return ReturnCode;
|
|||
|
|
|||
|
} // End SoundExcludeRoutine()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT pDriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPathName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
DriverEntry()
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine performs initialization for the sound blaster
|
|||
|
device driver when it is first loaded
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDriverObject - Pointer to a driver object.
|
|||
|
RegistryPathName - the path to our driver services node
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the final status from the initialization operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SOUND_CARD_INSTANCE CardInstance;
|
|||
|
NTSTATUS Status;
|
|||
|
PGLOBAL_DEVICE_INFO pGDI;
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
*
|
|||
|
* Initialize debugging
|
|||
|
*
|
|||
|
********************************************************************/
|
|||
|
#if DBG
|
|||
|
DriverName = "SNDBLST";
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (SoundDebugLevel >= 4) {
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
*
|
|||
|
* Initialize each card in turn
|
|||
|
*
|
|||
|
********************************************************************/
|
|||
|
|
|||
|
CardInstance.PrevGDI = NULL;
|
|||
|
CardInstance.pDriverObject = pDriverObject;
|
|||
|
|
|||
|
/*
|
|||
|
** Initialize the driver object dispatch table.
|
|||
|
*/
|
|||
|
|
|||
|
pDriverObject->DriverUnload = SoundUnload;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_CREATE] = SoundDispatch;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = SoundDispatch;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SoundDispatch;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_READ] = SoundDispatch;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_WRITE] = SoundDispatch;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = SoundDispatch;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = SoundShutdown;
|
|||
|
|
|||
|
Status = SoundEnumSubkeys(RegistryPathName,
|
|||
|
PARMS_SUBKEY,
|
|||
|
SoundCardInstanceInit,
|
|||
|
(PVOID)&CardInstance);
|
|||
|
|
|||
|
/*
|
|||
|
** Save the return statuses
|
|||
|
*/
|
|||
|
|
|||
|
if (CardInstance.PrevGDI) {
|
|||
|
pGDI = CardInstance.PrevGDI;
|
|||
|
|
|||
|
for (;;) {
|
|||
|
//
|
|||
|
// Save the Driver load status in the registry for the
|
|||
|
// User-mode DLL to pick up
|
|||
|
//
|
|||
|
SoundWriteRegistryDWORD( pGDI->RegistryPathName,
|
|||
|
SOUND_REG_CONFIGERROR,
|
|||
|
pGDI->LoadStatus);
|
|||
|
|
|||
|
pGDI = pGDI->Next;
|
|||
|
if (pGDI == CardInstance.PrevGDI) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** If this failed then free everything
|
|||
|
*/
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
if (CardInstance.PrevGDI) {
|
|||
|
SoundCleanup(CardInstance.PrevGDI);
|
|||
|
|
|||
|
/*
|
|||
|
** Log a meaningful error for the one that failed!
|
|||
|
*/
|
|||
|
|
|||
|
}
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SBCreateDevice(
|
|||
|
IN OUT PGLOBAL_DEVICE_INFO pGDI,
|
|||
|
IN ULONG DeviceId
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PLOCAL_DEVICE_INFO pLDI;
|
|||
|
|
|||
|
PVOID DeviceSpecificData;
|
|||
|
|
|||
|
/*
|
|||
|
** Set failure type for if we fail
|
|||
|
*/
|
|||
|
|
|||
|
pGDI->LoadStatus = SOUND_CONFIG_RESOURCE;
|
|||
|
|
|||
|
switch (DeviceId) {
|
|||
|
case WaveInDevice:
|
|||
|
case WaveOutDevice:
|
|||
|
DeviceSpecificData = &pGDI->WaveInfo;
|
|||
|
break;
|
|||
|
|
|||
|
case MidiInDevice:
|
|||
|
case MidiOutDevice:
|
|||
|
DeviceSpecificData = &pGDI->MidiInfo;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
DeviceSpecificData = NULL;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
Status = SoundCreateDevice( &DeviceInit[DeviceId],
|
|||
|
(BOOLEAN)FALSE, // No range for midi out
|
|||
|
pGDI->DriverObject,
|
|||
|
pGDI,
|
|||
|
DeviceSpecificData,
|
|||
|
&pGDI->Hw,
|
|||
|
(int)DeviceId,
|
|||
|
&pGDI->DeviceObject[DeviceId] );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf1(("Failed to create device %ls - status %8X",
|
|||
|
DeviceInit[DeviceId].PrototypeName, Status));
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Add the device name to the registry
|
|||
|
*/
|
|||
|
|
|||
|
pLDI = (PLOCAL_DEVICE_INFO)pGDI->DeviceObject[DeviceId]->DeviceExtension;
|
|||
|
|
|||
|
/*
|
|||
|
** Save the device name where the non-kernel part can pick it up.
|
|||
|
*/
|
|||
|
|
|||
|
Status = SoundSaveDeviceName(pGDI->RegistryPathName, pLDI);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SoundCardInstanceInit(
|
|||
|
IN PWSTR RegistryPathName,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
{
|
|||
|
/*
|
|||
|
** Instance data
|
|||
|
*/
|
|||
|
|
|||
|
PSOUND_CARD_INSTANCE CardInstance;
|
|||
|
|
|||
|
//
|
|||
|
// Return code from last function called
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
//
|
|||
|
// Configuration data :
|
|||
|
//
|
|||
|
|
|||
|
SB_CONFIG_DATA ConfigData;
|
|||
|
|
|||
|
//
|
|||
|
// Where we keep all general driver information
|
|||
|
// We avoid using static data because :
|
|||
|
// 1. Accesses are slower with 32-bit offsets
|
|||
|
// 2. We support more than one card instance
|
|||
|
//
|
|||
|
|
|||
|
PGLOBAL_DEVICE_INFO pGDI;
|
|||
|
|
|||
|
//
|
|||
|
// The number of devices we actually create
|
|||
|
//
|
|||
|
|
|||
|
int NumberOfDevicesCreated;
|
|||
|
|
|||
|
//
|
|||
|
// The context is the global device info pointer from the previous
|
|||
|
// instance
|
|||
|
//
|
|||
|
|
|||
|
CardInstance = Context;
|
|||
|
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
*
|
|||
|
* Allocate our global info
|
|||
|
*
|
|||
|
********************************************************************/
|
|||
|
|
|||
|
pGDI =
|
|||
|
(PGLOBAL_DEVICE_INFO)ExAllocatePool(
|
|||
|
NonPagedPool,
|
|||
|
sizeof(GLOBAL_DEVICE_INFO));
|
|||
|
|
|||
|
if (pGDI == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
dprintf4((" DriverEntry(): GlobalInfo : %08lXH", pGDI));
|
|||
|
|
|||
|
RtlZeroMemory(pGDI, sizeof(GLOBAL_DEVICE_INFO));
|
|||
|
pGDI->Key = GDI_KEY;
|
|||
|
pGDI->Hw.Key = HARDWARE_KEY;
|
|||
|
pGDI->RegistryPathName = RegistryPathName;
|
|||
|
|
|||
|
pGDI->Usage = 0xFF; // Free
|
|||
|
pGDI->DriverObject = CardInstance->pDriverObject;
|
|||
|
|
|||
|
//
|
|||
|
// Mutual exclusion :
|
|||
|
//
|
|||
|
// Everything is synchronized on the DeviceMutex (except for
|
|||
|
// some stuff inside the wave device) except
|
|||
|
//
|
|||
|
// Midi external output which takes too long and so is synchronized
|
|||
|
// separately but grabs the DeviceMutex every time it actually
|
|||
|
// writes anything and
|
|||
|
//
|
|||
|
// Synth output where the hardware is separate but callbacks to the
|
|||
|
// mixer must grab the DeviceMutex.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeMutex(&pGDI->DeviceMutex,
|
|||
|
2 // High level
|
|||
|
);
|
|||
|
|
|||
|
KeInitializeMutex(&pGDI->MidiMutex,
|
|||
|
1 // Low level
|
|||
|
);
|
|||
|
|
|||
|
KeInitializeMutex(&pGDI->Hw.DSPMutex,
|
|||
|
3 // Highest level
|
|||
|
);
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
*
|
|||
|
* Add ourselves to the ring of cards
|
|||
|
*
|
|||
|
********************************************************************/
|
|||
|
|
|||
|
if (CardInstance->PrevGDI == NULL) {
|
|||
|
pGDI->Next = pGDI;
|
|||
|
} else {
|
|||
|
PGLOBAL_DEVICE_INFO pNext;
|
|||
|
pNext = CardInstance->PrevGDI->Next;
|
|||
|
CardInstance->PrevGDI->Next = pGDI;
|
|||
|
pGDI->Next = pNext;
|
|||
|
}
|
|||
|
CardInstance->PrevGDI = pGDI;
|
|||
|
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
*
|
|||
|
* See if we can find our bus. We run on both ISA and EISA
|
|||
|
* We ASSUME that if there's an ISA bus we're on that
|
|||
|
*
|
|||
|
********************************************************************/
|
|||
|
|
|||
|
Status = SoundGetBusNumber(Isa, &pGDI->BusNumber);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
//
|
|||
|
// Cound not find an ISA bus so try EISA
|
|||
|
//
|
|||
|
Status = SoundGetBusNumber(Eisa, &pGDI->BusNumber);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
Status = SoundGetBusNumber(MicroChannel, &pGDI->BusNumber);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf1(("driver does not work on non-Isa/Eisa/Mca"));
|
|||
|
return Status;
|
|||
|
}
|
|||
|
pGDI->BusType = MicroChannel;
|
|||
|
|
|||
|
} else {
|
|||
|
pGDI->BusType = Eisa;
|
|||
|
}
|
|||
|
} else {
|
|||
|
pGDI->BusType = Isa;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set configuration to default in case we don't get all the
|
|||
|
// values back from the registry.
|
|||
|
//
|
|||
|
// Also set default volume for all devices
|
|||
|
//
|
|||
|
|
|||
|
ConfigData.Port = SOUND_DEF_PORT;
|
|||
|
ConfigData.InterruptNumber = SOUND_DEF_INT;
|
|||
|
ConfigData.DmaChannel = SOUND_DEF_DMACHANNEL;
|
|||
|
ConfigData.DmaChannel16 = SOUND_DEF_DMACHANNEL16;
|
|||
|
ConfigData.DmaBufferSize = DEFAULT_DMA_BUFFERSIZE;
|
|||
|
ConfigData.MPU401Port = SOUND_DEF_MPU401_PORT;
|
|||
|
ConfigData.LoadType = SOUND_LOADTYPE_NORMAL;
|
|||
|
ConfigData.MixerSettingsFound = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Get the system configuration information for this driver.
|
|||
|
//
|
|||
|
//
|
|||
|
// Port, Interrupt, DMA channel, DMA 16-bit channel DMA buffer size
|
|||
|
//
|
|||
|
|
|||
|
{
|
|||
|
RTL_QUERY_REGISTRY_TABLE Table[2];
|
|||
|
|
|||
|
RtlZeroMemory(Table, sizeof(Table));
|
|||
|
|
|||
|
Table[0].QueryRoutine = SoundReadConfiguration;
|
|||
|
|
|||
|
Status = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE,
|
|||
|
pGDI->RegistryPathName,
|
|||
|
Table,
|
|||
|
&ConfigData,
|
|||
|
NULL );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf1(("ERROR: DriverEntry(): RtlQueryRegistryValues() Failed with Status = %XH", Status));
|
|||
|
return Status;
|
|||
|
} // End IF (!NT_SUCCESS(Status))
|
|||
|
} // End Query Resistry Values
|
|||
|
|
|||
|
//
|
|||
|
// Save additional Config data
|
|||
|
//
|
|||
|
pGDI->InterruptNumber = ConfigData.InterruptNumber;
|
|||
|
pGDI->DmaChannel = ConfigData.DmaChannel;
|
|||
|
pGDI->DmaChannel16 = ConfigData.DmaChannel16;
|
|||
|
|
|||
|
//
|
|||
|
// Verify the DMA Buffer Size
|
|||
|
//
|
|||
|
if ( ConfigData.DmaBufferSize < MIN_DMA_BUFFERSIZE ) {
|
|||
|
ConfigData.DmaBufferSize = MIN_DMA_BUFFERSIZE;
|
|||
|
dprintf1((" DriverEntry(): Adjusting the DMA Buffer size, size in Registry was too small"));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// print out some info about the configuration
|
|||
|
//
|
|||
|
|
|||
|
dprintf2((" DriverEntry(): Base I/O Port = %XH", ConfigData.Port));
|
|||
|
dprintf2((" DriverEntry(): Interrupt = %u", ConfigData.InterruptNumber));
|
|||
|
dprintf2((" DriverEntry(): DMA Channel = %u", ConfigData.DmaChannel));
|
|||
|
dprintf2((" DriverEntry(): DMA Channel (16 bit) = %u", ConfigData.DmaChannel16));
|
|||
|
dprintf2((" DriverEntry(): DMA Buffer Size = %XH", ConfigData.DmaBufferSize));
|
|||
|
dprintf2((" DriverEntry(): MPU 401 port = %XH", ConfigData.MPU401Port));
|
|||
|
|
|||
|
/*
|
|||
|
** Create our wave devices to ease reporting problems
|
|||
|
*/
|
|||
|
|
|||
|
Status = SBCreateDevice(pGDI, WaveInDevice);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
Status = SBCreateDevice(pGDI, WaveOutDevice);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Say we want to be called at shutdown time
|
|||
|
*/
|
|||
|
|
|||
|
Status = IoRegisterShutdownNotification(pGDI->DeviceObject[WaveInDevice]);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
pGDI->ShutdownRegistered = TRUE;
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
** Check the configuration and acquire the resources
|
|||
|
*/
|
|||
|
|
|||
|
Status = SoundInitHardwareConfig(pGDI, &ConfigData);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf1(("ERROR: DriverEntry(): SoundInitHardwareConfig() Failed with Status = %XH",
|
|||
|
Status));
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Initialize generic device environments - first get the
|
|||
|
** hardware routines in place.
|
|||
|
** We do this here after we've found out what hardware we've got
|
|||
|
*/
|
|||
|
|
|||
|
HwInitialize(pGDI);
|
|||
|
|
|||
|
SoundInitMidiIn(&pGDI->MidiInfo,
|
|||
|
&pGDI->Hw);
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
** Register most of our resources. Some may be registered on
|
|||
|
** the wave output device :
|
|||
|
** 16-bit dma channel
|
|||
|
**
|
|||
|
** We wipe out the registration of the MPU401 port when we do this
|
|||
|
** but this is OK.
|
|||
|
*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG PortToReport;
|
|||
|
PortToReport = ConfigData.Port + MIX_ADDR_PORT;
|
|||
|
|
|||
|
Status = SoundReportResourceUsage(
|
|||
|
pGDI->DeviceObject[WaveInDevice], // As good as any device to own
|
|||
|
// it
|
|||
|
pGDI->BusType,
|
|||
|
pGDI->BusNumber,
|
|||
|
&ConfigData.InterruptNumber,
|
|||
|
INTERRUPT_MODE,
|
|||
|
(BOOLEAN)(SB16(&pGDI->Hw) ? TRUE : FALSE), // Sharable for SB16
|
|||
|
&ConfigData.DmaChannel,
|
|||
|
&PortToReport,
|
|||
|
NUMBER_OF_SOUND_PORTS - MIX_ADDR_PORT);
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf1(("Error - failed to claim resources - code %8X", Status));
|
|||
|
pGDI->LoadStatus = SOUND_CONFIG_ERROR;
|
|||
|
return Status;
|
|||
|
}
|
|||
|
/*
|
|||
|
** Now we know what device we've got we can create the appropriate
|
|||
|
** devices
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
** Thuderboard has no external midi and we're going to
|
|||
|
** drive the SB16 in MPU401 mode if we can
|
|||
|
*/
|
|||
|
|
|||
|
if (!pGDI->Hw.ThunderBoard) {
|
|||
|
//
|
|||
|
// Don't create the MPU401 device if it is disabled
|
|||
|
//
|
|||
|
if (!SB16(&pGDI->Hw) ||
|
|||
|
(ULONG)-1 != ConfigData.MPU401Port)
|
|||
|
{
|
|||
|
Status = SBCreateDevice(pGDI, MidiOutDevice);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
Status = SBCreateDevice(pGDI, MidiInDevice);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Create volume control stuff for those devices which have it
|
|||
|
*/
|
|||
|
|
|||
|
if (SBPRO(&pGDI->Hw) || SB16(&pGDI->Hw)
|
|||
|
#ifdef SB_CD
|
|||
|
||
|
|||
|
pGDI->Hw.SBCDBase != NULL
|
|||
|
#endif // SB_CD
|
|||
|
) {
|
|||
|
Status = SBCreateDevice(pGDI, LineInDevice);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
Status = SBCreateDevice(pGDI, CDInternal);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
Status = SBCreateDevice(pGDI, MixerDevice);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
} else {
|
|||
|
/* Volume setting not supported for our wave device */
|
|||
|
((PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveOutDevice]->DeviceExtension)->CreationFlags |=
|
|||
|
SOUND_CREATION_NO_VOLUME;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Init the WaveInfo structure
|
|||
|
*/
|
|||
|
|
|||
|
SoundInitializeWaveInfo(&pGDI->WaveInfo,
|
|||
|
(UCHAR)(SB1(&pGDI->Hw) ?
|
|||
|
SoundReprogramOnInterruptDMA :
|
|||
|
SoundAutoInitDMA),
|
|||
|
SoundQueryFormat,
|
|||
|
&pGDI->Hw);
|
|||
|
|
|||
|
if (ConfigData.SynthType == SOUND_SYNTH_TYPE_NONE) {
|
|||
|
/*
|
|||
|
** This will happen if the user selected a basic config without
|
|||
|
** a synthesizer.
|
|||
|
*/
|
|||
|
dprintf2(("No synth!"));
|
|||
|
} else {
|
|||
|
Status = SynthInit(CardInstance->pDriverObject,
|
|||
|
pGDI->RegistryPathName,
|
|||
|
&pGDI->Synth,
|
|||
|
SB16(&pGDI->Hw) ||
|
|||
|
SBPRO(&pGDI->Hw) && INPORT(&pGDI->Hw, 0) == 0 ?
|
|||
|
ConfigData.Port : SYNTH_PORT,
|
|||
|
FALSE, // Interrupt not enabled
|
|||
|
pGDI->BusType,
|
|||
|
pGDI->BusNumber,
|
|||
|
NULL,
|
|||
|
SOUND_MIXER_INVALID_CONTROL_ID,// Set volume later
|
|||
|
TRUE, // Allow multiple
|
|||
|
SoundMidiOutGetSynthCaps
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
/*
|
|||
|
** OK - we can get along without a synth
|
|||
|
*/
|
|||
|
|
|||
|
dprintf2(("No synth!"));
|
|||
|
} else {
|
|||
|
if (!pGDI->Synth.IsOpl3) {
|
|||
|
dprintf2(("Synth is Adlib"));
|
|||
|
} else {
|
|||
|
|
|||
|
/*
|
|||
|
** Bad aliasing in early sound blasters means we can
|
|||
|
** guess wrong.
|
|||
|
*/
|
|||
|
|
|||
|
if (!(SB16(&pGDI->Hw) || SBPRO(&pGDI->Hw) && INPORT(&pGDI->Hw, 0) == 0)) {
|
|||
|
pGDI->Synth.IsOpl3 = FALSE;
|
|||
|
}
|
|||
|
dprintf2(("Synth is Opl3"));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Set the mixer up.
|
|||
|
**
|
|||
|
** Note that the mixer info depends on what hardware is present
|
|||
|
** so this must be called after we have checked out the hardware.
|
|||
|
*/
|
|||
|
|
|||
|
if (pGDI->DeviceObject[MixerDevice]) {
|
|||
|
Status = SoundMixerInit(pGDI->DeviceObject[MixerDevice]->DeviceExtension,
|
|||
|
&ConfigData.MixerSettings,
|
|||
|
ConfigData.MixerSettingsFound);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Save new settings
|
|||
|
*/
|
|||
|
|
|||
|
Status = SoundSaveConfig(pGDI->RegistryPathName,
|
|||
|
ConfigData.Port,
|
|||
|
ConfigData.DmaChannel,
|
|||
|
ConfigData.DmaChannel16,
|
|||
|
ConfigData.InterruptNumber,
|
|||
|
ConfigData.MPU401Port,
|
|||
|
(BOOLEAN)(pGDI->Synth.DeviceObject != NULL),
|
|||
|
pGDI->Synth.IsOpl3,
|
|||
|
pGDI->WaveInfo.DMABuf.BufferSize);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Finally test the PRO interrupt
|
|||
|
*/
|
|||
|
|
|||
|
if (SBPRO(&pGDI->Hw)) {
|
|||
|
BOOLEAN Ok;
|
|||
|
|
|||
|
SoundEnter(pGDI->DeviceObject[WaveOutDevice]->DeviceExtension,
|
|||
|
TRUE);
|
|||
|
Ok = 0 == SoundTestWaveDevice(pGDI->DeviceObject[WaveOutDevice]);
|
|||
|
SoundEnter(pGDI->DeviceObject[WaveOutDevice]->DeviceExtension,
|
|||
|
FALSE);
|
|||
|
if (!Ok) {
|
|||
|
pGDI->LoadStatus = SOUND_CONFIG_BADDMA;
|
|||
|
return STATUS_DEVICE_CONFIGURATION_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Exit OK Message
|
|||
|
*/
|
|||
|
|
|||
|
pGDI->LoadStatus = SOUND_CONFIG_OK;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SoundCleanup(
|
|||
|
IN PGLOBAL_DEVICE_INFO pGDI
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Clean up all resources allocated by our initialization
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pGDI - Pointer to global data
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
PGLOBAL_DEVICE_INFO NextGDI;
|
|||
|
PGLOBAL_DEVICE_INFO FirstGDI;
|
|||
|
PDRIVER_OBJECT DriverObject;
|
|||
|
|
|||
|
FirstGDI = pGDI;
|
|||
|
|
|||
|
for (;;) {
|
|||
|
NextGDI = pGDI->Next;
|
|||
|
|
|||
|
//
|
|||
|
// Free the synth device
|
|||
|
//
|
|||
|
|
|||
|
SynthCleanup(&pGDI->Synth);
|
|||
|
|
|||
|
//
|
|||
|
// Reset MPU401 if any
|
|||
|
//
|
|||
|
|
|||
|
if (pGDI->Hw.MPU401.PortBase != NULL) {
|
|||
|
//
|
|||
|
// Just in case
|
|||
|
//
|
|||
|
READ_PORT_UCHAR(pGDI->Hw.MPU401.PortBase + MPU401_REG_DATA);
|
|||
|
MPU401Write(pGDI->Hw.MPU401.PortBase, TRUE, MPU401_CMD_RESET);
|
|||
|
}
|
|||
|
//
|
|||
|
// Free our interrupt
|
|||
|
//
|
|||
|
if (pGDI->WaveInfo.Interrupt) {
|
|||
|
IoDisconnectInterrupt(pGDI->WaveInfo.Interrupt);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free our DMA Buffer
|
|||
|
//
|
|||
|
SoundFreeCommonBuffer(&pGDI->WaveInfo.DMABuf);
|
|||
|
|
|||
|
//
|
|||
|
// Unregister shutdown notification
|
|||
|
//
|
|||
|
if (pGDI->ShutdownRegistered) {
|
|||
|
IoUnregisterShutdownNotification(pGDI->DeviceObject[WaveInDevice]);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free our I/O Ports
|
|||
|
//
|
|||
|
if (pGDI->MemType == 0) {
|
|||
|
if (pGDI->Hw.PortBase != NULL) {
|
|||
|
|
|||
|
MmUnmapIoSpace(pGDI->Hw.PortBase, NUMBER_OF_SOUND_PORTS);
|
|||
|
}
|
|||
|
if (pGDI->Hw.MPU401.PortBase != NULL) {
|
|||
|
|
|||
|
MmUnmapIoSpace(pGDI->Hw.MPU401.PortBase, NUMBER_OF_MPU401_PORTS);
|
|||
|
}
|
|||
|
#ifdef SB_CD
|
|||
|
if (pGDI->Hw.SBCDBase != NULL) {
|
|||
|
|
|||
|
MmUnmapIoSpace(pGDI->Hw.SBCDBase, 6);
|
|||
|
}
|
|||
|
#endif // SB_CD
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free device name
|
|||
|
//
|
|||
|
if (pGDI->RegistryPathName) {
|
|||
|
HANDLE hKey;
|
|||
|
|
|||
|
//
|
|||
|
// Free devices key
|
|||
|
//
|
|||
|
if (NT_SUCCESS(SoundOpenDevicesKey(pGDI->RegistryPathName, &hKey))) {
|
|||
|
ZwDeleteKey(hKey);
|
|||
|
ZwClose(hKey);
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(pGDI->RegistryPathName);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save driver object
|
|||
|
//
|
|||
|
|
|||
|
DriverObject = pGDI->DriverObject;
|
|||
|
|
|||
|
//
|
|||
|
// Free the Pool
|
|||
|
//
|
|||
|
ExFreePool(pGDI);
|
|||
|
|
|||
|
if (NextGDI == FirstGDI) {
|
|||
|
break;
|
|||
|
} else {
|
|||
|
pGDI = NextGDI;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free all devices for this driver. This will free everything for
|
|||
|
// every card.
|
|||
|
//
|
|||
|
|
|||
|
if (DriverObject != NULL) {
|
|||
|
while (DriverObject->DeviceObject != NULL) {
|
|||
|
|
|||
|
/*
|
|||
|
** Undeclare resources used by device and
|
|||
|
** delete the device object and associated data
|
|||
|
*/
|
|||
|
|
|||
|
SoundFreeDevice(DriverObject->DeviceObject);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SoundUnload(
|
|||
|
IN OUT PDRIVER_OBJECT pDriverObject
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pGDI - Pointer to global data
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PGLOBAL_DEVICE_INFO pGDI;
|
|||
|
PGLOBAL_DEVICE_INFO pGDIFirst;
|
|||
|
|
|||
|
//
|
|||
|
// Find our global data -
|
|||
|
// HACK HACK !!! we may the synth stuff
|
|||
|
//
|
|||
|
|
|||
|
pGDI = ((PLOCAL_DEVICE_INFO)pDriverObject->DeviceObject->DeviceExtension)
|
|||
|
->pGlobalInfo;
|
|||
|
|
|||
|
if (pGDI->Key == SYNTH_KEY) {
|
|||
|
pGDI = CONTAINING_RECORD((PGLOBAL_SYNTH_INFO)pGDI, GLOBAL_DEVICE_INFO, Synth);
|
|||
|
}
|
|||
|
|
|||
|
pGDIFirst = pGDI;
|
|||
|
|
|||
|
//
|
|||
|
// Write out volume settings
|
|||
|
//
|
|||
|
|
|||
|
do {
|
|||
|
if (pGDI->DeviceObject[MixerDevice]) {
|
|||
|
SoundSaveMixerSettings(pGDI);
|
|||
|
}
|
|||
|
pGDI = pGDI->Next;
|
|||
|
} while (pGDI != pGDIFirst);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Assume all handles (and therefore interrupts etc) are closed down
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Delete the things we allocated - devices, Interrupt objects,
|
|||
|
// adapter objects. The driver object has a chain of devices
|
|||
|
// across it.
|
|||
|
//
|
|||
|
|
|||
|
SoundCleanup(pGDI);
|
|||
|
}
|
|||
|
|
|||
|
|