reactos/drivers/wdm/audio/sysaudio/control.c

463 lines
18 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Kernel Streaming
* FILE: drivers/wdm/audio/sysaudio/control.c
* PURPOSE: System Audio graph builder
* PROGRAMMER: Johannes Anderwald
*/
#include "sysaudio.h"
#define NDEBUG
#include <debug.h>
const GUID KSPROPSETID_Sysaudio = {0xCBE3FAA0L, 0xCC75, 0x11D0, {0xB4, 0x65, 0x00, 0x00, 0x1A, 0x18, 0x18, 0xE6}};
const GUID KSPROPSETID_Sysaudio_Pin = {0xA3A53220L, 0xC6E4, 0x11D0, {0xB4, 0x65, 0x00, 0x00, 0x1A, 0x18, 0x18, 0xE6}};
const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}};
const GUID KSPROPSETID_Connection = {0x1D58C920L, 0xAC9B, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
const GUID KSDATAFORMAT_TYPE_AUDIO = {0x73647561L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
const GUID KSDATAFORMAT_SPECIFIER_WAVEFORMATEX = {0x05589f81L, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}};
NTSTATUS
SetIrpIoStatus(
IN PIRP Irp,
IN NTSTATUS Status,
IN ULONG Length)
{
Irp->IoStatus.Information = Length;
Irp->IoStatus.Status = Status;
if (Status != STATUS_PENDING)
{
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
else
{
IoMarkIrpPending(Irp);
}
return Status;
}
PKSAUDIO_DEVICE_ENTRY
GetListEntry(
IN PLIST_ENTRY Head,
IN ULONG Index)
{
PLIST_ENTRY Entry = Head->Flink;
while(Index-- && Entry != Head)
Entry = Entry->Flink;
if (Entry == Head)
return NULL;
return (PKSAUDIO_DEVICE_ENTRY)CONTAINING_RECORD(Entry, KSAUDIO_DEVICE_ENTRY, Entry);
}
NTSTATUS
SysAudioOpenVirtualDevice(
IN PIRP Irp,
IN ULONG DeviceNumber,
PSYSAUDIODEVEXT DeviceExtension)
{
PKSAUDIO_DEVICE_ENTRY Entry;
PIO_STACK_LOCATION IoStack;
/* get current irp stack */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* sanity check */
ASSERT(IoStack->FileObject);
if (DeviceNumber >= DeviceExtension->NumberOfKsAudioDevices)
{
/* invalid device index */
return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
}
/* get device context */
Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, DeviceNumber);
ASSERT(Entry != NULL);
/* store device entry in FsContext
* see pin.c DispatchCreateSysAudioPin for details
*/
IoStack->FileObject->FsContext = (PVOID)Entry;
return SetIrpIoStatus(Irp, STATUS_SUCCESS, 0);
}
NTSTATUS
HandleSysAudioFilterPinProperties(
PIRP Irp,
PKSPROPERTY Property,
PSYSAUDIODEVEXT DeviceExtension)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
PKSAUDIO_DEVICE_ENTRY Entry;
ULONG BytesReturned;
// in order to access pin properties of a sysaudio device
// the caller must provide a KSP_PIN struct, where
// Reserved member points to virtual device index
IoStack = IoGetCurrentIrpStackLocation(Irp);
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSP_PIN))
{
/* too small buffer */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSPROPERTY) + sizeof(ULONG));
}
Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, ((KSP_PIN*)Property)->Reserved);
if (!Entry)
{
/* invalid device index */
return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
}
/* forward request to the filter implementing the property */
Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY,
(PVOID)IoStack->Parameters.DeviceIoControl.Type3InputBuffer,
IoStack->Parameters.DeviceIoControl.InputBufferLength,
Irp->UserBuffer,
IoStack->Parameters.DeviceIoControl.OutputBufferLength,
&BytesReturned);
return SetIrpIoStatus(Irp, Status, BytesReturned);
}
NTSTATUS
ComputeCompatibleFormat(
IN PKSAUDIO_DEVICE_ENTRY Entry,
IN ULONG PinId,
IN PKSDATAFORMAT_WAVEFORMATEX ClientFormat,
OUT PKSDATAFORMAT_WAVEFORMATEX MixerFormat)
{
BOOL bFound;
ULONG BytesReturned;
PKSP_PIN PinRequest;
NTSTATUS Status;
PKSMULTIPLE_ITEM MultipleItem;
ULONG Length;
PKSDATARANGE_AUDIO AudioRange;
ULONG Index;
Length = sizeof(KSP_PIN) + sizeof(KSMULTIPLE_ITEM) + ClientFormat->DataFormat.FormatSize;
PinRequest = AllocateItem(NonPagedPool, Length);
if (!PinRequest)
return STATUS_UNSUCCESSFUL;
PinRequest->PinId = PinId;
PinRequest->Property.Set = KSPROPSETID_Pin;
PinRequest->Property.Flags = KSPROPERTY_TYPE_GET;
PinRequest->Property.Id = KSPROPERTY_PIN_DATAINTERSECTION;
MultipleItem = (PKSMULTIPLE_ITEM)(PinRequest + 1);
MultipleItem->Count = 1;
MultipleItem->Size = ClientFormat->DataFormat.FormatSize;
RtlMoveMemory(MultipleItem + 1, ClientFormat, ClientFormat->DataFormat.FormatSize);
/* Query the miniport data intersection handler */
Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)PinRequest, Length, (PVOID)MixerFormat, sizeof(KSDATAFORMAT_WAVEFORMATEX), &BytesReturned);
DPRINT("Status %x\n", Status);
if (NT_SUCCESS(Status))
{
FreeItem(PinRequest);
return Status;
}
/* Setup request block */
PinRequest->Property.Id = KSPROPERTY_PIN_DATARANGES;
/* Query pin data ranges */
Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)PinRequest, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
if (Status != STATUS_MORE_ENTRIES)
{
/* Failed to get data ranges */
return Status;
}
MultipleItem = AllocateItem(NonPagedPool, BytesReturned);
if (!MultipleItem)
{
FreeItem(PinRequest);
return STATUS_NO_MEMORY;
}
Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)PinRequest, sizeof(KSP_PIN), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
if (!NT_SUCCESS(Status))
{
DPRINT("Property Request KSPROPERTY_PIN_DATARANGES failed with %x\n", Status);
FreeItem(MultipleItem);
FreeItem(PinRequest);
return STATUS_UNSUCCESSFUL;
}
AudioRange = (PKSDATARANGE_AUDIO)(MultipleItem + 1);
bFound = FALSE;
for(Index = 0; Index < MultipleItem->Count; Index++)
{
if (AudioRange->DataRange.FormatSize != sizeof(KSDATARANGE_AUDIO))
{
UNIMPLEMENTED;
AudioRange = (PKSDATARANGE_AUDIO)((PUCHAR)AudioRange + AudioRange->DataRange.FormatSize);
continue;
}
/* Select best quality available */
MixerFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX);
MixerFormat->DataFormat.Flags = 0;
MixerFormat->DataFormat.Reserved = 0;
MixerFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
MixerFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
MixerFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
MixerFormat->DataFormat.SampleSize = 4;
MixerFormat->WaveFormatEx.wFormatTag = ClientFormat->WaveFormatEx.wFormatTag;
#ifndef NO_AC97_HACK
/* HACK: AC97 does not support mono render / record */
MixerFormat->WaveFormatEx.nChannels = 2;
/*HACK: AC97 only supports 16-Bit Bits */
MixerFormat->WaveFormatEx.wBitsPerSample = 16;
#else
MixerFormat->WaveFormatEx.nChannels = min(ClientFormat->WaveFormatEx.nChannels, AudioRange->MaximumChannels);
MixerFormat->WaveFormatEx.wBitsPerSample = AudioRange->MaximumBitsPerSample;
#endif
#ifdef KMIXER_RESAMPLING_IMPLEMENTED
MixerFormat->WaveFormatEx.nSamplesPerSec = AudioRange->MaximumSampleFrequency;
#else
MixerFormat->WaveFormatEx.nSamplesPerSec = max(AudioRange->MinimumSampleFrequency, min(ClientFormat->WaveFormatEx.nSamplesPerSec, AudioRange->MaximumSampleFrequency));
#endif
MixerFormat->WaveFormatEx.cbSize = 0;
MixerFormat->WaveFormatEx.nBlockAlign = (MixerFormat->WaveFormatEx.nChannels * MixerFormat->WaveFormatEx.wBitsPerSample) / 8;
MixerFormat->WaveFormatEx.nAvgBytesPerSec = MixerFormat->WaveFormatEx.nChannels * MixerFormat->WaveFormatEx.nSamplesPerSec * (MixerFormat->WaveFormatEx.wBitsPerSample / 8);
bFound = TRUE;
break;
AudioRange = (PKSDATARANGE_AUDIO)((PUCHAR)AudioRange + AudioRange->DataRange.FormatSize);
}
#if 0
DPRINT1("\nNum Max Channels %u Channels %u Old Channels %u\n Max SampleRate %u SampleRate %u Old SampleRate %u\n Max BitsPerSample %u BitsPerSample %u Old BitsPerSample %u\n",
AudioRange->MaximumChannels, MixerFormat->WaveFormatEx.nChannels, ClientFormat->WaveFormatEx.nChannels,
AudioRange->MaximumSampleFrequency, MixerFormat->WaveFormatEx.nSamplesPerSec, ClientFormat->WaveFormatEx.nSamplesPerSec,
AudioRange->MaximumBitsPerSample, MixerFormat->WaveFormatEx.wBitsPerSample, ClientFormat->WaveFormatEx.wBitsPerSample);
#endif
FreeItem(MultipleItem);
FreeItem(PinRequest);
if (bFound)
return STATUS_SUCCESS;
else
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
GetPinInstanceCount(
PKSAUDIO_DEVICE_ENTRY Entry,
PKSPIN_CINSTANCES PinInstances,
PKSPIN_CONNECT PinConnect)
{
KSP_PIN PinRequest;
ULONG BytesReturned;
/* query the instance count */
PinRequest.PinId = PinConnect->PinId;
PinRequest.Property.Set = KSPROPSETID_Pin;
PinRequest.Property.Flags = KSPROPERTY_TYPE_GET;
PinRequest.Property.Id = KSPROPERTY_PIN_CINSTANCES;
ASSERT(Entry->FileObject);
return KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PinRequest, sizeof(KSP_PIN), (PVOID)PinInstances, sizeof(KSPIN_CINSTANCES), &BytesReturned);
}
NTSTATUS
SysAudioHandleProperty(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
KSPROPERTY PropertyRequest;
KSCOMPONENTID ComponentId;
PULONG Index;
PKSPROPERTY Property;
PSYSAUDIODEVEXT DeviceExtension;
PKSAUDIO_DEVICE_ENTRY Entry;
PSYSAUDIO_INSTANCE_INFO InstanceInfo;
ULONG BytesReturned;
UNICODE_STRING GuidString;
PKSP_PIN Pin;
LPWSTR DeviceName;
IoStack = IoGetCurrentIrpStackLocation(Irp);
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSPROPERTY))
{
/* buffer must be at least of sizeof KSPROPERTY */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSPROPERTY));
}
Property = (PKSPROPERTY)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
DeviceExtension = (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension;
if (IsEqualGUIDAligned(&Property->Set, &KSPROPSETID_Pin))
{
return HandleSysAudioFilterPinProperties(Irp, Property, DeviceExtension);
}
else if(IsEqualGUIDAligned(&Property->Set, &KSPROPSETID_Topology))
{
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSP_PIN))
{
/* too small buffer */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSP_PIN));
}
Pin = (PKSP_PIN)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, Pin->Reserved);
ASSERT(Entry != NULL);
/* forward request to the filter implementing the property */
Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY,
(PVOID)IoStack->Parameters.DeviceIoControl.Type3InputBuffer,
IoStack->Parameters.DeviceIoControl.InputBufferLength,
Irp->UserBuffer,
IoStack->Parameters.DeviceIoControl.OutputBufferLength,
&BytesReturned);
return SetIrpIoStatus(Irp, Status, BytesReturned);
}
else if (IsEqualGUIDAligned(&Property->Set, &KSPROPSETID_Sysaudio))
{
if (Property->Id == KSPROPERTY_SYSAUDIO_DEVICE_INTERFACE_NAME)
{
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSPROPERTY) + sizeof(ULONG))
{
/* invalid request */
return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, sizeof(KSPROPERTY) + sizeof(ULONG));
}
Index = (PULONG)(Property + 1);
if (DeviceExtension->NumberOfKsAudioDevices <= *Index)
{
/* invalid index */
return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
}
Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, *Index);
ASSERT(Entry != NULL);
BytesReturned = Entry->DeviceName.Length + sizeof(WCHAR);
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < BytesReturned)
{
/* too small buffer */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, BytesReturned);
}
/* copy device name */
DeviceName = (LPWSTR)Irp->UserBuffer;
RtlMoveMemory(DeviceName, Entry->DeviceName.Buffer, Entry->DeviceName.Length);
DeviceName[Entry->DeviceName.Length / sizeof(WCHAR)] = L'\0';
return SetIrpIoStatus(Irp, STATUS_SUCCESS, BytesReturned);
}
if (Property->Id == KSPROPERTY_SYSAUDIO_COMPONENT_ID)
{
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSPROPERTY) + sizeof(ULONG))
{
/* too small buffer */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSPROPERTY) + sizeof(ULONG));
}
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KSCOMPONENTID))
{
/* too small buffer */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSCOMPONENTID));
}
Index = (PULONG)(Property + 1);
if (DeviceExtension->NumberOfKsAudioDevices <= *Index)
{
/* invalid index */
return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
}
Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, *Index);
ASSERT(Entry != NULL);
PropertyRequest.Set = KSPROPSETID_General;
PropertyRequest.Id = KSPROPERTY_GENERAL_COMPONENTID;
PropertyRequest.Flags = KSPROPERTY_TYPE_GET;
/* call the filter */
Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PropertyRequest, sizeof(KSPROPERTY), (PVOID)&ComponentId, sizeof(KSCOMPONENTID), &BytesReturned);
if (!NT_SUCCESS(Status))
{
DPRINT("KsSynchronousIoControlDevice failed with %x for KSPROPERTY_GENERAL_COMPONENTID\n", Status);
return SetIrpIoStatus(Irp, Status, 0);
}
RtlMoveMemory(Irp->UserBuffer, &ComponentId, sizeof(KSCOMPONENTID));
return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(KSCOMPONENTID));
}
else if (Property->Id == KSPROPERTY_SYSAUDIO_DEVICE_COUNT)
{
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
{
/* too small buffer */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(ULONG));
}
*((PULONG)Irp->UserBuffer) = DeviceExtension->NumberOfKsAudioDevices;
return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(ULONG));
}
else if (Property->Id == KSPROPERTY_SYSAUDIO_DEVICE_INSTANCE)
{
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
{
/* too small buffer */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(ULONG));
}
if (Property->Flags & KSPROPERTY_TYPE_SET)
{
Index = (PULONG)Irp->UserBuffer;
return SysAudioOpenVirtualDevice(Irp, *Index, DeviceExtension);
}
}
else if (Property->Id == KSPROPERTY_SYSAUDIO_INSTANCE_INFO)
{
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SYSAUDIO_INSTANCE_INFO))
{
/* too small buffer */
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(SYSAUDIO_INSTANCE_INFO));
}
/* get input parameter */
InstanceInfo = (PSYSAUDIO_INSTANCE_INFO)Property;
if (Property->Flags & KSPROPERTY_TYPE_SET)
{
return SysAudioOpenVirtualDevice(Irp, InstanceInfo->DeviceNumber, DeviceExtension);
}
}
}
RtlStringFromGUID(&Property->Set, &GuidString);
DPRINT1("Unhandled property Set |%S| Id %u Flags %x\n", GuidString.Buffer, Property->Id, Property->Flags);
RtlFreeUnicodeString(&GuidString);
return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
}