reactos/drivers/wdm/audio/backpln/portcls/pin_wavert.cpp
Benjamin Aerni 66cf7b6028
[PORTCLS] Stub missing IoControlCode switches (#1756)
Stub missing switches: IOCTL_KS_HANDSHAKE and IOCTL_KS_METHOD.
2020-03-30 12:58:25 +02:00

710 lines
19 KiB
C++

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Kernel Streaming
* FILE: drivers/wdm/audio/backpln/portcls/pin_wavert.cpp
* PURPOSE: WaveRT IRP Audio Pin
* PROGRAMMER: Johannes Anderwald
*/
#include "private.hpp"
#ifndef YDEBUG
#define NDEBUG
#endif
#include <debug.h>
class CPortPinWaveRT : public IPortPinWaveRT
{
public:
STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
STDMETHODIMP_(ULONG) AddRef()
{
InterlockedIncrement(&m_Ref);
return m_Ref;
}
STDMETHODIMP_(ULONG) Release()
{
InterlockedDecrement(&m_Ref);
if (!m_Ref)
{
delete this;
return 0;
}
return m_Ref;
}
IMP_IPortPinWaveRT;
CPortPinWaveRT(IUnknown *OuterUnknown){}
virtual ~CPortPinWaveRT(){}
protected:
IPortWaveRT * m_Port;
IPortFilterWaveRT * m_Filter;
KSPIN_DESCRIPTOR * m_KsPinDescriptor;
PMINIPORTWAVERT m_Miniport;
PMINIPORTWAVERTSTREAM m_Stream;
PPORTWAVERTSTREAM m_PortStream;
KSSTATE m_State;
PKSDATAFORMAT m_Format;
KSPIN_CONNECT * m_ConnectDetails;
PVOID m_CommonBuffer;
ULONG m_CommonBufferSize;
ULONG m_CommonBufferOffset;
IIrpQueue * m_IrpQueue;
BOOL m_Capture;
ULONG m_TotalPackets;
ULONG m_PreCompleted;
ULONG m_PostCompleted;
ULONGLONG m_Delay;
MEMORY_CACHING_TYPE m_CacheType;
PMDL m_Mdl;
LONG m_Ref;
NTSTATUS NTAPI HandleKsProperty(IN PIRP Irp);
NTSTATUS NTAPI HandleKsStream(IN PIRP Irp);
VOID NTAPI SetStreamState(IN KSSTATE State);
friend VOID NTAPI SetStreamWorkerRoutine(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context);
friend VOID NTAPI CloseStreamRoutine(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context);
};
typedef struct
{
CPortPinWaveRT *Pin;
PIO_WORKITEM WorkItem;
KSSTATE State;
}SETSTREAM_CONTEXT, *PSETSTREAM_CONTEXT;
//==================================================================================================================================
NTSTATUS
NTAPI
CPortPinWaveRT::QueryInterface(
IN REFIID refiid,
OUT PVOID* Output)
{
DPRINT("IServiceSink_fnQueryInterface entered\n");
if (IsEqualGUIDAligned(refiid, IID_IIrpTarget) ||
IsEqualGUIDAligned(refiid, IID_IUnknown))
{
*Output = PVOID(PUNKNOWN((IIrpTarget*)this));
PUNKNOWN(*Output)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
//==================================================================================================================================
NTSTATUS
NTAPI
CPortPinWaveRT::NewIrpTarget(
OUT struct IIrpTarget **OutTarget,
IN PCWSTR Name,
IN PUNKNOWN Unknown,
IN POOL_TYPE PoolType,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN KSOBJECT_CREATE *CreateObject)
{
UNIMPLEMENTED;
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NTAPI
CPortPinWaveRT::HandleKsProperty(
IN PIRP Irp)
{
PKSPROPERTY Property;
NTSTATUS Status;
UNICODE_STRING GuidString;
PIO_STACK_LOCATION IoStack;
IoStack = IoGetCurrentIrpStackLocation(Irp);
DPRINT("IPortPinWave_HandleKsProperty entered\n");
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSPROPERTY))
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
Property = (PKSPROPERTY)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
if (IsEqualGUIDAligned(Property->Set, KSPROPSETID_Connection))
{
if (Property->Id == KSPROPERTY_CONNECTION_STATE)
{
PKSSTATE State = (PKSSTATE)Irp->UserBuffer;
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KSSTATE))
{
Irp->IoStatus.Information = sizeof(KSSTATE);
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_BUFFER_TOO_SMALL;
}
if (Property->Flags & KSPROPERTY_TYPE_SET)
{
Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
if (m_Stream)
{
Status = m_Stream->SetState(*State);
DPRINT("Setting state %u %x\n", *State, Status);
if (NT_SUCCESS(Status))
{
m_State = *State;
}
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
else if (Property->Flags & KSPROPERTY_TYPE_GET)
{
*State = m_State;
Irp->IoStatus.Information = sizeof(KSSTATE);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
}
else if (Property->Id == KSPROPERTY_CONNECTION_DATAFORMAT)
{
PKSDATAFORMAT DataFormat = (PKSDATAFORMAT)Irp->UserBuffer;
if (Property->Flags & KSPROPERTY_TYPE_SET)
{
PKSDATAFORMAT NewDataFormat;
if (!RtlCompareMemory(DataFormat, m_Format, DataFormat->FormatSize))
{
Irp->IoStatus.Information = DataFormat->FormatSize;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NewDataFormat = (PKSDATAFORMAT)AllocateItem(NonPagedPool, DataFormat->FormatSize, TAG_PORTCLASS);
if (!NewDataFormat)
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_NO_MEMORY;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_NO_MEMORY;
}
RtlMoveMemory(NewDataFormat, DataFormat, DataFormat->FormatSize);
if (m_Stream)
{
#if 0
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
ASSERT(NewDataFormat->FormatSize == sizeof(KSDATAFORMAT_WAVEFORMATEX));
ASSERT(IsEqualGUIDAligned(&((PKSDATAFORMAT_WAVEFORMATEX)NewDataFormat)->DataFormat.MajorFormat, &KSDATAFORMAT_TYPE_AUDIO));
ASSERT(IsEqualGUIDAligned(&((PKSDATAFORMAT_WAVEFORMATEX)NewDataFormat)->DataFormat.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM));
ASSERT(IsEqualGUIDAligned(&((PKSDATAFORMAT_WAVEFORMATEX)NewDataFormat)->DataFormat.Specifier, &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX));
ASSERT(m_State == KSSTATE_STOP);
#endif
DPRINT("NewDataFormat: Channels %u Bits %u Samples %u\n", ((PKSDATAFORMAT_WAVEFORMATEX)NewDataFormat)->WaveFormatEx.nChannels,
((PKSDATAFORMAT_WAVEFORMATEX)NewDataFormat)->WaveFormatEx.wBitsPerSample,
((PKSDATAFORMAT_WAVEFORMATEX)NewDataFormat)->WaveFormatEx.nSamplesPerSec);
Status = m_Stream->SetFormat(NewDataFormat);
if (NT_SUCCESS(Status))
{
if (m_Format)
FreeItem(m_Format, TAG_PORTCLASS);
m_Format = NewDataFormat;
Irp->IoStatus.Information = DataFormat->FormatSize;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
}
DPRINT("Failed to set format\n");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
else if (Property->Flags & KSPROPERTY_TYPE_GET)
{
if (!m_Format)
{
DPRINT("No format\n");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
if (m_Format->FormatSize > IoStack->Parameters.DeviceIoControl.OutputBufferLength)
{
Irp->IoStatus.Information = m_Format->FormatSize;
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_BUFFER_TOO_SMALL;
}
RtlMoveMemory(DataFormat, m_Format, m_Format->FormatSize);
Irp->IoStatus.Information = DataFormat->FormatSize;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
}
}
RtlStringFromGUID(Property->Set, &GuidString);
DPRINT("Unhandled property Set |%S| Id %u Flags %x\n", GuidString.Buffer, Property->Id, Property->Flags);
RtlFreeUnicodeString(&GuidString);
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
NTAPI
CPortPinWaveRT::HandleKsStream(
IN PIRP Irp)
{
DPRINT("IPortPinWaveRT_HandleKsStream entered State %u Stream %p is UNIMPLEMENTED\n", m_State, m_Stream);
return STATUS_PENDING;
}
NTSTATUS
NTAPI
CPortPinWaveRT::DeviceIoControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
IoStack = IoGetCurrentIrpStackLocation(Irp);
switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_KS_PROPERTY:
return HandleKsProperty(Irp);
case IOCTL_KS_ENABLE_EVENT:
/* FIXME UNIMPLEMENTED */
UNIMPLEMENTED_ONCE;
break;
case IOCTL_KS_DISABLE_EVENT:
/* FIXME UNIMPLEMENTED */
UNIMPLEMENTED_ONCE;
break;
case IOCTL_KS_HANDSHAKE:
/* FIXME UNIMPLEMENTED */
UNIMPLEMENTED_ONCE;
break;
case IOCTL_KS_METHOD:
/* FIXME UNIMPLEMENTED */
UNIMPLEMENTED_ONCE;
return KsDefaultDeviceIoCompletion(DeviceObject, Irp);
case IOCTL_KS_RESET_STATE:
/* FIXME UNIMPLEMENTED */
UNIMPLEMENTED_ONCE;
break;
case IOCTL_KS_WRITE_STREAM:
case IOCTL_KS_READ_STREAM:
return HandleKsStream(Irp);
default:
return KsDefaultDeviceIoCompletion(DeviceObject, Irp);
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NTAPI
CPortPinWaveRT::Read(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
}
NTSTATUS
NTAPI
CPortPinWaveRT::Write(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
}
NTSTATUS
NTAPI
CPortPinWaveRT::Flush(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
}
VOID
NTAPI
CloseStreamRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context)
{
PMINIPORTWAVERTSTREAM Stream;
NTSTATUS Status;
ISubdevice *ISubDevice;
PSUBDEVICE_DESCRIPTOR Descriptor;
CPortPinWaveRT * This;
PCLOSESTREAM_CONTEXT Ctx = (PCLOSESTREAM_CONTEXT)Context;
This = (CPortPinWaveRT*)Ctx->Pin;
if (This->m_Stream)
{
if (This->m_State != KSSTATE_STOP)
{
This->m_Stream->SetState(KSSTATE_STOP);
KeStallExecutionProcessor(10);
}
}
Status = This->m_Port->QueryInterface(IID_ISubdevice, (PVOID*)&ISubDevice);
if (NT_SUCCESS(Status))
{
Status = ISubDevice->GetDescriptor(&Descriptor);
if (NT_SUCCESS(Status))
{
Descriptor->Factory.Instances[This->m_ConnectDetails->PinId].CurrentPinInstanceCount--;
}
ISubDevice->Release();
}
if (This->m_Format)
{
FreeItem(This->m_Format, TAG_PORTCLASS);
This->m_Format = NULL;
}
if (This->m_IrpQueue)
{
This->m_IrpQueue->Release();
}
// complete the irp
Ctx->Irp->IoStatus.Information = 0;
Ctx->Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Ctx->Irp, IO_NO_INCREMENT);
// free the work item
IoFreeWorkItem(Ctx->WorkItem);
// free work item ctx
FreeItem(Ctx, TAG_PORTCLASS);
if (This->m_Stream)
{
Stream = This->m_Stream;
This->m_Stream = NULL;
DPRINT("Closing stream at Irql %u\n", KeGetCurrentIrql());
Stream->Release();
}
}
NTSTATUS
NTAPI
CPortPinWaveRT::Close(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PCLOSESTREAM_CONTEXT Ctx;
if (m_Stream)
{
Ctx = (PCLOSESTREAM_CONTEXT)AllocateItem(NonPagedPool, sizeof(CLOSESTREAM_CONTEXT), TAG_PORTCLASS);
if (!Ctx)
{
DPRINT("Failed to allocate stream context\n");
goto cleanup;
}
Ctx->WorkItem = IoAllocateWorkItem(DeviceObject);
if (!Ctx->WorkItem)
{
DPRINT("Failed to allocate work item\n");
goto cleanup;
}
Ctx->Irp = Irp;
Ctx->Pin = this;
IoMarkIrpPending(Irp);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_PENDING;
// defer work item
IoQueueWorkItem(Ctx->WorkItem, CloseStreamRoutine, DelayedWorkQueue, (PVOID)Ctx);
// Return result
return STATUS_PENDING;
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
cleanup:
if (Ctx)
FreeItem(Ctx, TAG_PORTCLASS);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NTAPI
CPortPinWaveRT::QuerySecurity(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
}
NTSTATUS
NTAPI
CPortPinWaveRT::SetSecurity(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
}
BOOLEAN
NTAPI
CPortPinWaveRT::FastDeviceIoControl(
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength,
IN ULONG IoControlCode,
OUT PIO_STATUS_BLOCK StatusBlock,
IN PDEVICE_OBJECT DeviceObject)
{
return FALSE;
}
BOOLEAN
NTAPI
CPortPinWaveRT::FastRead(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
IN PVOID Buffer,
OUT PIO_STATUS_BLOCK StatusBlock,
IN PDEVICE_OBJECT DeviceObject)
{
return FALSE;
}
BOOLEAN
NTAPI
CPortPinWaveRT::FastWrite(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
IN PVOID Buffer,
OUT PIO_STATUS_BLOCK StatusBlock,
IN PDEVICE_OBJECT DeviceObject)
{
return FALSE;
}
NTSTATUS
NTAPI
CPortPinWaveRT::Init(
IN PPORTWAVERT Port,
IN PPORTFILTERWAVERT Filter,
IN KSPIN_CONNECT * ConnectDetails,
IN KSPIN_DESCRIPTOR * KsPinDescriptor,
IN PDEVICE_OBJECT DeviceObject)
{
NTSTATUS Status;
PKSDATAFORMAT DataFormat;
BOOLEAN Capture;
KSRTAUDIO_HWLATENCY Latency;
Port->AddRef();
Filter->AddRef();
m_Port = Port;
m_Filter = Filter;
m_KsPinDescriptor = KsPinDescriptor;
m_ConnectDetails = ConnectDetails;
m_Miniport = GetWaveRTMiniport(Port);
DataFormat = (PKSDATAFORMAT)(ConnectDetails + 1);
DPRINT("CPortPinWaveRT::Init entered\n");
m_Format = (PKSDATAFORMAT)AllocateItem(NonPagedPool, DataFormat->FormatSize, TAG_PORTCLASS);
if (!m_Format)
return STATUS_INSUFFICIENT_RESOURCES;
RtlMoveMemory(m_Format, DataFormat, DataFormat->FormatSize);
Status = NewIrpQueue(&m_IrpQueue);
if (!NT_SUCCESS(Status))
{
goto cleanup;
}
Status = m_IrpQueue->Init(ConnectDetails, KsPinDescriptor, 0, 0, FALSE);
if (!NT_SUCCESS(Status))
{
goto cleanup;
}
Status = NewPortWaveRTStream(&m_PortStream);
if (!NT_SUCCESS(Status))
{
goto cleanup;
}
if (KsPinDescriptor->Communication == KSPIN_COMMUNICATION_SINK && KsPinDescriptor->DataFlow == KSPIN_DATAFLOW_IN)
{
Capture = FALSE;
}
else if (KsPinDescriptor->Communication == KSPIN_COMMUNICATION_SINK && KsPinDescriptor->DataFlow == KSPIN_DATAFLOW_OUT)
{
Capture = TRUE;
}
else
{
DPRINT("Unexpected Communication %u DataFlow %u\n", KsPinDescriptor->Communication, KsPinDescriptor->DataFlow);
KeBugCheck(0);
while(TRUE);
}
Status = m_Miniport->NewStream(&m_Stream, m_PortStream, ConnectDetails->PinId, Capture, m_Format);
DPRINT("CPortPinWaveRT::Init Status %x\n", Status);
if (!NT_SUCCESS(Status))
goto cleanup;
m_Stream->GetHWLatency(&Latency);
// delay of 10 millisec
m_Delay = Int32x32To64(10, -10000);
Status = m_Stream->AllocateAudioBuffer(16384 * 11, &m_Mdl, &m_CommonBufferSize, &m_CommonBufferOffset, &m_CacheType);
if (!NT_SUCCESS(Status))
{
DPRINT("AllocateAudioBuffer failed with %x\n", Status);
goto cleanup;
}
m_CommonBuffer = MmGetSystemAddressForMdlSafe(m_Mdl, NormalPagePriority);
if (!m_CommonBuffer)
{
DPRINT("Failed to get system address %x\n", Status);
IoFreeMdl(m_Mdl);
m_Mdl = NULL;
goto cleanup;
}
DPRINT("Setting state to acquire %x\n", m_Stream->SetState(KSSTATE_ACQUIRE));
DPRINT("Setting state to pause %x\n", m_Stream->SetState(KSSTATE_PAUSE));
m_State = KSSTATE_PAUSE;
return STATUS_SUCCESS;
cleanup:
if (m_IrpQueue)
{
m_IrpQueue->Release();
m_IrpQueue = NULL;
}
if (m_Format)
{
FreeItem(m_Format, TAG_PORTCLASS);
m_Format = NULL;
}
if (m_Stream)
{
m_Stream->Release();
m_Stream = NULL;
}
else
{
if (m_PortStream)
{
m_PortStream->Release();
m_PortStream = NULL;
}
}
return Status;
}
NTSTATUS
NewPortPinWaveRT(
OUT IPortPinWaveRT ** OutPin)
{
CPortPinWaveRT * This;
This = new(NonPagedPool, TAG_PORTCLASS) CPortPinWaveRT(NULL);
if (!This)
return STATUS_INSUFFICIENT_RESOURCES;
This->AddRef();
// store result
*OutPin = (PPORTPINWAVERT)This;
return STATUS_SUCCESS;
}