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

1669 lines
38 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
"@(#) NEC soundlib.c 1.1 95/03/22 21:23:35"
Copyright (c) 1995 NEC Corporation.
Copyright (c) 1992 Microsoft Corporation
Module Name:
soundlib.c
Abstract:
This module contains common code for Sound Kernel mode device
drivers.
Environment:
Kernel mode
Revision History:
--*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h> // For vsprintf
#include <stdarg.h> // For va_list
#include <soundlib.h> // Definition of what's in here
//
// Internal routine definintions
//
NTSTATUS SoundConfigurationCallout(
IN PVOID Context,
IN PUNICODE_STRING PathName,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
IN CONFIGURATION_TYPE ControllerType,
IN ULONG ControllerNumber,
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
IN CONFIGURATION_TYPE PeripheralType,
IN ULONG PeripheralNumber,
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
);
//
// Remove initialization stuff from resident memory
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,SoundGetBusNumber)
#pragma alloc_text(INIT,SoundConfigurationCallout)
#pragma alloc_text(INIT,SoundReportResourceUsage)
#pragma alloc_text(INIT,SoundCreateDevice)
#pragma alloc_text(INIT,SoundConnectInterrupt)
#pragma alloc_text(INIT,SoundSetErrorCode)
#pragma alloc_text(INIT,SoundSaveRegistryPath)
#pragma alloc_text(PAGE,SoundMapPortAddress)
#pragma alloc_text(PAGE,SoundCreateDeviceName)
#pragma alloc_text(PAGE,SoundFreeDevice)
#pragma alloc_text(PAGE,SoundWriteRegistryDWORD)
#pragma alloc_text(PAGE,SoundSaveDeviceName)
#endif
NTSTATUS
SoundGetBusNumber(
IN OUT INTERFACE_TYPE InterfaceType,
OUT PULONG BusNumber
)
/*++
Routine Description :
Find the bus of the type we are looking for - and hope this is
the one with our card on! Actually if bus is not
bus number 0 we fail.
Arguments :
BusNumber - Where to put the answer
Return Value :
NT status code - STATUS_SUCCESS if no problems
--*/
{
ULONG TestBusNumber = 0;
NTSTATUS Status;
BOOLEAN Ok = FALSE; // Must match type passed by reference to
// SoundConfigurationCallout by
// IoQueryDeviceDescription.
//
// See if our bus type exists by calling IoQueryDeviceDescription
//
Status = IoQueryDeviceDescription(
&InterfaceType,
&TestBusNumber,
NULL,
NULL,
NULL,
NULL,
SoundConfigurationCallout,
(PVOID)&Ok);
if (Ok) {
*BusNumber = TestBusNumber;
}
return Ok ? STATUS_SUCCESS : STATUS_DEVICE_DOES_NOT_EXIST;
}
NTSTATUS
SoundConfigurationCallout(
IN PVOID Context,
IN PUNICODE_STRING PathName,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
IN CONFIGURATION_TYPE ControllerType,
IN ULONG ControllerNumber,
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
IN CONFIGURATION_TYPE PeripheralType,
IN ULONG PeripheralNumber,
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
)
/*++
Routine Description :
Set the return to OK to indicate we've found the bus type
we were looking for. Merely being called is confirmation
that our bus is there.
Arguments :
See the arguments defined by PIO_QUERY_DEVICE_ROUTINE
Return Value :
STATUS_SUCCESS if no problems
--*/
{
*(PBOOLEAN)Context = (BOOLEAN)TRUE;
return STATUS_SUCCESS;
}
NTSTATUS SoundReportResourceUsage(
IN PDEVICE_OBJECT DeviceObject,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN PULONG InterruptNumber OPTIONAL,
IN KINTERRUPT_MODE InterruptMode,
IN BOOLEAN InterruptShareDisposition,
IN PULONG DmaChannel OPTIONAL,
IN PULONG FirstIoPort OPTIONAL,
IN ULONG IoPortLength
)
/*++
Routine Description :
Calls IoReportResourceUsage for the device and resources
passed in. NOTE that this supercedes previous resources
declared for this device.
It is assumed that all resources owned by the device cannot
be shared, except for level-sensitive interrupts which can be
shared.
Arguments :
DeviceObject - The device which 'owns' the resources
This can also be a pointer to a driver object
BusType - The type of bus on which the device lives
BusNumber - The bus number (of type BusType) where the device is
InterruptNumber - The interrupt the devices uses (if any)
DmaChannel - The DMA channel the device uses
FirstIoPort - The start Io port for the device
IoPortLength - The number of bytes of IO space the device uses
(starting at FirstIoPort)
Return Value :
STATUS_SUCCESS if no problems
The return from IoReportResourceUsage if this fails
STATUS_DEVICE_CONFIGURATION_ERROR is IoReportResourceUsage reports
a conflict
--*/
{
NTSTATUS Status;
//
// Our resource list to report back to the system
//
/*
Compiler rejects this
UCHAR ResBuffer[FIELD_OFFSET(
CM_RESOURCE_LIST,
List[0].PartialResourceList.PartialDescriptors[3].Type)];
*/
UCHAR ResBuffer[3 * sizeof(CM_RESOURCE_LIST)];
BOOLEAN ResourceConflict;
PCM_RESOURCE_LIST ResourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
ResourceList = (PCM_RESOURCE_LIST)ResBuffer;
Descriptor = ResourceList->List[0].PartialResourceList.PartialDescriptors;
ResourceConflict = FALSE;
//
// Zero out any unused data
//
RtlZeroMemory(ResBuffer, sizeof(ResBuffer));
//
// We assume there's only 1 bus so we only need one list.
// Fill in the bus description
//
ResourceList->Count = 1;
ResourceList->List[0].InterfaceType = BusType;
ResourceList->List[0].BusNumber = BusNumber;
//
// If the device is using IO Ports add this to the list
//
if (ARGUMENT_PRESENT(FirstIoPort)) {
PHYSICAL_ADDRESS PortAddress;
ULONG MemType;
PHYSICAL_ADDRESS MappedAddress;
PortAddress.LowPart = *FirstIoPort;
PortAddress.HighPart = 0;
dprintf5((">>>> FirstPort = %8x", *FirstIoPort));
MemType = 0;
dprintf4(("HalTranslateBusAddress(): Bustype = %d", BusType));
dprintf4(("HalTranslateBusAddress(): BusNumber = %d", BusNumber));
dprintf4(("HalTranslateBusAddress(): PortAddr.lo = 0x%8x", PortAddress.LowPart));
dprintf4(("HalTranslateBusAddress(): PortAddr.hi = 0x%8x", PortAddress.HighPart));
dprintf4(("HalTranslateBusAddress(): MemType = %d", MemType));
HalTranslateBusAddress(
BusType,
BusNumber,
PortAddress,
&MemType,
&MappedAddress);
dprintf4(("HalTranslateBusAddress(): MemType(Out) = %d", MemType));
dprintf4(("HalTranslateBusAddress(): MapAddr(Out) = 0x%16x", MappedAddress));
ResourceList->List[0].PartialResourceList.Count++;
Descriptor->Type = CmResourceTypePort;
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
Descriptor->u.Port.Start.LowPart = *FirstIoPort;
Descriptor->u.Port.Length = IoPortLength;
Descriptor->Flags = MemType == 0 ? CM_RESOURCE_PORT_MEMORY :
CM_RESOURCE_PORT_IO;
//
// Move on to next resource descriptor entry
//
Descriptor++;
}
//
// Add interrupt information (if any) to the list
//
if (ARGUMENT_PRESENT(InterruptNumber)) {
KAFFINITY Affinity;
KIRQL InterruptRequestLevel;
ULONG InterruptVector;
//
// Get the processor affinity and vector
//
dprintf4(("HalGetInterruptVector(): BusType = %d", BusType));
dprintf4(("HalGetInterruptVector(): BusNumber = %d", BusNumber));
dprintf4(("HalGetInterruptVector(): InterruptLevel = %d", INTERRUPT_LEVEL));
dprintf4(("HalGetInterruptVector(): InterruptNumber = %d", *InterruptNumber));
InterruptVector = HalGetInterruptVector(BusType,
BusNumber,
INTERRUPT_LEVEL, // InterruptLevel
*InterruptNumber, // InterruptVector
&InterruptRequestLevel,
&Affinity);
dprintf4(("HalGetInterruptVector(): InterruptLevel(Out) = %d", InterruptRequestLevel));
dprintf4(("HalGetInterruptVector(): Affinity(Out) = %d", Affinity));
dprintf4(("HalGetInterruptVector(): InterruptVector(ret)= %d", InterruptVector));
ResourceList->List[0].PartialResourceList.Count++;
Descriptor->Type = CmResourceTypeInterrupt;
Descriptor->ShareDisposition = (UCHAR)(InterruptShareDisposition ?
CmResourceShareShared :
CmResourceShareDeviceExclusive);
Descriptor->Flags =
InterruptMode == Latched ? CM_RESOURCE_INTERRUPT_LATCHED :
CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
// Descriptor->u.Interrupt.Level = *InterruptNumber;
Descriptor->u.Interrupt.Level = INTERRUPT_LEVEL;
Descriptor->u.Interrupt.Vector = InterruptVector;
Descriptor->u.Interrupt.Affinity = (ULONG)Affinity;
//
// Move on to next resource descriptor entry
//
Descriptor++;
}
//
// Add DMA description if any
//
if (ARGUMENT_PRESENT(DmaChannel)) {
ResourceList->List[0].PartialResourceList.Count++;
Descriptor->Type = CmResourceTypeDma;
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
Descriptor->u.Dma.Channel = *DmaChannel;
Descriptor->u.Dma.Port = 0; // ???
//
// Move on to next resource descriptor entry
//
Descriptor++;
}
//
// Report our resource usage and detect conflicts
//
switch (DeviceObject->Type) {
case IO_TYPE_DEVICE:
Status = IoReportResourceUsage(NULL,
DeviceObject->DriverObject,
NULL,
0,
DeviceObject,
ResourceList,
(PUCHAR)Descriptor - (PUCHAR)ResourceList,
FALSE,
&ResourceConflict);
break;
case IO_TYPE_DRIVER:
Status = IoReportResourceUsage(NULL,
(PDRIVER_OBJECT)DeviceObject,
ResourceList,
(PUCHAR)Descriptor - (PUCHAR)ResourceList,
NULL,
NULL,
0,
FALSE,
&ResourceConflict);
break;
default:
ASSERTMSG("SoundReportResourceUsage - invalid object", FALSE);
}
if (ResourceConflict) {
dprintf1(("Resource conflict reported"));
Status = STATUS_DEVICE_CONFIGURATION_ERROR;
}
return Status;
}
VOID
SoundFreeDevice(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description :
Free the all resources related to this device :
The device object itself
Any declared hardware resources (via IoReportResourceUsage)
Any symbolic link related to this device
Arguments :
DeviceObject - the device to free
Return Value :
None
--*/
{
CM_RESOURCE_LIST NullResourceList;
BOOLEAN ResourceConflict;
//
// Free the device if any
//
if (DeviceObject != NULL) {
//
// Undeclare any resources used by the device
// (delete anything the driver has at the same time!)
//
NullResourceList.Count = 0;
IoReportResourceUsage(NULL,
DeviceObject->DriverObject,
&NullResourceList,
sizeof(ULONG),
DeviceObject,
&NullResourceList,
sizeof(ULONG),
FALSE,
&ResourceConflict);
//
// Remove the device's symbolic link
//
{
PLOCAL_DEVICE_INFO pLDI;
UNICODE_STRING DeviceName;
NTSTATUS Status;
pLDI = DeviceObject->DeviceExtension;
Status = SoundCreateDeviceName(
L"\\DosDevices\\",
pLDI->DeviceInit->PrototypeName + wcslen(L"\\Device\\"),
pLDI->DeviceNumber,
&DeviceName);
if (NT_SUCCESS(Status)) {
IoDeleteSymbolicLink(&DeviceName);
ExFreePool(DeviceName.Buffer);
}
}
//
// Delete the device object
//
IoDeleteDevice(DeviceObject);
}
}
NTSTATUS
SoundCreateDeviceName(
PCWSTR PrePrefix,
PCWSTR Prefix,
UCHAR Index,
PUNICODE_STRING DeviceName
)
{
UNICODE_STRING Number;
WCHAR NumberBuffer[5];
UNICODE_STRING uPrePrefix;
UNICODE_STRING uPrefix;
ULONG Size;
RtlInitUnicodeString(&uPrePrefix, PrePrefix);
RtlInitUnicodeString(&uPrefix, Prefix);
Size = uPrePrefix.Length + uPrefix.Length + sizeof(NumberBuffer) +
sizeof(UNICODE_NULL);
DeviceName->Buffer = ExAllocatePool(PagedPool, Size);
DeviceName->MaximumLength = (USHORT)Size;
if (DeviceName->Buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyUnicodeString(DeviceName, &uPrePrefix);
RtlAppendUnicodeStringToString(DeviceName, &uPrefix);
if (Index != 255) {
Number.Buffer = NumberBuffer;
Number.MaximumLength = sizeof(NumberBuffer);
RtlIntegerToUnicodeString((ULONG)Index, 10, &Number);
RtlAppendUnicodeStringToString(DeviceName, &Number);
}
/*
** Null terminate for some uses (but don't increase the 'length' or
** the UNICODE_STRING version will have a 0 on the end.
*/
DeviceName->Buffer[DeviceName->Length / sizeof(UNICODE_NULL)] = UNICODE_NULL;
return STATUS_SUCCESS;
}
NTSTATUS
SoundCreateDevice(
IN PCSOUND_DEVICE_INIT DeviceInit,
IN UCHAR CreationFlags,
IN PDRIVER_OBJECT pDriverObject,
IN PVOID pGDI,
IN PVOID DeviceSpecificData,
IN PVOID pHw,
IN int i,
OUT PDEVICE_OBJECT *ppDevObj
)
/*++
Routine Description:
Create a new device using a name derived from szPrototypeName
by adding a number on to the end such that the no device with the
qualified name exists.
A symbolic link in \DosDevices is also created
Arguments:
DeviceInit - device initialization data
NoRange - if this is set then no number is concatenated, the
explicit name is used.
pDriverObject - our driver
pGDI - global context
pHw - hardware context
i - device number for back reference
ppDevObj - where to write back the device object pointer
Return Value:
An NTSTATUS code.
--*/
{
UCHAR DeviceNumber;
NTSTATUS Status;
UNICODE_STRING DeviceName;
OBJECT_ATTRIBUTES ObjectAttributes;
PLOCAL_DEVICE_INFO pLDI;
for (DeviceNumber = 0; ; DeviceNumber++) {
//
// Append the device number if required
//
if ((CreationFlags & SOUND_CREATION_NO_NAME_RANGE)) {
DeviceNumber = 255;
}
Status = SoundCreateDeviceName(
L"",
DeviceInit->PrototypeName,
DeviceNumber,
&DeviceName);
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = IoCreateDevice(
pDriverObject,
sizeof(LOCAL_DEVICE_INFO),
&DeviceName,
DeviceInit->Type,
0,
FALSE, // Non-Exclusive
ppDevObj
);
if (NT_SUCCESS(Status)) {
dprintf2(("Created device %d", DeviceNumber));
//
// Set up the rest of the device stuff
//
(*ppDevObj)->Flags |= DeviceInit->IoMethod;
(*ppDevObj)->AlignmentRequirement = FILE_BYTE_ALIGNMENT;
if (DeviceInit->DeferredRoutine) {
IoInitializeDpcRequest((*ppDevObj), DeviceInit->DeferredRoutine);
}
pLDI = (*ppDevObj)->DeviceExtension;
RtlZeroMemory(pLDI, sizeof(*pLDI));
//
// Try to create a symbolic link object for this device
//
// No security
//
// We make (eg)
// \DosDevices\WaveOut0
// Point to
// \Device\WaveOut0
//
{
UNICODE_STRING LinkObject;
Status = SoundCreateDeviceName(
L"\\DosDevices\\",
DeviceInit->PrototypeName + wcslen(L"\\Device\\"),
DeviceNumber,
&LinkObject);
if (NT_SUCCESS(Status)) {
Status = IoCreateSymbolicLink(&LinkObject, &DeviceName);
ExFreePool(LinkObject.Buffer);
}
ExFreePool(DeviceName.Buffer);
if (!NT_SUCCESS(Status)) {
dprintf1(("Failed to create symbolic link object"));
IoDeleteDevice(*ppDevObj);
*ppDevObj = NULL;
return Status;
}
}
//
// Fill in the rest of the device information
//
#ifdef VOLUME_NOTIFY
InitializeListHead(&pLDI->VolumeQueue);
#endif // VOLUME_NOTIFY
pLDI->DeviceNumber = DeviceNumber;
pLDI->DeviceInit = DeviceInit;
pLDI->CreationFlags = CreationFlags;
pLDI->Key = *(PULONG)DeviceInit->Key;
pLDI->DeviceType = (UCHAR)DeviceInit->DeviceType;
pLDI->DeviceIndex = (UCHAR)i;
pLDI->pGlobalInfo = pGDI;
pLDI->DeviceSpecificData = DeviceSpecificData;
pLDI->HwContext = pHw;
pLDI->VolumeControlId = SOUND_MIXER_INVALID_CONTROL_ID;
return STATUS_SUCCESS;
} else {
ExFreePool(DeviceName.Buffer);
}
if (DeviceNumber >= SOUND_MAX_DEVICES) {
break;
}
}
//
// Failed !
//
return STATUS_INSUFFICIENT_RESOURCES;
}
PUCHAR
SoundMapPortAddress(
INTERFACE_TYPE BusType,
ULONG BusNumber,
ULONG PortBase,
ULONG Length,
PULONG MemType
)
/*++
Routine Description :
Map a physical device port address to an address we can pass
to READ/WRITE_PORT_UCHAR/USHORT etc
Arguments :
BusType - type of bus
BusNumber - bus number
PortBase - The port start address
Length - how many bytes of port space to map (needed by MmMapIoSpace)
Return Value :
The virtual port address
--*/
{
PHYSICAL_ADDRESS PortAddress;
PHYSICAL_ADDRESS MappedAddress;
*MemType = 0; // Memory Space
PortAddress.LowPart = PortBase;
PortAddress.HighPart = 0;
dprintf5((">>>> PortBase = %8x", PortBase));
dprintf4(("HalTranslateBusAddress()2: Bustype = %d", BusType));
dprintf4(("HalTranslateBusAddress()2: BusNumber = %d", BusNumber));
dprintf4(("HalTranslateBusAddress()2: PortAddr.lo = 0x%16x", PortAddress.LowPart));
dprintf4(("HalTranslateBusAddress()2: PortAddr.hi = 0x%16x", PortAddress.HighPart));
dprintf4(("HalTranslateBusAddress()2: MemType = %d", *MemType));
HalTranslateBusAddress(
BusType,
BusNumber,
PortAddress,
MemType,
&MappedAddress);
dprintf4(("HalTranslateBusAddress()2: MemType(Out) = %d", *MemType));
dprintf4(("HalTranslateBusAddress()2: MapAddr(Out) = 0x%16x", MappedAddress));
if (*MemType == 0) {
//
// Map memory type IO space into our address space
//
return (PUCHAR)MmMapIoSpace(MappedAddress, Length, FALSE);
} else {
return (PUCHAR)MappedAddress.LowPart;
}
}
NTSTATUS
SoundConnectInterrupt(
IN ULONG InterruptNumber,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN PKSERVICE_ROUTINE Isr,
IN PVOID ServiceContext,
IN KINTERRUPT_MODE InterruptMode,
IN BOOLEAN ShareVector,
OUT PKINTERRUPT *Interrupt
)
/*++
Routine Description :
Connect to an interrupt. From this point on our interrupt service
routine can receive interrupts
We assume that floating point arithmetic will not be used in the
service routine.
Arguments :
InterruptNumber - the interrupt number we're using
BusType - Our bus type
BusNumber - the number of our buse (of type BusType)
Isr - the interrupt service routine
ServiceContext - a value passed to the interrupt service routine
InterruptMode - whether it's latched or level sensitive
ShareVector - whether the interrupt can be shared
Interrupt - Returns the pointer to the interrupt object
Return Value :
An NTSTATUS return value - STATUS_SUCCESS if OK.
--*/
{
KAFFINITY Affinity;
KIRQL InterruptRequestLevel;
ULONG InterruptVector;
NTSTATUS Status;
//
// Call HalGetInterruptVector to get the interrupt vector,
// processor affinity and request level to pass to IoConnectInterrupt
//
//dprintf2(("BusType = %x", BusType));
//dprintf2(("BusNumber = %d",BusNumber));
//dprintf2(("InterruptNumber = %d", InterruptNumber));
dprintf4(("HalGetInterruptVector()2: BusType = %d", BusType));
dprintf4(("HalGetInterruptVector()2: BusNumber = %d", BusNumber));
dprintf4(("HalGetInterruptVector()2: InterruptLevel = %d", INTERRUPT_LEVEL));
dprintf4(("HalGetInterruptVector()2: InterruptNumber = %d", InterruptNumber));
InterruptVector = HalGetInterruptVector(BusType,
BusNumber,
INTERRUPT_LEVEL, // Interrupt Level
InterruptNumber, // Interrupt Vector
&InterruptRequestLevel,
&Affinity);
dprintf4(("IoConnectInterrupt(): ServiceRoutine = %8x", Isr));
dprintf4(("IoConnectInterrupt(): ServicContext = %8x", ServiceContext));
dprintf4(("IoConnectInterrupt(): SpinLock = NULL"));
dprintf4(("IoConnectInterrupt(): InterruptVector = %d", InterruptVector));
dprintf4(("IoConnectInterrupt(): InterruptReqLvl = %d", InterruptRequestLevel));
dprintf4(("IoConnectInterrupt(): SyncronizedIRQL = %d", InterruptRequestLevel));
dprintf4(("IoConnectInterrupt(): InterruptMode = %d", InterruptMode));
dprintf4(("IoConnectInterrupt(): ShareVector = %d", ShareVector));
dprintf4(("IoConnectInterrupt(): ProcessorMask = %d", Affinity));
dprintf4(("IoConnectInterrupt(): FloatingSave = %d", FALSE));
Status = IoConnectInterrupt(
Interrupt,
Isr,
ServiceContext,
(PKSPIN_LOCK)NULL,
InterruptVector,
InterruptRequestLevel,
InterruptRequestLevel,
InterruptMode,
ShareVector,
Affinity,
FALSE // No floating point save
);
dprintf4(("IoConnectInterrupt(): Status =%8x",Status));
return Status == STATUS_INVALID_PARAMETER ?
STATUS_DEVICE_CONFIGURATION_ERROR : Status;
}
NTSTATUS
SoundSetErrorCode(
IN PWSTR RegistryPath,
IN ULONG Value
)
/*++
Routine Description :
Write the given DWORD into the registry using the path
with a value name of SOUND_REG_CONFIGERROR
Arguments :
RegistryPath- path to registry key
Value - value to store
Return Value :
NTSTATUS code
--*/
{
return SoundWriteRegistryDWORD(RegistryPath, SOUND_REG_CONFIGERROR, Value);
}
NTSTATUS
SoundWriteRegistryDWORD(
IN PCWSTR RegistryPath,
IN PCWSTR ValueName,
IN ULONG Value
)
/*++
Routine Description :
Write the given DWORD into the registry using the path and
value name supplied by calling RtlWriteRegistryValue
Arguments :
RegistryPath- path to registry key
ValueName - name of value to write
Value - value to store
Return Value :
NTSTATUS code
--*/
{
NTSTATUS Status;
Status = RtlWriteRegistryValue(
RTL_REGISTRY_ABSOLUTE,
(LPWSTR)RegistryPath,
(LPWSTR)ValueName,
REG_DWORD,
&Value,
sizeof(Value));
if (!NT_SUCCESS(Status)) {
dprintf1(("Writing parameter %ls to registry failed status %8X",
ValueName, Status));
}
return Status;
}
VOID
SoundDelay(
IN ULONG Milliseconds
)
/*++
Routine Description :
Stall the current thread for the given number of milliseconds AT LEAST
Arguments :
Milliseconds - number of milliseconds to delay
Return Value :
None
--*/
{
LARGE_INTEGER Delay;
//
// Can't call SoundDelay() from high irql
//
if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
return;
}
//
// First a tiny delay to synch us up with the timer otherwise we
// may wait up to 15ms less time than we expected!
//
Delay = RtlConvertLongToLargeInteger(-1);
KeDelayExecutionThread(KernelMode,
FALSE, // Not alertable
&Delay);
Delay = RtlConvertLongToLargeInteger((-(LONG)Milliseconds) * 10000);
KeDelayExecutionThread(KernelMode,
FALSE, // Not alertable
&Delay);
}
VOID
SoundEnter(
PLOCAL_DEVICE_INFO pLDI,
BOOLEAN Enter
)
/*++
Routine Description:
Acquire this device's mutex. This is used for a device to synchronize
with its associated mixer device
Argmuments:
pLDI - the device local device info to synchronize with
Enter - TRUE means Enter, FALSE means leave
Return Value:
None
--*/
{
(pLDI->DeviceInit->ExclusionRoutine)(
pLDI,
Enter ? SoundExcludeEnter : SoundExcludeLeave);
}
LARGE_INTEGER
SoundGetTime(
VOID
)
/*++
Routine Description:
Get an accurate estimate of the current time by calling
KeQueryPerformanceCounter and converting the result to 100ns units
NOTE: A driver should call this once during init to get the thing
safely started if it can be called from more than one device at a time
Arguments:
None
Return Value:
--*/
{
static struct {
LARGE_INTEGER StartTime100ns, StartTimeTicks, TicksPerSecond;
ULONG Multiplier;
BOOLEAN Initialized;
} s = { 1 }; // Move from BSS to reduce size
ULONG Remainder;
if (!s.Initialized) {
KeQuerySystemTime(&s.StartTime100ns);
s.StartTimeTicks = KeQueryPerformanceCounter(&s.TicksPerSecond);
s.Multiplier = 10000000;
while (s.TicksPerSecond.HighPart != 0) {
s.Multiplier = s.Multiplier / 10;
s.TicksPerSecond =
RtlExtendedLargeIntegerDivide(s.TicksPerSecond, 10, &Remainder);
}
s.Initialized = TRUE;
}
//
// Convert ticks to 100ns units (and hope we don't overflow!)
//
return RtlLargeIntegerAdd(
RtlExtendedLargeIntegerDivide(
RtlExtendedIntegerMultiply(
RtlLargeIntegerSubtract(
KeQueryPerformanceCounter(NULL),
s.StartTimeTicks
),
s.Multiplier
),
s.TicksPerSecond.LowPart,
&Remainder
),
s.StartTime100ns
);
}
VOID
SoundFreeQ(
PLIST_ENTRY ListHead,
NTSTATUS IoStatus
)
/*++
Routine Description:
Free a list of Irps - setting the specified status and completing
them. The list will be empty on exit.
Arguments:
pListNode - the list to free
IoStatus - the status to set in each Irp.
Return Value:
None.
--*/
{
//
// Remove all the queue entries, completing all
// the Irps represented by the entries
//
for (;;) {
PIRP pIrp;
//
// The queue may be cancellable so use our routine to get the Irp
//
pIrp = SoundRemoveFromCancellableQ(ListHead);
if (pIrp == NULL) {
break;
}
pIrp->IoStatus.Status = IoStatus;
//
// Bump priority here because the application may still be trying
// to be real-time
//
IoCompleteRequest(pIrp, IO_SOUND_INCREMENT);
}
}
VOID
SoundRemoveAndComplete(
PDEVICE_OBJECT pDO,
PIRP Irp
)
/*++
Routine Description:
Removes the Irp from any queue it's on
Completes it as cancelled.
Arguments:
Irp - the Irp
Cancellable - If it is to be made cancellable
Return Value:
None.
Notes:
This routine is called with the cancel spin lock held
--*/
{
dprintf2(("Cancelling Irp from cancel routine"));
//
// Remove the Irp from the queue
//
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
//
// Release the cancel spin lock
//
IoReleaseCancelSpinLock(Irp->CancelIrql);
//
// Set status and complete
//
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
VOID
SoundAddIrpToCancellableQ(
PLIST_ENTRY QueueHead,
PIRP Irp,
BOOLEAN Head
)
/*++
Routine Description:
Add the Irp to the queue and set the cancel routine under the
protection of the cancel spin lock.
Arguments:
QueueHead - the queue to add it to
Irp - the Irp
Head - if TRUE insert at the head of the queue
Return Value:
None.
--*/
{
KIRQL OldIrql;
//
// Get the cancel spin lock so we can mess with the cancel stuff
//
IoAcquireCancelSpinLock(&OldIrql);
//
// Well, it may ALREADY be cancelled!
//
if (Irp->Cancel) {
dprintf2(("Irp already cancelled"));
//
// Release the cancel spin lock
//
IoReleaseCancelSpinLock(OldIrql);
//
// Set status and complete
//
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return;
}
//
// Set the cancel routine
//
IoSetCancelRoutine(Irp, SoundRemoveAndComplete);
//
// Insert it in the queue
//
if (Head) {
InsertHeadList(QueueHead, &Irp->Tail.Overlay.ListEntry);
} else {
InsertTailList(QueueHead, &Irp->Tail.Overlay.ListEntry);
}
//
// Free the spin lock
//
IoReleaseCancelSpinLock(OldIrql);
}
PIRP
SoundRemoveFromCancellableQ(
PLIST_ENTRY QueueHead
)
/*++
Routine Description:
Remove the Irp to the queue and remove the cancel routine under the
protection of the cancel spin lock.
Arguments:
QueueHead - the queue to remove it from
Return Value:
The Irp at the head of the queue or NULL if the queue is empty.
--*/
{
KIRQL OldIrql;
PIRP Irp;
LIST_ENTRY ListNode;
//
// Get the cancel spin lock so we can mess with the cancel stuff
//
IoAcquireCancelSpinLock(&OldIrql);
if (IsListEmpty(QueueHead)) {
Irp = NULL;
} else {
PLIST_ENTRY ListNode;
ListNode = RemoveHeadList(QueueHead);
Irp = CONTAINING_RECORD(ListNode, IRP, Tail.Overlay.ListEntry);
//
// Remove the cancel routine
//
IoSetCancelRoutine(Irp, NULL);
}
//
// Free the spin lock
//
IoReleaseCancelSpinLock(OldIrql);
//
// Return IRP (if any)
//
return Irp;
}
VOID
SoundMoveCancellableQueue(
IN OUT PLIST_ENTRY From,
IN OUT PLIST_ENTRY To
)
{
PIRP pIrp;
InitializeListHead(To);
while ((pIrp = SoundRemoveFromCancellableQ(From)) != NULL) {
SoundAddIrpToCancellableQ(To, pIrp, FALSE);
}
}
VOID
SoundFreePendingIrps(
PLIST_ENTRY QueueHead,
PFILE_OBJECT FileObject
)
{
KIRQL OldIrql;
//
// Free anyone waiting for the volume to change whose file object
// is the one about to be closed.
//
PLIST_ENTRY ListEntry, Next;
//
// Our list is cancellable so do this under the cancel spin
// lock
//
IoAcquireCancelSpinLock(&OldIrql);
for (ListEntry = QueueHead->Flink;
ListEntry != QueueHead;
ListEntry = Next) {
PIRP IrpList;
IrpList = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
Next = ListEntry->Flink;
ASSERT(IoGetCurrentIrpStackLocation(IrpList)->MajorFunction ==
IRP_MJ_DEVICE_CONTROL);
if (FileObject ==
IoGetCurrentIrpStackLocation(IrpList)->FileObject) {
RemoveEntryList(ListEntry);
//
// Make sure our cancel routine doesn't get in!
//
IoSetCancelRoutine(IrpList, NULL);
//
// Free the spin lock in case IoCompleteRequest doesn't
// like us to hold it
//
IoReleaseCancelSpinLock(OldIrql);
IrpList->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(IrpList, IO_NO_INCREMENT);
IoAcquireCancelSpinLock(&OldIrql);
//
// The world may have changed so restart
//
Next = QueueHead->Flink;
}
}
IoReleaseCancelSpinLock(OldIrql);
}
NTSTATUS
SoundSaveDeviceName(
IN PWSTR RegistryPathName,
IN PLOCAL_DEVICE_INFO pLDI
)
{
UNICODE_STRING DeviceName;
NTSTATUS Status;
HANDLE hKey;
ULONG DeviceType;
Status = SoundCreateDeviceName(
L"",
pLDI->DeviceInit->PrototypeName + wcslen(L"\\Device\\"),
pLDI->DeviceNumber,
&DeviceName);
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = SoundOpenDevicesKey(RegistryPathName, &hKey);
if (!NT_SUCCESS(Status)) {
ExFreePool(DeviceName.Buffer);
return Status;
}
/*
** The device's type is stored in a value whose name is the (unique)
** device name (minus \Device\).
*/
DeviceType = (ULONG)pLDI->DeviceType;
Status = ZwSetValueKey(hKey,
&DeviceName,
0,
REG_DWORD,
(PVOID)&DeviceType,
sizeof(DeviceType));
ExFreePool(DeviceName.Buffer);
ZwClose(hKey);
return Status;
}
NTSTATUS
SoundSaveRegistryPath(
IN PUNICODE_STRING RegistryPathName,
OUT PWSTR *SavedString
)
/*++
Routine Description:
Save the driver's registry path, appending the 'Parameters' key.
NOTE this registry path must only be accessed BELOW dispatch level
as the save area is allocated from paged pool.
The caller must free the unicode string buffer if the driver unloads.
Arguments:
RegistryPathName - Our driver's registry entry
SavedString - Saved version of RegistryPathName with the 'Parameters'
subkey string appended
Return Value:
NTSTATUS code
--*/
{
int Length;
ASSERT(*SavedString == NULL);
Length =
RegistryPathName->Length + sizeof(PARMS_SUBKEY) +
sizeof(UNICODE_NULL); // Include backslash
*SavedString =
ExAllocatePool(PagedPool, Length); // Only access on caller thread
if (*SavedString == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Copy the character data
//
RtlCopyMemory(*SavedString, RegistryPathName->Buffer,
RegistryPathName->Length);
(*SavedString)[RegistryPathName->Length / sizeof(WCHAR)] = L'\\';
(*SavedString)[RegistryPathName->Length / sizeof(WCHAR) + 1] = UNICODE_NULL;
//
// Append the parameters suffix prepended by a backslash
//
wcscat(*SavedString, PARMS_SUBKEY);
return STATUS_SUCCESS;
}
VOID
SoundFlushRegistryKey(
IN PWSTR RegistryPathName
)
/*++
Routine Description:
Flush a key - usually the driver's parameters key -
used mainly to save volume settings.
Arguments:
RegistryPathName - Our driver's parameters registry entry
Return Value:
None
--*/
{
HANDLE KeyHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING RegistryPathString;
RtlInitUnicodeString(&RegistryPathString, RegistryPathName);
InitializeObjectAttributes(&ObjectAttributes,
&RegistryPathString,
OBJ_CASE_INSENSITIVE,
NULL,
(PSECURITY_DESCRIPTOR)NULL);
//
// Just open the key and flush it. Not much we can do if this
// fails.
//
if (NT_SUCCESS(ZwOpenKey(&KeyHandle,
KEY_WRITE,
&ObjectAttributes))) {
ZwFlushKey(KeyHandle);
ZwClose(KeyHandle);
} else {
dprintf1(("Could not open device's key for flushing"));
}
}
#if 0
VOID
SoundRaiseHardError(
PWSTR ErrorText
)
/*++
Routine Description:
Cause a pop-up - note this doesn't seem to work!
Arguments:
ErrorText - text of message
Return Value:
None
--*/
{
UNICODE_STRING String;
RtlInitUnicodeString(&String, ErrorText);
IoRaiseInformationalHardError(STATUS_SUCCESS,
&String,
NULL);
}
#endif
#if DBG
char *DriverName = "Unknown Sound Driver";
ULONG SoundDebugLevel = 1;
void dDbgOut(char * szFormat, ...)
{
char buf[256];
va_list va;
va_start(va, szFormat);
vsprintf(buf, szFormat, va);
va_end(va);
DbgPrint(DriverName);
DbgPrint(" : ");
DbgPrint(buf);
DbgPrint("\n");
}
#endif // DBG