Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

993 lines
24 KiB

/*++
Copyright (c) 1998-2000 Microsoft Corporation. All rights reserved.
Module Name:
shreq.cpp
Abstract:
This module contains the implementation of the kernel streaming shell
requestor object.
Author:
Dale Sather (DaleSat) 31-Jul-1998
--*/
#include "private.h"
#include <kcom.h>
#include "stdio.h"
#define POOLTAG_REQUESTOR 'gbcP'
#define POOLTAG_STREAMHEADER 'hscP'
//
// CKsShellQueue is the implementation of the kernel shell requestor object.
//
class CKsShellRequestor:
public IKsShellTransport,
public IKsWorkSink,
public CBaseUnknown
{
private:
PIKSSHELLTRANSPORT m_TransportSource;
PIKSSHELLTRANSPORT m_TransportSink;
PDEVICE_OBJECT m_NextDeviceObject;
PFILE_OBJECT m_AllocatorFileObject;
KSSTREAMALLOCATOR_FUNCTIONTABLE m_AllocatorFunctionTable;
KSSTREAMALLOCATOR_STATUS m_AllocatorStatus;
ULONG m_StackSize;
ULONG m_ProbeFlags;
ULONG m_StreamHeaderSize;
ULONG m_FrameSize;
ULONG m_FrameCount;
ULONG m_ActiveIrpCountPlusOne;
BOOLEAN m_Flushing;
BOOLEAN m_EndOfStream;
KSSTATE m_State;
INTERLOCKEDLIST_HEAD m_IrpsToFree;
WORK_QUEUE_ITEM m_WorkItem;
PKSWORKER m_Worker;
KEVENT m_StopEvent;
public:
DEFINE_STD_UNKNOWN();
CKsShellRequestor(PUNKNOWN OuterUnknown):
CBaseUnknown(OuterUnknown) {
}
~CKsShellRequestor();
IMP_IKsShellTransport;
IMP_IKsWorkSink;
NTSTATUS
Init(
IN ULONG ProbeFlags,
IN ULONG StreamHeaderSize OPTIONAL,
IN ULONG FrameSize,
IN ULONG FrameCount,
IN PDEVICE_OBJECT NextDeviceObject,
IN PFILE_OBJECT AllocatorFileObject OPTIONAL
);
private:
NTSTATUS
Prime(
void
);
NTSTATUS
Unprime(
void
);
PVOID
AllocateFrame(
void
);
void
FreeFrame(
IN PVOID Frame
);
};
IMPLEMENT_STD_UNKNOWN(CKsShellRequestor)
#pragma code_seg("PAGE")
NTSTATUS
KspShellCreateRequestor(
OUT PIKSSHELLTRANSPORT* RequestorTransport,
IN ULONG ProbeFlags,
IN ULONG StreamHeaderSize OPTIONAL,
IN ULONG FrameSize,
IN ULONG FrameCount,
IN PDEVICE_OBJECT NextDeviceObject,
IN PFILE_OBJECT AllocatorFileObject OPTIONAL
)
/*++
Routine Description:
This routine creates a new requestor.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("KspShellCreateRequestor"));
PAGED_CODE();
ASSERT(RequestorTransport);
NTSTATUS status;
CKsShellRequestor *requestor =
new(NonPagedPool,POOLTAG_REQUESTOR) CKsShellRequestor(NULL);
if (requestor) {
requestor->AddRef();
status =
requestor->Init(
ProbeFlags,
StreamHeaderSize,
FrameSize,
FrameCount,
NextDeviceObject,
AllocatorFileObject);
if (NT_SUCCESS(status)) {
*RequestorTransport = PIKSSHELLTRANSPORT(requestor);
} else {
requestor->Release();
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
NTSTATUS
CKsShellRequestor::
Init(
IN ULONG ProbeFlags,
IN ULONG StreamHeaderSize OPTIONAL,
IN ULONG FrameSize,
IN ULONG FrameCount,
IN PDEVICE_OBJECT NextDeviceObject,
IN PFILE_OBJECT AllocatorFileObject OPTIONAL
)
/*++
Routine Description:
This routine initializes a requestor object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_VERBOSE,("CKsShellRequestor::Init"));
PAGED_CODE();
ASSERT(((StreamHeaderSize == 0) ||
(StreamHeaderSize >= sizeof(KSSTREAM_HEADER))) &&
((StreamHeaderSize & FILE_QUAD_ALIGNMENT) == 0));
if (StreamHeaderSize == 0)
{
StreamHeaderSize = sizeof(KSSTREAM_HEADER);
}
m_NextDeviceObject = NextDeviceObject;
m_AllocatorFileObject = AllocatorFileObject;
m_ProbeFlags = ProbeFlags;
m_StreamHeaderSize = StreamHeaderSize;
m_FrameSize = FrameSize;
m_FrameCount = FrameCount;
m_State = KSSTATE_STOP;
m_Flushing = FALSE;
m_EndOfStream = FALSE;
//
// This is a one-based count of IRPs in circulation. We decrement it when
// we go to stop state and block until it hits zero.
//
m_ActiveIrpCountPlusOne = 1;
KeInitializeEvent(&m_StopEvent,SynchronizationEvent,FALSE);
//
// Initialize IRP-freeing work item stuff.
//
InitializeInterlockedListHead(&m_IrpsToFree);
KsInitializeWorkSinkItem(&m_WorkItem,this);
NTSTATUS status = KsRegisterCountedWorker(DelayedWorkQueue,&m_WorkItem,&m_Worker);
if (!NT_SUCCESS(status))
{
return status;
}
//
// Get the function table and status from the allocator if there is an
// allocator.
//
if (m_AllocatorFileObject)
{
KSPROPERTY property;
property.Set = KSPROPSETID_StreamAllocator;
property.Id = KSPROPERTY_STREAMALLOCATOR_FUNCTIONTABLE;
property.Flags = KSPROPERTY_TYPE_GET;
ULONG bytesReturned;
status =
KsSynchronousIoControlDevice(
m_AllocatorFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
PVOID(&property),
sizeof(property),
PVOID(&m_AllocatorFunctionTable),
sizeof(m_AllocatorFunctionTable),
&bytesReturned);
if (NT_SUCCESS(status) &&
(bytesReturned != sizeof(m_AllocatorFunctionTable)))
{
status = STATUS_INVALID_BUFFER_SIZE;
}
if (NT_SUCCESS(status))
{
property.Id = KSPROPERTY_STREAMALLOCATOR_STATUS;
status =
KsSynchronousIoControlDevice(
m_AllocatorFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
PVOID(&property),
sizeof(property),
PVOID(&m_AllocatorStatus),
sizeof(m_AllocatorStatus),
&bytesReturned);
if (NT_SUCCESS(status) &&
(bytesReturned != sizeof(m_AllocatorStatus)))
{
status = STATUS_INVALID_BUFFER_SIZE;
}
if (NT_SUCCESS(status))
{
m_FrameSize = m_AllocatorStatus.Framing.FrameSize;
m_FrameCount = m_AllocatorStatus.Framing.Frames;
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.Init: using allocator 0x%08x, size %d, count %d",this,m_AllocatorFileObject,m_FrameSize,m_FrameCount));
}
else
{
_DbgPrintF(DEBUGLVL_TERSE,("#### Req%p.Init: allocator failed status query: 0x%08x",this,status));
}
}
else
{
_DbgPrintF(DEBUGLVL_TERSE,("#### Req%p.Init: allocator failed function table query: 0x%08x",this,status));
}
}
else
{
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.Init: not using an allocator, size %d, count %d",this,m_FrameSize,m_FrameCount));
}
return status;
}
CKsShellRequestor::
~CKsShellRequestor(
void
)
/*++
Routine Description:
This routine destructs a requestor object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CKsShellRequestor::~CKsShellRequestor"));
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.~",this));
PAGED_CODE();
ASSERT(! m_TransportSink);
ASSERT(! m_TransportSource);
if (m_Worker) {
KsUnregisterWorker(m_Worker);
m_Worker = NULL;
}
}
STDMETHODIMP_(NTSTATUS)
CKsShellRequestor::
NonDelegatedQueryInterface(
IN REFIID InterfaceId,
OUT PVOID* InterfacePointer
)
/*++
Routine Description:
This routine obtains an interface to a requestor object.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CKsShellRequestor::NonDelegatedQueryInterface"));
PAGED_CODE();
ASSERT(InterfacePointer);
NTSTATUS status = STATUS_SUCCESS;
if (IsEqualGUIDAligned(InterfaceId,__uuidof(IKsShellTransport))) {
*InterfacePointer = PVOID(PIKSSHELLTRANSPORT(this));
AddRef();
} else {
status = CBaseUnknown::NonDelegatedQueryInterface(
InterfaceId,InterfacePointer);
}
return status;
}
STDMETHODIMP_(void)
CKsShellRequestor::
Work(
void
)
/*++
Routine Description:
This routine performs work in a worker thread. In particular, it frees
IRPs.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CKsShellRequestor::Work"));
PAGED_CODE();
//
// Send all IRPs in the queue.
//
do
{
if (! IsListEmpty(&m_IrpsToFree.ListEntry))
{
PIRP irp;
PLIST_ENTRY pListHead = ExInterlockedRemoveHeadList(&m_IrpsToFree.ListEntry,&m_IrpsToFree.SpinLock);
if (pListHead)
{
irp = CONTAINING_RECORD(pListHead,IRP,Tail.Overlay.ListEntry);
}
else
{
return;
}
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.Work: freeing IRP 0x%08x",this,irp));
//
// 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);
}
//
// Free header and frame.
//
PKSSTREAM_HEADER streamHeader = PKSSTREAM_HEADER(irp->UserBuffer);
if (streamHeader) {
if (streamHeader->Data) {
FreeFrame(streamHeader->Data);
}
ExFreePool(streamHeader);
}
IoFreeIrp(irp);
//
// Count the active IRPs. If we have hit zero, this means that
// another thread is waiting to finish a transition to stop state.
//
if (! InterlockedDecrement(PLONG(&m_ActiveIrpCountPlusOne))) {
KeSetEvent(&m_StopEvent,IO_NO_INCREMENT,FALSE);
}
}
} while (KsDecrementCountedWorker(m_Worker));
}
#pragma code_seg()
STDMETHODIMP_(NTSTATUS)
CKsShellRequestor::
TransferKsIrp(
IN PIRP Irp,
IN PIKSSHELLTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles the arrival of a streaming IRP.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CKsShellRequestor::TransferKsIrp"));
ASSERT(Irp);
ASSERT(NextTransport);
ASSERT(m_TransportSink);
if (m_State != KSSTATE_RUN) {
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.TransferKsIrp: got IRP %p in state %d",this,Irp,m_State));
}
NTSTATUS status;
PKSSTREAM_HEADER streamHeader = PKSSTREAM_HEADER(Irp->UserBuffer);
//
// Check for end of stream.
//
if (streamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM) {
m_EndOfStream = TRUE;
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.TransferKsIrp: IRP %p is marked end-of-stream",this,Irp));
}
if (m_Flushing || m_EndOfStream || (m_State == KSSTATE_STOP)) {
//
// Stopping...destroy the IRP.
//
ExInterlockedInsertTailList(
&m_IrpsToFree.ListEntry,
&Irp->Tail.Overlay.ListEntry,
&m_IrpsToFree.SpinLock);
*NextTransport = NULL;
KsIncrementCountedWorker(m_Worker);
status = STATUS_PENDING;
} else {
//
// Recondition and forward it.
//
PVOID frame = streamHeader->Data;
RtlZeroMemory(streamHeader,m_StreamHeaderSize);
streamHeader->Size = m_StreamHeaderSize;
streamHeader->Data = frame;
streamHeader->FrameExtent = m_FrameSize;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
Irp->PendingReturned = 0;
Irp->Cancel = 0;
*NextTransport = m_TransportSink;
status = STATUS_SUCCESS;
}
return status;
}
#pragma code_seg("PAGE")
STDMETHODIMP_(void)
CKsShellRequestor::
Connect(
IN PIKSSHELLTRANSPORT NewTransport OPTIONAL,
OUT PIKSSHELLTRANSPORT *OldTransport OPTIONAL,
IN KSPIN_DATAFLOW DataFlow
)
/*++
Routine Description:
This routine establishes a transport connection.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CKsShellRequestor::Connect"));
PAGED_CODE();
KsShellStandardConnect(
NewTransport,
OldTransport,
DataFlow,
PIKSSHELLTRANSPORT(this),
&m_TransportSource,
&m_TransportSink);
}
STDMETHODIMP_(NTSTATUS)
CKsShellRequestor::
SetDeviceState(
IN KSSTATE ksStateTo,
IN KSSTATE ksStateFrom,
IN PIKSSHELLTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles notification that the device state has changed.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.SetDeviceState: set from %d to %d",this,ksStateFrom,ksStateTo));
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 != ksStateTo) {
//
// The state has changed. Just note the new state, indicate the next
// recipient, and get out. We will get the same state change again
// when it has gone all the way around the circuit.
//
m_State = ksStateTo;
if (ksStateTo > ksStateFrom){
*NextTransport = m_TransportSink;
} else {
*NextTransport = m_TransportSource;
}
status = STATUS_SUCCESS;
} else {
//
// The state change has gone all the way around the circuit and come
// back. All the other components are in the new state now. For
// transitions out of acquire state, there is work to be done.
//
*NextTransport = NULL;
if (ksStateFrom == KSSTATE_ACQUIRE) {
if (ksStateTo == KSSTATE_PAUSE) {
//
// Acquire-to-pause requires us to prime.
//
status = Prime();
} else {
//
// Acquire-to-stop requires us to wait until all IRPs are home to
// roost.
//
if (InterlockedDecrement(PLONG(&m_ActiveIrpCountPlusOne))) {
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.SetDeviceState: waiting for %d active IRPs to return",this,m_ActiveIrpCountPlusOne));
KeWaitForSingleObject(
&m_StopEvent,
Suspended,
KernelMode,
FALSE,
NULL
);
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.SetDeviceState: done waiting",this));
}
status = STATUS_SUCCESS;
}
}
else
{
#if 1
//
// Nothing to do.
//
#else
// Take a configuration pass through the circuit.
// The transport interface for each element in the circuit exposes GetTransportConfig
// and SetTransportConfig.
// The requestor's stack depth starts at 1
// Each element that reports in GetTransportConfig returns it's stack depth
// Queues, Intra-Pins report 1
// Extra-Pins report the depth of the connected device object plus one
// Splitters are handled somewhat differently (irrelevant to portcls?)
// After each GetTransportConfig, the requestor's stack depth is adjusted to be the
// highest depth seen thus far in configuration
// At the end, SetTransportConfig is used to set the requestors stack depth
//
// This mechanism is also used to decide probe flags.
// If you're interested in looking at the source, ksfilter\ks\shpipe.cpp
// ConfigureCompleteCircuit and sh*.cpp GetTransportConfig / SetTransportConfig
// are the ones to look at.
//
#endif
status = STATUS_SUCCESS;
}
}
return status;
}
NTSTATUS
CKsShellRequestor::
Prime(
void
)
/*++
Routine Description:
This routine primes the requestor.
Arguments:
None.
Return Value:
Status.
--*/
{
PAGED_CODE();
//
// Cache the stack size.
//
m_StackSize = m_NextDeviceObject->StackSize;
// HACK
//
// ADRIAO ISSUE 06/29/1999
// Arghhhh!!!!!!!!
//
// ISSUE MARTINP 2000/12/18 This is fixed with AVStream, so it isn't worth
// making large changes to address this issue for Windows XP. This code is
// no longer present in PortCls2. We should make sure this is fixed in for
// sure in Blackcomb.
//
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.Prime: stack size is %d",this,m_StackSize));
m_StackSize = 6;
//
// Reset the end of stream indicator.
//
m_EndOfStream = FALSE;
NTSTATUS status = STATUS_SUCCESS;
//
// Call the allocator or create some synthetic frames. Then wrap the
// frames in IRPs.
//
for (ULONG count = m_FrameCount; count--;) {
//
// Allocate the frame.
//
PVOID frame = AllocateFrame();
if (! frame) {
status = STATUS_INSUFFICIENT_RESOURCES;
_DbgPrintF(DEBUGLVL_TERSE,("#### Req%p.Prime: failed to allocate frame",this));
break;
}
//
// Allocate and initialize the stream header.
//
PKSSTREAM_HEADER streamHeader = (PKSSTREAM_HEADER)
ExAllocatePoolWithTag(
NonPagedPool,m_StreamHeaderSize,POOLTAG_STREAMHEADER);
if (! streamHeader) {
status = STATUS_INSUFFICIENT_RESOURCES;
_DbgPrintF(DEBUGLVL_TERSE,("#### Req%p.Prime: failed to allocate stream header",this));
FreeFrame(frame);
break;
}
RtlZeroMemory(streamHeader,m_StreamHeaderSize);
streamHeader->Size = m_StreamHeaderSize;
streamHeader->Data = frame;
streamHeader->FrameExtent = m_FrameSize;
//
// Count the active IRPs.
//
InterlockedIncrement(PLONG(&m_ActiveIrpCountPlusOne));
//
// Allocate an IRP.
//
ASSERT(m_StackSize);
PIRP irp = IoAllocateIrp(CCHAR(m_StackSize),FALSE);
if (! irp) {
status = STATUS_INSUFFICIENT_RESOURCES;
_DbgPrintF(DEBUGLVL_TERSE,("#### Req%p.Prime: failed to allocate IRP",this));
ExFreePool(streamHeader);
FreeFrame(frame);
break;
}
irp->UserBuffer = streamHeader;
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 =
IOCTL_KS_READ_STREAM;
irpSp->Parameters.DeviceIoControl.OutputBufferLength =
m_StreamHeaderSize;
//
// Let KsProbeStreamIrp() prepare the IRP as specified by the caller.
//
status = KsProbeStreamIrp(irp,m_ProbeFlags,sizeof(KSSTREAM_HEADER));
if (! NT_SUCCESS(status)) {
_DbgPrintF(DEBUGLVL_TERSE,("#### Req%p.Prime: KsProbeStreamIrp failed: 0x%08x",this,status));
IoFreeIrp(irp);
ExFreePool(streamHeader);
FreeFrame(frame);
break;
}
//
// Send the IRP to the next component.
//
//mgp
_DbgPrintF(DEBUGLVL_VERBOSE,("#### Req%p.SetDeviceState: transferring new IRP 0x%08x",this,irp));
status = KsShellTransferKsIrp(m_TransportSink,irp);
if (NT_SUCCESS(status) || (status == STATUS_MORE_PROCESSING_REQUIRED)) {
status = STATUS_SUCCESS;
} else {
_DbgPrintF(DEBUGLVL_TERSE,("#### Req%p.Prime: receiver failed transfer call: 0x%08x",this,status));
IoFreeIrp(irp);
ExFreePool(streamHeader);
FreeFrame(frame);
break;
}
}
return status;
}
STDMETHODIMP_(void)
CKsShellRequestor::
SetResetState(
IN KSRESET ksReset,
IN PIKSSHELLTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles notification that the reset state has changed.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_VERBOSE,("CKsShellRequestor::SetResetState] to %d",ksReset));
PAGED_CODE();
ASSERT(NextTransport);
if (m_Flushing != (ksReset == KSRESET_BEGIN)) {
*NextTransport = m_TransportSink;
m_Flushing = (ksReset == KSRESET_BEGIN);
} else {
*NextTransport = NULL;
}
}
PVOID
CKsShellRequestor::
AllocateFrame(
void
)
/*++
Routine Description:
This routine allocates a frame.
Arguments:
None.
Return Value:
The allocated frame or NULL if no frame could be allocated.
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CKsShellRequestor::AllocateFrame"));
PAGED_CODE();
PVOID frame;
if (m_AllocatorFileObject) {
m_AllocatorFunctionTable.AllocateFrame(m_AllocatorFileObject,&frame);
} else {
frame = ExAllocatePoolWithTag(NonPagedPool,m_FrameSize,'kHcP');
}
return frame;
}
void
CKsShellRequestor::
FreeFrame(
IN PVOID Frame
)
/*++
Routine Description:
This routine frees a frame.
Arguments:
Frame -
The frame to free.
Return Value:
None.
--*/
{
_DbgPrintF(DEBUGLVL_VERBOSE,("CKsShellRequestor::FreeFrame"));
PAGED_CODE();
if (m_AllocatorFileObject) {
m_AllocatorFunctionTable.FreeFrame(m_AllocatorFileObject,Frame);
} else {
ExFreePool(Frame);
}
}
#if DBG
STDMETHODIMP_(void)
CKsShellRequestor::
DbgRollCall(
IN ULONG MaxNameSize,
OUT PCHAR Name,
OUT PIKSSHELLTRANSPORT* NextTransport,
OUT PIKSSHELLTRANSPORT* PrevTransport
)
/*++
Routine Description:
This routine produces a component name and the transport pointers.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CKsShellRequestor::DbgRollCall"));
PAGED_CODE();
ASSERT(Name);
ASSERT(NextTransport);
ASSERT(PrevTransport);
ULONG references = AddRef() - 1; Release();
_snprintf(Name,MaxNameSize,"Req%p refs=%d\n",this,references);
*NextTransport = m_TransportSink;
*PrevTransport = m_TransportSource;
}
#endif