reactos/sdk/lib/drivers/sound/legacy/devname.c

483 lines
12 KiB
C

/*
ReactOS Sound System
Device naming & creation helper routines
Author:
Andrew Greenwood (silverblade@reactos.org)
History:
25 May 2008 - Created
*/
#include <ntddk.h>
#include <ntddsnd.h>
#include <sndnames.h>
#include <sndtypes.h>
#include <debug.h>
/*
Default device names
Just to keep things tidy, we define a structure to hold both the \\Device
and \\DosDevices names, and then fill this structure with the default
device names that can be found in NTDDSND.H
*/
typedef struct _DEVICE_NAME_GROUP
{
PCWSTR DeviceName;
PCWSTR DosDeviceName;
} DEVICE_NAME_GROUP;
DEVICE_NAME_GROUP SoundDeviceNameBodies[6] =
{
{
DD_WAVE_IN_DEVICE_NAME_U,
DD_WAVE_IN_DOS_DEVICE_NAME_U
},
{
DD_WAVE_OUT_DEVICE_NAME_U,
DD_WAVE_OUT_DOS_DEVICE_NAME_U
},
{
DD_MIDI_IN_DEVICE_NAME_U,
DD_MIDI_IN_DOS_DEVICE_NAME_U
},
{
DD_MIDI_OUT_DEVICE_NAME_U,
DD_MIDI_OUT_DOS_DEVICE_NAME_U
},
{
DD_MIX_DEVICE_NAME_U,
DD_MIX_DOS_DEVICE_NAME_U
},
{
DD_AUX_DEVICE_NAME_U,
DD_AUX_DOS_DEVICE_NAME_U
}
};
/*
ConstructDeviceName
This takes a wide-character string containing the device name body (for
example, "\\Device\\WaveOut") and appends the device index, forming a
string like "\\Device\\WaveOut0", and so on.
The resulting device name is a unicode string.
*/
NTSTATUS
ConstructDeviceName(
IN PCWSTR Path,
IN UCHAR Index,
OUT PUNICODE_STRING DeviceName)
{
UNICODE_STRING UnicodePath;
UNICODE_STRING UnicodeIndex;
WCHAR IndexStringBuffer[5];
USHORT Size;
USHORT LastCharacterIndex;
/* Check for NULL parameters */
if ( ( ! Path ) || ( ! DeviceName ) )
{
DPRINT("Unexpected NULL parameter");
return STATUS_INVALID_PARAMETER;
}
/* Range-check */
if ( Index >= SOUND_MAX_DEVICES )
{
DPRINT("Device index %d out of range", Index);
return STATUS_INVALID_PARAMETER;
}
/* Initialise the unicode path string */
RtlInitUnicodeString(&UnicodePath, Path);
/* Calculate the length to hold the full string */
Size = UnicodePath.Length +
sizeof(IndexStringBuffer) +
sizeof(UNICODE_NULL);
/* Allocate memory for DeviceName */
DeviceName->Buffer = ExAllocatePool(PagedPool, Size);
DeviceName->MaximumLength = Size;
if ( ! DeviceName->Buffer )
{
DPRINT("Couldn't allocate memory for device name string");
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Copy the path */
RtlCopyUnicodeString(DeviceName, &UnicodePath);
/* Convert Index to string and append */
UnicodeIndex.Buffer = IndexStringBuffer;
UnicodeIndex.MaximumLength = sizeof(IndexStringBuffer);
RtlIntegerToUnicodeString((ULONG)Index, 10, &UnicodeIndex);
RtlAppendUnicodeStringToString(DeviceName, &UnicodeIndex);
/* Terminate the string */
LastCharacterIndex = DeviceName->Length / sizeof(UNICODE_NULL);
DeviceName->Buffer[LastCharacterIndex] = UNICODE_NULL;
return STATUS_SUCCESS;
}
/*
FreeUnicodeStringBuffer
A small helper routine to free a unicode string buffer, nullify the
buffer and reset the lengths to zero.
*/
VOID
FreeUnicodeStringBuffer(IN PUNICODE_STRING String)
{
ASSERT(String != NULL);
ASSERT(String->Buffer != NULL);
ExFreePool(String->Buffer);
String->Buffer = NULL;
String->Length = 0;
String->MaximumLength = 0;
}
/*
GetDefaultSoundDeviceNameBodies
Simply accesses the SoundDeviceNameBodies struct defined earlier and
fills the DeviceNameBody and DosDeviceNameBody parameters accordingly.
Basically a "safe" way to access the array and perform two assignments
with one call, as this will assign the name and DOS name if a valid
DeviceType is passed, otherwise it will fail with STATUS_INVALID_PARAMETER.
*/
NTSTATUS
GetDefaultSoundDeviceNameBodies(
IN UCHAR DeviceType,
OUT PCWSTR* DeviceNameBody,
OUT PCWSTR* DosDeviceNameBody)
{
if ( ! IS_VALID_SOUND_DEVICE_TYPE(DeviceType) )
{
DPRINT("Invalid device type");
return STATUS_INVALID_PARAMETER;
}
if ( DeviceNameBody )
{
DPRINT("Reporting device name\n");
*DeviceNameBody = SoundDeviceNameBodies[DeviceType].DeviceName;
DPRINT("%ws\n", *DeviceNameBody);
}
if ( DosDeviceNameBody )
{
DPRINT("Reporting DOS device name\n");
*DosDeviceNameBody = SoundDeviceNameBodies[DeviceType].DosDeviceName;
DPRINT("%ws\n", *DosDeviceNameBody);
}
return STATUS_SUCCESS;
}
/*
ConstructSoundDeviceNames
Given two wide-character strings and a device index, convert these into
two unicode strings with the index appended to the end.
This is intended for converting a device name and a DOS device name at
the same time.
*/
NTSTATUS
ConstructSoundDeviceNames(
IN PCWSTR DeviceNameBody,
IN PCWSTR DosDeviceNameBody,
IN UCHAR Index,
OUT PUNICODE_STRING FullDeviceName,
OUT PUNICODE_STRING FullDosDeviceName)
{
NTSTATUS Status;
/* Check for NULL parameters */
if ( ( ! DeviceNameBody ) || ( ! DosDeviceNameBody ) ||
( ! FullDeviceName ) || ( ! FullDosDeviceName ) )
{
DPRINT("Unexpected NULL parameter");
return STATUS_INVALID_PARAMETER;
}
/* Range-check */
if ( Index >= SOUND_MAX_DEVICES )
{
DPRINT("Device %d exceeds maximum", Index);
return STATUS_INVALID_PARAMETER;
}
Status = ConstructDeviceName(DeviceNameBody, Index, FullDeviceName);
if ( ! NT_SUCCESS(Status) )
{
/* No need to clean up on failure here */
return Status;
}
Status = ConstructDeviceName(DosDeviceNameBody, Index, FullDosDeviceName);
if ( ! NT_SUCCESS(Status) )
{
/* We need to free the string we successfully got earlier */
FreeUnicodeStringBuffer(FullDeviceName);
return Status;
}
return STATUS_SUCCESS;
}
/*
CreateSoundDevice
Creates a device and symbolically-links a DOS device to this. Use this
when you want to specify alternative device names to the defaults
(eg: "\\Device\\MySoundDev" rather than "\\Device\\WaveOut")
*/
NTSTATUS
CreateSoundDevice(
IN PDRIVER_OBJECT DriverObject,
IN PCWSTR WideDeviceName,
IN PCWSTR WideDosDeviceName,
IN UCHAR Index,
IN ULONG ExtensionSize,
OUT PDEVICE_OBJECT* DeviceObject)
{
NTSTATUS Status;
UNICODE_STRING DeviceName;
UNICODE_STRING DosDeviceName;
/* Check for NULL parameters */
if ( ( ! DriverObject ) || ( ! DeviceObject ) ||
( ! WideDeviceName ) || ( ! WideDosDeviceName ) )
{
DPRINT("Unexpected NULL parameter");
return STATUS_INVALID_PARAMETER;
}
/* Range-check */
if ( Index >= SOUND_MAX_DEVICES )
{
DPRINT("Device index %d exceeds maximum", Index);
return STATUS_INVALID_PARAMETER;
}
/* Construct the device and DOS device names */
Status = ConstructSoundDeviceNames(WideDeviceName,
WideDosDeviceName,
Index,
&DeviceName,
&DosDeviceName);
if ( ! NT_SUCCESS(Status) )
{
return Status;
}
DPRINT("Creating device %ws\n", DeviceName.Buffer);
/* Now create the device */
Status = IoCreateDevice(DriverObject,
ExtensionSize,
&DeviceName,
FILE_DEVICE_SOUND,
0,
FALSE,
DeviceObject);
if ( ! NT_SUCCESS(Status) )
{
/* These will have been allocated by ConstructSoundDeviceNames */
FreeUnicodeStringBuffer(&DeviceName);
FreeUnicodeStringBuffer(&DosDeviceName);
return Status;
}
DPRINT("Creating link %ws\n", DosDeviceName.Buffer);
/* Create a symbolic link for the DOS deviec name */
Status = IoCreateSymbolicLink(&DosDeviceName, &DeviceName);
if ( ! NT_SUCCESS(Status) )
{
IoDeleteDevice(*DeviceObject);
/* These will have been allocated by ConstructSoundDeviceNames */
FreeUnicodeStringBuffer(&DeviceName);
FreeUnicodeStringBuffer(&DosDeviceName);
return Status;
}
return STATUS_SUCCESS;
}
/*
CreateSoundDeviceWithDefaultName
Similar to CreateSoundDevice, except this uses the default device names
("\\Device\\WaveOut" etc.) based on the DeviceType parameter.
*/
NTSTATUS
CreateSoundDeviceWithDefaultName(
IN PDRIVER_OBJECT DriverObject,
IN UCHAR DeviceType,
IN UCHAR Index,
IN ULONG ExtensionSize,
OUT PDEVICE_OBJECT* DeviceObject)
{
NTSTATUS Status;
PCWSTR WideDeviceName = NULL;
PCWSTR WideDosDeviceName = NULL;
/* Check for NULL parameters */
if ( ( ! DriverObject ) || ( ! DeviceObject ) )
{
DPRINT("Unexpected NULL parameter");
return STATUS_INVALID_PARAMETER;
}
/* Range-check */
if ( Index >= SOUND_MAX_DEVICES )
{
DPRINT("Device index %d exceeds maximum", Index);
return STATUS_INVALID_PARAMETER;
}
/* Look-up the default name based on the device type */
Status = GetDefaultSoundDeviceNameBodies(DeviceType,
&WideDeviceName,
&WideDosDeviceName);
if ( ! NT_SUCCESS(Status) )
{
return Status;
}
/* Go create the device! */
Status = CreateSoundDevice(DriverObject,
WideDeviceName,
WideDosDeviceName,
Index,
ExtensionSize,
DeviceObject);
if ( ! NT_SUCCESS(Status) )
{
/* No clean-up to do */
return Status;
}
return STATUS_SUCCESS;
}
NTSTATUS
DestroySoundDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PCWSTR WideDosDeviceName,
IN UCHAR Index)
{
NTSTATUS Status;
UNICODE_STRING DosDeviceName;
/* Check for NULL parameters */
if ( ( ! WideDosDeviceName ) || ( ! DeviceObject ) )
{
DPRINT("Unexpected NULL parameter");
return STATUS_INVALID_PARAMETER;
}
/* Range-check */
if ( Index >= SOUND_MAX_DEVICES )
{
DPRINT("Device %d exceeds maximum", Index);
return STATUS_INVALID_PARAMETER;
}
Status = ConstructDeviceName(WideDosDeviceName, Index, &DosDeviceName);
if ( ! NT_SUCCESS(Status) )
{
return Status;
}
DPRINT("Deleting symlink %ws\n", DosDeviceName.Buffer);
Status = IoDeleteSymbolicLink(&DosDeviceName);
DPRINT("Status of symlink deletion is 0x%08x\n", Status);
/*
ASSERT(NT_SUCCESS(Status));
*/
IoDeleteDevice(DeviceObject);
return STATUS_SUCCESS;
}
NTSTATUS
DestroySoundDeviceWithDefaultName(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR DeviceType,
IN UCHAR Index)
{
NTSTATUS Status;
PCWSTR WideDosDeviceName = NULL;
/* Check for NULL parameters */
if ( ( ! DeviceObject ) )
{
DPRINT("Unexpected NULL parameter");
return STATUS_INVALID_PARAMETER;
}
/* Range-check */
if ( Index >= SOUND_MAX_DEVICES )
{
DPRINT("Device index %d exceeds maximum", Index);
return STATUS_INVALID_PARAMETER;
}
/* Look-up the default name based on the device type */
Status = GetDefaultSoundDeviceNameBodies(DeviceType,
NULL,
&WideDosDeviceName);
if ( ! NT_SUCCESS(Status) )
{
return Status;
}
DPRINT("DOS device name at %p\n", WideDosDeviceName);
DPRINT("DOS device name is based on %ws\n", WideDosDeviceName);
return DestroySoundDevice(DeviceObject, WideDosDeviceName, Index);
}