/* * 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" #define NDEBUG #include class CPortPinWaveRT : public CUnknownImpl { public: STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); 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; 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; }