Windows2003-3790/drivers/ksfilter/ks/shsplit.cpp
2020-09-30 16:53:55 +02:00

2238 lines
52 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 1998 - 1999
Module Name:
shsplit.cpp
Abstract:
This module contains the implementation of the kernel streaming
splitter object.
Author:
Dale Sather (DaleSat) 31-Jul-1998
--*/
// Child IRP list for cancellation: pin
// Sync/Async transfer: queue
// Allocate IRPs/headers: requestor
#ifndef __KDEXT_ONLY__
#include "ksp.h"
#include <kcom.h>
#endif // __KDEXT_ONLY__
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif // ALLOC_DATA_PRAGMA
#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA
#define FRAME_HEADER_IRP_STORAGE(Irp)\
*((PKSPPARENTFRAME_HEADER*)&Irp->Tail.Overlay.DriverContext[0])
typedef struct _KSSPLITPIN
{
PKSPIN Pin;
PKSSTREAM_HEADER StreamHeader;
} KSSPLITPIN, *PKSSPLITPIN;
typedef struct _KSPPARENTFRAME_HEADER
{
LIST_ENTRY ListEntry;
PIRP Irp;
PKSSTREAM_HEADER StreamHeader;
PVOID Data;
PKSSPLITPIN SplitPins;
ULONG StreamHeaderSize;
ULONG ChildrenOut;
} KSPPARENTFRAME_HEADER, *PKSPPARENTFRAME_HEADER;
typedef struct _KSPSUBFRAME_HEADER
{
LIST_ENTRY ListEntry;
PIRP Irp;
PKSPPARENTFRAME_HEADER ParentFrameHeader;
KSSTREAM_HEADER StreamHeader;
} KSPSUBFRAME_HEADER, *PKSPSUBFRAME_HEADER;
/*
A list of child IRPs must be maintained for cancellation. This is only needed
when the parent IRP is cancelled, because other pins in the circuit will handle
cancellation due to resets and stops. The argument overlay used in the pin is
not used here because this would conflict with source pins that forward the
child IRPs. The LIST_ENTRY and PIRP required for the child IRP resides in an
extension to the stream header.
The splitter 'branches' maintain lookaside lists of child IRPs with stream headers
to avoid allocation per arrival. This will require prior knowledge of the
number of IRPs required or PASSIVE_LEVEL execution to allocate new IRPs as
required. The lookaside list is freed when the circuit is destroyed.
*/
//
// CKsSplitter is the implementation of the kernel splitter object.
//
class CKsSplitter:
public IKsSplitter,
public CBaseUnknown
{
#ifndef __KDEXT_ONLY__
private:
#else // __KDEXT_ONLY__
public:
#endif // __KDEXT_ONLY__
PIKSTRANSPORT m_TransportSource;
PIKSTRANSPORT m_TransportSink;
BOOLEAN m_Flushing;
KSSTATE m_State;
BOOLEAN m_UseMdls;
ULONG m_BranchCount;
LIST_ENTRY m_BranchList;
INTERLOCKEDLIST_HEAD m_FrameHeadersAvailable;
INTERLOCKEDLIST_HEAD m_IrpsOutstanding;
LONG m_IrpsWaitingToTransfer;
LONG m_FailedRemoveCount;
public:
DEFINE_LOG_CONTEXT(m_Log);
DEFINE_STD_UNKNOWN();
CKsSplitter(PUNKNOWN OuterUnknown):
CBaseUnknown(OuterUnknown) {
}
~CKsSplitter();
IMP_IKsSplitter;
NTSTATUS
Init(
IN PKSPIN Pin
);
PIKSTRANSPORT
GetTransportSource(
void
)
{
return m_TransportSource;
};
PIKSTRANSPORT
GetTransportSink(
void
)
{
return m_TransportSink;
};
void
TransferParentIrp(
void
);
static
void
CancelRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
private:
PKSPPARENTFRAME_HEADER
NewFrameHeader(
IN ULONG HeaderSize
);
void
DeleteFrameHeader(
IN PKSPPARENTFRAME_HEADER FrameHeader
);
};
//
// CKsSplitterBranch is the implementation of the kernel splitter
// branch object.
//
class CKsSplitterBranch:
public IKsTransport,
public CBaseUnknown
{
#ifndef __KDEXT_ONLY__
private:
#else // __KDEXT_ONLY__
public:
#endif // __KDEXT_ONLY__
PIKSTRANSPORT m_TransportSource;
PIKSTRANSPORT m_TransportSink;
CKsSplitter* m_Splitter;
PKSPIN m_Pin;
ULONG m_Offset;
ULONG m_Size;
KS_COMPRESSION m_Compression;
LIST_ENTRY m_ListEntry;
PLIST_ENTRY m_ListHead;
INTERLOCKEDLIST_HEAD m_IrpsAvailable;
ULONG m_IoControlCode;
ULONG m_StackSize;
ULONG m_OutstandingIrpCount;
ULONG m_DataUsed;
ULONG m_FrameExtent;
ULONG m_Irps;
public:
DEFINE_LOG_CONTEXT(m_Log);
DEFINE_STD_UNKNOWN();
CKsSplitterBranch(PUNKNOWN OuterUnknown):
CBaseUnknown(OuterUnknown) {
}
~CKsSplitterBranch();
IMP_IKsTransport;
NTSTATUS
Init(
IN CKsSplitter* Splitter,
IN PLIST_ENTRY ListHead,
IN PKSPIN Pin,
IN const KSALLOCATOR_FRAMING_EX* AllocatorFraming OPTIONAL
);
PIKSTRANSPORT
GetTransportSource(
void
)
{
return m_TransportSource;
};
PIKSTRANSPORT
GetTransportSink(
void
)
{
return m_TransportSink;
};
void
Orphan(
void
)
{
if (m_Splitter) {
m_Splitter = NULL;
RemoveEntryList(&m_ListEntry);
}
}
NTSTATUS
TransferSubframe(
IN PKSPSUBFRAME_HEADER SubframeHeader
);
private:
PIRP
AllocateIrp(
void
);
void
FreeIrp(
IN PIRP Irp
)
{
IoFreeIrp(Irp);
}
friend CKsSplitter;
};
NTSTATUS
KspCreateSplitterBranch(
OUT CKsSplitterBranch** SplitterBranch,
IN CKsSplitter* Splitter,
IN PLIST_ENTRY ListHead,
IN PKSPIN Pin,
IN const KSALLOCATOR_FRAMING_EX* AllocatorFraming OPTIONAL
);
#ifndef __KDEXT_ONLY__
IMPLEMENT_STD_UNKNOWN(CKsSplitter)
NTSTATUS
KspCreateSplitter(
OUT PIKSSPLITTER* Splitter,
IN PKSPIN Pin
)
/*++
Routine Description:
This routine creates a new splitter.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[KspCreateSplitter]"));
PAGED_CODE();
ASSERT(Splitter);
ASSERT(Pin);
NTSTATUS status;
CKsSplitter *splitter =
new(NonPagedPool,POOLTAG_SPLITTER) CKsSplitter(NULL);
if (splitter) {
splitter->AddRef();
status = splitter->Init(Pin);
if (NT_SUCCESS(status)) {
*Splitter = splitter;
} else {
splitter->Release();
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
NTSTATUS
CKsSplitter::
Init(
IN PKSPIN Pin
)
/*++
Routine Description:
This routine initializes a splitter object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::Init]"));
PAGED_CODE();
ASSERT(Pin);
m_State = KSSTATE_STOP;
m_Flushing = FALSE;
InitializeListHead(&m_BranchList);
InitializeInterlockedListHead(&m_FrameHeadersAvailable);
InitializeInterlockedListHead(&m_IrpsOutstanding);
KsLogInitContext(&m_Log,Pin,this);
KsLog(&m_Log,KSLOGCODE_SPLITTER_CREATE,NULL,NULL);
return STATUS_SUCCESS;
}
CKsSplitter::
~CKsSplitter(
void
)
/*++
Routine Description:
This routine destructs a splitter object.
Arguments:
None.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::~CKsSplitter(0x%08x)]",this));
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Split%p.~",this));
PAGED_CODE();
ASSERT(! m_TransportSink);
ASSERT(! m_TransportSource);
//
// Release all the branches.
//
CKsSplitterBranch *prevBranch = NULL;
while (! IsListEmpty(&m_BranchList)) {
CKsSplitterBranch *branch =
CONTAINING_RECORD(m_BranchList.Flink,CKsSplitterBranch,m_ListEntry);
branch->Orphan();
branch->Release();
ASSERT(branch != prevBranch);
prevBranch = branch;
}
KsLog(&m_Log,KSLOGCODE_SPLITTER_DESTROY,NULL,NULL);
}
STDMETHODIMP_(NTSTATUS)
CKsSplitter::
NonDelegatedQueryInterface(
IN REFIID InterfaceId,
OUT PVOID* InterfacePointer
)
/*++
Routine Description:
This routine obtains an interface to a splitter object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::NonDelegatedQueryInterface]"));
PAGED_CODE();
ASSERT(InterfacePointer);
NTSTATUS status = STATUS_SUCCESS;
if (IsEqualGUIDAligned(InterfaceId,__uuidof(IKsTransport)) ||
IsEqualGUIDAligned(InterfaceId,__uuidof(IKsSplitter))) {
*InterfacePointer =
reinterpret_cast<PVOID>(static_cast<PIKSSPLITTER>(this));
AddRef();
} else {
status =
CBaseUnknown::NonDelegatedQueryInterface(
InterfaceId,InterfacePointer);
}
return status;
}
#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif // ALLOC_PRAGMA
STDMETHODIMP_(NTSTATUS)
CKsSplitter::
TransferKsIrp(
IN PIRP Irp,
IN PIKSTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles the arrival of a streaming IRP.
Arguments:
Irp -
Contains a pointer to the streaming IRP to be transferred.
NextTransport -
Contains a pointer to a location at which to deposit a pointer
to the next transport interface to recieve the IRP. May be set
to NULL indicating the IRP should not be forwarded further.
Return Value:
STATUS_PENDING or some error status.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::TransferKsIrp]"));
ASSERT(Irp);
ASSERT(NextTransport);
ASSERT(m_TransportSink);
KsLog(&m_Log,KSLOGCODE_SPLITTER_RECV,Irp,NULL);
if (m_State != KSSTATE_RUN) {
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Split%p.TransferKsIrp: got IRP %p in state %d",this,Irp,m_State));
}
//
// Shunt non-successful Irps.
//
if (!NT_SUCCESS (Irp->IoStatus.Status)) {
_DbgPrintF(DEBUGLVL_FLOWEXCEPTIONS,("#### Splitter%p.TransferKsIrp: shunting irp%p",this,Irp));
KsLog(&m_Log,KSLOGCODE_SPLITTER_SEND,Irp,NULL);
*NextTransport = m_TransportSink;
return STATUS_SUCCESS;
}
//
// Get a pointer to the stream header.
//
PKSSTREAM_HEADER streamHeader =
reinterpret_cast<PKSSTREAM_HEADER>(Irp->AssociatedIrp.SystemBuffer);
//
// Get a frame header.
//
PKSPPARENTFRAME_HEADER frameHeader = NewFrameHeader(streamHeader->Size);
if (! frameHeader) {
_DbgPrintF(DEBUGLVL_TERSE,("#### Split%p.TransferKsIrp: failed to allocate frame header for IRP %p",this,Irp));
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
KsLog(&m_Log,KSLOGCODE_SPLITTER_SEND,Irp,NULL);
KspDiscardKsIrp(m_TransportSink,Irp);
*NextTransport = NULL;
return STATUS_PENDING;
}
//
// Attach the frame header to the IRP.
//
FRAME_HEADER_IRP_STORAGE(Irp) = frameHeader;
//
// Initialize the frame header.
//
frameHeader->Irp = Irp;
frameHeader->StreamHeader = streamHeader;
frameHeader->Data =
m_UseMdls ?
MmGetSystemAddressForMdl(Irp->MdlAddress) : streamHeader->Data;
//
// Initialize the subframe headers.
//
PKSSPLITPIN splitPin = frameHeader->SplitPins;
for(PLIST_ENTRY listEntry = m_BranchList.Flink;
listEntry != &m_BranchList;
listEntry = listEntry->Flink, splitPin++) {
CKsSplitterBranch *branch =
CONTAINING_RECORD(listEntry,CKsSplitterBranch,m_ListEntry);
RtlCopyMemory(splitPin->StreamHeader,streamHeader,streamHeader->Size);
splitPin->StreamHeader->Data = frameHeader->Data;
if (branch->m_Compression.RatioNumerator) {
splitPin->StreamHeader->FrameExtent =
ULONG((ULONGLONG(splitPin->StreamHeader->FrameExtent -
branch->m_Compression.RatioConstantMargin) *
ULONGLONG(branch->m_Compression.RatioDenominator)) /
ULONGLONG(branch->m_Compression.RatioNumerator));
}
}
//
// TODO non-trivial subframe
// TODO multiple frames per IRP
//
frameHeader->ChildrenOut = m_BranchCount + 2;
//
// Transfer subframes to each branch.
//
splitPin = frameHeader->SplitPins;
for(listEntry = m_BranchList.Flink;
listEntry != &m_BranchList;
listEntry = listEntry->Flink, splitPin++) {
CKsSplitterBranch *branch =
CONTAINING_RECORD(listEntry,CKsSplitterBranch,m_ListEntry);
branch->TransferSubframe(
CONTAINING_RECORD(
splitPin->StreamHeader,
KSPSUBFRAME_HEADER,
StreamHeader));
}
//
// Remove the count which prevents parent IRP transfer during setup. If
// the result is one, all children have come back.
//
if (InterlockedDecrement(PLONG(&frameHeader->ChildrenOut)) == 1) {
KsLog(&m_Log,KSLOGCODE_SPLITTER_SEND,Irp,NULL);
DeleteFrameHeader(frameHeader);
*NextTransport = m_TransportSink;
} else {
*NextTransport = NULL;
//
// Add the IRP to the list of oustanding parent IRPs. After this call
// the IRP is cancelable, but we still have one count on it. The
// cancel routine will not complete the IRP until the count is gone.
//
IoMarkIrpPending(Irp);
KsAddIrpToCancelableQueue(
&m_IrpsOutstanding.ListEntry,
&m_IrpsOutstanding.SpinLock,
Irp,
KsListEntryTail,
CKsSplitter::CancelRoutine);
if (InterlockedDecrement(PLONG(&frameHeader->ChildrenOut)) == 0) {
TransferParentIrp();
}
}
return STATUS_PENDING;
}
PKSPPARENTFRAME_HEADER
CKsSplitter::
NewFrameHeader(
IN ULONG HeaderSize
)
/*++
Routine Description:
This routine obtains a new frame header.
Arguments:
HeaderSize -
Contains the size in bytes of the KSSTREAM_HEADERs to be allocated
for subframes.
Return Value:
A new frame header or NULL if memory could not be allocated.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::NewFrameHeader]"));
ASSERT(HeaderSize >= sizeof(KSSTREAM_HEADER));
ASSERT((HeaderSize & FILE_QUAD_ALIGNMENT) == 0);
ASSERT((sizeof(KSPPARENTFRAME_HEADER) & FILE_QUAD_ALIGNMENT) == 0);
ASSERT((sizeof(KSPSUBFRAME_HEADER) & FILE_QUAD_ALIGNMENT) == 0);
//
// See if there is a frame header already available.
//
PLIST_ENTRY listEntry =
ExInterlockedRemoveHeadList(
&m_FrameHeadersAvailable.ListEntry,
&m_FrameHeadersAvailable.SpinLock);
//
// Make sure the stream headers are large enough.
//
PKSPPARENTFRAME_HEADER frameHeader;
if (listEntry) {
frameHeader = CONTAINING_RECORD(listEntry,KSPPARENTFRAME_HEADER,ListEntry);
if (frameHeader->StreamHeaderSize >= HeaderSize) {
return frameHeader;
}
ExFreePool(frameHeader);
}
//
// Calculate size of frame/subframes/index
//
ULONG subframeHeaderSize =
sizeof(KSPSUBFRAME_HEADER) +
HeaderSize -
sizeof(KSSTREAM_HEADER);
ULONG size =
sizeof(KSPPARENTFRAME_HEADER) +
m_BranchCount * (subframeHeaderSize + sizeof(KSSPLITPIN));
frameHeader = reinterpret_cast<PKSPPARENTFRAME_HEADER>(
ExAllocatePoolWithTag(NonPagedPool,size,POOLTAG_FRAMEHEADER));
if (! frameHeader) {
return NULL;
}
//
// Zero the whole thing.
//
RtlZeroMemory(frameHeader,size);
//
// Locate the first subframe header.
//
PKSPSUBFRAME_HEADER subframeHeader =
reinterpret_cast<PKSPSUBFRAME_HEADER>(frameHeader + 1);
//
// Initialize the frame header.
//
frameHeader->SplitPins =
reinterpret_cast<PKSSPLITPIN>(
reinterpret_cast<PUCHAR>(subframeHeader) +
subframeHeaderSize * m_BranchCount);
frameHeader->StreamHeaderSize = HeaderSize;
//
// Initialize the subframe headers and the index.
//
PKSSPLITPIN splitPin = frameHeader->SplitPins;
for(listEntry = m_BranchList.Flink;
listEntry != &m_BranchList;
listEntry = listEntry->Flink, splitPin++) {
CKsSplitterBranch *branch =
CONTAINING_RECORD(listEntry,CKsSplitterBranch,m_ListEntry);
splitPin->Pin = branch->m_Pin;
splitPin->StreamHeader = &subframeHeader->StreamHeader;
subframeHeader->ParentFrameHeader = frameHeader;
subframeHeader =
reinterpret_cast<PKSPSUBFRAME_HEADER>(
reinterpret_cast<PUCHAR>(subframeHeader) +
subframeHeaderSize);
}
return frameHeader;
}
void
CKsSplitter::
DeleteFrameHeader(
IN PKSPPARENTFRAME_HEADER FrameHeader
)
/*++
Routine Description:
This routine releases a frame header.
Arguments:
FrameHeader -
Contains a pointer to the frame header to be deleted.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::NewFrameDeleteFrameHeaderHeader]"));
ExInterlockedInsertTailList(
&m_FrameHeadersAvailable.ListEntry,
&FrameHeader->ListEntry,
&m_FrameHeadersAvailable.SpinLock);
}
STDMETHODIMP_(void)
CKsSplitter::
DiscardKsIrp(
IN PIRP Irp,
IN PIKSTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles the arrival of a streaming IRP.
Arguments:
Irp -
Contains a pointer to the streaming IRP to be discarded.
NextTransport -
Contains a pointer to a location at which to deposit a pointer
to the next transport interface to recieve the IRP. May be set
to NULL indicating the IRP should not be forwarded further.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::DiscardKsIrp]"));
ASSERT(Irp);
ASSERT(NextTransport);
ASSERT(m_TransportSink);
*NextTransport = m_TransportSink;
}
#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA
STDMETHODIMP_(void)
CKsSplitter::
Connect(
IN PIKSTRANSPORT NewTransport OPTIONAL,
OUT PIKSTRANSPORT *OldTransport OPTIONAL,
OUT PIKSTRANSPORT *BranchTransport OPTIONAL,
IN KSPIN_DATAFLOW DataFlow
)
/*++
Routine Description:
This routine establishes a transport connection.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::Connect]"));
PAGED_CODE();
if (BranchTransport) {
if (IsListEmpty(&m_BranchList)) {
*BranchTransport = NULL;
} else if (DataFlow == KSPIN_DATAFLOW_OUT) {
*BranchTransport =
CONTAINING_RECORD(
m_BranchList.Flink,
CKsSplitterBranch,
m_ListEntry);
} else {
*BranchTransport =
CONTAINING_RECORD(
m_BranchList.Blink,
CKsSplitterBranch,
m_ListEntry);
}
}
KspStandardConnect(
NewTransport,
OldTransport,
NULL,
DataFlow,
PIKSTRANSPORT(this),
&m_TransportSource,
&m_TransportSink);
}
STDMETHODIMP_(NTSTATUS)
CKsSplitter::
SetDeviceState(
IN KSSTATE NewState,
IN KSSTATE OldState,
IN PIKSTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles notification that the device state has changed.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Split%p.SetDeviceState: set from %d to %d",this,OldState,NewState));
PAGED_CODE();
ASSERT(NextTransport);
NTSTATUS status;
//
// If this is a change of state, note the new state and indicate the next
// recipient.
//
if (m_State != NewState) {
//
// The state has changed.
//
m_State = NewState;
if (IsListEmpty(&m_BranchList)) {
if (NewState > OldState) {
*NextTransport = m_TransportSink;
} else {
*NextTransport = m_TransportSource;
}
} else {
if (NewState > OldState) {
*NextTransport =
CONTAINING_RECORD(
m_BranchList.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
} else {
*NextTransport =
CONTAINING_RECORD(
m_BranchList.Blink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSource();
}
}
status = STATUS_SUCCESS;
} else {
status = STATUS_SUCCESS;
}
return status;
}
STDMETHODIMP_(void)
CKsSplitter::
GetTransportConfig(
OUT PKSPTRANSPORTCONFIG Config,
OUT PIKSTRANSPORT* NextTransport,
OUT PIKSTRANSPORT* PrevTransport
)
/*++
Routine Description:
This routine gets transport configuration information.
Arguments:
Config -
Contains a pointer to the location where configuration requirements
for this object should be depobranchd.
NextTransport -
Contains a pointer to the location at which the next transport
interface should be depobranchd.
PrevTransport -
Contains a pointer to the location at which the previous transport
interfaction should be depobranchd.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::GetTransportConfig]"));
PAGED_CODE();
ASSERT(Config);
ASSERT(NextTransport);
ASSERT(PrevTransport);
Config->TransportType = KSPTRANSPORTTYPE_SPLITTER;
Config->IrpDisposition = KSPIRPDISPOSITION_NONE;
Config->StackDepth = 1;
if (IsListEmpty(&m_BranchList)) {
*PrevTransport = m_TransportSource;
*NextTransport = m_TransportSink;
} else {
*PrevTransport =
CONTAINING_RECORD(
m_BranchList.Blink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSource();
*NextTransport =
CONTAINING_RECORD(
m_BranchList.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
}
}
STDMETHODIMP_(void)
CKsSplitter::
SetTransportConfig(
IN const KSPTRANSPORTCONFIG* Config,
OUT PIKSTRANSPORT* NextTransport,
OUT PIKSTRANSPORT* PrevTransport
)
/*++
Routine Description:
This routine sets transport configuration information.
Arguments:
Config -
Contains a pointer to the new configuration settings for this object.
NextTransport -
Contains a pointer to the location at which the next transport
interface should be depobranchd.
PrevTransport -
Contains a pointer to the location at which the previous transport
interfaction should be depobranchd.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::SetTransportConfig]"));
PAGED_CODE();
ASSERT(Config);
ASSERT(NextTransport);
ASSERT(PrevTransport);
#if DBG
if (Config->IrpDisposition == KSPIRPDISPOSITION_ROLLCALL) {
ULONG references = AddRef() - 1; Release();
DbgPrint(" Split%p refs=%d\n",this,references);
if (Config->StackDepth) {
DbgPrint(" IRPs waiting to transfer = %d\n",m_IrpsWaitingToTransfer);
DbgPrint(" failed removes = %d\n",m_FailedRemoveCount);
DbgPrint(" IRPs outstanding\n");
KIRQL oldIrql;
KeAcquireSpinLock(&m_IrpsOutstanding.SpinLock,&oldIrql);
for(PLIST_ENTRY listEntry = m_IrpsOutstanding.ListEntry.Flink;
listEntry != &m_IrpsOutstanding.ListEntry;
listEntry = listEntry->Flink) {
PIRP irp =
CONTAINING_RECORD(listEntry,IRP,Tail.Overlay.ListEntry);
PKSPPARENTFRAME_HEADER frameHeader =
FRAME_HEADER_IRP_STORAGE(irp);
DbgPrint(" IRP %p, %d branches outstanding\n",irp,frameHeader->ChildrenOut);
PKSSPLITPIN splitPin = frameHeader->SplitPins;
for (ULONG count = m_BranchCount; count--; splitPin++) {
PKSPSUBFRAME_HEADER subframeHeader =
CONTAINING_RECORD(splitPin->StreamHeader,KSPSUBFRAME_HEADER,StreamHeader);
if (subframeHeader->Irp) {
DbgPrint(" branch IRP %p, pin%p\n",subframeHeader->Irp,splitPin->Pin);
}
}
}
KeReleaseSpinLock(&m_IrpsOutstanding.SpinLock,oldIrql);
}
} else
#endif
{
m_UseMdls = (Config->IrpDisposition & KSPIRPDISPOSITION_USEMDLADDRESS) != 0;
}
if (IsListEmpty(&m_BranchList)) {
*PrevTransport = m_TransportSource;
*NextTransport = m_TransportSink;
} else {
*PrevTransport =
CONTAINING_RECORD(
m_BranchList.Blink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSource();
*NextTransport =
CONTAINING_RECORD(
m_BranchList.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
}
}
STDMETHODIMP_(void)
CKsSplitter::
ResetTransportConfig (
OUT PIKSTRANSPORT* NextTransport,
OUT PIKSTRANSPORT* PrevTransport
)
/*++
Routine Description:
Reset the transport configuration of the splitter. This indicates that
something is wrong with the pipe and the previously set configuration is
no longer valid.
Arguments:
None
Return Value:
None
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::ResetTransportConfig]"));
PAGED_CODE ();
ASSERT (NextTransport);
ASSERT (PrevTransport);
m_UseMdls = 0;
if (IsListEmpty(&m_BranchList)) {
*PrevTransport = m_TransportSource;
*NextTransport = m_TransportSink;
} else {
*PrevTransport =
CONTAINING_RECORD(
m_BranchList.Blink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSource();
*NextTransport =
CONTAINING_RECORD(
m_BranchList.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
}
}
STDMETHODIMP_(void)
CKsSplitter::
SetResetState(
IN KSRESET ksReset,
IN PIKSTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles notification that the reset state has changed.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_VERBOSE,("[CKsSplitter::SetResetState] to %d",ksReset));
PAGED_CODE();
ASSERT(NextTransport);
if (m_Flushing != (ksReset == KSRESET_BEGIN)) {
if (IsListEmpty(&m_BranchList)) {
*NextTransport = m_TransportSink;
} else {
*NextTransport =
CONTAINING_RECORD(
m_BranchList.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
}
m_Flushing = (ksReset == KSRESET_BEGIN);
} else {
*NextTransport = NULL;
}
}
STDMETHODIMP_(NTSTATUS)
CKsSplitter::
AddBranch(
IN PKSPIN Pin,
IN const KSALLOCATOR_FRAMING_EX* AllocatorFraming OPTIONAL
)
/*++
Routine Description:
This routine adds a new branch (branch) to a splitter.
Arguments:
Pin -
Contains a pointer to the pin to be associated with the new branch.
AllocatorFraming -
Contains an optional pointer to allocator framing information for
use in establishing default subframe allocation.
Return Value:
STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::AddBranch]"));
ASSERT(Pin);
CKsSplitterBranch* branch;
NTSTATUS status =
KspCreateSplitterBranch(
&branch,
this,
&m_BranchList,
Pin,
AllocatorFraming);
//
// The branch is still referenced by the splitter.
//
if (NT_SUCCESS(status)) {
branch->Release();
m_BranchCount++;
}
return status;
}
#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif // ALLOC_PRAGMA
void
CKsSplitter::
TransferParentIrp(
void
)
/*++
Routine Description:
This routine transfers parent IRPs whose children have all returned. It
starts at the head of the m_IrpsOutstanding queue and stops when it runs
out of IRPs or returns as many IRPs as are waiting to transfer.
Arguments:
None.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitter::TransferParentIrp]"));
ASSERT(m_TransportSink);
InterlockedIncrement(&m_IrpsWaitingToTransfer);
while (m_IrpsWaitingToTransfer) {
//
// Get an IRP from the head of the queue.
//
PIRP irp =
KsRemoveIrpFromCancelableQueue(
&m_IrpsOutstanding.ListEntry,
&m_IrpsOutstanding.SpinLock,
KsListEntryHead,
KsAcquireOnly);
//
// If none were available, quit.
//
if (! irp) {
InterlockedIncrement(&m_FailedRemoveCount);
break;
}
//
// Determine if the IRP is ready to be transferred.
//
PKSPPARENTFRAME_HEADER frameHeader = FRAME_HEADER_IRP_STORAGE(irp);
if (InterlockedCompareExchange(PLONG(&frameHeader->ChildrenOut),1,0) == 0) {
//
// This IRP is ready to be transferred. Remove it, delete its header,
// transfer it, and decrement the waiting count.
//
KsRemoveSpecificIrpFromCancelableQueue(irp);
DeleteFrameHeader(frameHeader);
KsLog(&m_Log,KSLOGCODE_SPLITTER_SEND,irp,NULL);
KspTransferKsIrp(m_TransportSink,irp);
InterlockedDecrement(&m_IrpsWaitingToTransfer);
} else {
//
// This IRP has children out.
//
KsReleaseIrpOnCancelableQueue(irp,CKsSplitter::CancelRoutine);
break;
}
}
}
void
CKsSplitter::
CancelRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
ASSERT(DeviceObject);
ASSERT(Irp);
//
// Mark the IRP cancelled and call the standard routine. Doing the
// marking first has the effect of not completing the IRP in the standard
// routine. The standard routine removes the IRP from the queue and
// releases the cancel spin lock.
//
Irp->IoStatus.Status = STATUS_CANCELLED;
KsCancelRoutine(DeviceObject,Irp);
//
// TODO: Cancel child IRPs
//
IoCompleteRequest(Irp,IO_NO_INCREMENT);
}
#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA
IMPLEMENT_STD_UNKNOWN(CKsSplitterBranch)
NTSTATUS
KspCreateSplitterBranch(
OUT CKsSplitterBranch** SplitterBranch,
IN CKsSplitter* Splitter,
IN PLIST_ENTRY ListHead,
IN PKSPIN Pin,
IN const KSALLOCATOR_FRAMING_EX* AllocatorFraming OPTIONAL
)
/*++
Routine Description:
This routine creates a new splitter branch.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[KspCreateSplitterBranch]"));
PAGED_CODE();
ASSERT(SplitterBranch);
ASSERT(Splitter);
ASSERT(ListHead);
ASSERT(Pin);
NTSTATUS status;
CKsSplitterBranch *branch =
new(NonPagedPool,POOLTAG_SPLITTERBRANCH) CKsSplitterBranch(NULL);
if (branch) {
branch->AddRef();
status = branch->Init(Splitter,ListHead,Pin,AllocatorFraming);
if (NT_SUCCESS(status)) {
*SplitterBranch = branch;
} else {
branch->Release();
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
NTSTATUS
CKsSplitterBranch::
Init(
IN CKsSplitter* Splitter,
IN PLIST_ENTRY ListHead,
IN PKSPIN Pin,
IN const KSALLOCATOR_FRAMING_EX* AllocatorFraming OPTIONAL
)
/*++
Routine Description:
This routine initializes a splitter branch object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::Init]"));
PAGED_CODE();
ASSERT(Splitter);
ASSERT(ListHead);
ASSERT(Pin);
m_Splitter = Splitter;
m_ListHead = ListHead;
m_Pin = Pin;
m_IoControlCode =
(Pin->DataFlow == KSPIN_DATAFLOW_IN) ?
IOCTL_KS_READ_STREAM :
IOCTL_KS_WRITE_STREAM;
if (AllocatorFraming &&
(AllocatorFraming->OutputCompression.RatioNumerator >
AllocatorFraming->OutputCompression.RatioDenominator)) {
m_Compression = AllocatorFraming->OutputCompression;
}
InitializeInterlockedListHead(&m_IrpsAvailable);
//
// Add this branch to the list and add the resulting reference.
//
InsertTailList(ListHead,&m_ListEntry);
AddRef();
//
// Connect to the pin in both directions.
//
PIKSTRANSPORT pinTransport =
CONTAINING_RECORD(Pin,KSPIN_EXT,Public)->Interface;
Connect(pinTransport,NULL,NULL,KSPIN_DATAFLOW_IN);
Connect(pinTransport,NULL,NULL,KSPIN_DATAFLOW_OUT);
KsLogInitContext(&m_Log,Pin,this);
KsLog(&m_Log,KSLOGCODE_BRANCH_CREATE,NULL,NULL);
return STATUS_SUCCESS;
}
CKsSplitterBranch::
~CKsSplitterBranch(
void
)
/*++
Routine Description:
This routine destructs a splitter branch object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::~CKsSplitterBranch(0x%08x)]",this));
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Branch%p.~",this));
if (m_DataUsed) {
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Branch%p.~: m_DataUsed=%d",this,m_DataUsed));
}
if (m_FrameExtent) {
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Branch%p.~: m_FrameExtent=%d",this,m_FrameExtent));
}
if (m_Irps) {
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Branch%p.~: m_Irps=%d",this,m_Irps));
}
PAGED_CODE();
ASSERT(! m_TransportSink);
ASSERT(! m_TransportSource);
Orphan();
//
// Free all IRPs.
//
while (! IsListEmpty(&m_IrpsAvailable.ListEntry)) {
PLIST_ENTRY listEntry = RemoveHeadList(&m_IrpsAvailable.ListEntry);
PIRP irp = CONTAINING_RECORD(listEntry,IRP,Tail.Overlay.ListEntry);
FreeIrp(irp);
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Branch%p.~: freeing IRP %p",this,irp));
}
KsLog(&m_Log,KSLOGCODE_BRANCH_DESTROY,NULL,NULL);
}
STDMETHODIMP_(NTSTATUS)
CKsSplitterBranch::
NonDelegatedQueryInterface(
IN REFIID InterfaceId,
OUT PVOID* InterfacePointer
)
/*++
Routine Description:
This routine obtains an interface to a splitter branch object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::NonDelegatedQueryInterface]"));
PAGED_CODE();
ASSERT(InterfacePointer);
NTSTATUS status = STATUS_SUCCESS;
if (IsEqualGUIDAligned(InterfaceId,__uuidof(IKsTransport))) {
*InterfacePointer =
reinterpret_cast<PVOID>(static_cast<PIKSTRANSPORT>(this));
AddRef();
} else {
status =
CBaseUnknown::NonDelegatedQueryInterface(
InterfaceId,InterfacePointer);
}
return status;
}
#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif // ALLOC_PRAGMA
STDMETHODIMP_(NTSTATUS)
CKsSplitterBranch::
TransferKsIrp(
IN PIRP Irp,
IN PIKSTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles the arrival of a streaming IRP.
Arguments:
Irp -
Contains a pointer to the streaming IRP to be transferred.
NextTransport -
Contains a pointer to a location at which to deposit a pointer
to the next transport interface to recieve the IRP. May be set
to NULL indicating the IRP should not be forwarded further.
Return Value:
STATUS_PENDING or some error status.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::TransferKsIrp]"));
ASSERT(Irp);
ASSERT(NextTransport);
ASSERT(m_TransportSink);
KsLog(&m_Log,KSLOGCODE_BRANCH_RECV,Irp,NULL);
InterlockedDecrement(PLONG(&m_OutstandingIrpCount));
//
// Get the subframe header from the imbedded stream header.
//
PKSPSUBFRAME_HEADER subframeHeader =
CONTAINING_RECORD(
Irp->AssociatedIrp.SystemBuffer,KSPSUBFRAME_HEADER,StreamHeader);
//
// Make sure the parent header's DataUsed is no smaller than the offset of
// this subframe plus its DataUsed.
//
// TODO: The client should be able to do this. The default would be
// cheaper to calculate if the subframe with the largest offset was
// tagged in advance.
//
ULONG dataUsed =
subframeHeader->StreamHeader.DataUsed +
ULONG(
PUCHAR(subframeHeader->StreamHeader.Data) -
PUCHAR(subframeHeader->ParentFrameHeader->Data));
PKSSTREAM_HEADER parentHeader =
subframeHeader->ParentFrameHeader->StreamHeader;
if (parentHeader->DataUsed < dataUsed) {
parentHeader->DataUsed = dataUsed;
}
parentHeader->OptionsFlags |=
subframeHeader->StreamHeader.OptionsFlags &
KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM;
//
// Free MDL(s).
//
PMDL nextMdl;
for (PMDL mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) {
nextMdl = mdl->Next;
if (mdl->MdlFlags & MDL_PAGES_LOCKED) {
MmUnlockPages(mdl);
}
IoFreeMdl(mdl);
}
Irp->MdlAddress = NULL;
//
// Put the IRP on the list of available IRPs. Do this before transferring
// the parent in case the transfer results in the arrival of another
// parent.
//
ExInterlockedInsertTailList(
&m_IrpsAvailable.ListEntry,
&Irp->Tail.Overlay.ListEntry,
&m_IrpsAvailable.SpinLock);
subframeHeader->Irp = NULL;
//
// If this was the last subframe, the parent IRP must be transferred.
//
if (InterlockedDecrement(PLONG(&subframeHeader->ParentFrameHeader->ChildrenOut)) == 0) {
m_Splitter->TransferParentIrp();
}
//
// The child IRP is not going anywhere right now.
//
*NextTransport = NULL;
return STATUS_PENDING;
}
STDMETHODIMP_(void)
CKsSplitterBranch::
DiscardKsIrp(
IN PIRP Irp,
IN PIKSTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles the arrival of a streaming IRP.
Arguments:
Irp -
Contains a pointer to the streaming IRP to be discarded.
NextTransport -
Contains a pointer to a location at which to deposit a pointer
to the next transport interface to recieve the IRP. May be set
to NULL indicating the IRP should not be forwarded further.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::DiscardKsIrp]"));
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Branch%p.DiscardKsIrp: %p",this,Irp));
ASSERT(Irp);
ASSERT(NextTransport);
TransferKsIrp(Irp,NextTransport);
}
#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA
STDMETHODIMP_(void)
CKsSplitterBranch::
Connect(
IN PIKSTRANSPORT NewTransport OPTIONAL,
OUT PIKSTRANSPORT *OldTransport OPTIONAL,
OUT PIKSTRANSPORT *BranchTransport OPTIONAL,
IN KSPIN_DATAFLOW DataFlow
)
/*++
Routine Description:
This routine establishes a transport connection.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::Connect]"));
PAGED_CODE();
if (BranchTransport) {
if (! m_Splitter) {
*BranchTransport = NULL;
} else if (DataFlow == KSPIN_DATAFLOW_OUT) {
if (m_ListEntry.Flink != m_ListHead) {
*BranchTransport =
CONTAINING_RECORD(
m_ListEntry.Flink,
CKsSplitterBranch,
m_ListEntry);
} else {
*BranchTransport = NULL;
}
} else {
if (m_ListEntry.Blink != m_ListHead) {
*BranchTransport =
CONTAINING_RECORD(
m_ListEntry.Blink,
CKsSplitterBranch,
m_ListEntry);
} else {
*BranchTransport = NULL;
}
}
}
KspStandardConnect(
NewTransport,
OldTransport,
NULL,
DataFlow,
PIKSTRANSPORT(this),
&m_TransportSource,
&m_TransportSink);
}
STDMETHODIMP_(NTSTATUS)
CKsSplitterBranch::
SetDeviceState(
IN KSSTATE NewState,
IN KSSTATE OldState,
IN PIKSTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles notification that the device state has changed.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### SplitBranch%p.SetDeviceState: set from %d to %d",this,OldState,NewState));
PAGED_CODE();
ASSERT(NextTransport);
//
// Direction is based on sign of the state delta.
//
if (NewState > OldState) {
//
// If there is a next branch, go to its sink, otherwise go to the
// splitter's sink.
//
if (m_ListEntry.Flink != m_ListHead) {
*NextTransport =
CONTAINING_RECORD(
m_ListEntry.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
} else {
*NextTransport = m_Splitter->GetTransportSink();
}
} else {
//
// If there is a next branch, go to its source, otherwise go to the
// splitter's source.
//
if (m_ListEntry.Blink != m_ListHead) {
*NextTransport =
CONTAINING_RECORD(
m_ListEntry.Blink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSource();
} else {
*NextTransport = m_Splitter->GetTransportSource();
}
}
return STATUS_SUCCESS;
}
STDMETHODIMP_(void)
CKsSplitterBranch::
GetTransportConfig(
OUT PKSPTRANSPORTCONFIG Config,
OUT PIKSTRANSPORT* NextTransport,
OUT PIKSTRANSPORT* PrevTransport
)
/*++
Routine Description:
This routine gets transport configuration information.
Arguments:
Config -
Contains a pointer to the location where configuration requirements
for this object should be depobranchd.
NextTransport -
Contains a pointer to the location at which the next transport
interface should be depobranchd.
PrevTransport -
Contains a pointer to the location at which the previous transport
interfaction should be depobranchd.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::GetTransportConfig]"));
PAGED_CODE();
ASSERT(Config);
ASSERT(NextTransport);
ASSERT(PrevTransport);
Config->TransportType = KSPTRANSPORTTYPE_SPLITTERBRANCH;
Config->IrpDisposition = KSPIRPDISPOSITION_NONE;
Config->StackDepth = 1;
if (m_ListEntry.Blink != m_ListHead) {
*PrevTransport =
CONTAINING_RECORD(
m_ListEntry.Blink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSource();
} else {
Config->StackDepth = KSPSTACKDEPTH_FIRSTBRANCH;
*PrevTransport = m_Splitter->GetTransportSource();
}
if (m_ListEntry.Flink != m_ListHead) {
*NextTransport =
CONTAINING_RECORD(
m_ListEntry.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
} else {
Config->StackDepth = KSPSTACKDEPTH_LASTBRANCH;
*NextTransport = m_Splitter->GetTransportSink();
}
}
STDMETHODIMP_(void)
CKsSplitterBranch::
SetTransportConfig(
IN const KSPTRANSPORTCONFIG* Config,
OUT PIKSTRANSPORT* NextTransport,
OUT PIKSTRANSPORT* PrevTransport
)
/*++
Routine Description:
This routine sets transport configuration information.
Arguments:
Config -
Contains a pointer to the new configuration settings for this object.
NextTransport -
Contains a pointer to the location at which the next transport
interface should be depobranchd.
PrevTransport -
Contains a pointer to the location at which the previous transport
interfaction should be depobranchd.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::SetTransportConfig]"));
PAGED_CODE();
ASSERT(Config);
ASSERT(NextTransport);
ASSERT(PrevTransport);
#if DBG
if (Config->IrpDisposition == KSPIRPDISPOSITION_ROLLCALL) {
ULONG references = AddRef() - 1; Release();
DbgPrint(" Branch%p refs=%d\n",this,references);
} else
#endif
{
m_StackSize = Config->StackDepth;
ASSERT(m_StackSize);
}
if (m_ListEntry.Blink != m_ListHead) {
*PrevTransport =
CONTAINING_RECORD(
m_ListEntry.Blink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSource();
} else {
*PrevTransport = m_Splitter->GetTransportSource();
}
if (m_ListEntry.Flink != m_ListHead) {
*NextTransport =
CONTAINING_RECORD(
m_ListEntry.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
} else {
*NextTransport = m_Splitter->GetTransportSink();
}
}
STDMETHODIMP_(void)
CKsSplitterBranch::
ResetTransportConfig(
OUT PIKSTRANSPORT* NextTransport,
OUT PIKSTRANSPORT* PrevTransport
)
/*++
Routine Description:
Reset the transport configuration of the branch. This indicates that
something is wrong with the pipe and the previously set configuration is
no longer valid.
Arguments:
NextTransport -
Contains a pointer to the location at which the next transport
interface should be depobranchd.
PrevTransport -
Contains a pointer to the location at which the previous transport
interfaction should be depobranchd.
Return Value:
None
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::ResetTransportConfig]"));
PAGED_CODE ();
ASSERT (NextTransport);
ASSERT (PrevTransport);
m_StackSize = 0;
if (m_ListEntry.Blink != m_ListHead) {
*PrevTransport =
CONTAINING_RECORD(
m_ListEntry.Blink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSource();
} else {
*PrevTransport = m_Splitter->GetTransportSource();
}
if (m_ListEntry.Flink != m_ListHead) {
*NextTransport =
CONTAINING_RECORD(
m_ListEntry.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
} else {
*NextTransport = m_Splitter->GetTransportSink();
}
}
STDMETHODIMP_(void)
CKsSplitterBranch::
SetResetState(
IN KSRESET ksReset,
IN PIKSTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles notification that the reset state has changed.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_VERBOSE,("[CKsSplitterBranch::SetResetState] to %d",ksReset));
PAGED_CODE();
ASSERT(NextTransport);
//
// If there is a next branch, go to its sink, otherwise go to the
// splitter's sink.
//
if (m_ListEntry.Flink != m_ListHead) {
*NextTransport =
CONTAINING_RECORD(
m_ListEntry.Flink,
CKsSplitterBranch,
m_ListEntry)->GetTransportSink();
} else {
*NextTransport = m_Splitter->GetTransportSink();
}
}
#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif // ALLOC_PRAGMA
NTSTATUS
CKsSplitterBranch::
TransferSubframe(
IN PKSPSUBFRAME_HEADER SubframeHeader
)
/*++
Routine Description:
This routine transfers a subframe from a splitter branch.
Arguments:
SubframeHeader -
Contains a pointer to the header of the subframe to transfer.
Return Value:
STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::TransferSubframe]"));
ASSERT(SubframeHeader);
//
// Count stuff.
//
m_DataUsed += SubframeHeader->StreamHeader.DataUsed;
m_FrameExtent += SubframeHeader->StreamHeader.FrameExtent;
m_Irps++;
//
// Get an IRP from the branch's lookaside list.
//
PLIST_ENTRY listEntry =
ExInterlockedRemoveHeadList(
&m_IrpsAvailable.ListEntry,
&m_IrpsAvailable.SpinLock);
PIRP irp;
if (listEntry) {
irp = CONTAINING_RECORD(listEntry,IRP,Tail.Overlay.ListEntry);
} else {
//
// Create the IRP now.
//
irp = AllocateIrp();
if (! irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Branch%p.TransferSubframe: allocated IRP %p",this,irp));
}
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0;
irp->PendingReturned = 0;
irp->Cancel = 0;
//
// Transfer the IRP to the next component.
//
SubframeHeader->Irp = irp;
irp->AssociatedIrp.SystemBuffer =
irp->UserBuffer =
&SubframeHeader->StreamHeader;
IoGetCurrentIrpStackLocation(irp)->
Parameters.DeviceIoControl.OutputBufferLength =
SubframeHeader->StreamHeader.Size;
InterlockedIncrement(PLONG(&m_OutstandingIrpCount));
KsLog(&m_Log,KSLOGCODE_BRANCH_SEND,irp,NULL);
return KspTransferKsIrp(m_TransportSink,irp);
}
PIRP
CKsSplitterBranch::
AllocateIrp(
void
)
/*++
Routine Description:
This routine allocates a new IRP for subframe transfer.
Arguments:
None.
Return Value:
The allocated IRP or NULL if an IRP could not be allocated.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsSplitterBranch::AllocateIrp]"));
ASSERT(m_StackSize);
PIRP irp = IoAllocateIrp(CCHAR(m_StackSize),FALSE);
if (irp) {
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Branch%p.AllocateIrp: %p",this,irp));
irp->RequestorMode = KernelMode;
irp->Flags = IRP_NOCACHE;
//
// Set the stack pointer to the first location and fill it in.
//
IoSetNextIrpStackLocation(irp);
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpSp->Parameters.DeviceIoControl.IoControlCode = m_IoControlCode;
}
return irp;
}
#endif