1669 lines
38 KiB
C
1669 lines
38 KiB
C
|
/*++
|
|||
|
"@(#) 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
|
|||
|
|
|||
|
|
|||
|
|