Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

983 lines
23 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
init.c
Abstract:
This module contains code for the initialization phase of the
Microsoft Sound System device driver.
Author:
Robin Speed (RobinSp) 17-Oct-1992
Environment:
Kernel mode
Revision History:
--*/
#include "sound.h"
#include "string.h"
#include "stdlib.h"
//
// Local typedefs
//
typedef struct {
PGLOBAL_DEVICE_INFO PrevGDI;
PDRIVER_OBJECT pDriverObject;
} SOUND_CARD_INSTANCE, *PSOUND_CARD_INSTANCE;
//
// Local functions
//
SOUND_REGISTRY_CALLBACK_ROUTINE
SoundCardInstanceInit;
BOOLEAN
SoundExcludeRoutine(
IN OUT PLOCAL_DEVICE_INFO pLDI,
IN SOUND_EXCLUDE_CODE Code
);
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING RegistryPathName
);
VOID
SoundCleanup(
IN PGLOBAL_DEVICE_INFO pGDI
);
VOID SoundUnload(
IN PDRIVER_OBJECT pDriverObject
);
NTSTATUS
SoundShutdown(
IN PDEVICE_OBJECT pDO,
IN PIRP pIrp
);
//
// Remove initialization stuff from resident memory
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,DriverEntry)
#pragma alloc_text(INIT,SoundCardInstanceInit)
#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,
FILE_DEVICE_WAVE_IN,
WAVE_IN,
"LDWi",
L"\\Device\\WSSWaveIn",
SoundWaveDeferred,
SoundExcludeRoutine,
SoundWaveDispatch,
SoundWaveInGetCaps,
SoundNoVolume,
DO_DIRECT_IO
},
{
NULL, NULL,
0,
FILE_DEVICE_WAVE_OUT,
WAVE_OUT,
"LDWo",
L"\\Device\\WSSWaveOut",
SoundWaveDeferred,
SoundExcludeRoutine,
SoundWaveDispatch,
SoundWaveOutGetCaps,
SoundNoVolume,
DO_DIRECT_IO
},
{
NULL, NULL,
0,
FILE_DEVICE_SOUND,
MIXER_DEVICE,
"LDMx",
L"\\Device\\WSSMixer",
NULL, // No Dpc routine
SoundExcludeRoutine,
SoundMixerDispatch,
SoundMixerDumpConfiguration,
SoundNoVolume, // No volume setting
DO_BUFFERED_IO
},
{
REG_VALUENAME_LEFTLINEIN, REG_VALUENAME_RIGHTLINEIN,
DEF_AUX_VOLUME,
FILE_DEVICE_SOUND,
AUX_DEVICE,
"LDLi",
L"\\Device\\WSSAux",
NULL,
SoundExcludeRoutine,
SoundAuxDispatch,
SoundAuxGetCaps,
SoundNoVolume,
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
--*/
{
//
// Save volume for all devices
//
PLOCAL_DEVICE_INFO pLDI;
PGLOBAL_DEVICE_INFO pGDI;
pLDI = pDO->DeviceExtension;
pGDI = pLDI->pGlobalInfo;
//
// Save mixer settings!
//
SoundSaveMixerSettings(pGDI);
return STATUS_SUCCESS;
}
BOOLEAN
SoundExcludeRoutine(
IN OUT PLOCAL_DEVICE_INFO pLDI,
IN SOUND_EXCLUDE_CODE Code
)
/*++
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;
int NotifyLine;
pGDI = pLDI->pGlobalInfo;
if (Code == SoundExcludeEnter) {
KeWaitForSingleObject(&pGDI->WaveMutex,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
return TRUE;
} else {
if (Code == SoundExcludeLeave) {
KeReleaseMutex(&pGDI->WaveMutex, FALSE);
return TRUE;
}
}
//
// Synchronize!
//
KeWaitForSingleObject(&pGDI->WaveMutex,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
ReturnCode = FALSE;
switch (Code) {
case SoundExcludeOpen:
switch (pLDI->DeviceIndex) {
case WaveInDevice:
case WaveOutDevice:
if (pGDI->DeviceInUse == 0xFF) {
pGDI->DeviceInUse = 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->DeviceInUse == WaveInDevice &&
( WaveInfo->LowPriorityHandle != NULL &&
!WaveInfo->LowPrioritySaved)) {
pGDI->DeviceInUse = pLDI->DeviceIndex;
ReturnCode = TRUE;
}
}
break;
default:
//
// aux and mixer devices should not receive this call
//
ASSERT(FALSE);
break;
}
break;
case SoundExcludeClose:
ReturnCode = TRUE;
switch (pLDI->DeviceIndex) {
case WaveInDevice:
case WaveOutDevice:
if (!pGDI->WaveInfo.LowPrioritySaved) {
pGDI->DeviceInUse = 0xFF;
} else {
if (pLDI->DeviceIndex == WaveOutDevice) {
pGDI->DeviceInUse = WaveInDevice;
}
}
break;
default:
//
// aux devices should not receive this call
//
ASSERT(FALSE);
break;
}
break;
case SoundExcludeQueryOpen:
switch (pLDI->DeviceIndex) {
case WaveInDevice:
case WaveOutDevice:
ReturnCode = pGDI->DeviceInUse == pLDI->DeviceIndex ||
pGDI->WaveInfo.LowPrioritySaved &&
pLDI->DeviceIndex == WaveInDevice;
break;
default:
ASSERT(FALSE);
break;
}
break;
}
KeReleaseMutex(&pGDI->WaveMutex, FALSE);
return ReturnCode;
}
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING RegistryPathName
)
/*++
Routine Description:
This routine performs initialization for the sound system
device driver when it is first loaded.
It is called DriverEntry by convention as this is the entry
point the IO subsystem looks for by default.
The design is as follows :
0. Cleanup is always by calling SoundCleanup. This routine
is also called by the unload entry point.
1. Find which bus our device is on (this is needed for
mapping things via the Hal).
1. Allocate space to store our global info
1. Open the driver's registry information and read it
2. Fill in the driver object with our routines
3. Create devices
1. Wave input
2. Wave output
3. Mixer
4. Line In
Customize each device type and initialize data
Also store the registry string in our global info so we can
open it again to store volume settings etc on shutdown
4. Check hardware conflicts by calling IoReportResourceUsage
for each device (as required)
(this may need to be called again later if
5. Find our IO port and check the device is really there
6. Allocate DMA channel
7. Connect interrupt
8. Test interrupt and DMA channel and write config data
back to the registry
During this phase the interrupt and channel may get changed
if conflicts arise
In any even close our registry handle
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;
/********************************************************************
*
* Initialize debugging
*
********************************************************************/
#if DBG
DriverName = "SNDSYS";
#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);
/*
** 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;
}
// SoundRaiseHardError(L"Microsoft Sound System Driver Loaded!");
return STATUS_SUCCESS;
}
NTSTATUS
SoundCardInstanceInit(
IN PWSTR RegistryPathName,
IN PVOID Context
)
{
/********************************************************************
*
* Local variables
*
********************************************************************/
/*
** Instance data
*/
PSOUND_CARD_INSTANCE CardInstance;
/*
**
** Return code from last function called
*/
NTSTATUS Status;
/*
** Configuration data :
*/
SOUND_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. If we supported more than one card with the same driver
** we could not use static data
*/
PGLOBAL_DEVICE_INFO pGDI;
//
// 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) {
ExFreePool(RegistryPathName);
return STATUS_INSUFFICIENT_RESOURCES;
}
dprintf4((" GlobalInfo : %08lXH", pGDI));
RtlZeroMemory(pGDI, sizeof(GLOBAL_DEVICE_INFO));
/********************************************************************
*
* Initialize some of the device global info.
*
********************************************************************/
pGDI->Key = GDI_KEY;
pGDI->RegistryPathName = RegistryPathName;
pGDI->DeviceInUse = 0xFF; // Free
KeInitializeMutex(&pGDI->WaveMutex,
2 // Level - 2 so that the
// synth can call our mixer
// safely
);
SoundInitializeWaveInfo(&pGDI->WaveInfo,
SoundAutoInitDMA,
SoundQueryFormat,
&pGDI->Hw);
/********************************************************************
*
* 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"));
// SoundCleanup(pGDI);
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.DmaBufferSize = DEFAULT_DMA_BUFFERSIZE;
ConfigData.SingleModeDMA = FALSE;
ConfigData.MixerSettingsFound = FALSE;
//
// Get the system configuration information for this driver.
//
//
// Port, Interrupt, DMA channel
// Volume settings
//
{
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)) {
// SoundCleanup(pGDI);
return Status;
}
}
pGDI->SingleModeDMA = ConfigData.SingleModeDMA != 0;
//
// print out some info about the configuration
//
dprintf2(("port %3X", ConfigData.Port));
dprintf2(("int %u", ConfigData.InterruptNumber));
dprintf2(("DMA channel %u", ConfigData.DmaChannel));
//
// Create our devices
//
{
int i;
PLOCAL_DEVICE_INFO pLDI;
for (i = 0; i < NumberOfDevices ; i++) {
Status = SoundCreateDevice(
&DeviceInit[i],
(UCHAR)0,
CardInstance->pDriverObject,
pGDI,
i == WaveInDevice || i == WaveOutDevice ?
&pGDI->WaveInfo : NULL,
&pGDI->Hw,
i,
&pGDI->DeviceObject[i]);
if (!NT_SUCCESS(Status)) {
dprintf1(("Failed to create device %ls - status %8X",
DeviceInit[i].PrototypeName, Status));
// SoundCleanup(pGDI);
return Status;
}
//
// Add the device name to the registry
//
pLDI =
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[i]->DeviceExtension;
//
// Save the device name where the non-kernel part can pick it up.
//
Status = SoundSaveDeviceName(pGDI->RegistryPathName, pLDI);
if (!NT_SUCCESS(Status)) {
// SoundCleanup(pGDI);
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 out and (possibly) remap hardware
**
** This is complicated because we have to IoReportResourceUsage
** everything every time we try it!
**
** Note that this has to be done after we've created at least
** one device because we need to call IoReportResourceUsage and
** this takes a device object as parameter
*/
Status = SoundInitHardwareConfig(pGDI,
&ConfigData.Port,
&ConfigData.InterruptNumber,
&ConfigData.DmaChannel,
ConfigData.DmaBufferSize);
if (!NT_SUCCESS(Status)) {
// SoundCleanup(pGDI);
return Status;
}
/*
** Save new settings
*/
Status = SoundSaveConfig(pGDI->RegistryPathName,
ConfigData.Port,
ConfigData.DmaChannel,
ConfigData.InterruptNumber);
if (!NT_SUCCESS(Status)) {
// SoundCleanup(pGDI);
return Status;
}
/*
** Test the interrupt and DMA channel on old Compaq machines.
** Note we'll have to test both input and output channels.
*/
if (pGDI->Hw.NoPCR) {
if (!SoundTestInterruptAndDMA(pGDI)) {
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
}
/*
** Set up the Midi if it's there
*/
Status = SynthInit(CardInstance->pDriverObject,
pGDI->RegistryPathName,
&pGDI->Synth,
SYNTH_PORT,
TRUE,
pGDI->BusType,
pGDI->BusNumber,
&pGDI->LocalMixerData.LineNotification
[DestLineoutSourceMidiout],
ControlLineoutMidioutVolume,
FALSE, // Just the standard one
SoundMidiOutGetCaps
);
if (NT_SUCCESS(Status)) {
if (!pGDI->Synth.IsOpl3) {
dprintf1(("Synth does not respond as Opl3!"));
}
} else {
dprintf1(("No synth!"));
}
/*
** 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.
*/
Status = SoundMixerInit(pGDI->DeviceObject[MixerDevice]->DeviceExtension,
ConfigData.MixerSettings,
ConfigData.MixerSettingsFound);
if (!NT_SUCCESS(Status)) {
// SoundCleanup(pGDI);
return Status;
}
return Status;
}
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 pDriverObject;
FirstGDI = pGDI;
pDriverObject = NULL;
for (;;) {
NextGDI = pGDI->Next;
//
// Free our interrupt
//
if (pGDI->WaveInfo.Interrupt) {
IoDisconnectInterrupt(pGDI->WaveInfo.Interrupt);
}
SoundFreeCommonBuffer(&pGDI->WaveInfo.DMABuf);
if (pGDI->DeviceObject[WaveInDevice]) {
if (pGDI->ShutdownRegistered) {
IoUnregisterShutdownNotification(pGDI->DeviceObject[WaveInDevice]);
}
//
// There are some devices to delete
//
pDriverObject = pGDI->DeviceObject[WaveInDevice]->DriverObject;
}
if (pGDI->Hw.PortBase && pGDI->MemType == 0) {
MmUnmapIoSpace(pGDI->Hw.PortBase, NUMBER_OF_SOUND_PORTS);
}
if (pGDI->Hw.CompaqBA != NULL &&
pGDI->Hw.CompaqBA != pGDI->Hw.PortBase &&
pGDI->MemType == 0) {
MmUnmapIoSpace(pGDI->Hw.CompaqBA, 4);
}
//
// 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);
}
ExFreePool(pGDI);
if (NextGDI == FirstGDI) {
break;
} else {
pGDI = NextGDI;
}
}
//
// Free all devices for this driver. This will free everything for
// every card.
//
if (pDriverObject != NULL) {
while (pDriverObject->DeviceObject != NULL) {
//
// Undeclare resources used by device and
// delete the device object and associated data
//
SoundFreeDevice(pDriverObject->DeviceObject);
}
}
}
VOID
SoundUnload(
IN OUT PDRIVER_OBJECT pDriverObject
)
{
PGLOBAL_DEVICE_INFO pGDI;
PGLOBAL_DEVICE_INFO pGDIFirst;
dprintf3(("Unload request"));
//
// 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 {
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);
}