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

2230 lines
70 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:
shpipe.cpp
Abstract:
This module contains the implementation of the kernel streaming
pipe section object.
Author:
Dale Sather (DaleSat) 31-Jul-1998
--*/
#ifndef __KDEXT_ONLY__
#include "ksp.h"
#include <kcom.h>
#endif // __KDEXT_ONLY__
#if (DBG)
#define _DbgPrintFail(status, lvl, strings) \
{ \
if ((! NT_SUCCESS(status)) && ((lvl) <= DEBUG_VARIABLE)) {\
DbgPrint(STR_MODULENAME);\
DbgPrint##strings;\
DbgPrint("\n");\
if ((lvl) == DEBUGLVL_ERROR) {\
DbgBreakPoint();\
} \
} \
}
#else // !DBG
#define _DbgPrintFail(status, lvl, strings)
#endif // !DBG
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif // ALLOC_DATA_PRAGMA
#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA
//
// CKsPipeSection is the implementation of the kernel pipe section
// object.
//
class CKsPipeSection:
public IKsPipeSection,
public CBaseUnknown
{
#ifndef __KDEXT_ONLY__
private:
#else // __KDEXT_ONLY__
public:
#endif // __KDEXT_ONLY__
PVOID m_Id;
PIKSFILTER m_Filter;
PIKSDEVICE m_Device;
KSSTATE m_DeviceState;
KSRESET m_ResetState;
KSPPROCESSPIPESECTION m_ProcessPipeSection;
PIKSPIN m_MasterPin;
BOOLEAN m_ConfigurationSet;
BOOLEAN m_EmergencyShutdown;
NTSTATUS
GetTransport(
IN PKSPPROCESSPIN ProcessPin OPTIONAL,
OUT PIKSTRANSPORT* Transport,
IN OUT PFILE_OBJECT* Allocator,
IN OUT PIKSRETIREFRAME* RetireFrame,
IN OUT PIKSIRPCOMPLETION* IrpCompletion,
IN const KSALLOCATOR_FRAMING_EX* AllocatorFramingIn OPTIONAL,
IN const KSALLOCATOR_FRAMING_EX** AllocatorFramingOut OPTIONAL
);
BOOLEAN
IsCircuitComplete(
OUT PIKSTRANSPORT* Top
);
NTSTATUS
ConfigureCompleteCircuit(
IN PIKSTRANSPORT Top,
IN PIKSTRANSPORT Next OPTIONAL
);
NTSTATUS
UnconfigureCompleteCircuit(
IN PIKSTRANSPORT Top
);
NTSTATUS
DistributeDeviceStateChange(
IN PIKSTRANSPORT Transport,
IN KSSTATE NewState,
IN KSSTATE OldState
);
public:
DEFINE_STD_UNKNOWN();
IMP_IKsPipeSection;
CKsPipeSection(PUNKNOWN OuterUnknown):
CBaseUnknown(OuterUnknown)
{
}
~CKsPipeSection();
NTSTATUS
Init(
IN PVOID PipeId,
IN PIKSPIN Pin,
IN PIKSFILTER Filter,
IN PIKSDEVICE Device
);
};
void
DisconnectCircuit(
IN PIKSTRANSPORT Transport
);
#ifndef __KDEXT_ONLY__
IMPLEMENT_STD_UNKNOWN(CKsPipeSection)
NTSTATUS
KspCreatePipeSection(
IN PVOID PipeId,
IN PIKSPIN Pin,
IN PIKSFILTER Filter,
IN PIKSDEVICE Device
)
/*++
Routine Description:
This routine creates a pipe section object. This routine is called by the
first pin in a given filter in a given pipe section to make the transition
from stop state to acquire state. When other pins in the pipe section make
the transition, they refrain from calling this function upon determining
that they are already associated with a pipe section (the one we are
creating here). If the pin's PipeId is NULL, this indicates that the pin
was not assigned a pipe ID by the graph builder, and the pin has its own
pipe section.
The filter's control mutex must be acquired before this function is called.
Arguments:
Return Value:
Status.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[KspCreatePipeSection]"));
PAGED_CODE();
ASSERT(Pin);
ASSERT(Filter);
ASSERT(Device);
CKsPipeSection *pipeSection =
new(NonPagedPool,POOLTAG_PIPESECTION) CKsPipeSection(NULL);
NTSTATUS status;
if (pipeSection) {
pipeSection->AddRef();
status = pipeSection->Init(PipeId,Pin,Filter,Device);
pipeSection->Release();
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
STDMETHODIMP_(NTSTATUS)
CKsPipeSection::
NonDelegatedQueryInterface(
IN REFIID InterfaceId,
OUT PVOID * InterfacePointer
)
/*++
Routine Description:
This routine obtains an interface on a queue object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::NonDelegatedQueryInterface]"));
PAGED_CODE();
ASSERT(InterfacePointer);
NTSTATUS status = STATUS_SUCCESS;
if (IsEqualGUIDAligned(InterfaceId,__uuidof(IKsPipeSection))) {
*InterfacePointer = reinterpret_cast<PVOID>(static_cast<PIKSPIPESECTION>(this));
AddRef();
} else {
status = CBaseUnknown::NonDelegatedQueryInterface(InterfaceId,InterfacePointer);
}
return status;
}
void
AddTransport(
IN PIKSTRANSPORT TransportToAdd OPTIONAL,
IN OUT PIKSTRANSPORT *FirstTransport,
IN OUT PIKSTRANSPORT *LastTransport
)
/*++
Routine Description:
This routine adds a transport to a list of transports.
Arguments:
TransportToAdd -
Contains a pointer to the transport to add to the list. If this
argument is NULL, no action should be taken.
FirstTransport -
Contains a pointer to the location at which the first transport is to
be deposited. If the the list is empty, *FirstTransport is NULL. If
a transport is added to an empty list, *FirstTransport points to the
added transport. Otherwise, *FirstTransport is unchanged.
LastTransport -
Contains a pointer to the location at which the last transport is to
be deposited. If the list is empty, *LastTransport is NULL. When a
transport is added to the list, *LastTransport points to the added
transport.
Return Value:
None.
--*/
{
if (TransportToAdd) {
if (*LastTransport) {
(*LastTransport)->Connect(TransportToAdd,NULL,NULL,KSPIN_DATAFLOW_OUT);
} else {
*FirstTransport = TransportToAdd;
}
*LastTransport = TransportToAdd;
}
}
NTSTATUS
CKsPipeSection::
Init(
IN PVOID PipeId,
IN PIKSPIN Pin,
IN PIKSFILTER Filter,
IN PIKSDEVICE Device
)
/*++
Routine Description:
This routine initializes a pipe section object. This includes locating all
the pins associated with the pipe section, setting the PipeSection and
NextPinInPipeSection pointers in the appropriate pin structures, setting
all the fields in the pipe section structure and building the transport
circuit for the pipe section. The pipe section and the associated
components are left in acquire state.
The filter's control mutex must be acquired before this function is called.
Arguments:
Return Value:
Status.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::Init]"));
_DbgPrintF(DEBUGLVL_LIFETIME,("#### Pipe%p.Init: filter %p",this,Filter));
PAGED_CODE();
ASSERT(Pin);
ASSERT(Filter);
ASSERT(Device);
m_Id = PipeId;
m_Filter = Filter;
m_Device = Device;
m_DeviceState = KSSTATE_STOP;
m_ProcessPipeSection.PipeSection = this;
InitializeListHead(&m_ProcessPipeSection.CopyDestinations);
m_ProcessPipeSection.CopyPinId = ULONG(-1);
//
// Look for pins in the pipe section.
//
PKSGATE andGate;
NTSTATUS status =
m_Filter->BindProcessPinsToPipeSection(
&m_ProcessPipeSection,
m_Id,
m_Id ? NULL : Pin->GetStruct(),
&m_MasterPin,
&andGate);
//
// Get transport interfaces for input and ouput.
//
PFILE_OBJECT allocator = NULL;
PIKSRETIREFRAME retireFrame = NULL;
PIKSIRPCOMPLETION irpCompletion = NULL;
const KSALLOCATOR_FRAMING_EX* allocatorFraming = NULL;
PIKSTRANSPORT outTransport = NULL;
if (NT_SUCCESS(status)) {
status =
GetTransport(
m_ProcessPipeSection.Outputs,
&outTransport,
&allocator,
&retireFrame,
&irpCompletion,
NULL,
&allocatorFraming);
} else {
andGate = NULL;
}
PIKSTRANSPORT inTransport = NULL;
if (NT_SUCCESS(status)) {
status =
GetTransport(
m_ProcessPipeSection.Inputs,
&inTransport,
&allocator,
&retireFrame,
&irpCompletion,
allocatorFraming,
NULL);
}
//
// Create the queue if one is needed.
//
if (NT_SUCCESS(status) && ! (allocator && retireFrame)) {
//
// First figure out who will be processing.
//
const KSPIN_DESCRIPTOR_EX* pinDescriptor =
m_MasterPin->GetStruct()->Descriptor;
const KSFILTER_DESCRIPTOR* filterDescriptor =
m_Filter->GetStruct()->Descriptor;
//
// NOTE:
//
// This used to be done per pipe section by having pin processing
// dispatches or retirement callbacks override filter processing.
// Now, a filter is either filter centric or pin centric. Placing
// a filter process dispatch makes it filter centric. If the pins
// specify pin dispatches when the filter does, an assert will fire.
//
PIKSPROCESSINGOBJECT processingObject;
if (filterDescriptor->Dispatch &&
filterDescriptor->Dispatch->Process) {
//
// Ensure that the pin isn't trying to override the filter. It
// will fail to do so, but we should put a debug message so that
// clients know why the pin is getting overriden.
//
if (retireFrame ||
(pinDescriptor->Dispatch && pinDescriptor->Dispatch->Process)) {
_DbgPrintF(DEBUGLVL_ERROR,("#### Pipe%p.Init: pin%p wants to process as does filter. Pin processing ignored."));
}
//
// The filter has a process function, so it will process.
//
m_Filter->QueryInterface(
__uuidof(IKsProcessingObject),
(PVOID*)&processingObject);
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.Init: filter%p will process",this,m_Filter));
} else
if (retireFrame ||
(pinDescriptor->Dispatch && pinDescriptor->Dispatch->Process)) {
//
// The pin has a process function, so it will process.
//
m_MasterPin->QueryInterface(
__uuidof(IKsProcessingObject),
(PVOID*)&processingObject);
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.Init: pin%p will process",this,m_MasterPin));
} else {
//
// No processing function was found, so no queue.
//
processingObject = NULL;
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.Init: no processing object - no queue will be constructed",this));
}
if (processingObject) {
PADAPTER_OBJECT adapterObject;
ULONG maxMappingByteCount;
ULONG mappingTableStride;
m_Device->GetAdapterObject(
&adapterObject,
&maxMappingByteCount,
&mappingTableStride);
PKSPPROCESSPIN processPin = m_MasterPin->GetProcessPin();
//
// Make certain that minidrivers performing DMA have registered
// a valid DMA adapter and settings with the device. Otherwise,
// the queue won't be happy
//
if ((processPin->Pin->Descriptor->Flags &
KSPIN_FLAG_GENERATE_MAPPINGS) &&
(adapterObject == NULL ||
maxMappingByteCount == 0 ||
mappingTableStride < sizeof (KSMAPPING))) {
//
// We can't build the pipe if the queue is performing DMA
// and the adapter isn't registered yet.
//
status = STATUS_INVALID_DEVICE_REQUEST;
}
else {
//
// GFX:
//
// Enforce FIFO on any input pipe if we're frame holding and
// that pipe is not involved in any inplace transform.
//
ULONG ForceFlags = 0;
if (m_Filter->IsFrameHolding() &&
processPin->Pin->DataFlow == KSPIN_DATAFLOW_IN &&
m_ProcessPipeSection.Outputs == NULL) {
ForceFlags = KSPIN_FLAG_ENFORCE_FIFO;
}
status =
KspCreateQueue(
&m_ProcessPipeSection.Queue,
processPin->Pin->Descriptor->Flags | ForceFlags,
this,
processingObject,
processPin->Pin,
processPin->FrameGate,
processPin->FrameGateIsOr,
processPin->StateGate,
m_Device,
m_Device->GetStruct()->FunctionalDeviceObject,
adapterObject,
maxMappingByteCount,
mappingTableStride,
inTransport != NULL,
outTransport != NULL);
_DbgPrintFail(status,DEBUGLVL_TERSE,("#### Pipe%p.Init: KspCreateQueue failed (%p)",this,status));
//
// For pin-centric splitting, once we have finished creating
// the copy source queue, have the filter register for
// callbacks on it.
//
// Only call this if this pipe section is the copy source
// section.
//
if (NT_SUCCESS (status) &&
m_ProcessPipeSection.CopyPinId != ULONG(-1)) {
m_Filter -> RegisterForCopyCallbacks (
m_ProcessPipeSection.Queue,
TRUE
);
}
}
processingObject->Release();
}
}
//
// SYSAUDIO HACK TO RUN WITHOUT AN ALLOCATOR.
//
if (NT_SUCCESS(status) && (! allocator) && (! retireFrame)) {
//if (NT_SUCCESS(status) && (! retireFrame)) {
allocator = PFILE_OBJECT(-1);
for(PKSPPROCESSPIN processPin = m_ProcessPipeSection.Inputs;
processPin && allocator;
processPin = processPin->Next) {
if (processPin->Pin->Communication == KSPIN_COMMUNICATION_SINK) {
allocator = NULL;
}
}
for(processPin = m_ProcessPipeSection.Outputs;
processPin && allocator;
processPin = processPin->Next) {
if (processPin->Pin->Communication == KSPIN_COMMUNICATION_SINK) {
allocator = NULL;
}
}
}
//
// Create the requestor if one is needed.
//
if (NT_SUCCESS(status) && allocator) {
status =
KspCreateRequestor(
&m_ProcessPipeSection.Requestor,
this,
m_MasterPin,
retireFrame ? NULL : allocator,
retireFrame,
irpCompletion);
_DbgPrintFail(status,DEBUGLVL_TERSE,("#### Pipe%p.Init: KspCreateRequestor failed (%p)",this,status));
}
//
// Connect the circuit for this pipe section.
//
if (NT_SUCCESS(status)) {
ASSERT(m_ProcessPipeSection.Queue || m_ProcessPipeSection.Requestor);
PIKSTRANSPORT firstTransport = NULL;
PIKSTRANSPORT lastTransport = NULL;
AddTransport(m_ProcessPipeSection.Queue,&firstTransport,&lastTransport);
AddTransport(outTransport,&firstTransport,&lastTransport);
AddTransport(m_ProcessPipeSection.Requestor,&firstTransport,&lastTransport);
AddTransport(inTransport,&firstTransport,&lastTransport);
AddTransport(firstTransport,&firstTransport,&lastTransport);
}
#if (DBG)
if (DEBUGLVL_PIPES <= DEBUG_VARIABLE) {
_DbgPrintF(DEBUGLVL_TERSE,("TRANSPORT CIRCUIT FOR PIPE %p (%p) BEFORE BYPASS",m_Id,this));
DbgPrintCircuit(m_ProcessPipeSection.Requestor ? PIKSTRANSPORT(m_ProcessPipeSection.Requestor) : PIKSTRANSPORT(m_ProcessPipeSection.Queue),0,0);
}
#endif //DBG
//
// Tell all the pins to bypass.
//
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.Init: bypassing pins",this));
BOOLEAN completeIntraPipe = TRUE;
if (NT_SUCCESS(status)) {
for(PKSPPROCESSPIN processPin = m_ProcessPipeSection.Inputs;
processPin;
processPin = processPin->Next) {
if (! NT_SUCCESS(KspPinInterface(processPin->Pin)->AttemptBypass())) {
completeIntraPipe = FALSE;
}
}
for(processPin = m_ProcessPipeSection.Outputs;
processPin;
processPin = processPin->Next) {
if (! NT_SUCCESS(KspPinInterface(processPin->Pin)->AttemptBypass())) {
completeIntraPipe = FALSE;
}
}
}
#if (DBG)
if (DEBUGLVL_PIPES <= DEBUG_VARIABLE) {
_DbgPrintF(DEBUGLVL_TERSE,("TRANSPORT CIRCUIT FOR PIPE %p (%p) AFTER BYPASS",m_Id,this));
DbgPrintCircuit(m_ProcessPipeSection.Requestor ? PIKSTRANSPORT(m_ProcessPipeSection.Requestor) : PIKSTRANSPORT(m_ProcessPipeSection.Queue),0,0);
}
#endif //DBG
//
// Allow processing now that we are done setting up.
//
if (andGate) {
KsGateRemoveOffInputFromAnd(andGate);
_DbgPrintF(DEBUGLVL_PROCESSINGCONTROL,("#### Pipe%p.Init: on%p-->%d",this,andGate,andGate->Count));
}
//
// Determine if we have a complete intra- pipe.
//
PIKSTRANSPORT top;
if (NT_SUCCESS(status) &&
completeIntraPipe &&
IsCircuitComplete(&top)) {
//
// Make sure we have a top component.
//
if (! top) {
status = STATUS_UNSUCCESSFUL;
_DbgPrintFail(status,DEBUGLVL_TERSE,("#### Pipe%p.Init: no top (%p)",this,status));
} else {
//
// Configure and acquire the circuit if it is complete.
//
_DbgPrintF(DEBUGLVL_TERSE,("TRANSPORT CIRCUIT FOR PIPE %p (%p)",m_Id,this));
DbgPrintCircuit(m_ProcessPipeSection.Requestor ? PIKSTRANSPORT(m_ProcessPipeSection.Requestor) : PIKSTRANSPORT(m_ProcessPipeSection.Queue),0,1);
status = ConfigureCompleteCircuit(top,NULL);
_DbgPrintFail(status,DEBUGLVL_TERSE,("#### Pipe%p.Init: ConfigureCompleteCircuit failed (%p)",this,status));
if (NT_SUCCESS(status)) {
status = DistributeDeviceStateChange(top,KSSTATE_ACQUIRE,KSSTATE_STOP);
_DbgPrintFail(status,DEBUGLVL_TERSE,("#### Pipe%p.Init: DistributeDeviceStateChange failed (%p)",this,status));
}
if (NT_SUCCESS(status)) {
m_DeviceState = KSSTATE_ACQUIRE;
}
}
}
if (! NT_SUCCESS(status)) {
_DbgPrintF(DEBUGLVL_TERSE,("#### Pipe%p.Init failed (%p)",this,status));
PIKSTRANSPORT Top;
if (IsCircuitComplete (&Top)) {
//
// If the graph builder screws up, Top can be NULL when
// IsCircuitComplete returns true.
//
if (Top) {
ASSERT (Top == top);
//
// If the circuit is complete, we completed the circuit and
// configured it. We need to unset the configuration set
// field of any other section in the circuit. Otherwise, it's
// possible that some in error builder can set the in charge
// pin to pause and we'll succeed it. The circuit is NO LONGER
// CONFIGURED at this point. Fix it up.
//
NTSTATUS UnconfigStatus = UnconfigureCompleteCircuit (Top);
ASSERT (NT_SUCCESS (UnconfigStatus));
}
}
//
// Unbypass all the pins that were bypassed in this pipe section.
// Otherwise, we tear down the entire circuit when one pin related to
// the circuit fails to acquire. Doing this would prevent us from
// going to pause without stopping every pin in the circuit or
// rebuilding the circuit later.
//
// Must do this before the unbind, otherwise the pins are already gone
// out of the lists.
//
// NOTE: This is the the only guaranteed safe location to do the
// unbypass because the pins keep unrefcounted pointers on the
// pre-bypass circuit elements. Those are only guaranteed to be safe
// to use while we're here.
//
for(PKSPPROCESSPIN processPin = m_ProcessPipeSection.Inputs;
processPin;
processPin = processPin->Next) {
KspPinInterface(processPin->Pin)->AttemptUnbypass();
}
for(processPin = m_ProcessPipeSection.Outputs;
processPin;
processPin = processPin->Next) {
KspPinInterface(processPin->Pin)->AttemptUnbypass();
}
m_Filter->UnbindProcessPinsFromPipeSection(&m_ProcessPipeSection);
//
// Dereference the queue if there is one.
//
if (m_ProcessPipeSection.Queue) {
DisconnectCircuit(m_ProcessPipeSection.Queue);
m_ProcessPipeSection.Queue->Release();
m_ProcessPipeSection.Queue = NULL;
}
//
// Dereference the requestor if there is one.
//
if (m_ProcessPipeSection.Requestor) {
DisconnectCircuit(m_ProcessPipeSection.Requestor);
m_ProcessPipeSection.Requestor->Release();
m_ProcessPipeSection.Requestor = NULL;
}
}
//
// GetTransport returns AddRef()ed transports, so release them here. If
// they are connected properly, they will not go away.
//
if (inTransport) {
inTransport->Release();
}
if (outTransport) {
outTransport->Release();
}
if (retireFrame) {
retireFrame->Release();
}
return status;
}
NTSTATUS
CKsPipeSection::
GetTransport(
IN PKSPPROCESSPIN ProcessPin OPTIONAL,
OUT PIKSTRANSPORT *Transport,
IN OUT PFILE_OBJECT *Allocator,
IN OUT PIKSRETIREFRAME *RetireFrame,
IN OUT PIKSIRPCOMPLETION *IrpCompletion,
IN const KSALLOCATOR_FRAMING_EX* AllocatorFramingIn OPTIONAL,
IN const KSALLOCATOR_FRAMING_EX ** AllocatorFramingOut OPTIONAL
)
/*++
Routine Description:
This routine gets a single transport interface for the input or output side
of a pipe section. The resulting transport may be NULL if there are no
pins with the indicated data flow. If there is one such pin, the resulting
transport will be the transport interface for that pin. If there are more
than one such pin, the resulting transport will be the transport interface
of a splitter that combines all the pins.
Arguments:
ProcessPin -
Contains a pointer to the first process pin in a list of input or
output process pins for which a single transport is to be obtained.
If this argument is NULL, the resulting transport will be NULL.
Transport -
Contains a pointer to the location at which the transport is to
be deposited. The transport is referenced on behalf of the caller,
and a matching Release() must occur at some point.
Allocator -
Contains a pointer to the location at which to deposit an allocator
file object if a qualifying pin has been assigned an allocator.
*Allocator is not modified if no such pin is found.
RetireFrame -
Contains a pointer to the location at which to deposit a retire frame
interface if a qualifying pin produces one. *RetireFrame is not
modified if no such pin is found.
IrpCompletion -
Contains a pointer to the location at which to deposit an Irp completion
callback interface if a qualifying pin produces one. *CompleteIrp is
not modified if no such pin is found.
AllocatorFramingIn -
Contains an optional pointer to allocator framing information to be
used in deciding how to set up the transport. In particular, this
information is used in setting up input transport to determine whether
a splitter is required for expansion filters.
AllocatorFramingOut -
Contains an optional pointer to a location at which a pointer to
allocator framing information is to be deposited. This information
is provided during the construction of output transports to inform
the construction of the corresponding input transport.
Return Value:
STATUS_SUCCESS or an error code from KspCreateSplitter().
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::GetTransport]"));
PAGED_CODE();
ASSERT(Transport);
ASSERT(Allocator);
ASSERT(RetireFrame);
PIKSTRANSPORT transport = NULL;
PIKSSPLITTER splitter = NULL;
PKSPIN firstPin;
NTSTATUS status = STATUS_SUCCESS;
for (; ProcessPin; ProcessPin = ProcessPin->Next) {
PKSPIN pin = ProcessPin->Pin;
PIKSPIN pinInterface = KspPinInterface(pin);
//
// Check it to see if the pin wants to submit frames.
//
if (ProcessPin->RetireFrameCallback) {
ASSERT(! *RetireFrame);
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.GetTransport: pin%p will submit frames",this,pinInterface));
pinInterface->QueryInterface(__uuidof(IKsRetireFrame),(PVOID *) RetireFrame);
} else {
//
// Check it to see if the pin wants to be notified of Irps returning
// to the requestor.
//
if (ProcessPin->IrpCompletionCallback) {
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.GetTransport: pin%p wants to be notified of Irp completion to a requestor",this,pinInterface));
pinInterface->QueryInterface(__uuidof(IKsIrpCompletion),(PVOID *) IrpCompletion);
}
//
// Check it to see if the pin wants to allocate.
//
if (ProcessPin->AllocatorFileObject) {
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.GetTransport: pin%p has allocator",this,pinInterface));
*Allocator = ProcessPin->AllocatorFileObject;
}
}
if (! transport) {
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.GetTransport: first pin%p",this,pinInterface));
//
// This is the first pin.
//
firstPin = pin;
transport = pinInterface;
transport->AddRef();
if (AllocatorFramingOut) {
*AllocatorFramingOut =
pin->Descriptor->AllocatorFraming;
}
//
// For expansion, we will require a splitter.
//
if (AllocatorFramingIn &&
(AllocatorFramingIn->OutputCompression.RatioNumerator >
AllocatorFramingIn->OutputCompression.RatioDenominator)) {
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.GetTransport: first pin%p expansion",this,pinInterface));
status = KspCreateSplitter(&splitter,pin);
transport->Release();
if (NT_SUCCESS(status)) {
transport = splitter;
splitter->AddBranch(firstPin,AllocatorFramingIn);
} else {
break;
}
}
} else {
if (! splitter) {
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.GetTransport: second pin%p",this,pinInterface));
//
// This is the second pin, so we need to make a splitter.
//
status = KspCreateSplitter(&splitter,pin);
transport->Release();
if (NT_SUCCESS(status)) {
transport = splitter;
splitter->AddBranch(firstPin,AllocatorFramingIn);
} else {
break;
}
} else {
_DbgPrintF(DEBUGLVL_PIPES,("#### Pipe%p.GetTransport: third+ pin%p",this,pinInterface));
}
splitter->AddBranch(pin,AllocatorFramingIn);
}
}
if (NT_SUCCESS(status)) {
*Transport = transport;
}
return status;
}
BOOLEAN
CKsPipeSection::
IsCircuitComplete(
OUT PIKSTRANSPORT* Top
)
/*++
Routine Description:
This routine determines if the pipe circuit is complete.
Arguments:
None.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::IsCircuitComplete(%p)]"));
PAGED_CODE();
ASSERT(Top);
PIKSTRANSPORT top = NULL;
BOOLEAN completeIntraPipe = TRUE;
PIKSTRANSPORT transportStart =
m_ProcessPipeSection.Requestor ? PIKSTRANSPORT(m_ProcessPipeSection.Requestor) : PIKSTRANSPORT(m_ProcessPipeSection.Queue);
PIKSTRANSPORT transport = transportStart;
while (1) {
if (! transport) {
completeIntraPipe = FALSE;
top = NULL;
break;
}
//
// Get the configuration from this component.
//
KSPTRANSPORTCONFIG config;
PIKSTRANSPORT nextTransport;
PIKSTRANSPORT prevTransport;
transport->GetTransportConfig(
&config,
&nextTransport,
&prevTransport);
//
// If we find any intra-pins, the circuit is not complete. All intra-
// pins will be bypassed when the circuit is complete. We can pass
// this back as the top component anyway because this function is
// reused for emergency shutdown. The FALSE return indicates this
// is may not really be the top.
//
if (config.TransportType & KSPTRANSPORTTYPE_PININTRA) {
completeIntraPipe = FALSE;
if (! top) {
top = transport;
}
break;
}
//
// Requestors and non-intra sink pins qualify as top components. Both
// are frame sources in the circuit.
//
if ((config.TransportType & KSPTRANSPORTTYPE_PINSINK) ||
(config.TransportType == KSPTRANSPORTTYPE_REQUESTOR)) {
if (top) {
_DbgPrintF(DEBUGLVL_TERSE,("#### Pipe%p.IsCircuitComplete: more than one 'top' component (%p and %p)",this,top,transport));
_DbgPrintF(DEBUGLVL_TERSE,("#### There should be exactly one top component in a given pipe. Top components"));
_DbgPrintF(DEBUGLVL_TERSE,("#### correspond to pins with allocators assigned to them and sink pins which"));
_DbgPrintF(DEBUGLVL_TERSE,("#### are not connected to a pin implemented by KS. This error occurred because"));
_DbgPrintF(DEBUGLVL_TERSE,("#### the graph builder put more than one of these in a single pipe."));
DbgPrintCircuit(transportStart,0,0);
*Top = NULL;
return TRUE;
}
top = transport;
}
transport = nextTransport;
//
// Stop when we've returned to our starting point.
//
if (transport == transportStart) {
break;
}
}
*Top = top;
//
// Make sure we have a top component.
//
if (completeIntraPipe && ! top) {
_DbgPrintF(DEBUGLVL_TERSE,("#### Pipe%p.IsCircuitComplete: no 'top' component",this));
_DbgPrintF(DEBUGLVL_TERSE,("#### There should be exactly one top component in a given pipe. Top components"));
_DbgPrintF(DEBUGLVL_TERSE,("#### correspond to pins with allocators assigned to them and sink pins which"));
_DbgPrintF(DEBUGLVL_TERSE,("#### are not connected to a pin implemented by KS. This error occurred because"));
_DbgPrintF(DEBUGLVL_TERSE,("#### the graph builder did not put one of these in a pipe."));
DbgPrintCircuit(transportStart,0,0);
}
return completeIntraPipe;
}
void
DbgPrintConfig(
IN PKSPTRANSPORTCONFIG Config,
IN BOOLEAN Set
)
{
#if DBG
if (DEBUGLVL_CONFIG > DEBUG_VARIABLE) {
return;
}
switch (Config->TransportType) {
case KSPTRANSPORTTYPE_QUEUE:
DbgPrint(" TransportType: QUEUE\n");
break;
case KSPTRANSPORTTYPE_REQUESTOR:
DbgPrint(" TransportType: REQUESTOR\n");
break;
case KSPTRANSPORTTYPE_SPLITTER:
DbgPrint(" TransportType: SPLITTER\n");
break;
case KSPTRANSPORTTYPE_SPLITTERBRANCH:
DbgPrint(" TransportType: SPLITTERBRANCH\n");
break;
default:
if (Config->TransportType & KSPTRANSPORTTYPE_PINEXTRA) {
DbgPrint(" TransportType: EXTRA ");
} else {
DbgPrint(" TransportType: INTRA ");
}
if (Config->TransportType & KSPTRANSPORTTYPE_PININPUT) {
DbgPrint("INPUT ");
} else {
DbgPrint("OUTPUT ");
}
if (Config->TransportType & KSPTRANSPORTTYPE_PINSOURCE) {
DbgPrint("SOURCE PIN\n");
} else {
DbgPrint("SINK PIN\n");
}
break;
}
DbgPrint(" IrpDisposition:");
if (Config->IrpDisposition == KSPIRPDISPOSITION_NONE) {
DbgPrint(" NONE");
} else {
if (Config->IrpDisposition & KSPIRPDISPOSITION_UNKNOWN) {
DbgPrint(" UNKNOWN");
}
if (Config->IrpDisposition & KSPIRPDISPOSITION_ISKERNELMODE) {
DbgPrint(" ISKERNELMODE");
}
if (Set) {
if (Config->IrpDisposition & KSPIRPDISPOSITION_USEMDLADDRESS) {
DbgPrint(" USEMDLADDRESS");
}
if (Config->IrpDisposition & KSPIRPDISPOSITION_CANCEL) {
DbgPrint(" CANCEL");
}
} else {
if (Config->TransportType == KSPTRANSPORTTYPE_QUEUE) {
if (Config->IrpDisposition & KSPIRPDISPOSITION_NEEDNONPAGED) {
DbgPrint(" NEEDNONPAGED");
}
if (Config->IrpDisposition & KSPIRPDISPOSITION_NEEDMDLS) {
DbgPrint(" NEEDMDLS");
}
} else {
if (Config->IrpDisposition & KSPIRPDISPOSITION_ISPAGED) {
DbgPrint(" ISPAGED");
}
if (Config->IrpDisposition & KSPIRPDISPOSITION_ISNONPAGED) {
DbgPrint(" ISNONPAGED");
}
}
}
if ((Config->IrpDisposition & KSPIRPDISPOSITION_PROBEFLAGMASK) == KSPIRPDISPOSITION_PROBEFORREAD) {
DbgPrint(" PROBEFORREAD");
} else
if ((Config->IrpDisposition & KSPIRPDISPOSITION_PROBEFLAGMASK) == KSPIRPDISPOSITION_PROBEFORWRITE) {
DbgPrint(" PROBEFORWRITE");
} else
if ((Config->IrpDisposition & KSPIRPDISPOSITION_PROBEFLAGMASK) == KSPIRPDISPOSITION_PROBEFORMODIFY) {
DbgPrint(" PROBEFORMODIFY");
} else {
if (Config->IrpDisposition & KSPROBE_STREAMWRITE) {
DbgPrint(" KSPROBE_STREAMWRITE");
}
if (Config->IrpDisposition & KSPROBE_ALLOCATEMDL) {
DbgPrint(" KSPROBE_ALLOCATEMDL");
}
if (Config->IrpDisposition & KSPROBE_PROBEANDLOCK) {
DbgPrint(" KSPROBE_PROBEANDLOCK");
}
if (Config->IrpDisposition & KSPROBE_SYSTEMADDRESS) {
DbgPrint(" KSPROBE_SYSTEMADDRESS");
}
if (Config->IrpDisposition & KSPROBE_MODIFY) {
DbgPrint(" KSPROBE_MODIFY");
}
if (Config->IrpDisposition & KSPROBE_ALLOWFORMATCHANGE) {
DbgPrint(" KSPROBE_ALLOWFORMATCHANGE");
}
}
}
DbgPrint("\n");
DbgPrint(" StackDepth: %d\n",Config->StackDepth);
#endif
}
NTSTATUS
CKsPipeSection::
UnconfigureCompleteCircuit(
IN PIKSTRANSPORT Top
)
/*++
Routine Description:
This routine unconfigures a completed circuit due to a failure in the
last stages of circuit acquisition.
Arguments:
Top -
Contains a pointer to the sink pin, requestor or splitter branch at
which unconfiguration will begin.
Return Value:
STATUS_SUCCESS.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::UnconfigureCompleteCircuit]"));
_DbgPrintF(DEBUGLVL_CONFIG,("#### Pipe%p.UnconfigureCompleteCircuit",this));
PAGED_CODE();
ASSERT(Top);
//
// NOTE: MUSTCHECK:
//
// This should be sufficient to hit everything in the circuit. This
// should be checked against a splitter with more than one branch to make
// sure I'm reading this correctly.
//
NTSTATUS status = STATUS_SUCCESS;
PIKSTRANSPORT transport = Top;
do {
PIKSTRANSPORT nextTransport;
PIKSTRANSPORT prevTransport;
transport -> ResetTransportConfig (
&nextTransport,
&prevTransport
);
transport = nextTransport;
} while (transport != Top);
return STATUS_SUCCESS;
}
NTSTATUS
CKsPipeSection::
ConfigureCompleteCircuit(
IN PIKSTRANSPORT Top,
IN PIKSTRANSPORT Next OPTIONAL
)
/*++
Routine Description:
This routine configures a complete transport circuit.
Arguments:
Top -
Contains a pointer to the sink pin, requestor or splitter branch at
which configuration will begin.
Next -
Contains a pointer to the component immediately following the top
component. If this argument is NULL, the pointer is obtained in
a GetTransportConfig() call to the Top component. This argument
should be supplied if and only if Top is a splitter branch.
Return Value:
STATUS_SUCCESS.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::ConfigureCompleteCircuit]"));
_DbgPrintF(DEBUGLVL_CONFIG,("#### Pipe%p.ConfigureCompleteCircuit",this));
PAGED_CODE();
ASSERT(Top);
class CActivation
{
public:
CActivation* m_NextActivation;
PIKSTRANSPORT m_Top;
PIKSTRANSPORT m_Next;
CActivation(
IN CActivation* NextActivation,
IN PIKSTRANSPORT Top,
IN PIKSTRANSPORT Next
) :
m_NextActivation(NextActivation),
m_Top(Top),
m_Next(Next)
{
}
};
CActivation* activations = NULL;
NTSTATUS status = STATUS_SUCCESS;
while (NT_SUCCESS(status)) {
//
// Get the configuration of the top component.
//
KSPTRANSPORTCONFIG topConfig;
PIKSTRANSPORT nextTransport;
PIKSTRANSPORT prevTransport;
Top->GetTransportConfig(
&topConfig,
&nextTransport,
&prevTransport);
_DbgPrintF(DEBUGLVL_CONFIG,("#### get config from top %p",Top));
DbgPrintConfig(&topConfig,FALSE);
KSPIRPDISPOSITION disposition;
KSPIRPDISPOSITION topDisposition = topConfig.IrpDisposition;
if (topConfig.TransportType == KSPTRANSPORTTYPE_REQUESTOR) {
ASSERT(! Next);
//
// The top is a requestor. Frames are allocated in kernel mode and
// are either paged or non-paged. Later, the requestor will need the
// max stack depth and probe flags. For now, we indicate the requestor
// will not need to probe. This may change as we look at the queues.
//
_DbgPrintF(DEBUGLVL_CONFIG,("#### top component is req%p",Top));
disposition = KSPIRPDISPOSITION_NONE;
topConfig.IrpDisposition = KSPIRPDISPOSITION_NONE;
} else if (topConfig.TransportType & KSPTRANSPORTTYPE_PINSINK) {
ASSERT(topConfig.TransportType & KSPTRANSPORTTYPE_PINEXTRA);
ASSERT(! Next);
//
// The top is an external sink pin. Because the frames are coming from
// a foreign source (not the ), we need to probe the IRP using
// KsProbeStreamIrp. Exactly how we will do that is based on data
// flow. In any case, we need to use the MDL's system address rather
// than the pointer in the header, and the IRP should be cancelled on
// a flush.
//
_DbgPrintF(DEBUGLVL_CONFIG,("#### top component is pin%p",Top));
disposition =
KSPIRPDISPOSITION_USEMDLADDRESS |
KSPIRPDISPOSITION_CANCEL;
if (topConfig.TransportType & KSPTRANSPORTTYPE_PINOUTPUT) {
//
// The top pin is an output. This means we will treat this like
// a read operation: we will want to write to (and possibly read
// from) the frame, and the header needs to get copied back.
//
disposition |= KSPIRPDISPOSITION_PROBEFORREAD;
} else if (nextTransport == prevTransport) {
//
// The top pin is an input, and there is just one queue. This
// means we will treat this like a write operation: we will want
// to read from the frame, and the header does not need to get
// copied back.
//
disposition |= KSPIRPDISPOSITION_PROBEFORWRITE;
} else {
//
// The top pin is an input, and there is more than just one queue.
// This means we will treat this like a modify operation: we will
// want to read from and write to the frame, and the header does
// not need to get copied back.
//
disposition |= KSPIRPDISPOSITION_PROBEFORMODIFY;
}
} else {
ASSERT(topConfig.TransportType == KSPTRANSPORTTYPE_SPLITTERBRANCH);
ASSERT(Next);
//
// The top is a splitter branch. We are configuring a branch of the
// pipe.
//
_DbgPrintF(DEBUGLVL_CONFIG,("#### top component is branch%p",Top));
disposition = KSPIRPDISPOSITION_NONE;
}
for(PIKSTRANSPORT transport = Next ? Next : nextTransport;
NT_SUCCESS(status) && (transport != Top);
transport = nextTransport) {
//
// Get the configuration from this component.
//
KSPTRANSPORTCONFIG config;
transport->GetTransportConfig(
&config,
&nextTransport,
&prevTransport);
_DbgPrintF(DEBUGLVL_CONFIG,("#### get config from component %p",transport));
DbgPrintConfig(&config,FALSE);
if (topConfig.StackDepth < config.StackDepth) {
topConfig.StackDepth = config.StackDepth;
}
switch (config.TransportType) {
case KSPTRANSPORTTYPE_PINSOURCE | KSPTRANSPORTTYPE_PINEXTRA | KSPTRANSPORTTYPE_PININPUT:
case KSPTRANSPORTTYPE_PINSOURCE | KSPTRANSPORTTYPE_PINEXTRA | KSPTRANSPORTTYPE_PINOUTPUT:
//
// This is an external source pin. We have its stack depth
// already.
//
break;
case KSPTRANSPORTTYPE_QUEUE:
//
// Queues may need to probe, and they need to be told whether to
// cancel and whether to use the MDL address.
//
if (topConfig.TransportType == KSPTRANSPORTTYPE_REQUESTOR) {
//
// The top component is a requestor. The queue will not need
// to probe, but the requestor may if the frames are paged or
// mdls are required.
//
if ((config.IrpDisposition & KSPIRPDISPOSITION_NEEDMDLS) ||
((config.IrpDisposition & KSPIRPDISPOSITION_NEEDNONPAGED) &&
(topDisposition & KSPIRPDISPOSITION_ISPAGED))) {
topConfig.IrpDisposition = KSPIRPDISPOSITION_PROBEFORMODIFY;
}
}
//
// Set the disposition on the queue.
//
config.IrpDisposition = disposition;
transport->SetTransportConfig(
&config,
&nextTransport,
&prevTransport);
_DbgPrintF(DEBUGLVL_CONFIG,(" set config to"));
DbgPrintConfig(&config,TRUE);
//
// Once the IRPs are probed, they don't need to be probed again.
//
disposition &= ~KSPIRPDISPOSITION_PROBEFLAGMASK;
break;
case KSPTRANSPORTTYPE_SPLITTER:
{
//
// Splitters may need to probe, and they need to be told whether to
// cancel and whether to use the MDL address. In addition, each
// branch of the splitter needs to be programmed as if it were its
// own circuit.
//
// Next and previous reporting through GetTransportConfig is a
// little complex in the splitter case. The intent is to traverse
// the perimeter of the pipe, whether in the Next or Prev direction.
// In a complete traversal of a circuit, a splitter is encountered
// N+1 times where N is the number of branches. Each time the
// splitter is encountered, the actual interface obtained is
// different. Working from the trunk, the _SPLITTER is encountered
// first, then the components on the first branch, then the
// _SPLITTER_BRANCH corresponding to the first branch, then the
// components on the second branch and so forth. After encountering
// the _SPLITTER_BRANCH for the last branch, traversal returns to
// the trunk.
//
// For a 2-branch splitter, the Next and Prev reporting is as
// follows:
//
// _SPLITTER:
// Next = _SPLITTER_BRANCH_1->Sink
// Prev = _SPLITTER_BRANCH_2->Source
// _SPLITTER_BRANCH1:
// Next = _SPLITTER_BRANCH_2->Sink
// Prev = _SPLITTER->Source
// _SPLITTER_BRANCH2:
// Next = _SPLITTER->Sink
// Prev = _SPLITTER_BRANCH_1->Source
//
// This is great if we want to walk the perimeter. This
// function wants to iteratively address the branches instead.
// When we hit a _SPLITTER, we want to enumerate the
// _SPLITTER_BRANCHes, using each one in turn as the Top of a
// new circuit. Because a branch's Next is not the first
// component in that branch, we specify Next as an argument
// rather than using _SPLITTER_BRANCH->Next.
//
// We iterate using the nextTransport pointer obtained from the
// _SPLITTER. This is not the _SPLITTER_BRANCH, but rather the
// first component in the branch. To get the _SPLITTER_BRANCH
// (the Top argument for the recursive call), we just get
// nextTransport->Prev. nextTransport is updated for the next
// iteration by obtaining top->Next, We stop after processing
// the last branch.
//
// TODO: Surely the _SPLITTER requires some IrpDisposition stuff.
while (1) {
//
// nextTransport points to the _SPLITTER_BRANCH's sink. Get
// nextTransport->Prev to obtain the _SPLITTER_BRANCH. This
// will be the Top argument for the recursive call.
//
PIKSTRANSPORT dontCare;
PIKSTRANSPORT top;
nextTransport->GetTransportConfig(
&config,
&dontCare,
&top);
//
// Save the 'recursive' call. We pass nextTransport because
// the _SPLITTER_BRANCH does not report this as its Next.
//
CActivation* activation =
new(PagedPool,POOLTAG_ACTIVATION)
CActivation(activations,top,nextTransport);
if (! activation) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
activations = activation;
//
// If there are more branches, top->Next is the first component
// in the next branch. If not, top->Next will be the next
// component on the trunk (_SPLITTER->Sink). In either case,
// nextTransport is the place to put it.
//
top->GetTransportConfig(
&config,
&nextTransport,
&dontCare);
_DbgPrintF(DEBUGLVL_CONFIG,(" get branch%p config",top));
DbgPrintConfig(&config,FALSE);
ASSERT(config.TransportType == KSPTRANSPORTTYPE_SPLITTERBRANCH);
//
// If the branch we processed last time is the last branch,
// it's time to stop iterating. nextTransport will be set
// to the first component on the trunk after the _SPLITTER.
// This is correct for the next iteration of the outer loop.
//
if (config.StackDepth == KSPSTACKDEPTH_LASTBRANCH) {
break;
}
}
break;
}
default:
_DbgPrintF(DEBUGLVL_ERROR,("#### Pipe%p.ConfigureCompleteCircuit: illegal component type %p",this,config.TransportType));
status = STATUS_UNSUCCESSFUL;
break;
}
}
if (NT_SUCCESS(status)) {
//
// The top component needs stack depth and, if it's a requestor, the
// IRP disposition.
//
Top->SetTransportConfig(
&topConfig,
&nextTransport,
&prevTransport);
_DbgPrintF(DEBUGLVL_CONFIG,(" set top component config to"));
DbgPrintConfig(&topConfig,TRUE);
} else {
//
// Error. Trash any pending activations.
//
while (activations) {
CActivation* activation = activations;
activations = activations->m_NextActivation;
delete activation;
}
}
if (! activations) {
break;
}
CActivation* activation = activations;
activations = activations->m_NextActivation;
Top = activation->m_Top;
Next = activation->m_Next;
delete activation;
} // while
return status;
}
CKsPipeSection::
~CKsPipeSection(
void
)
/*++
Routine Description:
This routine destructs a pipe object.
Arguments:
None.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::~CKsPipeSection(%p)]"));
_DbgPrintF(DEBUGLVL_LIFETIME,("#### Pipe%p.~",this));
PAGED_CODE();
}
void
DisconnectCircuit(
IN PIKSTRANSPORT Transport
)
/*++
Routine Description:
This routine disconnects a circuit.
Arguments:
Transport -
Contains a pointer to a component in the circuit to disconnect.
Return Value:
None.
--*/
{
//
// We are going to use Connect() to set the transport sink for each
// component in turn to NULL. Because Connect() takes care of the
// back links, transport source pointers for each component will
// also get set to NULL. Connect() gives us a referenced pointer
// to the previous transport sink for the component in question, so
// we will need to do a release for each pointer obtained in this
// way. For consistency's sake, we will release the pointer we
// start with (distribution) as well, so we need to AddRef it first.
//
Transport->AddRef();
while (Transport) {
PIKSTRANSPORT nextTransport;
PIKSTRANSPORT branchTransport;
Transport->Connect(NULL,&nextTransport,&branchTransport,KSPIN_DATAFLOW_OUT);
if (branchTransport) {
if (nextTransport) {
DisconnectCircuit(branchTransport);
} else {
nextTransport = branchTransport;
}
}
Transport->Release();
Transport = nextTransport;
}
}
void
CKsPipeSection::
UnbindProcessPins (
)
/*++
Routine Description:
Tells the pipe section to have the filter unbind any pins belonging
to this pipe section.
Arguments:
None
Return Value:
None
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::UnbindProcessPins(%p)]", this));
PAGED_CODE ();
m_Filter -> UnbindProcessPinsFromPipeSection (&m_ProcessPipeSection);
}
NTSTATUS
CKsPipeSection::
SetDeviceState(
IN PIKSPIN Pin OPTIONAL,
IN KSSTATE NewState
)
/*++
Routine Description:
This routine sets the state of the pipe, informing all components in the
pipe of the new state. A transition to stop state destroys the pipe.
Arguments:
Pin -
Contains a pointer to the pin supplying the state change request. The
request is ignored if this pin does not control the pipe state. If
this argument is NULL, the state change is distributed in any case.
This option is used for catastrophic shutdown of the circuit.
NewState -
The new state.
Return Value:
Status.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::SetDeviceState(%p)]",this));
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.SetDeviceState: from %d to %d",this,m_DeviceState,NewState));
PAGED_CODE();
//
// Ignore requests from any pin but the master.
//
if (Pin && (Pin != m_MasterPin)) {
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.SetDeviceState: ignoring because pin%p is not master pin%p",this,Pin,m_MasterPin));
return STATUS_SUCCESS;
}
KSSTATE state = m_DeviceState;
KSSTATE targetState = NewState;
NTSTATUS status = STATUS_SUCCESS;
//
// Determine if this pipe section controls the entire pipe.
//
PIKSTRANSPORT distribution;
if (! Pin) {
//
// No pin was supplied, so we are shutting down under emergency
// conditions. Find a requestor by hook or by crook.
//
IsCircuitComplete(&distribution);
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.SetDeviceState: got distribution from IsCircuitComplete: %p",this,distribution));
m_EmergencyShutdown = TRUE;
} else if (! m_ConfigurationSet) {
//
// The circuit is not complete.
//
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.SetDeviceState: circuit is not complete",this));
return STATUS_DEVICE_NOT_READY;
} else if (m_ProcessPipeSection.Requestor) {
//
// This section owns the requestor, so it does own the pipe, and the
// requestor is the starting point for any distribution.
//
distribution = m_ProcessPipeSection.Requestor;
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.SetDeviceState: req%p is distribution point",this,distribution));
} else if (ProcessPinIsFrameSource(m_MasterPin->GetProcessPin())) {
//
// This section is at the top of an open circuit, so it does own the
// pipe and the pin is the starting point for any distribution.
//
distribution = m_MasterPin;
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.SetDeviceState: queue%p is distribution point",this,distribution));
} else {
//
// This section does not own the pipe.
//
distribution = NULL;
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.SetDeviceState: no distribution point",this));
}
//
// Proceed sequentially through states.
//
while (state != targetState) {
KSSTATE oldState = state;
if (ULONG(state) < ULONG(targetState)) {
state = KSSTATE(ULONG(state) + 1);
} else {
state = KSSTATE(ULONG(state) - 1);
}
//
// If there is no queue, distribution to pin client callbacks will not
// be handled automatically for this pipe section. We do that part of
// the distribution here instead.
//
NTSTATUS statusThisPass;
if (m_ProcessPipeSection.Requestor && ! m_ProcessPipeSection.Queue) {
statusThisPass = DistributeStateChangeToPins(state,oldState);
} else {
statusThisPass = STATUS_SUCCESS;
}
//
// Distribute state changes if this section is in charge.
//
if (NT_SUCCESS(statusThisPass) && distribution) {
statusThisPass = DistributeDeviceStateChange(distribution,state,oldState);
}
if (NT_SUCCESS(status) && ! NT_SUCCESS(statusThisPass)) {
//
// First failure: go back to original state.
//
state = oldState;
targetState = m_DeviceState;
status = statusThisPass;
}
}
m_DeviceState = state;
//
// Clear the emergency shutdown flag just for safety.
//
m_EmergencyShutdown = FALSE;
if (state == KSSTATE_STOP) {
//
// Must disconnect the entire circuit. We'll start at the pin if the
// pin is not in charge.
//
if (! distribution) {
distribution = Pin;
}
if (distribution) {
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Pipe%p.SetDeviceState: disconnecting",this));
DisconnectCircuit(distribution);
}
//
// If there's no queue, unbind now; otherwise, the queue will be
// responsible for unbinding when it chooses.
//
if (!m_ProcessPipeSection.Queue)
m_Filter->UnbindProcessPinsFromPipeSection(&m_ProcessPipeSection);
//
// Take a reference around these released because these two objects
// may hold our only references.
//
AddRef();
//
// Dereference the queue if there is one.
//
if (m_ProcessPipeSection.Queue) {
m_ProcessPipeSection.Queue->Release();
m_ProcessPipeSection.Queue = NULL;
}
//
// Dereference the requestor if there is one.
//
if (m_ProcessPipeSection.Requestor) {
m_ProcessPipeSection.Requestor->Release();
m_ProcessPipeSection.Requestor = NULL;
}
Release();
}
return status;
}
NTSTATUS
CKsPipeSection::
DistributeDeviceStateChange(
IN PIKSTRANSPORT Transport,
IN KSSTATE NewState,
IN KSSTATE OldState
)
/*++
Routine Description:
This routine distributes a state change around the circuit.
Arguments:
Transport -
Contains a pointer to the first component to distribute the state
change to.
NewState -
The new device state.
OldState -
The old device state.
Return Value:
STATUS_SUCCESS or an error code from one of the components.
--*/
{
//
// Tell everyone about the state change.
//
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::DistributeDeviceStateChange(%p)] distributing transition from %d to %d",this,OldState,NewState));
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.DistributeDeviceStateChange: from %d to %d",this,OldState,NewState));
PAGED_CODE();
ASSERT(Transport);
//
// Distribute the state change around the circuit.
//
NTSTATUS status = STATUS_SUCCESS;
PIKSTRANSPORT previousTransport = NULL;
while (Transport) {
PIKSTRANSPORT nextTransport;
status =
Transport->SetDeviceState(
NewState,
OldState,
&nextTransport);
if (NT_SUCCESS(status)) {
previousTransport = Transport;
Transport = nextTransport;
} else {
NTSTATUS backoutStatus;
//
// Back out on failure.
//
_DbgPrintF(DEBUGLVL_DEVICESTATE,("#### Pipe%p.DistributeDeviceStateChange: failed transition from %d to %d",this,OldState,NewState));
while (previousTransport) {
Transport = previousTransport;
backoutStatus =
Transport->SetDeviceState(
OldState,
NewState,
&previousTransport);
ASSERT(NT_SUCCESS(backoutStatus) || ! previousTransport);
}
break;
}
}
return status;
}
void
CKsPipeSection::
SetResetState(
IN PIKSPIN Pin,
IN KSRESET NewState
)
/*++
Routine Description:
This routine informs transport components that the reset state has
changed.
Arguments:
Pin -
Contains a pointer to the pin supplying the state change request. The
request is ignored if this pin does not control the pipe state.
NewState -
The new reset state.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::SetResetState]"));
PAGED_CODE();
ASSERT(Pin);
//
// Ignore requests from any pin but the master.
//
if (Pin != m_MasterPin) {
return;
}
//
// If this section of the pipe owns the requestor, or there is a
// non- pin up the pipe (so there's no bypass), this pipe is
// in charge of telling all the components about state changes.
//
if (m_ProcessPipeSection.Requestor || m_MasterPin->GetStruct()->ConnectionIsExternal) {
//
// Set the state change around the circuit.
//
PIKSTRANSPORT transport =
m_ProcessPipeSection.Requestor ? PIKSTRANSPORT(m_ProcessPipeSection.Requestor) : PIKSTRANSPORT(m_ProcessPipeSection.Queue);
while (transport) {
transport->SetResetState(NewState,&transport);
}
}
//
// Forward the notification to all topologically related output pins.
// It's a requirement that if we flush after EOS, we be able to
// accept more data. This means that the output queues have to
// stop shunting data all over the place.
//
// If we deliver to a section which isn't in charge, the section will
// simply (as always) ignore the message.
//
m_Filter->DeliverResetState(&m_ProcessPipeSection, NewState);
m_ResetState = NewState;
}
#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif // ALLOC_PRAGMA
STDMETHODIMP_(void)
CKsPipeSection::
GenerateConnectionEvents(
IN ULONG OptionsFlags
)
/*++
Routine Description:
This routine tells pins associated with the pipe section to generate
connection events.
Arguments:
OptionsFlags -
Contains the options flags from the stream header which is causing
events to fire.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::GenerateConnectionEvents]"));
for(PKSPPROCESSPIN processPin = m_ProcessPipeSection.Inputs;
processPin;
processPin = processPin->Next) {
KspPinInterface(processPin->Pin)->
GenerateConnectionEvents(OptionsFlags);
}
for(processPin = m_ProcessPipeSection.Outputs;
processPin;
processPin = processPin->Next) {
KspPinInterface(processPin->Pin)->
GenerateConnectionEvents(OptionsFlags);
}
}
#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA
STDMETHODIMP_(NTSTATUS)
CKsPipeSection::
DistributeStateChangeToPins(
IN KSSTATE NewState,
IN KSSTATE OldState
)
/*++
Routine Description:
This routine tells pins associated with the pipe that the device state
has changed. This information is passed on to the client via the pin
dispatch function.
Arguments:
NewState -
Contains the new device state.
OldState -
Contains the previous device state.
Return Value:
Status.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::DistributeStateChangeToPins]"));
PAGED_CODE();
NTSTATUS status = STATUS_SUCCESS;
for(PKSPPROCESSPIN processPin = m_ProcessPipeSection.Inputs;
processPin;
processPin = processPin->Next) {
status = KspPinInterface(processPin->Pin)->
ClientSetDeviceState(NewState,OldState);
if (! NT_SUCCESS(status)) {
#if DBG
if (NewState < OldState) {
_DbgPrintF(DEBUGLVL_TERSE,("#### Pipe%p.DistributeStateChangeToPins: Pin %p failed transition from %ld to %ld",this,KspPinInterface(processPin->Pin),OldState,NewState));
}
#endif // DBG
//
// Only back out the changes if we're not stopping under emergency
// conditions. If we are, we ignore pin failure; otherwise,
// we can indefinitely block a stop.
//
if (!m_EmergencyShutdown) {
//
// Failed! Undo all the previous state changes.
//
for(PKSPPROCESSPIN processPinBack =
m_ProcessPipeSection.Inputs;
processPinBack != processPin;
processPinBack = processPinBack->Next) {
KspPinInterface(processPinBack->Pin)->
ClientSetDeviceState(OldState,NewState);
}
break;
} else {
//
// If we're running under emergency shutdown conditions,
// we ignore any error the client returns. We must attempt
// to stop regardless of what happens. Lie and say the client
// succeeded, then keep going.
//
status = STATUS_SUCCESS;
}
}
}
if (NT_SUCCESS(status)) {
for(processPin = m_ProcessPipeSection.Outputs;
processPin;
processPin = processPin->Next) {
status = KspPinInterface(processPin->Pin)->
ClientSetDeviceState(NewState,OldState);
if (! NT_SUCCESS(status)) {
#if DBG
if (NewState < OldState) {
_DbgPrintF(DEBUGLVL_TERSE,("#### Pipe%p.DistributeStateChangeToPins: Pin %p failed transition from %ld to %ld",this,KspPinInterface(processPin->Pin),OldState,NewState));
}
#endif // DBG
//
// Only back out changes in non-emergency conditions. See
// above for comments.
//
if (!m_EmergencyShutdown) {
//
// Failed! Undo all the previous state changes.
//
for(PKSPPROCESSPIN processPinBack =
m_ProcessPipeSection.Inputs;
processPinBack;
processPinBack = processPinBack->Next) {
KspPinInterface(processPinBack->Pin)->
ClientSetDeviceState(OldState,NewState);
}
for(processPinBack = m_ProcessPipeSection.Outputs;
processPinBack != processPin;
processPinBack = processPinBack->Next) {
KspPinInterface(processPinBack->Pin)->
ClientSetDeviceState(OldState,NewState);
}
break;
} else {
//
// Proceed even if the client fails when emergency stop
// happens. See above for comments.
//
status = STATUS_SUCCESS;
}
}
}
}
return status;
}
STDMETHODIMP_(void)
CKsPipeSection::
ConfigurationSet(
IN BOOLEAN Configured
)
/*++
Routine Description:
This routine indicates to the pipe that the configuration for associated
transport components has been set or for some reason has been reset.
Arguments:
Configured -
Indicates whether the configuration for the associated transport
components has been set. If this is false, the pipe section is
to consider itself no longer configured correctly.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("[CKsPipeSection::ConfigurationSet]"));
PAGED_CODE();
m_ConfigurationSet = Configured;
}
#endif