/* ReactOS Sound System Device naming & creation helper routines Author: Andrew Greenwood (silverblade@reactos.org) History: 25 May 2008 - Created */ #include #include #include #include #include /* 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); }