reactos/drivers/wdm/audio/backpln/portcls/irpstream.cpp
Amine Khaldi c424146e2c Create a branch for cmake bringup.
svn path=/branches/cmake-bringup/; revision=48236
2010-07-24 18:52:44 +00:00

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;
}