NT4/private/ntos/dd/sound/necsnd/init.c
2020-09-30 17:12:29 +02:00

839 lines
21 KiB
C

/*++
"@(#) NEC init.c 1.1 95/03/22 21:23:29"
Copyright (c) 1994 NEC Corporation
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.
Environment:
Kernel mode
Revision History:
--*/
#include "sound.h"
#include "string.h"
#include "stdlib.h"
//
// Function prototype
//
NTSTATUS
SoundGetRegistryInformation(
OUT PCONFIG_CONTROLLER_DATA *ConfigData
);
//
// 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\\NECWaveIn",
SoundWaveDeferred,
SoundExcludeRoutine,
SoundWaveDispatch,
SoundWaveInGetCaps,
SoundNoVolume,
DO_DIRECT_IO
},
{
NULL, NULL,
0,
FILE_DEVICE_WAVE_OUT,
WAVE_OUT,
"LDWo",
L"\\Device\\NECWaveOut",
SoundWaveDeferred,
SoundExcludeRoutine,
SoundWaveDispatch,
SoundWaveOutGetCaps,
SoundNoVolume,
DO_DIRECT_IO
},
{
NULL, NULL,
0,
FILE_DEVICE_SOUND,
MIXER_DEVICE,
"LDMx",
L"\\Device\\NECMixer",
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\\NECAux",
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;
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 SoundExcludeEnter:
ReturnCode = TRUE;
switch (pLDI->DeviceIndex) {
default:
KeWaitForSingleObject(&pGDI->WaveMutex,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
break;
}
break;
case SoundExcludeLeave:
ReturnCode = TRUE;
switch (pLDI->DeviceIndex) {
default:
KeReleaseMutex(&pGDI->WaveMutex, 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;
}
return ReturnCode;
}
// ------------------------------------------------------------------
// Name: DriverEntry
// Desc: 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).
//
// 2. Allocate space to store our global info
//
// 3. Open the driver's registry information and read it
//
// 4. Fill in the driver object with our routines
//
// 5. 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
//
// 6. Check hardware conflicts by calling
// IoReportResourceUsage for each device (as required)
// (this may need to be called again later if
//
// 7. Find our IO port and check the device is really there
//
// 8. Allocate DMA channel
//
// 9. Connect interrupt
//
// 10.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 event close our registry handle
//
// Params: pDriverObject - Pointer to a driver object.
// RegistryPathName - the path to our driver services node
//
// Returns: The function value is the final status from the
// initialization operation.
NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING RegistryPathName )
{
SOUND_CARD_INSTANCE CardInstance;
NTSTATUS Status;
// Initialize debugging
#if DBG
DriverName = "NECSND";
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;
// FirmWare Setting Configration Data
PCONFIG_CONTROLLER_DATA FwConfigData;
PHYSICAL_ADDRESS LongAddress;
// 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,
SoundReprogramOnInterruptDMA, //
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.
pGDI->BusType = Internal; //
pGDI->BusNumber = 0; //
// 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.InputSource = INPUT_MIC; // Default to microphone
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;
}
}
dprintf2((">>> SoundGetRegistryInformation() Call <<<<"));
Status = SoundGetRegistryInformation(&FwConfigData);
if (!NT_SUCCESS(Status))
{
dprintf2(("Don't get F/W Configuration"));
return Status;
}
LongAddress = FwConfigData->OriginalBaseAddress;
ConfigData.Port = LongAddress.LowPart;
ConfigData.InterruptNumber = FwConfigData->OriginalVector;
ConfigData.DmaChannel = FwConfigData->OriginalDmaChannel;
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));
dprintf2(("DMA size %8X", ConfigData.DmaBufferSize));
dprintf2(("SingleMode DMA %d", ConfigData.SingleModeDMA));
dprintf2(("MixerSettingfound %d", ConfigData.MixerSettingsFound));
// 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
IoRegisterShutdownNotification(pGDI->DeviceObject[WaveInDevice]);
// 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.InputSource,
ConfigData.DmaBufferSize);
if (!NT_SUCCESS(Status))
{
// SoundCleanup(pGDI);
return Status;
}
dprintf5(("SoundInitHardwareConfig() Success !!"));
// Save new settings
Status = SoundSaveConfig(pGDI->RegistryPathName,
ConfigData.Port,
ConfigData.DmaChannel,
ConfigData.InterruptNumber,
ConfigData.InputSource);
if (!NT_SUCCESS(Status))
{
// SoundCleanup(pGDI);
return Status;
}
dprintf5(("SoundSaveConfig() Success !!"));
// 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;
}
dprintf5(("SoundMixerInit() Success !!"));
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]) {
//
// There are some devices to delete
//
pDriverObject = pGDI->DeviceObject[WaveInDevice]->DriverObject;
IoUnregisterShutdownNotification(pGDI->DeviceObject[WaveInDevice]);
}
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) {
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;
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);
}