mirror of
https://github.com/reactos/reactos.git
synced 2024-11-10 16:48:16 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
610 lines
16 KiB
C++
610 lines
16 KiB
C++
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Kernel Streaming
|
|
* FILE: drivers/wdm/audio/backpln/portcls/irpstream.cpp
|
|
* PURPOSE: IRP Stream handling
|
|
* PROGRAMMER: Johannes Anderwald
|
|
*/
|
|
|
|
#include "private.hpp"
|
|
|
|
|
|
class CIrpQueue : public IIrpQueue
|
|
{
|
|
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_IIrpQueue;
|
|
CIrpQueue(IUnknown *OuterUnknown){}
|
|
virtual ~CIrpQueue(){}
|
|
|
|
protected:
|
|
volatile ULONG m_CurrentOffset;
|
|
LONG m_NumMappings;
|
|
ULONG m_NumDataAvailable;
|
|
PKSPIN_CONNECT m_ConnectDetails;
|
|
KSPIN_LOCK m_IrpListLock;
|
|
LIST_ENTRY m_IrpList;
|
|
LIST_ENTRY m_FreeIrpList;
|
|
PIRP m_Irp;
|
|
PVOID m_SilenceBuffer;
|
|
|
|
ULONG m_OutOfMapping;
|
|
ULONG m_MaxFrameSize;
|
|
ULONG m_Alignment;
|
|
|
|
LONG m_Ref;
|
|
|
|
};
|
|
|
|
#define OFFSET_HEADERINDEX (0)
|
|
#define OFFSET_STREAMHEADER (2)
|
|
#define OFFSET_HEADERCOUNT (3)
|
|
|
|
|
|
#define STREAMHEADER_INDEX(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX]))
|
|
#define STREAMHEADER_COUNT(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT]))
|
|
#define STREAMHEADER_CURRENT(Irp) (Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER])
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CIrpQueue::QueryInterface(
|
|
IN REFIID refiid,
|
|
OUT PVOID* Output)
|
|
{
|
|
if (IsEqualGUIDAligned(refiid, IID_IUnknown))
|
|
{
|
|
*Output = PVOID(PUNKNOWN(this));
|
|
PUNKNOWN(*Output)->AddRef();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CIrpQueue::Init(
|
|
IN KSPIN_CONNECT *ConnectDetails,
|
|
IN ULONG FrameSize,
|
|
IN ULONG Alignment,
|
|
IN PVOID SilenceBuffer)
|
|
{
|
|
m_ConnectDetails = ConnectDetails;
|
|
m_MaxFrameSize = FrameSize;
|
|
m_SilenceBuffer = SilenceBuffer;
|
|
m_Alignment = Alignment;
|
|
|
|
InitializeListHead(&m_IrpList);
|
|
InitializeListHead(&m_FreeIrpList);
|
|
KeInitializeSpinLock(&m_IrpListLock);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CIrpQueue::AddMapping(
|
|
IN PIRP Irp,
|
|
OUT PULONG Data)
|
|
{
|
|
PKSSTREAM_HEADER Header;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION IoStack;
|
|
ULONG NumHeaders, NumData, Index;
|
|
PMDL Mdl;
|
|
|
|
PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
// get current irp stack location
|
|
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (!Irp->MdlAddress)
|
|
{
|
|
// ioctl from KsStudio
|
|
// Wdmaud already probes buffers, therefore no need to probe it again
|
|
// probe the stream irp
|
|
if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_WRITE_STREAM)
|
|
Status = KsProbeStreamIrp(Irp, KSSTREAM_WRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
|
|
else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_READ_STREAM)
|
|
Status = KsProbeStreamIrp(Irp, KSSTREAM_READ | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
|
|
else
|
|
PC_ASSERT(0);
|
|
|
|
// check for success
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("KsProbeStreamIrp failed with %x\n", Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// get first stream header
|
|
|
|
if (Irp->RequestorMode == UserMode)
|
|
Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
|
|
else
|
|
Header = (PKSSTREAM_HEADER)Irp->UserBuffer;
|
|
|
|
// sanity check
|
|
PC_ASSERT(Header);
|
|
|
|
// calculate num headers
|
|
NumHeaders = IoStack->Parameters.DeviceIoControl.OutputBufferLength / Header->Size;
|
|
|
|
// assume headers of same length
|
|
PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength % Header->Size == 0);
|
|
|
|
|
|
// get first audio buffer
|
|
Mdl = Irp->MdlAddress;
|
|
// sanity check
|
|
PC_ASSERT(Mdl);
|
|
|
|
// store the current stream header
|
|
Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER] = (PVOID)Header;
|
|
// store header count
|
|
Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT] = UlongToPtr(NumHeaders);
|
|
|
|
// store current header index
|
|
Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
|
|
|
|
NumData = 0;
|
|
// prepare all headers
|
|
for(Index = 0; Index < NumHeaders; Index++)
|
|
{
|
|
// sanity checks
|
|
PC_ASSERT(Header);
|
|
PC_ASSERT(Mdl);
|
|
|
|
if (Irp->RequestorMode == UserMode)
|
|
{
|
|
Header->Data = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
|
|
}
|
|
|
|
if (!Header->Data)
|
|
{
|
|
// insufficient resources
|
|
ExFreePool(Irp->AssociatedIrp.SystemBuffer);
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
// complete and forget request
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// increment num mappings
|
|
InterlockedIncrement(&m_NumMappings);
|
|
|
|
// increment available data
|
|
InterlockedExchangeAdd((PLONG)&m_NumDataAvailable,
|
|
(max(Header->DataUsed, Header->FrameExtent)));
|
|
|
|
NumData += max(Header->DataUsed, Header->FrameExtent);
|
|
|
|
// move to next header
|
|
Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
|
|
|
|
// move to next mdl
|
|
Mdl = Mdl->Next;
|
|
}
|
|
|
|
DPRINT("StreamHeaders %u NumData %u FrameSize %u NumDataAvailable %u\n", NumHeaders, NumData, m_MaxFrameSize, m_NumDataAvailable);
|
|
*Data = NumData;
|
|
|
|
// mark irp as pending
|
|
IoMarkIrpPending(Irp);
|
|
|
|
// add irp to cancelable queue
|
|
KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
|
|
|
|
// disable mapping failed status
|
|
m_OutOfMapping = FALSE;
|
|
|
|
// done
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CIrpQueue::GetMapping(
|
|
OUT PUCHAR * Buffer,
|
|
OUT PULONG BufferSize)
|
|
{
|
|
PIRP Irp;
|
|
ULONG Offset;
|
|
//PIO_STACK_LOCATION IoStack;
|
|
PKSSTREAM_HEADER StreamHeader;
|
|
|
|
// check if there is an irp in the partially processed
|
|
if (m_Irp)
|
|
{
|
|
// use last irp
|
|
if (m_Irp->Cancel == FALSE)
|
|
{
|
|
Irp = m_Irp;
|
|
Offset = m_CurrentOffset;
|
|
}
|
|
else
|
|
{
|
|
// irp has been cancelled
|
|
m_Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
|
|
m_Irp = Irp = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// get a fresh new irp from the queue
|
|
m_Irp = Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
|
|
m_CurrentOffset = Offset = 0;
|
|
}
|
|
|
|
if (!Irp && m_SilenceBuffer && m_MaxFrameSize)
|
|
{
|
|
DPRINT("NoIrp\n");
|
|
// no irp available, use silence buffer
|
|
*Buffer = (PUCHAR)m_SilenceBuffer;
|
|
*BufferSize = m_MaxFrameSize;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!Irp)
|
|
{
|
|
// no irp buffer available
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
// get stream header
|
|
StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
|
|
|
|
// sanity check
|
|
PC_ASSERT(StreamHeader);
|
|
|
|
// store buffersize
|
|
if (StreamHeader->DataUsed)
|
|
*BufferSize = StreamHeader->DataUsed - Offset;
|
|
else
|
|
*BufferSize = StreamHeader->FrameExtent - Offset;
|
|
|
|
PC_ASSERT(*BufferSize);
|
|
|
|
// store buffer
|
|
*Buffer = &((PUCHAR)StreamHeader->Data)[Offset];
|
|
|
|
// unset flag that no irps are available
|
|
m_OutOfMapping = FALSE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CIrpQueue::UpdateMapping(
|
|
IN ULONG BytesWritten)
|
|
{
|
|
PKSSTREAM_HEADER StreamHeader;
|
|
ULONG Size, NumData, Index;
|
|
|
|
if (!m_Irp)
|
|
{
|
|
// silence buffer was used
|
|
return;
|
|
}
|
|
|
|
// get stream header
|
|
StreamHeader = (PKSSTREAM_HEADER)STREAMHEADER_CURRENT(m_Irp);
|
|
|
|
// sanity check
|
|
// ASSERT(StreamHeader);
|
|
|
|
// add to current offset
|
|
InterlockedExchangeAdd((volatile PLONG)&m_CurrentOffset, (LONG)BytesWritten);
|
|
|
|
// decrement available data counter
|
|
m_NumDataAvailable -= BytesWritten;
|
|
|
|
if (StreamHeader->DataUsed)
|
|
Size = StreamHeader->DataUsed;
|
|
else
|
|
Size = StreamHeader->FrameExtent;
|
|
|
|
PC_ASSERT(Size);
|
|
|
|
if (m_CurrentOffset >= Size)
|
|
{
|
|
if (STREAMHEADER_INDEX(m_Irp) + 1 < STREAMHEADER_COUNT(m_Irp))
|
|
{
|
|
// the irp has at least one more stream header
|
|
m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(STREAMHEADER_INDEX(m_Irp) + 1);
|
|
|
|
// get next stream header
|
|
StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
|
|
|
|
// store next stream header
|
|
STREAMHEADER_CURRENT(m_Irp) = (PVOID)StreamHeader;
|
|
|
|
// reset current offset
|
|
m_CurrentOffset = 0;
|
|
|
|
// done
|
|
return;
|
|
}
|
|
|
|
// irp has been processed completly
|
|
|
|
NumData = 0;
|
|
if (m_Irp->RequestorMode == KernelMode)
|
|
StreamHeader = (PKSSTREAM_HEADER)m_Irp->UserBuffer;
|
|
else
|
|
StreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
// loop all stream headers
|
|
for(Index = 0; Index < STREAMHEADER_COUNT(m_Irp); Index++)
|
|
{
|
|
PC_ASSERT(StreamHeader);
|
|
|
|
// add size of buffer
|
|
// depends on if the buffer is input / output
|
|
if (StreamHeader->DataUsed)
|
|
Size = StreamHeader->DataUsed;
|
|
else
|
|
Size = StreamHeader->FrameExtent;
|
|
|
|
// increment size
|
|
NumData += Size;
|
|
|
|
// get next stream header
|
|
StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
|
|
}
|
|
|
|
if (m_ConnectDetails->Interface.Id == KSINTERFACE_STANDARD_LOOPED_STREAMING)
|
|
{
|
|
// looped streaming repeat the buffers untill
|
|
// the caller decides to stop the streams
|
|
|
|
// reset stream header index
|
|
m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
|
|
// re-insert irp
|
|
KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
|
|
// clear current irp
|
|
m_Irp = NULL;
|
|
// reset offset
|
|
m_CurrentOffset = 0;
|
|
// increment available data
|
|
InterlockedExchangeAdd((PLONG)&m_NumDataAvailable, NumData);
|
|
// done
|
|
return;
|
|
}
|
|
|
|
m_Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
m_Irp->IoStatus.Information = NumData;
|
|
|
|
// complete the request
|
|
IoCompleteRequest(m_Irp, IO_SOUND_INCREMENT);
|
|
// remove irp as it is complete
|
|
m_Irp = NULL;
|
|
m_CurrentOffset = 0;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
CIrpQueue::NumData()
|
|
{
|
|
// returns the amount of audio stream data available
|
|
return m_NumDataAvailable;
|
|
}
|
|
|
|
BOOL
|
|
NTAPI
|
|
CIrpQueue::CancelBuffers()
|
|
{
|
|
// is there an active irp
|
|
if (m_Irp)
|
|
{
|
|
// re-insert it to cancelable queue
|
|
KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
|
|
//set it to zero
|
|
m_Irp = NULL;
|
|
}
|
|
|
|
// cancel all irps
|
|
KsCancelIo(&m_IrpList, &m_IrpListLock);
|
|
// reset number of mappings
|
|
m_NumMappings = 0;
|
|
// reset number of data available
|
|
m_NumDataAvailable = 0;
|
|
|
|
// done
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CIrpQueue::GetMappingWithTag(
|
|
IN PVOID Tag,
|
|
OUT PPHYSICAL_ADDRESS PhysicalAddress,
|
|
OUT PVOID *VirtualAddress,
|
|
OUT PULONG ByteCount,
|
|
OUT PULONG Flags)
|
|
{
|
|
PKSSTREAM_HEADER StreamHeader;
|
|
PIRP Irp;
|
|
|
|
*Flags = 0;
|
|
PC_ASSERT(Tag != NULL);
|
|
|
|
// get an irp from the queue
|
|
Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
|
|
|
|
// check if there is an irp
|
|
if (!Irp)
|
|
{
|
|
// no irp available
|
|
m_OutOfMapping = TRUE;
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
//FIXME support more than one stream header
|
|
PC_ASSERT(STREAMHEADER_COUNT(Irp) == 1);
|
|
|
|
// HACK get stream header
|
|
StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
|
|
|
|
// store mapping in the free list
|
|
ExInterlockedInsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry, &m_IrpListLock);
|
|
|
|
// return mapping
|
|
*PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
|
|
*VirtualAddress = StreamHeader->Data;
|
|
*ByteCount = StreamHeader->DataUsed;
|
|
|
|
// decrement mapping count
|
|
InterlockedDecrement(&m_NumMappings);
|
|
// decrement num data available
|
|
m_NumDataAvailable -= StreamHeader->DataUsed;
|
|
|
|
// store tag in irp
|
|
Irp->Tail.Overlay.DriverContext[3] = Tag;
|
|
|
|
// done
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CIrpQueue::ReleaseMappingWithTag(
|
|
IN PVOID Tag)
|
|
{
|
|
PIRP Irp;
|
|
PLIST_ENTRY CurEntry;
|
|
PKSSTREAM_HEADER StreamHeader;
|
|
|
|
DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag);
|
|
|
|
// remove irp from used list
|
|
CurEntry = ExInterlockedRemoveHeadList(&m_FreeIrpList, &m_IrpListLock);
|
|
// sanity check
|
|
PC_ASSERT(CurEntry);
|
|
|
|
// get irp from list entry
|
|
Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
// HACK get stream header
|
|
StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
|
|
|
|
// driver must release items in the same order
|
|
PC_ASSERT(Irp->Tail.Overlay.DriverContext[3] == Tag);
|
|
|
|
// irp has been processed completly
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
// frame extend contains the original request size, DataUsed contains the real buffer size
|
|
// is different when kmixer performs channel conversion, upsampling etc
|
|
|
|
Irp->IoStatus.Information = StreamHeader->FrameExtent;
|
|
|
|
// complete the request
|
|
IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CIrpQueue::HasLastMappingFailed()
|
|
{
|
|
return m_OutOfMapping;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
CIrpQueue::GetCurrentIrpOffset()
|
|
{
|
|
|
|
return m_CurrentOffset;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CIrpQueue::GetAcquiredTagRange(
|
|
IN PVOID * FirstTag,
|
|
IN PVOID * LastTag)
|
|
{
|
|
KIRQL OldLevel;
|
|
BOOLEAN Ret = FALSE;
|
|
PIRP Irp;
|
|
PLIST_ENTRY CurEntry;
|
|
|
|
KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
|
|
|
|
if (!IsListEmpty(&m_FreeIrpList))
|
|
{
|
|
// get first entry
|
|
CurEntry = RemoveHeadList(&m_FreeIrpList);
|
|
// get irp from list entry
|
|
Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
// get tag of first acquired buffer
|
|
*FirstTag = Irp->Tail.Overlay.DriverContext[3];
|
|
|
|
// put back irp
|
|
InsertHeadList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry);
|
|
|
|
// get last entry
|
|
CurEntry = RemoveTailList(&m_FreeIrpList);
|
|
// get irp from list entry
|
|
Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
// get tag of first acquired buffer
|
|
*LastTag = Irp->Tail.Overlay.DriverContext[3];
|
|
|
|
// put back irp
|
|
InsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry);
|
|
|
|
// indicate success
|
|
Ret = TRUE;
|
|
}
|
|
|
|
// release lock
|
|
KeReleaseSpinLock(&m_IrpListLock, OldLevel);
|
|
// done
|
|
return Ret;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NewIrpQueue(
|
|
IN IIrpQueue **Queue)
|
|
{
|
|
CIrpQueue *This = new(NonPagedPool, TAG_PORTCLASS)CIrpQueue(NULL);
|
|
if (!This)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
This->AddRef();
|
|
|
|
*Queue = (IIrpQueue*)This;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|