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

520 lines
15 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Kernel Streaming
* FILE: drivers/wdm/audio/sysaudio/deviface.c
* PURPOSE: System Audio graph builder
* PROGRAMMER: Johannes Anderwald
*/
#include "sysaudio.h"
#define NDEBUG
#include <debug.h>
NTSTATUS
NTAPI
Pin_fnDeviceIoControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PDISPATCH_CONTEXT Context;
NTSTATUS Status;
ULONG BytesReturned;
PFILE_OBJECT FileObject = NULL;
PIO_STACK_LOCATION IoStack;
DPRINT("Pin_fnDeviceIoControl called DeviceObject %p Irp %p\n", DeviceObject, Irp);
/* Get current stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* The dispatch context is stored in the FsContext member */
Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext;
/* Sanity check */
ASSERT(Context);
/* acquire real pin file object */
Status = ObReferenceObjectByHandle(Context->Handle, GENERIC_WRITE, *IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = Status;
/* Complete the irp */
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
/* Re-dispatch the request to the real target pin */
Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IoStack->Parameters.DeviceIoControl.IoControlCode,
IoStack->Parameters.DeviceIoControl.Type3InputBuffer,
IoStack->Parameters.DeviceIoControl.InputBufferLength,
Irp->UserBuffer,
IoStack->Parameters.DeviceIoControl.OutputBufferLength,
&BytesReturned);
/* release file object */
ObDereferenceObject(FileObject);
/* Save status and information */
Irp->IoStatus.Information = BytesReturned;
Irp->IoStatus.Status = Status;
/* Complete the irp */
IoCompleteRequest(Irp, IO_NO_INCREMENT);
/* Done */
return Status;
}
NTSTATUS
NTAPI
Pin_fnWrite(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PDISPATCH_CONTEXT Context;
PIO_STACK_LOCATION IoStack;
PFILE_OBJECT FileObject;
NTSTATUS Status;
/* Get current stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* The dispatch context is stored in the FsContext member */
Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext;
/* Sanity check */
ASSERT(Context);
if (Context->hMixerPin)
{
// FIXME
// call kmixer to convert stream
UNIMPLEMENTED;
}
/* acquire real pin file object */
Status = ObReferenceObjectByHandle(Context->Handle, GENERIC_WRITE, *IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("failed\n");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = Status;
/* Complete the irp */
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
/* skip current irp location */
IoSkipCurrentIrpStackLocation(Irp);
/* get next stack location */
IoStack = IoGetNextIrpStackLocation(Irp);
/* store file object of next device object */
IoStack->FileObject = FileObject;
IoStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
//ASSERT(Irp->AssociatedIrp.SystemBuffer);
/* now call the driver */
Status = IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp);
/* dereference file object */
ObDereferenceObject(FileObject);
return Status;
}
NTSTATUS
NTAPI
Pin_fnClose(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PDISPATCH_CONTEXT Context;
PIO_STACK_LOCATION IoStack;
//DPRINT("Pin_fnClose called DeviceObject %p Irp %p\n", DeviceObject, Irp);
/* Get current stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* The dispatch context is stored in the FsContext member */
Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext;
if (Context->Handle)
{
ZwClose(Context->Handle);
}
if (Context->hMixerPin)
{
ZwClose(Context->hMixerPin);
}
FreeItem(Context);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
static KSDISPATCH_TABLE PinTable =
{
Pin_fnDeviceIoControl,
KsDispatchInvalidDeviceRequest,
Pin_fnWrite,
KsDispatchInvalidDeviceRequest,
Pin_fnClose,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
KsDispatchFastIoDeviceControlFailure,
KsDispatchFastReadFailure,
KsDispatchFastWriteFailure,
};
NTSTATUS
SetMixerInputOutputFormat(
IN PFILE_OBJECT FileObject,
IN PKSDATAFORMAT InputFormat,
IN PKSDATAFORMAT OutputFormat)
{
KSP_PIN PinRequest;
ULONG BytesReturned;
NTSTATUS Status;
/* re-using pin */
PinRequest.Property.Set = KSPROPSETID_Connection;
PinRequest.Property.Flags = KSPROPERTY_TYPE_SET;
PinRequest.Property.Id = KSPROPERTY_CONNECTION_DATAFORMAT;
/* set the input format */
PinRequest.PinId = 0;
DPRINT("InputFormat %p Size %u WaveFormatSize %u DataFormat %u WaveEx %u\n", InputFormat, InputFormat->FormatSize, sizeof(KSDATAFORMAT_WAVEFORMATEX), sizeof(KSDATAFORMAT), sizeof(WAVEFORMATEX));
Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY,
(PVOID)&PinRequest,
sizeof(KSP_PIN),
(PVOID)InputFormat,
InputFormat->FormatSize,
&BytesReturned);
if (!NT_SUCCESS(Status))
return Status;
/* set the the output format */
PinRequest.PinId = 1;
DPRINT("OutputFormat %p Size %u WaveFormatSize %u DataFormat %u WaveEx %u\n", OutputFormat, OutputFormat->FormatSize, sizeof(KSDATAFORMAT_WAVEFORMATEX), sizeof(KSDATAFORMAT), sizeof(WAVEFORMATEX));
Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY,
(PVOID)&PinRequest,
sizeof(KSP_PIN),
(PVOID)OutputFormat,
OutputFormat->FormatSize,
&BytesReturned);
return Status;
}
NTSTATUS
CreateMixerPinAndSetFormat(
IN HANDLE KMixerHandle,
IN KSPIN_CONNECT *PinConnect,
IN PKSDATAFORMAT InputFormat,
IN PKSDATAFORMAT OutputFormat,
OUT PHANDLE MixerPinHandle)
{
NTSTATUS Status;
HANDLE PinHandle;
PFILE_OBJECT FileObject = NULL;
Status = KsCreatePin(KMixerHandle, PinConnect, GENERIC_READ | GENERIC_WRITE, &PinHandle);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to create Mixer Pin with %x\n", Status);
return STATUS_UNSUCCESSFUL;
}
Status = ObReferenceObjectByHandle(PinHandle,
GENERIC_READ | GENERIC_WRITE,
*IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to get file object with %x\n", Status);
return STATUS_UNSUCCESSFUL;
}
Status = SetMixerInputOutputFormat(FileObject, InputFormat, OutputFormat);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
ZwClose(PinHandle);
return Status;
}
ObDereferenceObject(FileObject);
*MixerPinHandle = PinHandle;
return Status;
}
NTSTATUS
NTAPI
InstantiatePins(
IN PKSAUDIO_DEVICE_ENTRY DeviceEntry,
IN PKSPIN_CONNECT Connect,
IN PDISPATCH_CONTEXT DispatchContext,
IN PSYSAUDIODEVEXT DeviceExtension)
{
NTSTATUS Status;
HANDLE RealPinHandle;
PKSDATAFORMAT_WAVEFORMATEX InputFormat;
PKSDATAFORMAT_WAVEFORMATEX OutputFormat = NULL;
PKSPIN_CONNECT MixerPinConnect = NULL;
KSPIN_CINSTANCES PinInstances;
DPRINT("InstantiatePins entered\n");
/* query instance count */
Status = GetPinInstanceCount(DeviceEntry, &PinInstances, Connect);
if (!NT_SUCCESS(Status))
{
/* failed to query instance count */
return Status;
}
/* can be the pin be instantiated */
if (PinInstances.PossibleCount == 0)
{
/* caller wanted to open an instance-less pin */
return STATUS_UNSUCCESSFUL;
}
/* has the maximum instance count been exceeded */
if (PinInstances.CurrentCount == PinInstances.PossibleCount)
{
/* FIXME pin already exists
* and kmixer infrastructure is not implemented
*/
return STATUS_UNSUCCESSFUL;
}
/* Fetch input format */
InputFormat = (PKSDATAFORMAT_WAVEFORMATEX)(Connect + 1);
/* Let's try to create the audio irp pin */
Status = KsCreatePin(DeviceEntry->Handle, Connect, GENERIC_READ | GENERIC_WRITE, &RealPinHandle);
if (!NT_SUCCESS(Status))
{
/* FIXME disable kmixer
*/
return STATUS_UNSUCCESSFUL;
}
#if 0
if (!NT_SUCCESS(Status))
{
/* the audio irp pin didnt accept the input format
* let's compute a compatible format
*/
MixerPinConnect = AllocateItem(NonPagedPool, sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX));
if (!MixerPinConnect)
{
/* not enough memory */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Zero pin connect */
RtlZeroMemory(MixerPinConnect, sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX));
/* Copy initial connect details */
RtlMoveMemory(MixerPinConnect, Connect, sizeof(KSPIN_CONNECT));
OutputFormat = (PKSDATAFORMAT_WAVEFORMATEX)(MixerPinConnect + 1);
Status = ComputeCompatibleFormat(DeviceEntry, Connect->PinId, InputFormat, OutputFormat);
if (!NT_SUCCESS(Status))
{
DPRINT1("ComputeCompatibleFormat failed with %x\n", Status);
FreeItem(MixerPinConnect);
return Status;
}
/* Retry with Mixer format */
Status = KsCreatePin(DeviceEntry->Handle, MixerPinConnect, GENERIC_READ | GENERIC_WRITE, &RealPinHandle);
if (!NT_SUCCESS(Status))
{
/* This should not fail */
DPRINT1("KsCreatePin failed with %x\n", Status);
DPRINT1(" InputFormat: SampleRate %u Bits %u Channels %u\n", InputFormat->WaveFormatEx.nSamplesPerSec, InputFormat->WaveFormatEx.wBitsPerSample, InputFormat->WaveFormatEx.nChannels);
DPRINT1("OutputFormat: SampleRate %u Bits %u Channels %u\n", OutputFormat->WaveFormatEx.nSamplesPerSec, OutputFormat->WaveFormatEx.wBitsPerSample, OutputFormat->WaveFormatEx.nChannels);
FreeItem(MixerPinConnect);
return Status;
}
}
#endif
//DeviceEntry->Pins[Connect->PinId].References = 0;
/* initialize dispatch context */
DispatchContext->Handle = RealPinHandle;
DispatchContext->PinId = Connect->PinId;
DispatchContext->AudioEntry = DeviceEntry;
DPRINT("RealPinHandle %p\n", RealPinHandle);
/* Do we need to transform the audio stream */
if (OutputFormat != NULL)
{
/* Now create the mixer pin */
Status = CreateMixerPinAndSetFormat(DeviceExtension->KMixerHandle,
MixerPinConnect,
(PKSDATAFORMAT)InputFormat,
(PKSDATAFORMAT)OutputFormat,
&DispatchContext->hMixerPin);
/* check for success */
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to create Mixer Pin with %x\n", Status);
FreeItem(MixerPinConnect);
}
}
/* done */
return Status;
}
NTSTATUS
GetConnectRequest(
IN PIRP Irp,
OUT PKSPIN_CONNECT * Result)
{
PIO_STACK_LOCATION IoStack;
ULONG ObjectLength, ParametersLength;
PVOID Buffer;
/* get current irp stack */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* get object class length */
ObjectLength = (wcslen(KSSTRING_Pin) + 1) * sizeof(WCHAR);
/* check for minium length requirement */
if (ObjectLength + sizeof(KSPIN_CONNECT) > IoStack->FileObject->FileName.MaximumLength)
return STATUS_UNSUCCESSFUL;
/* extract parameters length */
ParametersLength = IoStack->FileObject->FileName.MaximumLength - ObjectLength;
/* allocate buffer */
Buffer = AllocateItem(NonPagedPool, ParametersLength);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
/* copy parameters */
RtlMoveMemory(Buffer, &IoStack->FileObject->FileName.Buffer[ObjectLength / sizeof(WCHAR)], ParametersLength);
/* store result */
*Result = (PKSPIN_CONNECT)Buffer;
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
DispatchCreateSysAudioPin(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IoStack;
PKSAUDIO_DEVICE_ENTRY DeviceEntry;
PKSPIN_CONNECT Connect;
PDISPATCH_CONTEXT DispatchContext;
DPRINT("DispatchCreateSysAudioPin entered\n");
/* get current stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* sanity checks */
ASSERT(IoStack->FileObject);
ASSERT(IoStack->FileObject->RelatedFileObject);
ASSERT(IoStack->FileObject->RelatedFileObject->FsContext);
/* get current attached virtual device */
DeviceEntry = (PKSAUDIO_DEVICE_ENTRY)IoStack->FileObject->RelatedFileObject->FsContext;
/* check for success */
if (!NT_SUCCESS(Status))
{
/* failed */
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
/* get connect details */
Status = GetConnectRequest(Irp, &Connect);
/* check for success */
if (!NT_SUCCESS(Status))
{
/* failed to obtain connect details */
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
/* allocate dispatch context */
DispatchContext = AllocateItem(NonPagedPool, sizeof(DISPATCH_CONTEXT));
if (!DispatchContext)
{
/* failed */
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* zero dispatch context */
RtlZeroMemory(DispatchContext, sizeof(DISPATCH_CONTEXT));
/* allocate object header */
Status = KsAllocateObjectHeader(&DispatchContext->ObjectHeader, 0, NULL, Irp, &PinTable);
if (!NT_SUCCESS(Status))
{
/* failed */
FreeItem(DispatchContext);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
/* now instantiate the pins */
Status = InstantiatePins(DeviceEntry, Connect, DispatchContext, (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension);
if (!NT_SUCCESS(Status))
{
/* failed */
KsFreeObjectHeader(DispatchContext->ObjectHeader);
FreeItem(DispatchContext);
}
else
{
/* store dispatch context */
IoStack->FileObject->FsContext = (PVOID)DispatchContext;
}
/* FIXME create items for clocks / allocators */
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}