Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

7088 lines
198 KiB

/*
Copyright (c) 1996 Microsoft Corporation
Module Name:
upperapi.c
Abstract:
This is the WDM streaming class driver. This module contains code related
to the driver's upper edge api.
Author:
billpa
Environment:
Kernel mode only
Revision History:
--*/
#include "codcls.h"
#include "ksguid.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FilterDispatchGlobalCreate)
#pragma alloc_text(PAGE, FilterDispatchIoControl)
#pragma alloc_text(PAGE, FilterDispatchClose)
#pragma alloc_text(PAGE, StreamDispatchCreate)
#pragma alloc_text(PAGE, StreamDispatchIoControl)
#pragma alloc_text(PAGE, StreamDispatchClose)
#pragma alloc_text(PAGE, ClockDispatchCreate)
#pragma alloc_text(PAGE, ClockDispatchIoControl)
#pragma alloc_text(PAGE, ClockDispatchClose)
#pragma alloc_text(PAGE, StreamClassMinidriverDeviceGetProperty)
#pragma alloc_text(PAGE, StreamClassMinidriverDeviceSetProperty)
#pragma alloc_text(PAGE, StreamClassMinidriverStreamGetProperty)
#pragma alloc_text(PAGE, StreamClassMinidriverStreamSetProperty)
#pragma alloc_text(PAGE, StreamClassNull)
#pragma alloc_text(PAGE, SCStreamDeviceState)
#pragma alloc_text(PAGE, SCStreamDeviceRate)
#pragma alloc_text(PAGE, SCFilterPinInstances)
#pragma alloc_text(PAGE, SCFilterPinPropertyHandler)
#pragma alloc_text(PAGE, SCOpenStreamCallback)
#pragma alloc_text(PAGE, SCOpenMasterCallback)
#if ENABLE_MULTIPLE_FILTER_TYPES
#else
#pragma alloc_text(PAGE, SCGlobalInstanceCallback)
#endif
#pragma alloc_text(PAGE, SCSetMasterClock)
#pragma alloc_text(PAGE, SCClockGetTime)
#pragma alloc_text(PAGE, SCGetStreamDeviceState)
#pragma alloc_text(PAGE, SCStreamDeviceRateCapability)
#pragma alloc_text(PAGE, SCStreamProposeNewFormat)
#pragma alloc_text(PAGE, SCStreamSetFormat)
#pragma alloc_text(PAGE, AllocatorDispatchCreate)
#pragma alloc_text(PAGE, SCGetMasterClock)
#pragma alloc_text(PAGE, SCClockGetPhysicalTime)
#pragma alloc_text(PAGE, SCClockGetSynchronizedTime)
#pragma alloc_text(PAGE, SCClockGetFunctionTable)
#pragma alloc_text(PAGE, SCCloseClockCallback)
#pragma alloc_text(PAGE, SCFilterTopologyHandler)
#pragma alloc_text(PAGE, SCFilterPinIntersectionHandler)
#pragma alloc_text(PAGE, SCIntersectHandler)
#pragma alloc_text(PAGE, SCDataIntersectionCallback)
#pragma alloc_text(PAGE, SCGetStreamHeaderSize)
#pragma alloc_text(PAGE, DllUnload)
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
#pragma alloc_text(PAGE, SCStreamAllocator)
#pragma alloc_text(PAGE, AllocateFrame)
#pragma alloc_text(PAGE, FreeFrame)
#pragma alloc_text(PAGE, PrepareTransfer)
#pragma alloc_text(PAGE, PinCreateHandler)
#endif
#endif
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif
static const WCHAR ClockTypeName[] = KSSTRING_Clock;
static const WCHAR AllocatorTypeName[] = KSSTRING_Allocator;
//
// this structure is the dispatch table for a filter instance of the device
//
DEFINE_KSDISPATCH_TABLE(
FilterDispatchTable,
FilterDispatchIoControl,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
FilterDispatchClose,
NULL,
NULL,
NULL,
NULL,
NULL);
//
// dispatch table for pin properties that we support in the class driver
//
static DEFINE_KSPROPERTY_TABLE(PinPropertyHandlers)
{
DEFINE_KSPROPERTY_ITEM_PIN_CINSTANCES(SCFilterPinInstances),
DEFINE_KSPROPERTY_ITEM_PIN_CTYPES(SCFilterPinPropertyHandler),
DEFINE_KSPROPERTY_ITEM_PIN_DATAFLOW(SCFilterPinPropertyHandler),
DEFINE_KSPROPERTY_ITEM_PIN_DATARANGES(SCFilterPinPropertyHandler),
DEFINE_KSPROPERTY_ITEM_PIN_DATAINTERSECTION(SCFilterPinIntersectionHandler),
DEFINE_KSPROPERTY_ITEM_PIN_INTERFACES(SCFilterPinPropertyHandler),
DEFINE_KSPROPERTY_ITEM_PIN_MEDIUMS(SCFilterPinPropertyHandler),
DEFINE_KSPROPERTY_ITEM_PIN_COMMUNICATION(SCFilterPinPropertyHandler),
// DEFINE_KSPROPERTY_ITEM_PIN_GLOBALCINSTANCES(),
// DEFINE_KSPROPERTY_ITEM_PIN_NECESSARYINSTANCES(),
// DEFINE_KSPROPERTY_ITEM_PIN_PHYSICALCONNECTION(),
DEFINE_KSPROPERTY_ITEM_PIN_CATEGORY(SCFilterPinPropertyHandler),
DEFINE_KSPROPERTY_ITEM_PIN_NAME(SCFilterPinPropertyHandler)
};
static DEFINE_KSPROPERTY_TOPOLOGYSET(
TopologyPropertyHandlers,
SCFilterTopologyHandler);
//
// filter property sets supported by the class driver
//
static DEFINE_KSPROPERTY_SET_TABLE(FilterPropertySets)
{
DEFINE_KSPROPERTY_SET(
&KSPROPSETID_Pin,
SIZEOF_ARRAY(PinPropertyHandlers),
(PKSPROPERTY_ITEM) PinPropertyHandlers,
0, NULL),
DEFINE_KSPROPERTY_SET(
&KSPROPSETID_Topology,
SIZEOF_ARRAY(TopologyPropertyHandlers),
(PKSPROPERTY_ITEM) TopologyPropertyHandlers,
0, NULL),
};
//
// handlers for the control properties
//
static DEFINE_KSPROPERTY_TABLE(StreamControlHandlers)
{
DEFINE_KSPROPERTY_ITEM_CONNECTION_STATE(SCGetStreamDeviceState, SCStreamDeviceState),
// DEFINE_KSPROPERTY_ITEM_CONNECTION_PRIORITY(),
// DEFINE_KSPROPERTY_ITEM_CONNECTION_DATAFORMAT(),
// DEFINE_KSPROPERTY_ITEM_CONNECTION_ALLOCATORFRAMING(),
DEFINE_KSPROPERTY_ITEM_CONNECTION_PROPOSEDATAFORMAT(SCStreamProposeNewFormat),
DEFINE_KSPROPERTY_ITEM_CONNECTION_DATAFORMAT(NULL, SCStreamSetFormat),
// DEFINE_KSPROPERTY_ITEM_CONNECTION_ACQUIREORDERING(),
};
DEFINE_KSPROPERTY_TABLE(StreamStreamHandlers)
{
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
DEFINE_KSPROPERTY_ITEM_STREAM_ALLOCATOR(SCStreamAllocator,SCStreamAllocator),
#else
// DEFINE_KSPROPERTY_ITEM_STREAM_ALLOCATOR(),
#endif
// DEFINE_KSPROPERTY_ITEM_STREAM_QUALITY(),
// DEFINE_KSPROPERTY_ITEM_STREAM_DEGRADATION(),
DEFINE_KSPROPERTY_ITEM_STREAM_MASTERCLOCK(NULL, SCSetMasterClock),
// DEFINE_KSPROPERTY_ITEM_STREAM_TIMEFORMAT(),
// DEFINE_KSPROPERTY_ITEM_STREAM_PRESENTATIONTIME(),
// DEFINE_KSPROPERTY_ITEM_STREAM_PRESENTATIONEXTENT(),
// DEFINE_KSPROPERTY_ITEM_STREAM_FRAMETIME(),
DEFINE_KSPROPERTY_ITEM_STREAM_RATECAPABILITY(SCStreamDeviceRateCapability),
DEFINE_KSPROPERTY_ITEM_STREAM_RATE(NULL, SCStreamDeviceRate),
};
DEFINE_KSPROPERTY_TABLE(StreamInterfaceHandlers)
{
{
KSPROPERTY_STREAMINTERFACE_HEADERSIZE,
SCGetStreamHeaderSize,
0,
0,
NULL,
0,
0,
NULL
}
};
//
// stream property sets supported by the class driver
//
static DEFINE_KSPROPERTY_SET_TABLE(StreamProperties)
{
DEFINE_KSPROPERTY_SET(
&KSPROPSETID_Connection,
SIZEOF_ARRAY(StreamControlHandlers),
(PVOID) StreamControlHandlers,
0, NULL),
DEFINE_KSPROPERTY_SET(
&KSPROPSETID_Stream,
SIZEOF_ARRAY(StreamStreamHandlers),
(PVOID) StreamStreamHandlers,
0, NULL),
DEFINE_KSPROPERTY_SET(
&KSPROPSETID_StreamInterface,
SIZEOF_ARRAY(StreamInterfaceHandlers),
(PVOID) StreamInterfaceHandlers,
0, NULL),
};
//
// template for on the fly constructed properties
// DO NOT CHANGE without MODIFYING the code that references this set.
//
DEFINE_KSPROPERTY_TABLE(ConstructedStreamHandlers)
{
DEFINE_KSPROPERTY_ITEM_STREAM_MASTERCLOCK(SCGetMasterClock, SCSetMasterClock)
};
//
// template for on-the-fly constructed property sets.
// DO NOT CHANGE without MODIFYING the code that references this set.
//
static DEFINE_KSPROPERTY_SET_TABLE(ConstructedStreamProperties)
{
DEFINE_KSPROPERTY_SET(
&KSPROPSETID_Stream,
SIZEOF_ARRAY(ConstructedStreamHandlers),
(PVOID) ConstructedStreamHandlers,
0, NULL),
};
static const DEFINE_KSCREATE_DISPATCH_TABLE(StreamDriverDispatch)
{
DEFINE_KSCREATE_ITEM(ClockDispatchCreate,
ClockTypeName,
0),
DEFINE_KSCREATE_ITEM(AllocatorDispatchCreate,
AllocatorTypeName,
0),
};
//
// dispatch table for stream functions
//
DEFINE_KSDISPATCH_TABLE(
StreamDispatchTable,
StreamDispatchIoControl,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
StreamDispatchClose,
NULL,
NULL,
NULL,
NULL,
NULL);
DEFINE_KSDISPATCH_TABLE(
ClockDispatchTable,
ClockDispatchIoControl,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
ClockDispatchClose,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
KsDispatchFastIoDeviceControlFailure,
KsDispatchFastReadFailure,
KsDispatchFastWriteFailure);
DEFINE_KSPROPERTY_TABLE(ClockPropertyItems)
{
DEFINE_KSPROPERTY_ITEM_CLOCK_TIME(SCClockGetTime),
DEFINE_KSPROPERTY_ITEM_CLOCK_PHYSICALTIME(SCClockGetPhysicalTime),
DEFINE_KSPROPERTY_ITEM_CLOCK_CORRELATEDTIME(SCClockGetSynchronizedTime),
// DEFINE_KSPROPERTY_ITEM_CLOCK_CORRELATEDPHYSICALTIME(),
// DEFINE_KSPROPERTY_ITEM_CLOCK_RESOLUTION(SCClockGetResolution),
// DEFINE_KSPROPERTY_ITEM_CLOCK_STATE(SCClockGetState),
DEFINE_KSPROPERTY_ITEM_CLOCK_FUNCTIONTABLE(SCClockGetFunctionTable)
};
DEFINE_KSPROPERTY_SET_TABLE(ClockPropertySets)
{
DEFINE_KSPROPERTY_SET(
&KSPROPSETID_Clock,
SIZEOF_ARRAY(ClockPropertyItems),
ClockPropertyItems,
0,
NULL
)
};
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif
#if ENABLE_MULTIPLE_FILTER_TYPES
NTSTATUS
FilterDispatchGlobalCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
/*++
Routine Description:
This routine receives global createfile IRP's for the device.
After the Srb_Open_Device_Instance instance we immediate
send Srb_Get_Stream_Info for this filter.
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
The IRP status is set as appropriate
--*/
{
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION IrpStack;
PFILTER_INSTANCE FilterInstance;
NTSTATUS Status; // = STATUS_TOO_MANY_OPENED_FILES;
IFN_MF( PAGED_CODE());
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DebugPrint((DebugLevelTrace,
"'Closing global filter with Irp %x\n", Irp));
//
// show one more I/O pending & verify that we can actually do I/O.
//
Status = SCShowIoPending(DeviceObject->DeviceExtension, Irp);
if ( !NT_SUCCESS ( Status )) {
//
// the device is currently not accessible, so just return with error
//
return (Status);
} // if !showiopending
//
// if the device is not started, bail out.
// swenum enables device interfaces very early. It should not have
// done that for the pdo. we, the fdo, should be the one to
// enable this. for now, try to work around the problem that we
// come here before device is started.
//
if ( DeviceExtension->RegistryFlags & DRIVER_USES_SWENUM_TO_LOAD ) {
#define OPEN_TIMEOUT -1000*1000 // 100 mili second
#define OPEN_WAIT 50
LARGE_INTEGER liOpenTimeOut;
int i;
liOpenTimeOut.QuadPart = OPEN_TIMEOUT;
for ( i=0; i < OPEN_WAIT; i++ ) {
if ( DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED ) {
break;
}
KeDelayExecutionThread( KernelMode, FALSE, &liOpenTimeOut );
}
if ( 0 == (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) ) {
Status = STATUS_DEVICE_NOT_READY;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
DebugPrint((DebugLevelError,
"SWEnum device %p not ready!\n", DeviceObject));
return Status;
}
}
//
// show one more reference to driver.
//
SCReferenceDriver(DeviceExtension);
//
// set the context of createfiles for the filter
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
//
// Make sure adapter is powered on
//
SCCheckPoweredUp(DeviceExtension);
Status = SCOpenMinidriverInstance(DeviceExtension,
&FilterInstance,
NULL, //SCGlobalInstanceCallback,
Irp);
//
// if status != success, we failed so dereference the
// driver.
//
if (!NT_SUCCESS(Status)) {
//
// show one fewer reference to driver.
//
SCDereferenceDriver(DeviceExtension);
}
else {
//
// Open is successul. Fill in the filter dispatch table pointer
//
if ( 0 == DeviceExtension->NumberOfOpenInstances ||
0 != DeviceExtension->FilterExtensionSize ) {
//
// 1st open of 1x1 or non 1x1 ( i.e. instance opne )
//
// add FilterInstance to DeviceExtension except non-1st open of legacy 1x1
//
PHW_STREAM_DESCRIPTOR StreamDescriptor, StreamDescriptorI;
ULONG nPins;
//
// remeber DO for later
//
FilterInstance->DeviceObject = DeviceObject;
//
// Process stream info for this filterinstance
//
StreamDescriptorI = DeviceExtension->FilterTypeInfos
[FilterInstance->FilterTypeIndex].StreamDescriptor;
nPins = StreamDescriptorI->StreamHeader.NumberOfStreams;
StreamDescriptor =
ExAllocatePool( NonPagedPool,
sizeof(HW_STREAM_HEADER) +
sizeof(HW_STREAM_INFORMATION) * nPins );
if ( NULL != StreamDescriptor ) {
RtlCopyMemory( StreamDescriptor,
StreamDescriptorI,
sizeof(HW_STREAM_HEADER) +
sizeof(HW_STREAM_INFORMATION) * nPins );
Status = SciOnFilterStreamDescriptor(
FilterInstance,
StreamDescriptor);
if ( NT_SUCCESS( Status ) ) {
FilterInstance->StreamDescriptor = StreamDescriptor;
DebugPrint((DebugLevelInfo,
"NumNameExtensions=%x NumopenInstances=%x "
"FilterInstance %x StreamDescriptor %x\n",
DeviceExtension->NumberOfNameExtensions,
DeviceExtension->NumberOfOpenInstances,
FilterInstance,
StreamDescriptor));
}
}
else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
DeviceExtension->NumberOfOpenInstances++;
DebugPrint((DebugLevelVerbose,
"DevExt:%x, Open OpenCount=%x\n",
DeviceExtension,
DeviceExtension->NumberOfOpenInstances));
//
// Make FilterInstance the File Handle Context
//
IrpStack->FileObject->FsContext = FilterInstance;
DebugPrint((DebugLevelVerbose,
"CreateFilterInstance=%x ExtSize=%x\n",
FilterInstance,
DeviceExtension->MinidriverData->HwInitData.FilterInstanceExtensionSize ));
//
// Reference the FDO so that itwon't go away before all handles are closed.
//
ObReferenceObject(DeviceObject);
}
//
// we're done so release the event
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (SCCompleteIrp(Irp, Status, DeviceExtension));
}
#else // ENABLE_MULTIPLE_FILTER_TYPES
#endif // ENABLE_MULTIPLE_FILTER_TYPES
NTSTATUS
StreamDispatchCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine receives createfile IRP's for a stream.
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
The IRP status is set as appropriate
--*/
{
NTSTATUS Status;
PFILTER_INSTANCE FilterInstance;
PIO_STACK_LOCATION IrpStack;
PKSPIN_CONNECT Connect;
PFILE_OBJECT FileObject;
PSTREAM_OBJECT StreamObject;
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)
DeviceObject->DeviceExtension;
PHW_STREAM_INFORMATION CurrentInfo;
ULONG i;
BOOLEAN RequestIssued;
PADDITIONAL_PIN_INFO AdditionalInfo;
DebugPrint((DebugLevelTrace,
"'Creating stream with Irp %x\n", Irp));
PAGED_CODE();
DebugPrint((DebugLevelTrace,"entering StreamDispatchCreate()\n"));
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = DeviceObject->DeviceExtension;
//
// show one more I/O pending & verify that we can actually do I/O.
//
Status = SCShowIoPending(DeviceExtension, Irp);
if ( !NT_SUCCESS ( Status )) {
//
// the device is currently not accessible, so just return with error
//
DebugPrint((DebugLevelError,"exiting StreamDispatchCreate():error1\n"));
return (Status);
}
//
// get the parent file object from the child object.
//
FileObject = IrpStack->FileObject->RelatedFileObject;
//
// get the filter instance & additional info pointers
//
FilterInstance =
(PFILTER_INSTANCE) FileObject->FsContext;
AdditionalInfo = FilterInstance->PinInstanceInfo;
DebugPrint((DebugLevelVerbose,
"FilterInstance=%x NumberOfPins=%x PinInfo=%x\n",
FilterInstance,
FilterInstance->NumberOfPins,
FilterInstance->PinInformation));
Status = KsValidateConnectRequest(Irp,
FilterInstance->NumberOfPins,
FilterInstance->PinInformation,
&Connect);
if ( !NT_SUCCESS( Status )) {
DebugPrint((DebugLevelError,
"exiting StreamDispatchCreate():error2\n"));
return (SCCompleteIrp(Irp, Status, DeviceExtension));
}
//
// take the control event to protect the instance counter
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
//
// if the # of instances for this pin is already opened, error the
// request.
//
DebugPrint((DebugLevelVerbose,
"AdditionalInfo@%x PinId=%x CurrentInstances=%x Max=%x\n",
AdditionalInfo, Connect->PinId,
AdditionalInfo[Connect->PinId].CurrentInstances,
AdditionalInfo[Connect->PinId].MaxInstances));
if (AdditionalInfo[Connect->PinId].CurrentInstances ==
AdditionalInfo[Connect->PinId].MaxInstances) {
DebugPrint((DebugLevelWarning,
"StreamDispatchCreate: too many opens "));
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
DebugPrint((DebugLevelError,"exiting StreamDispatchCreate():error3\n"));
return (SCCompleteIrp(Irp, STATUS_TOO_MANY_OPENED_FILES, DeviceExtension));
}
//
// initialize the stream object for this instance
//
StreamObject = ExAllocatePool(NonPagedPool,
sizeof(STREAM_OBJECT) +
DeviceExtension->MinidriverData->
HwInitData.PerStreamExtensionSize
);
if (!StreamObject) {
DebugPrint((DebugLevelError,
"StreamDispatchCreate: No pool for stream info"));
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
DebugPrint((DebugLevelError,"exiting StreamDispatchCreate():error4\n"));
return (SCCompleteIrp(Irp, STATUS_INSUFFICIENT_RESOURCES, DeviceExtension));
}
RtlZeroMemory(StreamObject,
sizeof(STREAM_OBJECT) +
DeviceExtension->MinidriverData->
HwInitData.PerStreamExtensionSize
);
//
// TODO: Remove this once KS can multiplex CLEANUP requests.
//
StreamObject->ComObj.Cookie = STREAM_OBJECT_COOKIE;
//
// default state to stopped
//
StreamObject->CurrentState = KSSTATE_STOP;
KsAllocateObjectHeader(&StreamObject->ComObj.DeviceHeader,
SIZEOF_ARRAY(StreamDriverDispatch),
(PKSOBJECT_CREATE_ITEM) StreamDriverDispatch,
Irp,
(PKSDISPATCH_TABLE) & StreamDispatchTable);
StreamObject->HwStreamObject.StreamNumber = Connect->PinId;
StreamObject->FilterFileObject = FileObject;
StreamObject->FileObject = IrpStack->FileObject;
StreamObject->FilterInstance = FilterInstance;
StreamObject->DeviceExtension = DeviceExtension;
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
StreamObject->PinToHandle = Connect->PinToHandle;
#endif
KeInitializeEvent (&StreamObject -> StopEvent, SynchronizationEvent, FALSE);
//
// For potential "source" pins, don't start sourcing standard
// medium/interface stream requests across non-standard medium/interfaces.
//
if (!IsEqualGUIDAligned (&Connect->Medium.Set, &KSMEDIUMSETID_Standard) ||
!IsEqualGUIDAligned (&Connect->Interface.Set, &KSINTERFACESETID_Standard)) {
StreamObject->StandardTransport = FALSE;
} else {
StreamObject -> StandardTransport = TRUE;
}
//
// set the minidriver's parameters in the HwStreamObject struct.
//
StreamObject->HwStreamObject.SizeOfThisPacket = sizeof(HW_STREAM_OBJECT);
StreamObject->HwStreamObject.HwDeviceExtension =
DeviceExtension->HwDeviceExtension;
StreamObject->HwStreamObject.HwStreamExtension =
(PVOID) (StreamObject + 1);
//
// walk the minidriver's stream info structure to find the properties
// for this stream.
//
if ( NULL == FilterInstance->StreamDescriptor ) {
//
// has not reenum, use the global one
//
CurrentInfo = &DeviceExtension->StreamDescriptor->StreamInfo;
}
else {
CurrentInfo = &FilterInstance->StreamDescriptor->StreamInfo;
}
CurrentInfo = CurrentInfo + Connect->PinId;
//
// set the property info in the stream object.
//
StreamObject->PropertyInfo = FilterInstance->
StreamPropEventArray[Connect->PinId].StreamPropertiesArray;
StreamObject->PropInfoSize = CurrentInfo->
NumStreamPropArrayEntries;
//
// set the event info in the stream object
//
StreamObject->EventInfo = FilterInstance->
StreamPropEventArray[Connect->PinId].StreamEventsArray;
StreamObject->EventInfoCount = CurrentInfo->
NumStreamEventArrayEntries;
// moved from callback
InitializeListHead(&StreamObject->NotifyList);
//
// call the minidriver to open the stream. processing will continue
// when the callback procedure is called.
//
Status = SCSubmitRequest(SRB_OPEN_STREAM,
(PVOID) (Connect + 1),
0,
SCOpenStreamCallback,
DeviceExtension,
FilterInstance->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&DeviceExtension->PendingQueue,
(PVOID) DeviceExtension->
MinidriverData->HwInitData.
HwReceivePacket
);
if (!RequestIssued) {
//
// failure submitting the request
//
DEBUG_BREAKPOINT();
ExFreePool(StreamObject);
DebugPrint((DebugLevelWarning,
"StreamClassOpen: stream open failed"));
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
DebugPrint((DebugLevelError,"exiting StreamDispatchCreate():error6\n"));
return (SCCompleteIrp(Irp, Status, DeviceExtension));
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
DebugPrint((DebugLevelTrace,"exiting StreamDispatchCreate()\n"));
return (Status);
}
NTSTATUS
SCOpenStreamCallback(
IN PSTREAM_REQUEST_BLOCK SRB
)
/*++
Routine Description:
Process the completion of a stream open
Arguments:
SRB - address of the completed SRB
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD(
SRB->HwSRB.StreamObject,
STREAM_OBJECT,
HwStreamObject
);
PIRP Irp = SRB->HwSRB.Irp;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS Status = SRB->HwSRB.Status;
PADDITIONAL_PIN_INFO AdditionalInfo;
PVOID PropertyInfo;
PKSPROPERTY_ITEM PropertyItem;
PHW_STREAM_INFORMATION CurrentInfo;
ULONG i;
PAGED_CODE();
if (NT_SUCCESS(Status)) {
//
// if required parameters have not been filled in, fail the open.
//
if (!StreamObject->HwStreamObject.ReceiveControlPacket) {
DEBUG_BREAKPOINT();
ExFreePool(StreamObject);
SRB->HwSRB.Status = STATUS_ADAPTER_HARDWARE_ERROR;
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
return (SCProcessCompletedRequest(SRB));
}
//
// if the minidriver does not accept data, use dummy routine.
//
if (!StreamObject->HwStreamObject.ReceiveDataPacket) {
StreamObject->HwStreamObject.ReceiveDataPacket = SCErrorDataSRB;
}
//
// Save the pointer to our per stream structure in the FsContext
// field of FileObject. Null out the 2nd context param.
//
IrpStack->FileObject->FsContext = StreamObject;
IrpStack->FileObject->FsContext2 = NULL;
//
// Initialize ControlSetMasterClock to serialize the concurrent
// calls of the function on us, and lock the Read/write of the
// MasterLockInfo
//
KeInitializeEvent(&StreamObject->ControlSetMasterClock, SynchronizationEvent, TRUE);
KeInitializeSpinLock(&StreamObject->LockUseMasterClock );
DebugPrint((DebugLevelTrace, "'StreamClassOpen: Stream opened.\n"));
//
// Initialize minidriver timer and timer DPC for this stream
//
KeInitializeTimer(&StreamObject->ComObj.MiniDriverTimer);
KeInitializeDpc(&StreamObject->ComObj.MiniDriverTimerDpc,
SCMinidriverStreamTimerDpc,
StreamObject);
//
// initialize the lists for this stream
//
InitializeListHead(&StreamObject->DataPendingQueue);
InitializeListHead(&StreamObject->ControlPendingQueue);
InitializeListHead(&StreamObject->NextStream);
// a mini driver might start to call GetNextEvent once
// returns from SRB_OPNE_STREAM. Do it earlier than submit.
//InitializeListHead(&StreamObject->NotifyList);
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
InitializeListHead(&StreamObject->FreeQueue);
KeInitializeSpinLock(&StreamObject->FreeQueueLock );
InitializeListHead(&StreamObject->Queues[READ].ActiveQueue);
KeInitializeSpinLock(&StreamObject->Queues[READ].QueueLock );
InitializeListHead(&StreamObject->Queues[WRITE].ActiveQueue);
KeInitializeSpinLock(&StreamObject->Queues[WRITE].QueueLock );
StreamObject->PinId = StreamObject->HwStreamObject.StreamNumber;
StreamObject->PinType = IrpSink; // assume irp sink
if (StreamObject->PinToHandle) { // if irp source
StreamObject->PinType = IrpSource;
Status = PinCreateHandler( Irp, StreamObject );
if (!NT_SUCCESS(Status)) {
DebugPrint((DebugLevelError,
"\nStreamDispatchCreate: PinCreateHandler() returned ERROR"));
ExFreePool(StreamObject);
SRB->HwSRB.Status = Status;
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
return (SCProcessCompletedRequest(SRB));
}
}
#endif
//
// show we're ready for a request. Don't show this for data if the
// minidriver does not want data on this stream.
//
CurrentInfo = &DeviceExtension->StreamDescriptor->StreamInfo;
for (i = 0; i < StreamObject->HwStreamObject.StreamNumber; i++) {
//
// index to next streaminfo structure
//
CurrentInfo++;
}
if (CurrentInfo->DataAccessible) {
StreamObject->ReadyForNextDataReq = TRUE;
}
StreamObject->ReadyForNextControlReq = TRUE;
//
// call locked routine to insert this stream in the list
//
SCInsertStreamInFilter(StreamObject, DeviceExtension);
//
// reference the filter so we won't be called to close the instance
// before all streams are closed.
//
ObReferenceObject(IrpStack->FileObject->RelatedFileObject);
//
// call routine to update the persisted properties for this pin, if
// any.
//
SCUpdatePersistedProperties(StreamObject, DeviceExtension,
IrpStack->FileObject);
//
// show one more instance of this pin opened.
//
AdditionalInfo = ((PFILTER_INSTANCE) IrpStack->FileObject->
RelatedFileObject->FsContext)->PinInstanceInfo;
AdditionalInfo[StreamObject->HwStreamObject.StreamNumber].
CurrentInstances++;
//
// construct on-the-fly properties for the stream, if necessary
//
if (StreamObject->HwStreamObject.HwClockObject.HwClockFunction) {
//
// create a property set describing the characteristics of the
// clock.
//
PropertyInfo = ExAllocatePool(PagedPool,
sizeof(ConstructedStreamHandlers) +
sizeof(ConstructedStreamProperties));
if (PropertyInfo) {
PropertyItem = (PKSPROPERTY_ITEM) ((ULONG_PTR) PropertyInfo +
sizeof(ConstructedStreamProperties));
RtlCopyMemory(PropertyInfo,
&ConstructedStreamProperties,
sizeof(ConstructedStreamProperties));
RtlCopyMemory(PropertyItem,
&ConstructedStreamHandlers,
sizeof(ConstructedStreamHandlers));
//
// patch the address of the handler
//
((PKSPROPERTY_SET) PropertyInfo)->PropertyItem = PropertyItem;
//
// modify the master clock property based on the support
// level.
//
if (0 == (StreamObject->HwStreamObject.HwClockObject.ClockSupportFlags
& CLOCK_SUPPORT_CAN_RETURN_STREAM_TIME)) {
DEBUG_BREAKPOINT();
PropertyItem->GetPropertyHandler
= NULL;
} // if cannot return stream time
StreamObject->ConstructedPropInfoSize =
SIZEOF_ARRAY(ConstructedStreamProperties);
StreamObject->ConstructedPropertyInfo =
(PKSPROPERTY_SET) PropertyInfo;
} // if property info
} // if clock function
} else {
ExFreePool(StreamObject);
} // if good status
//
// signal the event and complete the IRP.
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
SCProcessCompletedRequest(SRB);
return (Status);
}
NTSTATUS
SCSetMasterClockWhenDeviceInaccessible(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
/*++
Description:
This function look for special case in pin property request when the device
is inaccessible, probably by surprise removal. Yet we need to process the
SetMasterClock(NULL) so that the MC ref'ed by us can be released. The MC could
be on our pin or external.
This function should only be called in StreamDispatchIoControl. We look for the
Stream property.SetMasterClock(NULL). We returned SUCCESS if it is. Otherwise
we return STATUS_UNCESSFUL to indicate that we don't process it.
Arguments:
DeviceObject - Device Object for the device
Irp - the request packet
Return:
SUCCESS : If it is streamproperty.setmasterclock(NULL).
UNSUCCESSFUL : otherwise.
--*/
{
NTSTATUS Status=STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG InputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
ULONG OutputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
PKSPROPERTY Property;
if ( IOCTL_KS_PROPERTY == IrpStack->Parameters.DeviceIoControl.IoControlCode &&
InputBufferLength >= sizeof(KSPROPERTY) &&
OutputBufferLength >= sizeof( HANDLE )) {
//
// only ksproperty is in our interest.
//
try {
//
// Validate the pointers if the client is not trusted.
//
if (Irp->RequestorMode != KernelMode) {
ProbeForRead(IrpStack->Parameters.DeviceIoControl.Type3InputBuffer,
InputBufferLength,
sizeof(BYTE));
ProbeForRead(Irp->UserBuffer,
OutputBufferLength,
sizeof(DWORD));
}
} except (EXCEPTION_EXECUTE_HANDLER) {
return STATUS_UNSUCCESSFUL;
}
//
// Capture the property request
//
Property = (PKSPROPERTY)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
if ( KSPROPERTY_TYPE_SET == Property->Flags &&
KSPROPERTY_STREAM_MASTERCLOCK == Property->Id &&
IsEqualGUIDAligned(&Property->Set, &KSPROPSETID_Stream) &&
NULL == *(PHANDLE) Irp->UserBuffer ) {
//
// All match. Now process it. In theory we should call mini driver.
// But we did not before. To avoid potential regression in mini drivers
// we refrain from sending set_master_clock in this condition.
//
PSTREAM_OBJECT StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
DebugPrint((DebugLevelInfo, "SCSetMasterClockWhen:Devobj %x Irp %x\n",
DeviceObject, Irp));
if (StreamObject->MasterClockInfo) {
ObDereferenceObject(StreamObject->MasterClockInfo->ClockFileObject);
ExFreePool(StreamObject->MasterClockInfo);
StreamObject->MasterClockInfo = NULL;
}
return STATUS_SUCCESS;
}
}
return Status;
}
NTSTATUS
StreamDispatchIoControl
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Process an ioctl to the stream.
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject = (PSTREAM_OBJECT)
IrpStack->FileObject->FsContext;
PAGED_CODE();
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
DeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// show one more I/O pending & verify that we can actually do I/O.
//
Status = STATUS_INVALID_DEVICE_REQUEST;
///Status = SCShowIoPending(DeviceExtension, Irp);
if (DeviceExtension->Flags & DEVICE_FLAGS_DEVICE_INACCESSIBLE) {
///
// Note. When our device is surprised removed && we have ref on the master clock
// && we receive the stream property to set the master clock to null,
// we need to process it to deref the MC so the MC can be released.
// We will special case it here otherwise there will be big code churn. And
// the perf impact of this special case should be minimum for we get
// in here quite rarely.
//
// (the device is currently not accessible, so just return with error)
//
NTSTATUS StatusProcessed;
StatusProcessed = SCSetMasterClockWhenDeviceInaccessible( DeviceObject, Irp );
if ( NT_SUCCESS( StatusProcessed ) ) {
Status = StatusProcessed;
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return (Status);
}
//
// show one more IO pending.
//
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_KS_READ_STREAM:
//
// process read data request
//
DebugPrint((DebugLevelTrace, "'SCReadStream:Irp %x\n", Irp));
Status = SCProcessDataTransfer(DeviceExtension,
Irp,
SRB_READ_DATA);
break;
case IOCTL_KS_WRITE_STREAM:
//
// process write data request
//
DebugPrint((DebugLevelTrace, "'SCWriteStream:Irp %x\n", Irp));
Status = SCProcessDataTransfer(DeviceExtension,
Irp,
SRB_WRITE_DATA);
break;
case IOCTL_KS_RESET_STATE:
{
BOOLEAN RequestIssued;
KSRESET *Reset,
ResetType;
Reset = (KSRESET *) IrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
if (Irp->RequestorMode != KernelMode) {
try {
ProbeForRead(Reset, sizeof(KSRESET), sizeof(ULONG));
ResetType = *Reset;
} except(EXCEPTION_EXECUTE_HANDLER) {
TRAP;
Status = GetExceptionCode();
break;
} // except
} // if !kernelmode
else {
//
// trusted kernel mode, just use it. #131858 prefixbug 17400
//
ResetType = *Reset;
}
ASSERT(ResetType == *Reset);
if (ResetType == KSRESET_BEGIN) {
StreamObject->InFlush = TRUE;
Status = SCSubmitRequest(SRB_BEGIN_FLUSH,
NULL,
0,
SCDequeueAndDeleteSrb,
DeviceExtension,
((PFILTER_INSTANCE)
(StreamObject->FilterInstance))
->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.
ReceiveControlPacket
);
StreamFlushIo(DeviceExtension, StreamObject);
} else {
Status = SCSubmitRequest(SRB_END_FLUSH,
NULL,
0,
SCDequeueAndDeleteSrb,
DeviceExtension,
((PFILTER_INSTANCE)
(StreamObject->FilterInstance))
->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.
ReceiveControlPacket
);
StreamObject->InFlush = FALSE;
} // if begin
break;
} // case reset
case IOCTL_KS_PROPERTY:
DebugPrint((DebugLevelTrace,
"'StreamDispatchIO: Property with Irp %x\n", Irp));
//
// assume that there are no minidriver properties.
//
Status = STATUS_PROPSET_NOT_FOUND;
//
// first try the minidriver's properties, giving it a chance to
// override our built in sets.
//
if (StreamObject->PropInfoSize) {
ASSERT( StreamObject->PropertyInfo );
Status = KsPropertyHandler(Irp,
StreamObject->PropInfoSize,
StreamObject->PropertyInfo);
} // if minidriver props
//
// if the minidriver did not support it, try our on the fly set.
//
if ((Status == STATUS_PROPSET_NOT_FOUND) ||
(Status == STATUS_NOT_FOUND)) {
if (StreamObject->ConstructedPropertyInfo) {
Status = KsPropertyHandler(Irp,
StreamObject->ConstructedPropInfoSize,
StreamObject->ConstructedPropertyInfo);
} // if constructed exists
} // if not found
//
// if neither supported it, try our built-in set.
//
if ((Status == STATUS_PROPSET_NOT_FOUND) ||
(Status == STATUS_NOT_FOUND)) {
Status =
KsPropertyHandler(Irp,
SIZEOF_ARRAY(StreamProperties),
(PKSPROPERTY_SET) StreamProperties);
} // if property not found
break;
case IOCTL_KS_ENABLE_EVENT:
DebugPrint((DebugLevelTrace,
"'StreamDispatchIO: Enable event with Irp %x\n", Irp));
Status = KsEnableEvent(Irp,
StreamObject->EventInfoCount,
StreamObject->EventInfo,
NULL, 0, NULL);
break;
case IOCTL_KS_DISABLE_EVENT:
{
KSEVENTS_LOCKTYPE LockType;
PVOID LockObject;
DebugPrint((DebugLevelTrace,
"'StreamDispatchIO: Disable event with Irp %x\n", Irp));
//
// determine the type of lock necessary based on whether we are
// using interrupt or spinlock synchronization.
//
#if DBG
if (DeviceExtension->SynchronizeExecution == SCDebugKeSynchronizeExecution) {
#else
if (DeviceExtension->SynchronizeExecution == KeSynchronizeExecution) {
#endif
LockType = KSEVENTS_INTERRUPT;
LockObject = DeviceExtension->InterruptObject;
} else {
LockType = KSEVENTS_SPINLOCK;
LockObject = &DeviceExtension->SpinLock;
}
Status = KsDisableEvent(Irp,
&StreamObject->NotifyList,
LockType,
LockObject);
}
break;
case IOCTL_KS_METHOD:
#ifdef ENABLE_KS_METHODS
DebugPrint((DebugLevelTrace,
"'StreamDispatchIO: Method in Irp %x\n", Irp));
//
// assume that there are no minidriver properties.
//
Status = STATUS_PROPSET_NOT_FOUND;
if ((Status == STATUS_PROPSET_NOT_FOUND) ||
(Status == STATUS_NOT_FOUND)) {
if (StreamObject->MethodInfo) {
Status = KsMethodHandler(Irp,
StreamObject->MethodInfoSize,
StreamObject->MethodInfo);
} // if constructed exists
} // if not found
break;
#else
Status = STATUS_PROPSET_NOT_FOUND;
break;
#endif
}
if (Status != STATUS_PENDING) {
SCCompleteIrp(Irp, Status, DeviceExtension);
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return Status;
}
NTSTATUS
SCStreamDeviceState
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PKSSTATE DeviceState
)
/*++
Routine Description:
Process get/set device state to the stream.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for device state property
DeviceState - state to which the device is to be set
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject;
BOOLEAN RequestIssued;
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
PFILTER_INSTANCE FilterInstance;
PADDITIONAL_PIN_INFO AdditionalInfo;
PAGED_CODE();
DebugPrint((DebugLevelTrace, "'SCStreamDeviceState:Irp %x, State = %x\n",
Irp, *DeviceState));
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
FilterInstance = ((PFILTER_INSTANCE) (StreamObject->FilterInstance));
AdditionalInfo = FilterInstance->PinInstanceInfo;
Status = STATUS_SUCCESS;
//
// Synchronize pin state changes
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
if (StreamObject->CurrentState == *DeviceState) {
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
return STATUS_SUCCESS;
}
switch (*DeviceState) {
case KSSTATE_RUN:
DebugPrint((DebugLevelTrace, "STREAM: KSSTATE_RUN on stream:%x\n",StreamObject));
break;
case KSSTATE_ACQUIRE:
DebugPrint((DebugLevelTrace, "STREAM: KSSTATE_ACQUIRE on stream:%x\n",StreamObject));
break;
case KSSTATE_PAUSE:
DebugPrint((DebugLevelTrace, "STREAM: KSSTATE_PAUSE on stream:%x\n",StreamObject));
break;
case KSSTATE_STOP:
DebugPrint((DebugLevelTrace, "STREAM: KSSTATE_STOP on stream:%x\n",StreamObject));
break;
default:
DebugPrint((DebugLevelTrace, "STREAM: Invalid Device State\n"));
break;
}
DebugPrint((DebugLevelTrace, "STREAM: Stream->AllocatorFileObject:%x\n",StreamObject->AllocatorFileObject));
DebugPrint((DebugLevelTrace, "STREAM: Stream->NextFileObject:%x\n",StreamObject->NextFileObject));
DebugPrint((DebugLevelTrace, "STREAM: Stream->FileObject:%x\n",StreamObject->FileObject));
DebugPrint((DebugLevelTrace, "STREAM: Stream->PinType:"));
if (StreamObject->PinType == IrpSource)
DebugPrint((DebugLevelTrace, "IrpSource\n"));
else if (StreamObject->PinType == IrpSink)
DebugPrint((DebugLevelTrace, "IrpSink\n"));
else {
DebugPrint((DebugLevelTrace, "neither\n")); // this is a bug.
}
//
// send a set state SRB to the stream.
//
//
// GUBGUB: "we may need to send this if Status == STATUS_SUCCESS only"
// is a bugus concern since Status is inited to Success.
//
Status = SCSubmitRequest(SRB_SET_STREAM_STATE,
(PVOID) * DeviceState,
0,
SCDequeueAndDeleteSrb,
DeviceExtension,
((PFILTER_INSTANCE)
(StreamObject->FilterInstance))
->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.
ReceiveControlPacket
);
//
// if good status, set the new state in the stream object.
//
if (NT_SUCCESS(Status)) {
StreamObject->CurrentState = *DeviceState;
}
else {
DebugPrint((DebugLevelTrace, "STREAM: error sending DeviceState Irp\n"));
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
switch (*DeviceState) {
//
// 1. should start sourcing irps at pause
// 2. worker thread shutdown if pins are connected in certain order.......
// 3. check MSTEE bugs assigned to dalesat.
//
case KSSTATE_RUN:
if(StreamObject->PinType == IrpSource &&
StreamObject->StandardTransport)
{
Status = BeginTransfer(
FilterInstance,
StreamObject);
}
break;
case KSSTATE_ACQUIRE:
Status = STATUS_SUCCESS;
break;
case KSSTATE_PAUSE:
if (NT_SUCCESS (Status)) {
if(StreamObject->PinType == IrpSource &&
StreamObject->StandardTransport)
{
Status = PrepareTransfer(
FilterInstance,
StreamObject);
}
}
break;
case KSSTATE_STOP:
if(StreamObject->PinType == IrpSource &&
StreamObject->StandardTransport)
Status = EndTransfer( FilterInstance, StreamObject );
else
//
// cancel any pending I/O on this stream if the state is STOP.
//
StreamFlushIo(DeviceExtension, StreamObject);
break;
default:
break;
}
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
return (Status);
}
#else
PAGED_CODE();
DebugPrint((DebugLevelTrace, "'SCStreamDeviceState:Irp %x, State = %x\n",
Irp, *DeviceState));
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
//
// cancel any pending I/O on this stream if the state is STOP.
//
if (*DeviceState == KSSTATE_STOP) {
StreamFlushIo(DeviceExtension, StreamObject);
}
//
// send a set state SRB to the stream.
//
DebugPrint((DebugLevelTrace,
"'SetStreamState: State %x with Irp %x\n", *DeviceState, Irp));
Status = SCSubmitRequest(SRB_SET_STREAM_STATE,
(PVOID) * DeviceState,
0,
SCDequeueAndDeleteSrb,
DeviceExtension,
((PFILTER_INSTANCE)
(StreamObject->FilterInstance))
->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.
ReceiveControlPacket
);
//
// if good status, set the new state in the stream object.
//
if (NT_SUCCESS(Status)) {
StreamObject->CurrentState = *DeviceState;
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
#endif
NTSTATUS
SCGetStreamDeviceStateCallback
(
IN PSTREAM_REQUEST_BLOCK SRB
)
{
// yep, its a do nothing routine.
return (SRB->HwSRB.Status);
}
NTSTATUS
SCGetStreamDeviceState
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PKSSTATE DeviceState
)
/*++
Routine Description:
Process get device state to the stream.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for device state property
DeviceState - state to which the device is to be set
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject;
BOOLEAN RequestIssued;
PSTREAM_REQUEST_BLOCK SRB;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
//
// send a get state SRB to the stream.
//
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
DebugPrint((DebugLevelTrace,
"'GetStreamState: State with StreamObj:%x\n", StreamObject));
if (StreamObject->PinType == IrpSource)
DebugPrint((DebugLevelTrace, "'GetStreamState: Is IrpSource\n"));
else
DebugPrint((DebugLevelTrace,"'GetStreamState: Is IrpSink\n"));
#endif
//
// set the returned data size to the correct size regardless of status.
//
Irp->IoStatus.Information = sizeof(KSSTATE);
Status = SCSubmitRequest(SRB_GET_STREAM_STATE,
(PVOID) DeviceState,
0,
SCGetStreamDeviceStateCallback,
DeviceExtension,
((PFILTER_INSTANCE)
(StreamObject->FilterInstance))
->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.
ReceiveControlPacket
);
SRB = (PSTREAM_REQUEST_BLOCK) Irp->Tail.Overlay.DriverContext[0];
*DeviceState = SRB->HwSRB.CommandData.StreamState;
SCDequeueAndDeleteSrb(SRB);
//
// if not supported, return the last known state of the stream.
//
if ((Status == STATUS_NOT_SUPPORTED)
|| (Status == STATUS_NOT_IMPLEMENTED)) {
Status = STATUS_SUCCESS;
*DeviceState = StreamObject->CurrentState;
}
DebugPrint((DebugLevelTrace,
"'GetStreamState: Returning:%x: DeviceState:", Status));
switch (*DeviceState) {
case KSSTATE_RUN:
DebugPrint((DebugLevelTrace, "KSSTATE_RUN\n"));
break;
case KSSTATE_ACQUIRE:
DebugPrint((DebugLevelTrace, "KSSTATE_AQUIRE\n"));
break;
case KSSTATE_PAUSE:
DebugPrint((DebugLevelTrace, "KSSTATE_PAUSE\n"));
break;
case KSSTATE_STOP:
DebugPrint((DebugLevelTrace, "KSSTATE_STOP\n"));
break;
default:
DebugPrint((DebugLevelTrace, "Invalid Device State\n"));
break;
}
return (Status);
}
NTSTATUS
SCStreamDeviceRate
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PKSRATE DeviceRate
)
/*++
Routine Description:
Process set device rate to the stream.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for device state property
DeviceRate - rate at which the device is to be set
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject;
BOOLEAN RequestIssued;
PAGED_CODE();
DebugPrint((DebugLevelTrace, "'SCStreamDeviceRate:Irp %x, Rate = %x\n",
Irp, *DeviceRate));
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
//
// send a set rate SRB to the stream.
//
Status = SCSubmitRequest(SRB_SET_STREAM_RATE,
(PVOID) DeviceRate,
0,
SCDequeueAndDeleteSrb,
DeviceExtension,
((PFILTER_INSTANCE)
(StreamObject->FilterInstance))
->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.
ReceiveControlPacket
);
//
// change STATUS_NOT_IMPLEMENTED to STATUS_NOT_FOUND so that the proxy
// does not get confused (GUBGUB). A necessary mapping between r0 and r3
// worlds.
//
if (Status == STATUS_NOT_IMPLEMENTED) {
Status = STATUS_NOT_FOUND;
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
SCStreamDeviceRateCapability
(
IN PIRP Irp,
IN PKSRATE_CAPABILITY RateCap,
IN OUT PKSRATE DeviceRate
)
/*++
Routine Description:
Process set device rate to the stream.
Arguments:
Irp - pointer to the irp
RateCap - pointer to the information for device state property
DeviceRate - rate to which the device was set
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject;
BOOLEAN RequestIssued;
PAGED_CODE();
DebugPrint((DebugLevelTrace, "'SCStreamDeviceRate:Irp %x, Rate = %x\n",
Irp, *DeviceRate));
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
//
// presuppose a successful completion, which means that the minidriver
// can normalize rate to 1.
//
*DeviceRate = RateCap->Rate;
DeviceRate->Rate = 1000;
Irp->IoStatus.Information = sizeof(KSRATE);
//
// send a set rate SRB to the stream.
//
Status = SCSubmitRequest(
SRB_PROPOSE_STREAM_RATE,
(PVOID) RateCap,
0,
SCDequeueAndDeleteSrb,
DeviceExtension,
((PFILTER_INSTANCE)(StreamObject->FilterInstance))->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.ReceiveControlPacket
);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
//
// change STATUS_NOT_IMPLEMENTED to STATUS_NOT_FOUND so that the proxy
// does not get confused (GUBGUB). A necessary mapping between r0 and r3
// worlds.
//
if (Status == STATUS_NOT_IMPLEMENTED) {
Status = STATUS_NOT_FOUND;
}
return (Status);
}
NTSTATUS
SCStreamProposeNewFormat
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PKSDATAFORMAT Format
)
/*++
Routine Description:
Process propose data format to the stream.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for propose format property
DeviceState - state to which the device is to be set
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject;
BOOLEAN RequestIssued;
PAGED_CODE();
DebugPrint((DebugLevelTrace, "'SCStreamProposeNewFormat:Irp %x, Format = %x\n",
Irp, *Format));
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
//
// send a propose format SRB to the stream.
//
Status = SCSubmitRequest(SRB_PROPOSE_DATA_FORMAT,
(PVOID) Format,
IrpStack->Parameters.DeviceIoControl.OutputBufferLength,
SCDequeueAndDeleteSrb,
DeviceExtension,
((PFILTER_INSTANCE)
(StreamObject->FilterInstance))
->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.
ReceiveControlPacket
);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
//
// change STATUS_NOT_IMPLEMENTED to STATUS_NOT_FOUND so that the proxy
// does not get confused (GUBGUB). A necessary mapping between r0 and r3
// worlds.
//
if (Status == STATUS_NOT_IMPLEMENTED) {
Status = STATUS_NOT_FOUND;
}
return (Status);
}
NTSTATUS
SCStreamSetFormat
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PKSDATAFORMAT Format
)
/*++
Routine Description:
Sets the data format on the stream.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for the set format property
DeviceState - state to which the device is to be set
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject;
BOOLEAN RequestIssued;
PAGED_CODE();
DebugPrint((DebugLevelTrace, "'SCStreamSetFormat:Irp %x, Format = %x\n",
Irp, *Format));
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
//
// send a set format SRB to the stream.
//
Status = SCSubmitRequest(SRB_SET_DATA_FORMAT,
(PVOID) Format,
IrpStack->Parameters.DeviceIoControl.OutputBufferLength,
SCDequeueAndDeleteSrb,
DeviceExtension,
((PFILTER_INSTANCE)
(StreamObject->FilterInstance))
->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
StreamObject->HwStreamObject.
ReceiveControlPacket
);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
//
// change STATUS_NOT_IMPLEMENTED to STATUS_NOT_FOUND so that the proxy
// does not get confused (GUBGUB). A necessary mapping between r0 and r3
// worlds.
//
if (Status == STATUS_NOT_IMPLEMENTED) {
Status = STATUS_NOT_FOUND;
}
return (Status);
}
NTSTATUS
StreamClassMinidriverDeviceGetProperty
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PVOID PropertyInfo
)
/*++
Routine Description:
Process get property to the device.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for the property
PropertyInfo - buffer to return the property data to
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PAGED_CODE();
Status = SCMinidriverDevicePropertyHandler(SRB_GET_DEVICE_PROPERTY,
Irp,
Property,
PropertyInfo
);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
StreamClassMinidriverDeviceSetProperty
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PVOID PropertyInfo
)
/*++
Routine Description:
Process set property to the device.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for the property
PropertyInfo - buffer that contains the property info
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PAGED_CODE();
Status = SCMinidriverDevicePropertyHandler(SRB_SET_DEVICE_PROPERTY,
Irp,
Property,
PropertyInfo);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
StreamClassMinidriverStreamGetProperty
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PVOID PropertyInfo
)
/*++
Routine Description:
Process get property of a stream.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for the property
PropertyInfo - buffer to return the property data to
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PAGED_CODE();
Status = SCMinidriverStreamPropertyHandler(SRB_GET_STREAM_PROPERTY,
Irp,
Property,
PropertyInfo
);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
StreamClassMinidriverStreamSetProperty
(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PVOID PropertyInfo
)
/*++
Routine Description:
Process set property to a stream.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for the property
PropertyInfo - buffer that contains the property info
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PAGED_CODE();
Status = SCMinidriverStreamPropertyHandler(SRB_SET_STREAM_PROPERTY,
Irp,
Property,
PropertyInfo);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
#ifdef ENABLE_KS_METHODS
NTSTATUS
StreamClassMinidriverStreamMethod(
IN PIRP Irp,
IN PKSMETHOD Method,
IN OUT PVOID MethodInfo)
/*++
Routine Description:
Process get property of a stream.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for the property
PropertyInfo - buffer to return the property data to
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PAGED_CODE();
Status = SCMinidriverStreamMethodHandler(SRB_STREAM_METHOD,
Irp,
Method,
MethodInfo
);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
StreamClassMinidriverDeviceMethod(
IN PIRP Irp,
IN PKSMETHOD Method,
IN OUT PVOID MethodInfo)
/*++
Routine Description:
Process get property of a device.
Arguments:
Irp - pointer to the irp
Property - pointer to the information for the property
PropertyInfo - buffer to return the property data to
Return Value:
NTSTATUS returned as appropriate.
--*/
{
NTSTATUS Status;
PAGED_CODE();
Status = SCMinidriverDeviceMethodHandler(SRB_DEVICE_METHOD,
Irp,
Method,
MethodInfo
);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
#endif
NTSTATUS
StreamClassEnableEventHandler(
IN PIRP Irp,
IN PKSEVENTDATA EventData,
IN PKSEVENT_ENTRY EventEntry
)
/*++
Routine Description:
Process an enable event for the stream.
Arguments:
Irp - pointer to the IRP
EventData - data describing the event
EventEntry - more info about the event :-)
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject;
NTSTATUS Status;
ULONG EventSetID;
KIRQL irql;
HW_EVENT_DESCRIPTOR Event;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
//
// clock events are indicated on the pin by the minidriver, for
// simplicity.
// but, we will receive clock events on the clock's handle. We need to
// determine if this file object is the clock's or the pin's.
//
StreamObject = IrpStack->FileObject->FsContext;
if ((PVOID) StreamObject == IrpStack->FileObject->FsContext2) {
StreamObject = ((PCLOCK_INSTANCE) StreamObject)->StreamObject;
}
//
// compute the index of the event set.
//
// this value is calculated by subtracting the base event set
// pointer from the requested event set pointer.
//
//
EventSetID = (ULONG) ((ULONG_PTR) EventEntry->EventSet -
(ULONG_PTR) StreamObject->EventInfo)
/ sizeof(KSEVENT_SET);
//
// build an event info structure to represent the event to the
// minidriver.
//
Event.EnableEventSetIndex = EventSetID;
Event.EventEntry = EventEntry;
Event.StreamObject = &StreamObject->HwStreamObject;
Event.Enable = TRUE;
Event.EventData = EventData;
//
// acquire the spinlock to protect the interrupt structures
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql);
//
// call the synchronized routine to add the event to the list
//
Status = DeviceExtension->SynchronizeExecution(
DeviceExtension->InterruptObject,
(PKSYNCHRONIZE_ROUTINE) SCEnableEventSynchronized,
&Event);
KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);
return (Status);
}
VOID
StreamClassDisableEventHandler(
IN PFILE_OBJECT FileObject,
IN PKSEVENT_ENTRY EventEntry
)
/*++
Routine Description:
Process an event disable for the stream.
NOTE: we are either at interrupt IRQL or the spinlock is taken on this call!
Arguments:
FileObject - file object for the pin
EventEntry - info about the event
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension;
PSTREAM_OBJECT StreamObject;
HW_EVENT_DESCRIPTOR Event;
//
// clock events are indicated on the pin by the minidriver, for
// simplicity.
// but, we will receive clock events on the clock's handle. We need to
// determine if this file object is the clock's or the pin's.
//
StreamObject = FileObject->FsContext;
if ((PVOID) StreamObject == FileObject->FsContext2) {
StreamObject = ((PCLOCK_INSTANCE) StreamObject)->StreamObject;
}
DeviceExtension = StreamObject->DeviceExtension;
//
// build an event info structure to represent the event to the
// minidriver.
//
Event.EventEntry = EventEntry;
Event.StreamObject = &StreamObject->HwStreamObject;
Event.Enable = FALSE;
if (StreamObject->HwStreamObject.HwEventRoutine) {
//
// call the minidriver. ignore the status. note that we are
// already at the correct synchronization level.
//
StreamObject->HwStreamObject.HwEventRoutine(&Event);
} // if eventroutine
//
// remove the event from the list.
//
RemoveEntryList(&EventEntry->ListEntry);
}
NTSTATUS
StreamClassEnableDeviceEventHandler(
IN PIRP Irp,
IN PKSEVENTDATA EventData,
IN PKSEVENT_ENTRY EventEntry
)
/*++
Routine Description:
Process an enable event for the device.
Arguments:
Irp - pointer to the IRP
EventData - data describing the event
EventEntry - more info about the event :-)
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
NTSTATUS Status;
ULONG EventSetID;
KIRQL irql;
HW_EVENT_DESCRIPTOR Event;
PFILTER_INSTANCE FilterInstance;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION)
(IrpStack->DeviceObject)->DeviceExtension;
FilterInstance = IrpStack->FileObject->FsContext;
//
// compute the index of the event set.
//
// this value is calculated by subtracting the base event set
// pointer from the requested event set pointer.
//
//
EventSetID = (ULONG) ((ULONG_PTR) EventEntry->EventSet -
(ULONG_PTR) FilterInstance->EventInfo)
/ sizeof(KSEVENT_SET);
//
// build an event info structure to represent the event to the
// minidriver.
//
Event.EnableEventSetIndex = EventSetID;
Event.EventEntry = EventEntry;
Event.DeviceExtension = DeviceExtension->HwDeviceExtension;
IF_MF( Event.HwInstanceExtension = FilterInstance->HwInstanceExtension; )
Event.Enable = TRUE;
Event.EventData = EventData;
//
// acquire the spinlock to protect the interrupt structures
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql);
//
// call the synchronized routine to add the event to the list
//
Status = DeviceExtension->SynchronizeExecution(
DeviceExtension->InterruptObject,
(PKSYNCHRONIZE_ROUTINE) SCEnableDeviceEventSynchronized,
&Event);
KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);
return (Status);
}
VOID
StreamClassDisableDeviceEventHandler(
IN PFILE_OBJECT FileObject,
IN PKSEVENT_ENTRY EventEntry
)
/*++
Routine Description:
Process an event disable for the stream.
NOTE: we are either at interrupt IRQL or the spinlock is taken on this call!
Arguments:
FileObject - file object for the pin
EventEntry - info about the event
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension;
HW_EVENT_DESCRIPTOR Event;
PFILTER_INSTANCE FilterInstance;
FilterInstance = (PFILTER_INSTANCE) FileObject->FsContext;
ASSERT_FILTER_INSTANCE( FilterInstance );
DeviceExtension = FilterInstance->DeviceExtension;
//
// build an event info structure to represent the event to the
// minidriver.
//
Event.EventEntry = EventEntry;
Event.DeviceExtension = DeviceExtension->HwDeviceExtension;
Event.Enable = FALSE;
Event.HwInstanceExtension = FilterInstance->HwInstanceExtension;
if (FilterInstance->HwEventRoutine) {
//
// call the minidriver. ignore the status. note that we are
// already at the correct synchronization level.
//
FilterInstance->HwEventRoutine(&Event);
}
//
// remove the event from the list.
//
RemoveEntryList(&EventEntry->ListEntry);
}
NTSTATUS
FilterDispatchIoControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
/*++
Routine Description:
This routine receives control IRP's for the device.
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
The IRP status is set as appropriate
--*/
{
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
PFILTER_INSTANCE FilterInstance;
NTSTATUS Status;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp);
Status = SCShowIoPending(DeviceExtension, Irp);
if ( !NT_SUCCESS ( Status )) {
//
// the device is currently not accessible, so just return.
//
return (Status);
}
switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_KS_PROPERTY:
Status = STATUS_PROPSET_NOT_FOUND;
FilterInstance = (PFILTER_INSTANCE) IrpStack->FileObject->FsContext;
ASSERT( FilterInstance );
if (FilterInstance->StreamDescriptor->
StreamHeader.NumDevPropArrayEntries) {
ASSERT( FilterInstance->DevicePropertiesArray );
Status = KsPropertyHandler(Irp,
FilterInstance->StreamDescriptor->
StreamHeader.NumDevPropArrayEntries,
FilterInstance->DevicePropertiesArray);
}
if ((Status == STATUS_PROPSET_NOT_FOUND) ||
(Status == STATUS_NOT_FOUND)) {
Status = KsPropertyHandler(Irp,
SIZEOF_ARRAY(FilterPropertySets),
(PKSPROPERTY_SET) &FilterPropertySets);
}
break;
case IOCTL_KS_ENABLE_EVENT:
DebugPrint((DebugLevelTrace,
"'FilterDispatchIO: Enable event with Irp %x\n", Irp));
FilterInstance = (PFILTER_INSTANCE) IrpStack->FileObject->FsContext;
Status = KsEnableEvent(Irp,
FilterInstance->EventInfoCount,
FilterInstance->EventInfo,
NULL, 0, NULL);
break;
case IOCTL_KS_DISABLE_EVENT:
{
KSEVENTS_LOCKTYPE LockType;
PVOID LockObject;
DebugPrint((DebugLevelTrace,
"'FilterDispatchIO: Disable event with Irp %x\n", Irp));
//
// determine the type of lock necessary based on whether we are
// using interrupt or spinlock synchronization.
//
#if DBG
if (DeviceExtension->SynchronizeExecution == SCDebugKeSynchronizeExecution) {
#else
if (DeviceExtension->SynchronizeExecution == KeSynchronizeExecution) {
#endif
LockType = KSEVENTS_INTERRUPT;
LockObject = DeviceExtension->InterruptObject;
} else {
LockType = KSEVENTS_SPINLOCK;
LockObject = &DeviceExtension->SpinLock;
}
FilterInstance = (PFILTER_INSTANCE) IrpStack->
FileObject->FsContext;
Status = KsDisableEvent(Irp,
&FilterInstance->NotifyList,
LockType,
LockObject);
}
break;
case IOCTL_KS_METHOD:
Status = STATUS_PROPSET_NOT_FOUND;
break;
default:
Status = STATUS_NOT_SUPPORTED;
}
if (Status != STATUS_PENDING) {
SCCompleteIrp(Irp, Status, DeviceExtension);
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
ClockDispatchIoControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine receives control IRP's for the clock.
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
The IRP status is set as appropriate
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
PSTREAM_OBJECT StreamObject;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp);
Status = SCShowIoPending(DeviceExtension, Irp);
if ( !NT_SUCCESS ( Status )) {
//
// the device is currently not accessible, so just return.
//
return (Status);
}
switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_KS_PROPERTY:
Status = KsPropertyHandler(Irp,
SIZEOF_ARRAY(ClockPropertySets),
(PKSPROPERTY_SET) & ClockPropertySets);
break;
case IOCTL_KS_ENABLE_EVENT:
DebugPrint((DebugLevelTrace,
"'StreamDispatchIO: Enable event with Irp %x\n", Irp));
//
// locate the stream object of the pin for this clock from the IRP.
// note that we use the event set of the pin for the clock events.
//
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->RelatedFileObject->
FsContext;
ASSERT(StreamObject);
Status = KsEnableEvent(Irp,
StreamObject->EventInfoCount,
StreamObject->EventInfo,
NULL, 0, NULL);
break;
case IOCTL_KS_DISABLE_EVENT:
{
KSEVENTS_LOCKTYPE LockType;
PVOID LockObject;
//
// locate the stream object of the pin for this clock from the
// IRP.
// note that we use the event set of the pin for the clock
// events.
//
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->RelatedFileObject->
FsContext;
ASSERT(StreamObject);
DebugPrint((DebugLevelTrace,
"'StreamDispatchIO: Disable event with Irp %x\n", Irp));
//
// determine the type of lock necessary based on whether we are
// using interrupt or spinlock synchronization.
//
#if DBG
if (DeviceExtension->SynchronizeExecution == SCDebugKeSynchronizeExecution) {
#else
if (DeviceExtension->SynchronizeExecution == KeSynchronizeExecution) {
#endif
LockType = KSEVENTS_INTERRUPT;
LockObject = DeviceExtension->InterruptObject;
} else {
LockType = KSEVENTS_SPINLOCK;
LockObject = &DeviceExtension->SpinLock;
}
Status = KsDisableEvent(Irp,
&StreamObject->NotifyList,
LockType,
LockObject);
}
break;
case IOCTL_KS_METHOD:
#ifdef ENABLE_KS_METHODS
Status = STATUS_PROPSET_NOT_FOUND;
{
PFILTER_INSTANCE FilterInstance;
PHW_STREAM_DESCRIPTOR StreamDescriptor;
FilterInstance = (PFILTER_INSTANCE) IrpStack->FileObject->FsContext;
if ( NULL == FilterInstance->StreamDescriptor ) {
StreamDescriptor = DeviceExtension->FilterTypeInfos
[FilterInstance->FilterTypeIndex].StreamDescriptor;
}
else {
StreamDescriptor = FilterInstance->StreamDescriptor;
}
Status = KsMethodHandler(Irp,
StreamDescriptor->
StreamHeader.NumDevMethodArrayEntries,
FilterInstance->DeviceMethodsArray);
}
break;
#else
Status = STATUS_PROPSET_NOT_FOUND;
break;
#endif
default:
DEBUG_BREAKPOINT();
Status = STATUS_NOT_SUPPORTED;
}
if (Status != STATUS_PENDING) {
SCCompleteIrp(Irp, Status, DeviceExtension);
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
FilterDispatchClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine receives CLOSE IRP's for the device/instance
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
The IRP status is set as appropriate
--*/
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PFILTER_INSTANCE FilterInstance =
(PFILTER_INSTANCE) IrpStack->FileObject->FsContext;
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
NTSTATUS Status;
BOOLEAN IsGlobal;
BOOLEAN RequestIssued;
PAGED_CODE();
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
//
// remove the filter instance structure from our list
//
#if DBG
IFN_MF(
if (DeviceExtension->NumberOfGlobalInstances == 1) {
ASSERT(IsListEmpty(&FilterInstance->FirstStream));
} // if global = 1
)
#endif
//
// check to see if this is a global instance
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
DebugPrint(( DebugLevelInfo,
"Closing FilterInstance %x NeameExts=%x\n",
FilterInstance,
DeviceExtension->NumberOfNameExtensions));
if ( 0 == DeviceExtension->FilterExtensionSize &&
DeviceExtension->NumberOfOpenInstances > 1) {
PFILE_OBJECT pFileObject;
//
// this is not the last close of the global instance, so just
// deref this instance and return good status.
//
DeviceExtension->NumberOfOpenInstances--;
DebugPrint(( DebugLevelInfo,
"DevExt=%x Close OpenCount=%x\n",
DeviceExtension,
DeviceExtension->NumberOfOpenInstances));
IrpStack->FileObject->FsContext = NULL;
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
ObDereferenceObject(DeviceObject);
SCDereferenceDriver(DeviceExtension);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (SCCompleteIrp(Irp, STATUS_SUCCESS, DeviceExtension));
}
//
// we now know that this is either a local instance, or the last open of
// the global instance. process the close.
//
if ( 0 != DeviceExtension->FilterExtensionSize ) {
Status = SCSubmitRequest(SRB_CLOSE_DEVICE_INSTANCE,
NULL,
0,
SCCloseInstanceCallback,
DeviceExtension,
FilterInstance->HwInstanceExtension,
NULL,
Irp,
&RequestIssued,
&DeviceExtension->PendingQueue,
(PVOID) DeviceExtension->
MinidriverData->HwInitData.
HwReceivePacket);
if (!RequestIssued) {
DEBUG_BREAKPOINT();
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
SCCompleteIrp(Irp, Status, DeviceExtension);
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
} else { // if instanceextension
//
// the minidriver doesn't need to be called as it does not support
// instancing. dereference the instance now.
//
DeviceExtension->NumberOfOpenInstances--;
DebugPrint(( DebugLevelInfo,
"DevExt=%x Close OpenCount=%x\n",
DeviceExtension,
DeviceExtension->NumberOfOpenInstances));
//
// we are ready to free the instance. if it is global, just zero
// the pointer. if it is local, remove it from the list.
//
IrpStack->FileObject->FsContext = NULL;
DebugPrint((DebugLevelInfo, "FilterCloseInstance=%x\n", FilterInstance));
if ( !IsListEmpty( &DeviceExtension->FilterInstanceList)) {
//
// The list could be emptied at surprise removal
// where all instances are removed. so when come in here
// check it first. Event is taken, check is safe.
//
RemoveEntryList(&FilterInstance->NextFilterInstance);
SciFreeFilterInstance( FilterInstance );
FilterInstance = NULL;
}
else {
//
// it has been closed by surprise removal. mark it.
//
FilterInstance= NULL;
}
//
// if this is the last close of a removed device, detach from
// the PDO now, since we couldn't do it on the remove. note that
// we will NOT do this if the NT style surprise remove IRP has been
// received, since we'll still receive an IRP_REMOVE in that case
// after
// this close.
//
if ((DeviceExtension->NumberOfOpenInstances == 0) &&
(DeviceExtension->Flags & DEVICE_FLAGS_DEVICE_INACCESSIBLE) &&
!(DeviceExtension->Flags & DEVICE_FLAGS_SURPRISE_REMOVE_RECEIVED)) {
DebugPrint((DebugLevelInfo,
"SCPNP: detaching %x from %x\n",
DeviceObject,
DeviceExtension->AttachedPdo));
//
// detach could happen at remove, check before leap.
// event is taken, check is safe.
//
if ( NULL != DeviceExtension->AttachedPdo ) {
IoDetachDevice(DeviceExtension->AttachedPdo);
DeviceExtension->AttachedPdo = NULL;
}
}
else {
//
// check if we can power down the device.
//
SCCheckPowerDown(DeviceExtension);
} // if inaccessible
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
if ( NULL != FilterInstance ) {
DebugPrint(( DebugLevelVerbose,
"Unregistering ReadWorker %x WriteWorker %x\n",
FilterInstance->WorkerRead,
FilterInstance->WorkerWrite));
KsUnregisterWorker( FilterInstance->WorkerRead );
KsUnregisterWorker( FilterInstance->WorkerWrite );
KsFreeObjectHeader(FilterInstance->DeviceHeader);
ExFreePool(FilterInstance);
}
SCDereferenceDriver(DeviceExtension);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
Status = SCCompleteIrp(Irp, STATUS_SUCCESS, DeviceExtension);
ObDereferenceObject(DeviceObject);
return (Status);
}
}
NTSTATUS
SCCloseInstanceCallback(
IN PSTREAM_REQUEST_BLOCK SRB
)
/*++
Routine Description:
Process the completion of an instance close.
Arguments:
SRB - address of the completed SRB
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
PIRP Irp = SRB->HwSRB.Irp;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PFILTER_INSTANCE FilterInstance =
(PFILTER_INSTANCE) SRB->HwSRB.HwInstanceExtension - 1;
NTSTATUS Status = SRB->HwSRB.Status;
KIRQL irql;
//
// Close should not fail. If it does, should clean up anyway
ASSERT( NT_SUCCESS(Status) && "Close Instance failed" );
///if (NT_SUCCESS(Status)) {
//
// we are ready to free the instance. if it is global, just zero
// the pointer. if it is local, remove it from the list.
//
DeviceExtension->NumberOfOpenInstances--;
KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql);
RemoveEntryList(&FilterInstance->NextFilterInstance);
//
// free the instance and return success.
//
KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);
//
// if this is the last close of a removed device, detach from
// the PDO now, since we couldn't do it on the remove.
//
if ((DeviceExtension->NumberOfOpenInstances == 0) &&
(DeviceExtension->Flags & DEVICE_FLAGS_DEVICE_INACCESSIBLE)) {
DebugPrint((DebugLevelTrace,
"'SCPNP: detaching from PDO\n"));
TRAP;
IoDetachDevice(DeviceExtension->AttachedPdo);
DeviceExtension->AttachedPdo = NULL;
}
//
// check if we can power down the device.
//
SCCheckPowerDown(DeviceExtension);
ObDereferenceObject(DeviceExtension->DeviceObject);
//
// free the instance and header and dereference the driver
//
SciFreeFilterInstance( FilterInstance );
///#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
///DebugPrint(( DebugLevelVerbose,
/// "Unregistering ReadWorker %x WriteWorker %x\n",
/// FilterInstance->WorkerRead,
/// FilterInstance->WorkerWrite));
///
///KsUnregisterWorker( FilterInstance->WorkerRead );
///KsUnregisterWorker( FilterInstance->WorkerWrite );
///#endif
///KsFreeObjectHeader(FilterInstance->DeviceHeader);
///ExFreePool(FilterInstance);
SCDereferenceDriver(DeviceExtension);
///} // if good status
//
// signal the event and complete the IRP.
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
SCProcessCompletedRequest(SRB);
return (Status);
}
NTSTATUS
StreamDispatchCleanup
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine receives CLEANUP IRP's for a stream
Arguments:
DeviceObject - device object for the device
Irp - The CLEANUP Irp
Return Value:
The IRP status set as appropriate
--*/
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation (Irp);
PSTREAM_OBJECT StreamObject =
(PSTREAM_OBJECT) IrpStack -> FileObject -> FsContext;
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) DeviceObject -> DeviceExtension;
BOOLEAN BreakClockCycle = FALSE;
KeWaitForSingleObject (
&DeviceExtension -> ControlEvent,
Executive,
KernelMode,
FALSE,
NULL
);
//
// If the stream in question is a source stream and it has not yet
// stopped the sourcing worker, it must be done at this point in time.
//
if (StreamObject -> CurrentState > KSSTATE_STOP &&
StreamObject -> PinType == IrpSource &&
StreamObject -> StandardTransport) {
EndTransfer (StreamObject -> FilterInstance, StreamObject);
}
//
// Check for the clock<->pin cycle and break it if present.
//
if (StreamObject -> MasterClockInfo) {
PFILE_OBJECT ClockFile = StreamObject -> MasterClockInfo ->
ClockFileObject;
if (ClockFile &&
ClockFile -> RelatedFileObject == StreamObject -> FileObject)
BreakClockCycle = TRUE;
}
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
//
// Synchronously submit an Irp down our own stack to get break the
// clock<->pin cycle. Otherwise, the stream can't close. The driver should
// guard against the clock disappearing while running. Stream class does
// on TOP of that if they do not.
//
if (BreakClockCycle) {
KSPROPERTY Property;
HANDLE NewClock = NULL;
ULONG BytesReturned;
NTSTATUS Status;
Property.Set = KSPROPSETID_Stream;
Property.Id = KSPROPERTY_STREAM_MASTERCLOCK;
Property.Flags = KSPROPERTY_TYPE_SET;
Status =
KsSynchronousIoControlDevice (
StreamObject -> FileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&Property,
sizeof (KSPROPERTY),
&NewClock,
sizeof (HANDLE),
&BytesReturned
);
ASSERT (NT_SUCCESS (Status));
}
Irp -> IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
StreamDispatchClose
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine receives CLOSE IRP's for a stream
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
The IRP status is set as appropriate
--*/
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PSTREAM_OBJECT StreamObject =
(PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
NTSTATUS Status;
BOOLEAN RequestIssued;
KSEVENTS_LOCKTYPE LockType;
PVOID LockObject;
PAGED_CODE();
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
ASSERT(IsListEmpty(&StreamObject->ControlPendingQueue));
ASSERT(IsListEmpty(&StreamObject->DataPendingQueue));
//
// free events associated with this stream. this will cause our remove
// handler to be called for each, and will hence notify the minidriver.
//
//
// determine the type of lock necessary based on whether we are
// using interrupt or spinlock synchronization.
//
#if DBG
if (DeviceExtension->SynchronizeExecution == SCDebugKeSynchronizeExecution) {
#else
if (DeviceExtension->SynchronizeExecution == KeSynchronizeExecution) {
#endif
LockType = KSEVENTS_INTERRUPT;
LockObject = DeviceExtension->InterruptObject;
} else {
LockType = KSEVENTS_SPINLOCK;
LockObject = &DeviceExtension->SpinLock;
}
KsFreeEventList(IrpStack->FileObject,
&StreamObject->NotifyList,
LockType,
LockObject);
//
// call the minidriver to close the stream. processing will continue
// when the callback procedure is called.
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
Status = SCSubmitRequest(SRB_CLOSE_STREAM,
NULL,
0,
SCCloseStreamCallback,
DeviceExtension,
StreamObject->
FilterInstance->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&DeviceExtension->PendingQueue,
(PVOID) DeviceExtension->
MinidriverData->HwInitData.
HwReceivePacket);
if (!RequestIssued) {
DEBUG_BREAKPOINT();
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
SCCompleteIrp(Irp, Status, DeviceExtension);
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
SCCloseStreamCallback(
IN PSTREAM_REQUEST_BLOCK SRB
)
/*++
Routine Description:
Process the completion of a stream close.
Arguments:
SRB - address of the completed SRB
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
PADDITIONAL_PIN_INFO AdditionalInfo;
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD(
SRB->HwSRB.StreamObject,
STREAM_OBJECT,
HwStreamObject
);
PIRP Irp = SRB->HwSRB.Irp;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
KIRQL Irql;
NTSTATUS Status = SRB->HwSRB.Status;
ASSERT( NT_SUCCESS(Status) && "CloseStream Failed by Minidriver");
//
// Close should not fail. Even it does, we want to clean up.
//
// if (NT_SUCCESS(Status)) {
//
// show one fewer instance open
//
DebugPrint((DebugLevelInfo, "SC Closing StreamObject %x\n", StreamObject));
AdditionalInfo = ((PFILTER_INSTANCE) IrpStack->FileObject->
RelatedFileObject->FsContext)->PinInstanceInfo;
AdditionalInfo[StreamObject->HwStreamObject.StreamNumber].
CurrentInstances--;
//
// free the object header for the stream
//
KsFreeObjectHeader(StreamObject->ComObj.DeviceHeader);
//
// free the constructed props, if any.
//
if (StreamObject->ConstructedPropertyInfo) {
ExFreePool(StreamObject->ConstructedPropertyInfo);
}
//
// signal the event.
// signal now so that we won't
// deadlock when we dereference the object and the filter is closed.
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
//
// Zero the pointer to our per stream structure in the FsContext
// field of
// of FileObject.
//
IrpStack->FileObject->FsContext = 0;
//
// remove the stream object from the filter instance list
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
RemoveEntryList(&StreamObject->NextStream);
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
//
// kill the timer, which might have been left dangling by the
// minidriver.
//
KeCancelTimer(&StreamObject->ComObj.MiniDriverTimer);
//
// dereference the master clock if any
//
if (StreamObject->MasterClockInfo) {
ObDereferenceObject(StreamObject->MasterClockInfo->ClockFileObject);
ExFreePool(StreamObject->MasterClockInfo);
}
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
//
// dereference the next file object
//
if (StreamObject->NextFileObject)
{
ObDereferenceObject(StreamObject->NextFileObject);
StreamObject->NextFileObject = NULL;
}
//
// Dereference the allocator object or stream obj won't be
// release while it should. Problems would follow particularly
// with SWEnum loaded driver.
//
if ( StreamObject->AllocatorFileObject ) {
ObDereferenceObject( StreamObject->AllocatorFileObject );
StreamObject->AllocatorFileObject = NULL;
}
#endif
//
// dereference the filter
//
ObDereferenceObject(StreamObject->FilterFileObject);
ExFreePool(StreamObject);
///} else { // if good status
/// KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
///} // if good status
SCProcessCompletedRequest(SRB);
return (Status);
}
BOOLEAN
StreamClassInterrupt(
IN PKINTERRUPT Interrupt,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Process interrupt from the device
Arguments:
Interrupt - interrupt object
Device Object - device object which is interrupting
Return Value:
Returns TRUE if interrupt expected.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
BOOLEAN returnValue;
UNREFERENCED_PARAMETER(Interrupt);
//
// check if the interrupt cannot currently go down
//
if (deviceExtension->DriverInfo->Flags & DRIVER_FLAGS_PAGED_OUT) {
return (FALSE);
}
//
// call the minidriver's interrupt service routine.
//
returnValue = deviceExtension->MinidriverData->
HwInitData.HwInterrupt(deviceExtension->HwDeviceExtension);
//
// Queue up a DPC if needed.
//
if ((deviceExtension->NeedyStream) || (deviceExtension->ComObj.
InterruptData.Flags & INTERRUPT_FLAGS_NOTIFICATION_REQUIRED)) {
KeInsertQueueDpc(&deviceExtension->WorkDpc, NULL, NULL);
}
return (returnValue);
} // end StreamClassInterrupt()
NTSTATUS
StreamClassNull(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine fails incoming irps.
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
The IRP status is returned
--*/
{
//
// complete the IRP with error status
//
PAGED_CODE();
return (SCCompleteIrp(Irp, STATUS_NOT_SUPPORTED, DeviceObject->DeviceExtension));
}
NTSTATUS
SCFilterPinInstances(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PVOID Data)
/*++
Routine Description:
Returns the # of instances supported by a pin
Arguments:
Irp - pointer to the irp
Property - pointer to the property info
Data - instance info
Return Value:
NTSTATUS returned as appropriate
--*/
{
ULONG Pin;
PKSPIN_CINSTANCES CInstances;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
PFILTER_INSTANCE FilterInstance;
PADDITIONAL_PIN_INFO AdditionalPinInfo;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = (PDEVICE_EXTENSION) IrpStack->
DeviceObject->DeviceExtension;
FilterInstance = IrpStack->FileObject->FsContext;
//
// get the pin #
//
Pin = ((PKSP_PIN) Property)->PinId;
//
// if max pin number exceeded, return error
//
IFN_MF(
if (Pin >= DeviceExtension->NumberOfPins) {
DEBUG_BREAKPOINT();
return (STATUS_INVALID_PARAMETER);
}
)
IF_MF(
if (Pin >= FilterInstance->NumberOfPins) {
DEBUG_BREAKPOINT();
return (STATUS_INVALID_PARAMETER);
}
)
CInstances = (PKSPIN_CINSTANCES) Data;
AdditionalPinInfo = FilterInstance->PinInstanceInfo;
CInstances->PossibleCount = AdditionalPinInfo[Pin].MaxInstances;
CInstances->CurrentCount = AdditionalPinInfo[Pin].CurrentInstances;
Irp->IoStatus.Information = sizeof(KSPIN_CINSTANCES);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (STATUS_SUCCESS);
}
NTSTATUS
SCFilterPinPropertyHandler(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PVOID Data)
/*++
Routine Description:
Dispatches a pin property request
Arguments:
Irp - pointer to the irp
Property - pointer to the property info
Data - property specific buffer
Return Value:
NTSTATUS returned as appropriate
--*/
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) IrpStack->
DeviceObject->DeviceExtension;
PFILTER_INSTANCE FilterInstance= (PFILTER_INSTANCE) IrpStack->
FileObject->FsContext;
PAGED_CODE();
return KsPinPropertyHandler(Irp,
Property,
Data,
FilterInstance->NumberOfPins,
FilterInstance->PinInformation);
}
VOID
StreamClassTickHandler(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
/*++
Routine Description:
Tick handler for device.
Arguments:
DeviceObject - pointer to the device object
Context - unreferenced
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
PLIST_ENTRY ListEntry;
PLIST_ENTRY SrbListEntry = ListEntry = &DeviceExtension->OutstandingQueue;
PSTREAM_REQUEST_BLOCK Srb;
UNREFERENCED_PARAMETER(Context);
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
//
// acquire the device spinlock to protect the queues.
//
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
//
// process any timed out requests on the device
//
while (SrbListEntry->Flink != ListEntry) {
SrbListEntry = SrbListEntry->Flink;
Srb = CONTAINING_RECORD(SrbListEntry,
STREAM_REQUEST_BLOCK,
SRBListEntry);
//
// first make sure the request is active, since it could have been
// called back but not yet removed from the queue.
//
if (Srb->Flags & SRB_FLAGS_IS_ACTIVE) {
//
// check for a timeout if the counter is currently nonzero.
//
if (Srb->HwSRB.TimeoutCounter != 0) {
if (--Srb->HwSRB.TimeoutCounter == 0) {
//
// request timed out. Call the minidriver to process it.
// first reset the timer in case the minidriver is
// busted.
//
DebugPrint((DebugLevelError, "SCTickHandler: Irp %x timed out! SRB = %x, SRB func = %x, Stream Object = %x\n",
Srb->HwSRB.Irp, Srb, Srb->HwSRB.Command, Srb->HwSRB.StreamObject));
Srb->HwSRB.TimeoutCounter = Srb->HwSRB.TimeoutOriginal;
DeviceExtension = (PDEVICE_EXTENSION)
Srb->HwSRB.HwDeviceExtension - 1;
//
// if we are not synchronizing the minidriver, release
// and reacquire the spinlock around the call into it.
//
if (DeviceExtension->NoSync) {
//
// we need to ensure that the SRB memory is valid for
// the async
// minidriver, EVEN if it happens to call back the
// request just
// before we call it to cancel it! This is done for
// two reasons:
// it obviates the need for the minidriver to walk
// its request
// queues to find the request, and I failed to pass
// the dev ext
// pointer to the minidriver in the below call, which
// means that
// the SRB HAS to be valid, and it's too late to
// change the API.
//
// Oh, well. Spinlock is now taken (by caller).
//
Srb->DoNotCallBack = TRUE;
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
(DeviceExtension->MinidriverData->HwInitData.HwRequestTimeoutHandler)
(&Srb->HwSRB);
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
//
// if the ACTIVE flag is now clear, it indicates that
// the
// SRB was completed during the above call into the
// minidriver.
// since we blocked the internal completion of the
// request,
// we must call it back ourselves in this case.
//
Srb->DoNotCallBack = FALSE;
if (!(Srb->Flags & SRB_FLAGS_IS_ACTIVE)) {
TRAP;
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
(Srb->Callback) (Srb);
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
} // if ! active
break;
} else { // if nosync
DeviceExtension->SynchronizeExecution(
DeviceExtension->InterruptObject,
(PVOID) DeviceExtension->MinidriverData->HwInitData.HwRequestTimeoutHandler,
&Srb->HwSRB);
// return now in case the minidriver aborted any
// other
// requests that
// may be timing out now.
//
break;
} // if nosync
} // if timed out
} // if counter != 0
} // if active
} // while list entry
//
// let my people go...
//
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
return;
} // end StreamClassTickHandler()
VOID
StreamClassCancelPendingIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Cancel routine for pending IRP's.
Arguments:
DeviceObject - pointer to the device object
Irp - pointer to IRP to be cancelled
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
PFILTER_INSTANCE FilterInstance;
PLIST_ENTRY ListHead, ListEntry;
KIRQL CancelIrql,
Irql;
PSTREAM_REQUEST_BLOCK SRB;
DebugPrint((DebugLevelWarning, "'SCCancelPending: trying to cancel Irp = %x\n",
Irp));
//
// acquire the device spinlock then release the cancel spinlock.
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
CancelIrql = Irp->CancelIrql;
IoReleaseCancelSpinLock(Irql);
//
// there are two possibilities here. 1) the IRP is on the pending queue
// for the particular stream. 2) the IRP was moved from pending to
// outstanding and has been submitted to the minidriver.
// If we are running above an external bus driver, don't
//
//
// now process all streams on the local filter instances.
//
ListHead = &DeviceExtension->FilterInstanceList;
ListEntry = ListHead->Flink;
while ( ListEntry != ListHead ) {
//
// follow the link to the instance
//
FilterInstance = CONTAINING_RECORD(ListEntry,
FILTER_INSTANCE,
NextFilterInstance);
//
// process the streams on this list
//
if (SCCheckFilterInstanceStreamsForIrp(FilterInstance, Irp)) {
goto found;
}
ListEntry = ListEntry->Flink;
}
//
// now process any requests on the device itself
//
if (SCCheckRequestsForIrp(
&DeviceExtension->OutstandingQueue, Irp, TRUE, DeviceExtension)) {
goto found;
}
//
// request is not on pending queue, so call to check the outstanding
// queue
//
SCCancelOutstandingIrp(DeviceExtension, Irp);
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
exit:
//
// now call the DPC in case the request was successfully aborted.
//
StreamClassDpc(NULL,
DeviceExtension->DeviceObject,
NULL,
NULL);
KeLowerIrql(CancelIrql);
return;
found:
//
// the irp is on one of our pending queues. remove it from the queue and
// complete it.
//
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
//
// retrieve the SRB.
//
SRB = Irp->Tail.Overlay.DriverContext[0];
//
// hack - the completion handlers will try to remove the SRB from the
// outstanding queue. Point the SRB's queues to itself so this will not
// cause a problem.
//
SRB->SRBListEntry.Flink = &SRB->SRBListEntry;
SRB->SRBListEntry.Blink = &SRB->SRBListEntry;
SRB->HwSRB.Status = STATUS_CANCELLED;
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
(SRB->Callback) (SRB);
goto exit;
}
VOID
StreamClassCancelOutstandingIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Cancel routine for IRP's outstanding in the minidriver
Arguments:
DeviceObject - pointer to the device object
Irp - pointer to IRP to be cancelled
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
KIRQL Irql,
CancelIrql;
DebugPrint((DebugLevelWarning, "'SCCancelOutstanding: trying to cancel Irp = %x\n",
Irp));
//
// acquire the device spinlock.
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
CancelIrql = Irp->CancelIrql;
IoReleaseCancelSpinLock(Irql);
SCCancelOutstandingIrp(DeviceExtension, Irp);
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
//
// now call the DPC in case the request was successfully aborted.
//
StreamClassDpc(NULL,
DeviceExtension->DeviceObject,
NULL,
NULL);
KeLowerIrql(CancelIrql);
return;
}
VOID
StreamFlushIo(
IN PDEVICE_EXTENSION DeviceExtension,
IN PSTREAM_OBJECT StreamObject
)
/*++
Routine Description:
Cancel all IRP's on the specified stream.
Arguments:
Return Value:
STATUS_SUCCESS
--*/
{
PLIST_ENTRY IrpEntry;
KIRQL Irql;
PSTREAM_REQUEST_BLOCK SRB;
PIRP Irp;
//
// abort all I/O on the specified stream. first acquire the spinlock.
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
//
// if there is I/O on our pending data queue, abort it.
//
while (!IsListEmpty(&StreamObject->DataPendingQueue)) {
//
// grab the IRP at the head of the queue and abort it.
//
IrpEntry = StreamObject->DataPendingQueue.Flink;
Irp = CONTAINING_RECORD(IrpEntry,
IRP,
Tail.Overlay.ListEntry);
//
// remove the IRP from our pending queue and call it back with
// cancelled status
//
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
//
// null out the cancel routine
//
IoSetCancelRoutine(Irp, NULL);
DebugPrint((DebugLevelTrace,
"'StreamFlush: Canceling Irp %x \n", Irp));
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
SCCompleteIrp(Irp, STATUS_CANCELLED, DeviceExtension);
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
}
//
// if there is I/O on our pending control queue, abort it.
//
while (!IsListEmpty(&StreamObject->ControlPendingQueue)) {
//
// grab the IRP at the head of the queue and abort it.
//
DEBUG_BREAKPOINT();
IrpEntry = StreamObject->ControlPendingQueue.Flink;
Irp = CONTAINING_RECORD(IrpEntry,
IRP,
Tail.Overlay.ListEntry);
//
// remove the IRP from our pending queue and call it back with
// cancelled status
//
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
//
// null out the cancel routine
//
IoSetCancelRoutine(Irp, NULL);
DebugPrint((DebugLevelTrace,
"'StreamFlush: Canceling Irp %x \n", Irp));
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
SCCompleteIrp(Irp, STATUS_CANCELLED, DeviceExtension);
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
}
//
// now cancel any irps for this stream on the outstanding queue.
// walk the outstanding queue trying to find an SRB for this stream.
//
IrpEntry = &DeviceExtension->OutstandingQueue;
while (IrpEntry->Flink != &DeviceExtension->OutstandingQueue) {
IrpEntry = IrpEntry->Flink;
//
// follow the link to the SRB
//
SRB = (PSTREAM_REQUEST_BLOCK) (CONTAINING_RECORD(IrpEntry,
STREAM_REQUEST_BLOCK,
SRBListEntry));
//
// if this SRB's stream object matches the one we're cancelling for,
// AND it has not been previously cancelled, AND the IRP itself has
// not been completed (non-null IRP field), abort this request.
//
if ((StreamObject == CONTAINING_RECORD(
SRB->HwSRB.StreamObject,
STREAM_OBJECT,
HwStreamObject)) &&
(SRB->HwSRB.Irp) &&
!(SRB->HwSRB.Irp->Cancel)) {
//
// The IRP has not been previously cancelled, so cancel it after
// releasing the spinlock to avoid deadlock with the cancel
// routine.
//
DebugPrint((DebugLevelTrace,
"'StreamFlush: Canceling Irp %x \n", SRB->HwSRB.Irp));
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
IoCancelIrp(SRB->HwSRB.Irp);
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
//
// restart at the top of the queue since we released the
// spinlock.
// we won't get in an endless loop since we set the cancel flag
// in the IRP.
//
IrpEntry = &DeviceExtension->OutstandingQueue;
} // if streamobjects match
} // while entries
//
// release the spinlock but remain at DPC level.
//
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
//
// now call the DPC in case the request was successfully aborted.
//
StreamClassDpc(NULL,
DeviceExtension->DeviceObject,
NULL,
NULL);
//
// lower IRQL
//
KeLowerIrql(Irql);
}
NTSTATUS
ClockDispatchCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS Status;
PCLOCK_INSTANCE ClockInstance=NULL; //Prefixbug 17399
PIO_STACK_LOCATION IrpStack;
PKSCLOCK_CREATE ClockCreate;
PFILE_OBJECT ParentFileObject;
PSTREAM_OBJECT StreamObject=NULL; // prefixbug 17399
BOOLEAN RequestIssued=FALSE; // prefixbug 17398
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp);
//
// show one more I/O pending & verify that we can actually do I/O.
//
Status = SCShowIoPending(DeviceObject->DeviceExtension, Irp);
if ( !NT_SUCCESS ( Status )) {
//
// the device is currently not accessible, so just return with error
//
return (Status);
}
Status = KsValidateClockCreateRequest(Irp,
&ClockCreate);
ParentFileObject = IrpStack->FileObject->RelatedFileObject;
DebugPrint((DebugLevelTrace,
"'ClockCreate: Creating clock with Irp %x \n", Irp));
if (NT_SUCCESS(Status)) {
//
// allocate a clock instance for the clock
//
ClockInstance =
(PCLOCK_INSTANCE)
ExAllocatePool(NonPagedPool, sizeof(CLOCK_INSTANCE));
if (ClockInstance) {
//
// fill in the clock instance structure and reference it in the
// file
// object for the clock
//
ClockInstance->ParentFileObject = ParentFileObject;
#if 0
ClockInstance->ClockFileObject = IrpStack->FileObject;
DebugPrint((DebugLevelInfo,
"++++++++ClockInstance=%x, FileObject=%x\n",
ClockInstance,
ClockInstance->ClockFileObject));
#endif
KsAllocateObjectHeader(&ClockInstance->DeviceHeader,
SIZEOF_ARRAY(StreamDriverDispatch),
(PKSOBJECT_CREATE_ITEM) NULL,
Irp,
(PKSDISPATCH_TABLE) & ClockDispatchTable);
IrpStack->FileObject->FsContext = ClockInstance;
//
// set the 2nd context parameter so that we can identify this
// object as the clock object.
//
IrpStack->FileObject->FsContext2 = ClockInstance;
//
// call the minidriver to indicate that this stream is the master
// clock. pass the file object as a handle to the master clock.
//
StreamObject = (PSTREAM_OBJECT) ParentFileObject->FsContext;
StreamObject->ClockInstance = ClockInstance;
ClockInstance->StreamObject = StreamObject;
Status = SCSubmitRequest(SRB_OPEN_MASTER_CLOCK,
(HANDLE) IrpStack->FileObject,
0,
SCOpenMasterCallback,
StreamObject->DeviceExtension,
StreamObject->FilterInstance->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
(PVOID) StreamObject->HwStreamObject.
ReceiveControlPacket
);
} else { // if clockinstance
Status = STATUS_INSUFFICIENT_RESOURCES;
} // if clockinstance
} // if validate success
if (!RequestIssued) {
if ( NULL != StreamObject && NULL != StreamObject->ClockInstance ) {
ExFreePool(StreamObject->ClockInstance);
StreamObject->ClockInstance = NULL; // prefixbug 17399
}
SCCompleteIrp(Irp,
STATUS_INSUFFICIENT_RESOURCES,
DeviceObject->DeviceExtension);
}
return (Status);
}
NTSTATUS
AllocatorDispatchCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Processes the allocator create IRP. Currently just uses the default
allocator.
Arguments:
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpStack;
PFILE_OBJECT ParentFileObject;
PSTREAM_OBJECT StreamObject;
NTSTATUS Status;
PAGED_CODE();
DebugPrint((DebugLevelTrace,"entering AllocatorDispatchCreate\n"));
IrpStack = IoGetCurrentIrpStackLocation(Irp);
ParentFileObject = IrpStack->FileObject->RelatedFileObject;
StreamObject = (PSTREAM_OBJECT) ParentFileObject->FsContext;
//
// show one more I/O pending & verify that we can actually do I/O.
//
Status = SCShowIoPending(DeviceObject->DeviceExtension, Irp);
if ( !NT_SUCCESS ( Status )) {
//
// the device is currently not accessible, so just return with error
//
DebugPrint((DebugLevelError,"exiting AllocatorDispatchCreate-REMOVED\n"));
return (Status);
}
//
// if allocator is not needed for this stream, just fail the call.
//
if (!StreamObject->HwStreamObject.Allocator) {
DebugPrint((DebugLevelTrace,"exiting AllocatorDispatchCreate-not implemented\n"));
return SCCompleteIrp(Irp,
STATUS_NOT_IMPLEMENTED,
DeviceObject->DeviceExtension);
}
DebugPrint((DebugLevelTrace,"exiting AllocatorDispatchCreate-complete\n"));
return SCCompleteIrp(Irp,
KsCreateDefaultAllocator(Irp),
DeviceObject->DeviceExtension);
}
NTSTATUS
SCOpenMasterCallback(
IN PSTREAM_REQUEST_BLOCK SRB
)
/*++
Routine Description:
Process the completion of a master clock open.
Arguments:
SRB - address of the completed SRB
Return Value:
None.
--*/
{
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD(
SRB->HwSRB.StreamObject,
STREAM_OBJECT,
HwStreamObject
);
PIRP Irp = SRB->HwSRB.Irp;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PAGED_CODE();
// log 'oMC ', StreamObject, DevExt, Status
SCLOG( ' CMo', StreamObject, StreamObject->DeviceExtension, SRB->HwSRB.Status);
if (!NT_SUCCESS(SRB->HwSRB.Status)) {
//
// if we could not set the master, free the clock handle and zero
// the link to the clock.
//
ExFreePool(StreamObject->ClockInstance);
StreamObject->ClockInstance = NULL;
} else { // if status success
//
// reference the pin handle so we won't be called to close the pin
// before the clock is closed
//
ObReferenceObject(IrpStack->FileObject->RelatedFileObject);
} // if status success
//
// complete the SRB
//
return (SCProcessCompletedRequest(SRB));
}
NTSTATUS
SCGetMasterClock(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PHANDLE ClockHandle
)
{
//
// WorkWork - for now do nothing.
//
PAGED_CODE();
return (STATUS_NOT_SUPPORTED);
}
VOID
SciSetMasterClockInfo(
IN PSTREAM_OBJECT pStreamObject,
IN PMASTER_CLOCK_INFO pMasterClockInfo )
/*++
Decription:
This function simply set the new masterclock info for the stream
with LockUseMasterClock hold. Because of taking a spinlock we need
this function in lock memory. This function is intended to be called
by SCSetMasterClockOnly. pStreamObject is assumed valid.
Parameters:
pStreamObject: the target stream object to set to the new MasterCLockInfo
pMasterClockInfo: the new master clock info.
Return: None.
--*/
{
KIRQL SavedIrql;
KeAcquireSpinLock( &pStreamObject->LockUseMasterClock, &SavedIrql );
pStreamObject->MasterClockInfo = pMasterClockInfo;
KeReleaseSpinLock( &pStreamObject->LockUseMasterClock, SavedIrql );
return;
}
NTSTATUS
SCSetMasterClock(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN PHANDLE ClockHandle
)
/*++
Description:
This is a Set property on a the stream. The request may be setting to
NULL CLockHandle which indicates master clock is revoked. If ClockHandle
is non-NULL, it is a new Master clock chosen by the graph manager.
Parameters:
Irp: the IO request packet to Set the master clock.
Property: the Set Master clock property
ClockHanlde: the handle of the clock designated as the new master clcok.
Return:
NTSTAUS: depending on the result of processing the request.
Comments:
This function must be called at IRQL < DISPATCH_LEVEL
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PSTREAM_OBJECT StreamObject;
KSPROPERTY FuncProperty;
PMASTER_CLOCK_INFO NewMasterClockInfo=NULL; //prefixbug 17396
PMASTER_CLOCK_INFO OldMasterClockInfo;
ULONG BytesReturned;
PFILE_OBJECT ClockFileObject = NULL;
BOOLEAN RequestIssued=FALSE;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp);
StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
//
// This function can be called from multiple threads. We will serialize
// this function on the Stream to protect against concurrent accesses.
//
KeWaitForSingleObject(&StreamObject->ControlSetMasterClock,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
//
// N.B.
//
// If our clock is open, we are potentially the master clock. But this
// is not guaranteed. Ksproxy opens our clock in attempt to use it as
// the master clock. But it can change its mind to choose another clock,
// while keeping our clock open.
//
//
// log 'sMC ', StreamObject, MasterClockInfo, *ClockHandle )
//
SCLOG( ' CMs', StreamObject, StreamObject->MasterClockInfo, *ClockHandle );
/*
Not so soon. We have not told mini drivers the new master clock yet.
Mini drivers might think they still have the retiring Master clock and
can query the clock in the mean time. We would crash on accessing NULL
MasterClockInfo. We should not nullify it before we notify the mini
driver first.
if (StreamObject->MasterClockInfo) {
ObDereferenceObject(StreamObject->MasterClockInfo->ClockFileObject);
ExFreePool(StreamObject->MasterClockInfo);
StreamObject->MasterClockInfo = NULL;
}
*/
OldMasterClockInfo = StreamObject->MasterClockInfo;
//
// if there is a clock, reference it. If not, we'll send down a null handle.
//
if (*ClockHandle) {
//
// alloc a structure to represent the master clock
//
NewMasterClockInfo = ExAllocatePool(NonPagedPool, sizeof(MASTER_CLOCK_INFO));
if (!NewMasterClockInfo) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
//
// This is too early to assign. We have not setup MasterClockInfo yet.
//
// StreamObject->MasterClockInfo = MasterClockInfo;
//
// reference the clock handle, thereby getting the file object for it.
//
if (!NT_SUCCESS((Status = ObReferenceObjectByHandle(*ClockHandle,
FILE_READ_ACCESS | SYNCHRONIZE,
*IoFileObjectType,
Irp->RequestorMode,
&ClockFileObject,
NULL
)))) {
ExFreePool(NewMasterClockInfo);
NewMasterClockInfo = NULL;
goto exit;
} // if Ob succeeded
NewMasterClockInfo->ClockFileObject = ClockFileObject;
// check master clock
#if 0
{
if ( StreamObject->ClockInstance ) {
//
// we are chosen the master clock
//
DebugPrint((DebugLevelInfo,
"--------ClockInstance=%x, FileObject=%x "
"Indicated ClockFileObject=%x context=%x\n",
StreamObject->ClockInstance,
StreamObject->ClockInstance->ParentFileObject,
ClockFileObject,
ClockFileObject->FsContext));
}
else {
DebugPrint((DebugLevelInfo,
"--------Indicated ClockFileObject=%x context=%x\n",
ClockFileObject,
ClockFileObject->FsContext));
}
}
#endif
//
// issue the IOCtl to get the function table of the master clock.
//
FuncProperty.Id = KSPROPERTY_CLOCK_FUNCTIONTABLE;
FuncProperty.Flags = KSPROPERTY_TYPE_GET;
RtlMoveMemory(&FuncProperty.Set, &KSPROPSETID_Clock, sizeof(GUID));
if (!NT_SUCCESS((Status = KsSynchronousIoControlDevice(
ClockFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&FuncProperty,
sizeof(KSPROPERTY),
&NewMasterClockInfo->FunctionTable,
sizeof(KSCLOCK_FUNCTIONTABLE),
&BytesReturned)))) {
ObDereferenceObject(NewMasterClockInfo->ClockFileObject);
ExFreePool(NewMasterClockInfo);
NewMasterClockInfo = NULL;
goto exit;
}
} // if *ClockHandle
//
// call the minidriver to indicate the master clock.
//
if ( NULL != NewMasterClockInfo ) {
//
// but first, let's put in the MasterClockInfo. When mini driver
// gets notified with the masterclock, it could fire GetTime right away
// before the notification returns. Get ready to deal with it. This is
// critical if oldMasterClockInfo is NULL. Not much so otherwise.
//
//
// Make sure no one is querying master clock when setting the new clock info.
//
SciSetMasterClockInfo( StreamObject, NewMasterClockInfo );
}
Status = SCSubmitRequest(SRB_INDICATE_MASTER_CLOCK,
ClockFileObject,
0,
SCDequeueAndDeleteSrb,
StreamObject->DeviceExtension,
StreamObject->FilterInstance->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
(PVOID) StreamObject->HwStreamObject.
ReceiveControlPacket);
ASSERT( RequestIssued );
ASSERT( NT_SUCCESS( Status ) );
//
// SCSubmitRequest is a synch call. When we return here, We can finish our work
// based on the Status code.
//
if ( NT_SUCCESS( Status )) {
//
// Everything is cool. Finish up. The assignment is redundent if
// NewMasterClockInfo is not NULL. Better assign unconditionally than check.
//
//
// Make sure no one is querying master clock when updating MasterClockInfo
//
SciSetMasterClockInfo( StreamObject, NewMasterClockInfo );
if (NULL != OldMasterClockInfo) {
ObDereferenceObject(OldMasterClockInfo->ClockFileObject);
ExFreePool(OldMasterClockInfo);
}
} else {
//
// Failed to tell mini driver the new clock. Clean up shop. But don't update
// StreamObject->MasterClockInfo. Keep the status quo.
//
//
// Make sure no one is querying master clock when updateing MasterClockInfo.
//
SciSetMasterClockInfo( StreamObject, OldMasterClockInfo );
if (NewMasterClockInfo) {
ObDereferenceObject(ClockFileObject);
ExFreePool(NewMasterClockInfo);
}
}
exit:
KeSetEvent(&StreamObject->ControlSetMasterClock, IO_NO_INCREMENT, FALSE);
return (Status);
}
NTSTATUS
SCClockGetTime(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PULONGLONG StreamTime
)
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PCLOCK_INSTANCE ClockInstance =
(PCLOCK_INSTANCE) IrpStack->FileObject->FsContext;
PSTREAM_OBJECT StreamObject = ClockInstance->ParentFileObject->FsContext;
PAGED_CODE();
if (StreamObject->HwStreamObject.HwClockObject.ClockSupportFlags &
CLOCK_SUPPORT_CAN_RETURN_STREAM_TIME) {
*StreamTime = SCGetStreamTime(IrpStack->FileObject);
Irp->IoStatus.Information = sizeof(ULONGLONG);
return STATUS_SUCCESS;
} else {
return (STATUS_NOT_SUPPORTED);
}
}
NTSTATUS
SCClockGetPhysicalTime(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PULONGLONG PhysicalTime
)
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PCLOCK_INSTANCE ClockInstance =
(PCLOCK_INSTANCE) IrpStack->FileObject->FsContext;
PSTREAM_OBJECT StreamObject = ClockInstance->ParentFileObject->FsContext;
PAGED_CODE();
if (StreamObject->HwStreamObject.HwClockObject.ClockSupportFlags &
CLOCK_SUPPORT_CAN_READ_ONBOARD_CLOCK) {
*PhysicalTime = SCGetPhysicalTime(IrpStack->FileObject->FsContext);
Irp->IoStatus.Information = sizeof(ULONGLONG);
return (STATUS_SUCCESS);
} else {
return (STATUS_NOT_SUPPORTED);
}
}
NTSTATUS
SCClockGetSynchronizedTime(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PKSCORRELATED_TIME SyncTime
)
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PCLOCK_INSTANCE ClockInstance =
(PCLOCK_INSTANCE) IrpStack->FileObject->FsContext;
PSTREAM_OBJECT StreamObject = ClockInstance->ParentFileObject->FsContext;
PAGED_CODE();
if (StreamObject->HwStreamObject.HwClockObject.ClockSupportFlags &
CLOCK_SUPPORT_CAN_RETURN_STREAM_TIME) {
SyncTime->Time = SCGetSynchronizedTime(IrpStack->FileObject,
&SyncTime->SystemTime);
Irp->IoStatus.Information = sizeof(KSCORRELATED_TIME);
return (STATUS_SUCCESS);
} else {
return (STATUS_NOT_SUPPORTED);
}
}
NTSTATUS
SCClockGetFunctionTable(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PKSCLOCK_FUNCTIONTABLE FunctionTable
)
{
PCLOCK_INSTANCE ClockInstance;
PIO_STACK_LOCATION IrpStack;
PSTREAM_OBJECT StreamObject;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp);
ClockInstance = (PCLOCK_INSTANCE) IrpStack->FileObject->FsContext;
StreamObject = ClockInstance->ParentFileObject->FsContext;
RtlZeroMemory(FunctionTable, sizeof(KSCLOCK_FUNCTIONTABLE));
if (StreamObject->HwStreamObject.HwClockObject.ClockSupportFlags &
CLOCK_SUPPORT_CAN_RETURN_STREAM_TIME) {
FunctionTable->GetTime = (PFNKSCLOCK_GETTIME) SCGetStreamTime;
FunctionTable->GetCorrelatedTime = (PFNKSCLOCK_CORRELATEDTIME) SCGetSynchronizedTime;
}
if (StreamObject->HwStreamObject.HwClockObject.ClockSupportFlags &
CLOCK_SUPPORT_CAN_READ_ONBOARD_CLOCK) {
FunctionTable->GetPhysicalTime = (PFNKSCLOCK_GETTIME) SCGetPhysicalTime;
}
Irp->IoStatus.Information = sizeof(KSCLOCK_FUNCTIONTABLE);
return STATUS_SUCCESS;
}
NTSTATUS
ClockDispatchClose
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine receives CLOSE IRP's for a stream
Arguments:
DeviceObject - device object for the device
Irp - probably an IRP, silly
Return Value:
The IRP status is set as appropriate
--*/
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
NTSTATUS Status;
BOOLEAN RequestIssued;
PCLOCK_INSTANCE ClockInstance = (PCLOCK_INSTANCE)
IrpStack->FileObject->FsContext;
PSTREAM_OBJECT StreamObject = ClockInstance->StreamObject;
PAGED_CODE();
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
//
// call the minidriver to indicate that there is no master clock.
// processing will continue when the callback procedure is called.
//
Status = SCSubmitRequest(SRB_CLOSE_MASTER_CLOCK,
NULL,
0,
SCCloseClockCallback,
DeviceExtension,
StreamObject->FilterInstance->HwInstanceExtension,
&StreamObject->HwStreamObject,
Irp,
&RequestIssued,
&StreamObject->ControlPendingQueue,
(PVOID) StreamObject->HwStreamObject.
ReceiveControlPacket
);
if (!RequestIssued) {
DEBUG_BREAKPOINT();
SCCompleteIrp(Irp, Status, DeviceExtension);
}
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
return (Status);
}
NTSTATUS
SCCloseClockCallback(
IN PSTREAM_REQUEST_BLOCK SRB
)
/*++
Routine Description:
Process the completion of a stream close.
Arguments:
SRB - address of the completed SRB
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD(
SRB->HwSRB.StreamObject,
STREAM_OBJECT,
HwStreamObject
);
PIRP Irp = SRB->HwSRB.Irp;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS Status = SRB->HwSRB.Status;
PCLOCK_INSTANCE ClockInstance;
PAGED_CODE();
// log 'cMC ', StreamObject, ClockInstance, Status )
SCLOG( ' CMc', StreamObject, IrpStack->FileObject->FsContext, Status );
if (NT_SUCCESS(Status)) {
//
// free the clock instance structure and the object header
//
ClockInstance =
(PCLOCK_INSTANCE) IrpStack->FileObject->FsContext;
KsFreeObjectHeader(ClockInstance->DeviceHeader);
ExFreePool(ClockInstance);
StreamObject->ClockInstance = NULL;
//
// dereference the pin handle
//
ObDereferenceObject(IrpStack->FileObject->RelatedFileObject);
} // if good status
SCProcessCompletedRequest(SRB);
return (Status);
}
NTSTATUS
SCFilterTopologyHandler(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PVOID Data)
/*++
Routine Description:
Dispatches a pin property request
Arguments:
Irp - pointer to the irp
Property - pointer to the property info
Data - property specific buffer
Return Value:
NTSTATUS returned as appropriate
--*/
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) IrpStack->
DeviceObject->DeviceExtension;
PAGED_CODE();
IFN_MF(
return KsTopologyPropertyHandler(Irp,
Property,
Data,
DeviceExtension->StreamDescriptor->StreamHeader.Topology
);
)
IF_MFS(
PFILTER_INSTANCE FilterInstance;
FilterInstance = (PFILTER_INSTANCE) IrpStack->FileObject->FsContext;
return KsTopologyPropertyHandler(
Irp,
Property,
Data,
FilterInstance->StreamDescriptor->StreamHeader.Topology);
)
}
NTSTATUS
SCFilterPinIntersectionHandler(
IN PIRP Irp,
IN PKSP_PIN Pin,
OUT PVOID Data
)
/*++
Routine Description:
Handles the KSPROPERTY_PIN_DATAINTERSECTION property in the Pin property set.
Returns the first acceptable data format given a list of data ranges for a specified
Pin factory. Actually just calls the Intersection Enumeration helper, which then
calls the IntersectHandler callback with each data range.
Arguments:
Irp -
Device control Irp.
Pin -
Specific property request followed by Pin factory identifier, followed by a
KSMULTIPLE_ITEM structure. This is followed by zero or more data range structures.
Data -
The place in which to return the data format selected as the first intersection
between the list of data ranges passed, and the acceptable formats.
Return Values:
returns STATUS_SUCCESS or STATUS_NO_MATCH, else STATUS_INVALID_PARAMETER,
STATUS_BUFFER_TOO_SMALL, or STATUS_INVALID_BUFFER_SIZE.
--*/
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) IrpStack->
DeviceObject->DeviceExtension;
PAGED_CODE();
IFN_MF(
return KsPinDataIntersection(
Irp,
Pin,
Data,
DeviceExtension->NumberOfPins,
DeviceExtension->PinInformation,
SCIntersectHandler);
)
IF_MFS(
PSTREAM_OBJECT StreamObject;
PFILTER_INSTANCE FilterInstance;
FilterInstance = (PFILTER_INSTANCE) IrpStack->FileObject->FsContext;
DebugPrint((DebugLevelVerbose,
"PinIntersection FilterInstance=%p\n", FilterInstance ));
return KsPinDataIntersection(
Irp,
Pin,
Data,
FilterInstance->NumberOfPins,
FilterInstance->PinInformation,
SCIntersectHandler);
)
}
NTSTATUS
SCIntersectHandler(
IN PIRP Irp,
IN PKSP_PIN Pin,
IN PKSDATARANGE DataRange,
OUT PVOID Data
)
/*++
Routine Description:
This is the data range callback for KsPinDataIntersection, which is called by
FilterPinIntersection to enumerate the given list of data ranges, looking for
an acceptable match. If a data range is acceptable, a data format is copied
into the return buffer. If there is a wave format selected in a current pin
connection, and it is contained within the data range passed in, it is chosen
as the data format to return. A STATUS_NO_MATCH continues the enumeration.
Arguments:
Irp -
Device control Irp.
Pin -
Specific property request followed by Pin factory identifier, followed by a
KSMULTIPLE_ITEM structure. This is followed by zero or more data range structures.
This enumeration callback does not need to look at any of this though. It need
only look at the specific pin identifier.
DataRange -
Contains a specific data range to validate.
Data -
The place in which to return the data format selected as the first intersection
between the list of data ranges passed, and the acceptable formats.
Return Values:
returns STATUS_SUCCESS or STATUS_NO_MATCH, else STATUS_INVALID_PARAMETER,
STATUS_BUFFER_TOO_SMALL, or STATUS_INVALID_BUFFER_SIZE.
--*/
{
PIO_STACK_LOCATION IrpStack;
NTSTATUS Status;
PFILTER_INSTANCE FilterInstance;
STREAM_DATA_INTERSECT_INFO IntersectInfo;
PDEVICE_EXTENSION DeviceExtension;
BOOLEAN RequestIssued;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp);
FilterInstance = (PFILTER_INSTANCE) IrpStack->FileObject->FsContext;
DeviceExtension = (PDEVICE_EXTENSION)
IrpStack->DeviceObject->DeviceExtension;
ASSERT_FILTER_INSTANCE( FilterInstance );
ASSERT_DEVICE_EXTENSION( DeviceExtension );
//
// fill in the intersect info struct from the input params.
//
IntersectInfo.DataRange = DataRange;
IntersectInfo.DataFormatBuffer = Data;
IntersectInfo.SizeOfDataFormatBuffer =
IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
IntersectInfo.StreamNumber = Pin->PinId;
//
// call the minidriver to process the intersection. processing will
// continue
// when the callback procedure is called. take the event to ensure that
// pins don't come and go as we process the intersection.
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
Status = SCSubmitRequest(SRB_GET_DATA_INTERSECTION,
&IntersectInfo,
0,
SCDataIntersectionCallback,
DeviceExtension,
FilterInstance->HwInstanceExtension,
NULL,
Irp,
&RequestIssued,
&DeviceExtension->PendingQueue,
(PVOID) DeviceExtension->
MinidriverData->HwInitData.
HwReceivePacket);
if (!RequestIssued) {
DEBUG_BREAKPOINT();
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
}
return Status;
}
NTSTATUS
SCDataIntersectionCallback(
IN PSTREAM_REQUEST_BLOCK SRB
)
/*++
Routine Description:
Process the completion of a data intersection query.
Arguments:
SRB - address of the completed SRB
Return Value:
None.
--*/
{
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
PIRP Irp = SRB->HwSRB.Irp;
NTSTATUS Status = SRB->HwSRB.Status;
PAGED_CODE();
Irp->IoStatus.Information = SRB->HwSRB.ActualBytesTransferred;
//
// signal the event
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
SCDequeueAndDeleteSrb(SRB);
return (Status);
}
NTSTATUS
SCGetStreamHeaderSize(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PULONG StreamHeaderSize
)
/*++
Routine Description:
Process the get stream header extension property
Arguments:
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PSTREAM_OBJECT StreamObject = (PSTREAM_OBJECT) IrpStack->FileObject->FsContext;
PAGED_CODE();
ASSERT(StreamObject);
*StreamHeaderSize = StreamObject->HwStreamObject.StreamHeaderMediaSpecific;
Irp->IoStatus.Information = sizeof(ULONG);
return (STATUS_SUCCESS);
}
NTSTATUS
DllUnload(
VOID
)
{
NTSTATUS Status=STATUS_SUCCESS;
#if DBG
NTSTATUS DbgDllUnload();
DebugPrint((1, "Stream Class DllUnload: Unloading\n"));
Status = DbgDllUnload();
#endif
return Status;
}
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
NTSTATUS
SCStreamAllocator(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PHANDLE AllocatorHandle
)
/*++
Routine Description:
If KSPROPERTY_TYPE_SET, this function sets the stream allocator
for this connection by referencing the file handle to obtain
the file object pointer and stores this pointer in the filter(stream?)
instance structure.
Otherwise, a KSPROPERTY_TYPE_GET request returns a NULL handle
and STATUS_SUCCESS to show that we support allocator creation.
Arguments:
IN PIRP Irp -
pointer to the I/O request packet
IN PKSPROPERTY Property -
pointer to the property structure
IN OUT PHANDLE AllocatorHandle -
pointer to the handle representing the file object
Return:
STATUS_SUCCESS or an appropriate error code
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PSTREAM_OBJECT StreamObject;
PDEVICE_EXTENSION DeviceExtension;
IrpStack = IoGetCurrentIrpStackLocation( Irp );
StreamObject = IrpStack->FileObject->FsContext;
DebugPrint((DebugLevelTrace, "STREAM:entering SCStreamAllocator:Stream:%x\n",StreamObject));
if (Property->Flags & KSPROPERTY_TYPE_GET) {
//
// This is a query to see if we support the creation of
// allocators. The returned handle is always NULL, but we
// signal that we support the creation of allocators by
// returning STATUS_SUCCESS.
//
*AllocatorHandle = NULL;
Status = STATUS_SUCCESS;
DebugPrint((DebugLevelTrace,"SCStreamAllocator-GET"));
} else {
PFILTER_INSTANCE FilterInstance;
FilterInstance =
(PFILTER_INSTANCE) StreamObject->FilterFileObject->FsContext;
DeviceExtension = StreamObject->DeviceExtension;
DebugPrint((DebugLevelTrace,"SCStreamAllocator-SET"));
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
//
// The allocator can only be specified when the device is
// in KSSTATE_STOP.
//
if (StreamObject->CurrentState != KSSTATE_STOP) {
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
DebugPrint((DebugLevelTrace,"SCStreamAllocator-device not in STOP"));
return STATUS_INVALID_DEVICE_STATE;
}
// if we are in _STOP, the flush was already done.
// this call may have to be enabled.
//
// StreamFlushIo(DeviceExtension, StreamObject);
//
// Release the previous allocator, if any.
//
if (StreamObject->AllocatorFileObject) {
ObDereferenceObject( StreamObject->AllocatorFileObject );
StreamObject->AllocatorFileObject = NULL;
}
//
// Reference this handle and store the resultant pointer
// in the filter instance. Note that the default allocator
// does not ObReferenceObject() for its parent
// (which would be the pin handle). If it did reference
// the pin handle, we could never close this pin as there
// would always be a reference to the pin file object held
// by the allocator and the pin object has a reference to the
// allocator file object.
//
if (*AllocatorHandle != NULL) {
Status =
ObReferenceObjectByHandle(
*AllocatorHandle,
FILE_READ_DATA | SYNCHRONIZE,
*IoFileObjectType,
ExGetPreviousMode(),
&StreamObject->AllocatorFileObject,
NULL );
DebugPrint((DebugLevelTrace, "SCStreamAllocator: got %x as Allocator file object\n",StreamObject->AllocatorFileObject));
} else {
Status = STATUS_SUCCESS;
}
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
}
DebugPrint((DebugLevelTrace,"exiting SCStreamAllocator-normal path\n"));
return Status;
}
//---------------------------------------------------------------------------
BOOLEAN
HeaderTransfer(
IN PFILTER_INSTANCE FilterInstance,
IN PSTREAM_OBJECT StreamObject,
IN PFILE_OBJECT DestinationFileObject,
IN OUT PSTREAM_HEADER_EX *StreamHeader
)
/*++
Routine Description:
Sets up the stream header for a no-copy transfer to the
opposite pin.
Arguments:
IN PFILTER_INSTANCE FilterInstance -
pointer to the filter instance
IN PSTREAM_OBJECT StreamObject -
pointer to the transform instance structure
IN PSTREAM_OBJECT DestinationInstance -
pointer to the opposite transform instance structure
IN OUT PSTREAM_HEADER_EX *StreamHeader -
pointer containing a pointer to the current stream header,
this member is updated with a pointer to the next stream
header to submit to the opposite pin or NULL if there is
no header to submit.
Return:
An indication of whether stop can proceed now or not
Comments:
Not pageable, uses SpinLocks.
--*/
{
KIRQL irqlQueue, irqlFree;
ULONG WhichQueue = (*StreamHeader)->WhichQueue;
ULONG OppositeQueue = WhichQueue ^ 0x00000001; // 1 to 0, 0 to 1
BOOLEAN SignalStop = FALSE;
ASSERT(DestinationFileObject);
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
if (StreamObject->PinState > PinStopPending) { // if normal running case
//
// If we are here after submitting an ENDOFSTREAM Irp to the
// outflow pin, then we have already read the end of stream
// from the input and there is no need to continue I/O.
//
if (DestinationFileObject) {
ULONG HeaderFlags = (*StreamHeader)->Header.OptionsFlags;
//
// Clear the options flags so that we continue
// reading from where we left off.
//
// (*StreamHeader)->Header.OptionsFlags = 0;
//
// Reset the stream segment valid data length
//
// (*StreamHeader)->Header.DataUsed = 0;
// (*StreamHeader)->Header.Duration = 0;
//
// Check for the end of the stream.
//
if ((HeaderFlags & KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM) ||
StreamObject->EndOfStream) {
DebugPrint((DebugLevelTrace,
"end of stream") );
//
// Make sure that this is set for the next time through.
//
StreamObject->EndOfStream = TRUE;
if (!(*StreamHeader)->ReferenceCount) {
//
// Put the header back on the free list of the inflow pin.
//
KeAcquireSpinLock( &StreamObject->FreeQueueLock, &irqlFree );
#if (DBG)
if ((*StreamHeader)->OnFreeList) {
DebugPrint((DebugLevelTrace,
"stream header already on free list.") );
}
#endif
DebugPrint((DebugLevelTrace,
"EOS adding %x to free queue", *StreamHeader) );
InsertTailList(
&StreamObject->FreeQueue,
&(*StreamHeader)->ListEntry );
if (!InterlockedDecrement (
&StreamObject -> QueuedFramesPlusOne
))
SignalStop = TRUE;
#if (DBG)
(*StreamHeader)->OnFreeList = TRUE;
if ((*StreamHeader)->OnActiveList) {
DebugPrint((DebugLevelTrace,
"stream header on both lists.") );
}
#endif
KeReleaseSpinLock( &StreamObject->FreeQueueLock, irqlFree );
}
//
// No more I/O to opposite pin.
//
*StreamHeader = NULL;
}
}
//
// Grab the spin lock for the other queue, insert this
// stream header on the queue.
//
if (*StreamHeader) {
KeAcquireSpinLock( &StreamObject->Queues[OppositeQueue].QueueLock, &irqlQueue );
#if (DBG)
if ((*StreamHeader)->OnActiveList) {
DebugPrint((DebugLevelTrace,
"stream header already on active list.") );
}
#endif
InsertTailList(
&StreamObject->Queues[OppositeQueue].ActiveQueue,
&(*StreamHeader)->ListEntry );
#if (DBG)
(*StreamHeader)->OnActiveList = TRUE;
if ((*StreamHeader)->OnFreeList) {
DebugPrint((DebugLevelTrace,
"stream header on both lists.") );
}
#endif
KeReleaseSpinLock( &StreamObject->Queues[OppositeQueue].QueueLock, irqlQueue );
}
}
else // pin stop IS pending
{
//
// Location of frames (for this type of transfer, all frames
// are held on the source pin).
//
if (!(*StreamHeader)->ReferenceCount) {
DebugPrint((DebugLevelTrace,
"stop: adding %x to free queue.", *StreamHeader) );
KeAcquireSpinLock( &StreamObject->FreeQueueLock, &irqlFree );
#if (DBG)
if ((*StreamHeader)->OnFreeList) {
DebugPrint((DebugLevelTrace,
"stream header already on free list.") );
}
#endif
InsertTailList(
&StreamObject->FreeQueue, &(*StreamHeader)->ListEntry );
if (!InterlockedDecrement (&StreamObject -> QueuedFramesPlusOne))
SignalStop = TRUE;
#if (DBG)
(*StreamHeader)->OnFreeList = TRUE;
if ((*StreamHeader)->OnActiveList) {
DebugPrint((DebugLevelTrace,
"stream header on both lists.") );
}
#endif
KeReleaseSpinLock( &StreamObject->FreeQueueLock, irqlFree );
}
//
// No I/O to opposite pin this round.
//
*StreamHeader = NULL;
}
return SignalStop;
}
//---------------------------------------------------------------------------
VOID
IoWorker(
PVOID Context,
ULONG WhichQueue
)
/*++
Routine Description:
This is the work item for the source pins. Walks the queue
associated with the stream header looking for sequentially
completed headers and submits those headers to the opposite
pin.
Arguments:
PVOID Context -
pointer to the stream header
Return:
Nothing.
Comments:
Not pageable, uses SpinLocks.
--*/
{
KIRQL irqlOld;
PFILTER_INSTANCE FilterInstance;
PADDITIONAL_PIN_INFO AdditionalInfo;
PLIST_ENTRY Node;
PSTREAM_OBJECT StreamObject;
PFILE_OBJECT DestinationFileObject;
PSTREAM_HEADER_EX StreamHeader;
NTSTATUS Status;
ULONG Operation;
PDEVICE_EXTENSION DeviceExtension;
BOOLEAN SignalStop = FALSE;
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
StreamObject = (PSTREAM_OBJECT) Context;
DeviceExtension = StreamObject->DeviceExtension;
#if (DBG)
DebugPrint((DebugLevelTrace,
"entering IoWorker:Source StreamObject:%x\n",StreamObject));
#endif
FilterInstance =
(PFILTER_INSTANCE)
StreamObject->FilterFileObject->FsContext;
if (!FilterInstance) {
//
// For some reason, the filter instance has gone missing.
//
DebugPrint((DebugLevelTrace,
"error: FilterInstance has gone missing.\n") );
return;
}
AdditionalInfo = FilterInstance->PinInstanceInfo;
//
// Synchronize with control changes and protect from reentrancy.
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
//
// Synchronize with queues.
//
KeAcquireSpinLock( &StreamObject->Queues[WhichQueue].QueueLock, &irqlOld );
//
// Loop while there are completed items on the queue.
//
while (!IsListEmpty( &StreamObject->Queues[WhichQueue].ActiveQueue )) {
Node = StreamObject->Queues[WhichQueue].ActiveQueue.Flink;
StreamHeader =
CONTAINING_RECORD(
Node,
STREAM_HEADER_EX,
ListEntry );
#if (DBG)
DebugPrint((DebugLevelTrace,
"got StreamHeader:%08x\n", StreamHeader ));
#endif
if (StreamHeader->ReferenceCount) {
DebugPrint((DebugLevelTrace,
"breaking StreamHeader:%08x\n", StreamHeader ));
break;
} else {
//
// Remove this header from the current queue.
//
RemoveHeadList( &StreamObject->Queues[WhichQueue].ActiveQueue );
#if (DBG)
StreamHeader->OnActiveList = FALSE;
#endif
KeReleaseSpinLock( &StreamObject->Queues[WhichQueue].QueueLock, irqlOld );
//
// Wait for the APC to complete. Note that if an error was
// returned, the I/O status block is not updated and the
// event is not signalled.
//
DebugPrint((DebugLevelTrace,
"waiting for StreamHeader (%08x) to complete\n", StreamHeader ));
KeWaitForSingleObject(
&StreamHeader->CompletionEvent,
Executive,
KernelMode,
FALSE,
NULL);
DebugPrint((DebugLevelTrace,
"StreamHeader (%08x) completed\n", StreamHeader));
DestinationFileObject =
StreamHeader->NextFileObject;
//
// At the time this returns TRUE, the loop will be finished.
//
SignalStop = HeaderTransfer(
FilterInstance,
StreamObject,
DestinationFileObject,
&StreamHeader );
if (StreamHeader)
{
DebugPrint((DebugLevelTrace, "IoWorker issuing: "));
if (DestinationFileObject == StreamObject->NextFileObject)
{
DebugPrint((DebugLevelTrace,"KSSTREAM_WRITE:dest=%x\n",DestinationFileObject));
Operation = KSSTREAM_WRITE;
StreamHeader->NextFileObject =
StreamObject->FileObject;
#if (DBG)
if (StreamHeader->Id == 7)
DebugPrint((DebugLevelVerbose,"iw%x\n",StreamHeader->Id));
else
DebugPrint((DebugLevelVerbose,"iw%x",StreamHeader->Id));
#endif
}
else
{
DebugPrint((DebugLevelTrace,"KSSTREAM_READ:dest=%x\n",DestinationFileObject));
Operation = KSSTREAM_READ;
StreamHeader->Header.OptionsFlags = 0;
//
// Reset the stream segment valid data length
//
StreamHeader->Header.DataUsed = 0;
StreamHeader->Header.Duration = 0;
StreamHeader->NextFileObject = StreamObject->NextFileObject;
#if (DBG)
if (StreamHeader->Id == 7)
DebugPrint((DebugLevelVerbose,"ir%x\n",StreamHeader->Id));
else
DebugPrint((DebugLevelVerbose,"ir%x",StreamHeader->Id));
#endif
}
InterlockedIncrement( &StreamHeader->ReferenceCount );
StreamHeader->WhichQueue = WhichQueue ^ 0x00000001;
Status =
KsStreamIo(
DestinationFileObject,
&StreamHeader->CompletionEvent, // Event
NULL, // PortContext
IoCompletionRoutine,
StreamHeader, // CompletionContext
KsInvokeOnSuccess |
KsInvokeOnCancel |
KsInvokeOnError,
&StreamHeader->IoStatus,
&StreamHeader->Header,
StreamHeader->Header.Size,
KSSTREAM_SYNCHRONOUS | Operation,
KernelMode );
if (Status != STATUS_PENDING) {
//
// If this I/O completes immediately (failure or not), the
// event is not signalled.
//
KeSetEvent( &StreamHeader->CompletionEvent, IO_NO_INCREMENT, FALSE );
}
}
KeAcquireSpinLock( &StreamObject->Queues[WhichQueue].QueueLock, &irqlOld );
}
//
// Ok to schedule another work item now.
//
} // end while
InterlockedExchange( &StreamObject->Queues[WhichQueue].WorkItemQueued, FALSE );
//
// If a stop needs to be signalled, signal it.
//
if (SignalStop) {
KeSetEvent( &StreamObject->StopEvent,
IO_NO_INCREMENT,
FALSE );
}
KeReleaseSpinLock( &StreamObject->Queues[WhichQueue].QueueLock, irqlOld );
//
// Release the control event
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
DebugPrint((DebugLevelTrace,"exiting IoWorker\n"));
}
//---------------------------------------------------------------------------
/*++
Routine Description:
These are the work items for the source and destination pins.
Calls the IoWorker code above, passing in READ or WRITE header
queue information.
Arguments:
PVOID Context -
pointer to the stream header
Return:
Nothing.
Comments:
--*/
VOID
IoWorkerRead(
PVOID Context
)
{
IoWorker(Context,READ);
}
VOID
IoWorkerWrite(
PVOID Context
)
{
IoWorker(Context,WRITE);
}
//---------------------------------------------------------------------------
NTSTATUS
IoCompletionRoutine(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
/*++
Routine Description:
Processes the completion of the given Irp by marking the
associated stream header as completed and scheduling a
worker item to complete processing if necessary.
Arguments:
PDEVICE_OBJECT DeviceObject -
pointer to the device object
PIRP Irp -
pointer to the I/O request packet
PVOID Context -
a context pointer (pointer to the associated stream header)
Return:
The IoStatus.Status member of the Irp.
Comments:
Not pageable, uses SpinLocks and may be called at DISPATCH_LEVEL.
--*/
{
KIRQL irqlOld;
PSTREAM_HEADER_EX StreamHeader = Context;
PFILTER_INSTANCE FilterInstance;
PSTREAM_OBJECT StreamObject;
ULONG WhichQueue;
#if (DBG)
ASSERT( StreamHeader->Data == StreamHeader->Header.Data );
#endif
StreamObject =
(PSTREAM_OBJECT) StreamHeader->OwnerFileObject->FsContext;
DebugPrint((DebugLevelTrace,
"IoCompletionRoutine:StreamHeader %08x, StreamObject %08x\n",StreamHeader,StreamObject));
FilterInstance =
(PFILTER_INSTANCE)
StreamHeader->OwnerFileObject->RelatedFileObject->FsContext;
WhichQueue = StreamHeader->WhichQueue;
KeAcquireSpinLock( &StreamObject->Queues[WhichQueue].QueueLock, &irqlOld );
//
// Remove this reference count on the IRP so that we can continue
// the loop if this work item is not the head item of the list.
//
InterlockedDecrement( &StreamHeader->ReferenceCount );
//
// Copy the status block so that we don't have to wait for the APC.
//
StreamHeader->IoStatus = Irp->IoStatus;
//
// Sweep the active queue in the worker to complete the transfer.
//
if (!StreamObject->Queues[WhichQueue].WorkItemQueued) {
//
// A work item is not pending, initialize the worker item
// for the new context and queue it.
//
ExInitializeWorkItem(
&StreamObject->Queues[WhichQueue].WorkItem,
(WhichQueue == READ) ? IoWorkerRead : IoWorkerWrite,
StreamObject );
InterlockedExchange( &StreamObject->Queues[WhichQueue].WorkItemQueued, TRUE );
KsQueueWorkItem(
(WhichQueue == READ) ? FilterInstance->WorkerRead :
FilterInstance->WorkerWrite,
&StreamObject->Queues[WhichQueue].WorkItem );
}
KeReleaseSpinLock( &StreamObject->Queues[WhichQueue].QueueLock, irqlOld );
DebugPrint((DebugLevelTrace,
"exiting IoCompletionRoutine:Irp->IoStatus.Status:%x\n",Irp->IoStatus.Status));
return Irp->IoStatus.Status;
}
//---------------------------------------------------------------------------
NTSTATUS
PrepareTransfer(
IN PFILTER_INSTANCE FilterInstance,
IN PSTREAM_OBJECT StreamObject
)
/*++
Routine Description:
Prepares for the data transfer by distributing the assigned allocators
for the source and destination pins.
Arguments:
IN PFILTER_INSTANCE FilterInstance,
pointer to the filter instance
IN PSTREAM_OBJECT StreamObject -
pointer to the transform instance
Return:
STATUS_SUCCESS or an appropriate error code.
--*/
{
KSPROPERTY Property;
KSSTREAMALLOCATOR_STATUS AllocatorStatus;
NTSTATUS Status;
PSTREAM_HEADER_EX StreamHeader;
ULONG i, Returned;
PADDITIONAL_PIN_INFO AdditionalInfo;
//
// If the PinState is not PinStopped, then return.
//
DebugPrint((DebugLevelTrace,"entering PrepareTransfer\n"));
if (!StreamObject->AllocatorFileObject) {
DebugPrint((DebugLevelTrace,"!! AllocatorFileObject is NULL"));
return STATUS_SUCCESS;
}
if (StreamObject->PinState != PinStopped) {
//
// We only need to do this work when the pin has been
// completely stopped. If we were running, just reflect the
// state.
//
DebugPrint((DebugLevelTrace,"PrepareTransfer exiting, PinState != PinStopped\n"));
StreamObject->PinState = PinPrepared;
return STATUS_SUCCESS;
}
AdditionalInfo = FilterInstance->PinInstanceInfo;
//
// Retrieve the allocator framing information for the pin.
//
Property.Set = KSPROPSETID_StreamAllocator;
Property.Id = KSPROPERTY_STREAMALLOCATOR_STATUS;
Property.Flags = KSPROPERTY_TYPE_GET;
Status =
KsSynchronousIoControlDevice(
StreamObject->AllocatorFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&Property,
sizeof( Property ),
&AllocatorStatus,
sizeof( AllocatorStatus ),
&Returned );
if (!NT_SUCCESS( Status ))
{
DebugPrint((DebugLevelTrace,
"PrepareTransfer exiting, unable to retrieve allocator status\n"));
return Status;
}
//
// Save the framing information
//
StreamObject->Framing = AllocatorStatus.Framing;
//
// Allocate the frames from the allocator
//
// 1. Always allocate frames when starting the IrpSource.
//
// 2. If the allocator is not shared, then allocate the frames when
// the (each) destination pin is started.
//
if (StreamObject->PinType == IrpSource) {
InterlockedExchange (&StreamObject -> QueuedFramesPlusOne, 1);
#if (DBG)
DebugPrint((DebugLevelTrace,"Framing.Frames:%x\n", StreamObject->Framing.Frames));
DebugPrint((DebugLevelTrace,"Framing.FrameSize:%x\n", StreamObject->Framing.FrameSize));
#endif
for (i = 0; i < StreamObject->Framing.Frames; i++) {
DebugPrint((DebugLevelTrace,"StreamObject->ExtendedHeaderSize:%x\n", StreamObject->HwStreamObject.StreamHeaderMediaSpecific));
StreamHeader =
ExAllocatePoolWithTag(
NonPagedPool,
sizeof( STREAM_HEADER_EX ) +
StreamObject->HwStreamObject.StreamHeaderMediaSpecific,
STREAMCLASS_TAG_STREAMHEADER );
if (NULL == StreamHeader) {
DebugPrint((DebugLevelTrace,
"out of pool while allocating frames\n") );
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlZeroMemory(
StreamHeader,
sizeof( STREAM_HEADER_EX ) +
StreamObject->HwStreamObject.StreamHeaderMediaSpecific);
KeInitializeEvent(
&StreamHeader->CompletionEvent,
SynchronizationEvent,
FALSE );
StreamHeader->Header.Size =
sizeof( KSSTREAM_HEADER ) +
StreamObject->HwStreamObject.StreamHeaderMediaSpecific;
if (StreamObject->HwStreamObject.StreamHeaderMediaSpecific) {
*(PULONG)((&StreamHeader->Header) + 1) =
StreamObject->HwStreamObject.StreamHeaderMediaSpecific;
}
Status =
AllocateFrame(
StreamObject->AllocatorFileObject,
&StreamHeader->Header.Data );
#if (DBG)
//
// Track who is stomping on the headers...
//
StreamHeader->Data = StreamHeader->Header.Data;
#endif
StreamHeader->WhichQueue = READ;
StreamHeader->Id = i;
if (!NT_SUCCESS( Status )) {
DebugPrint((DebugLevelTrace,
"failed to allocate a frame\n") );
//
// Free this header here and the routine below will
// clean up whatever has been added to the queue.
//
ExFreePool( StreamHeader );
} else {
//
// Start with the owner file object as this connection,
// if a no-copy condition exists, this will be adjusted
// in the transfer function.
//
StreamHeader->OwnerFileObject =
StreamObject->FileObject;
StreamHeader->Header.DataUsed = 0;
StreamHeader->Header.FrameExtent =
StreamObject->Framing.FrameSize;
#if (DBG)
if (StreamHeader->OnFreeList) {
DebugPrint((DebugLevelTrace,"stream header already on free list.\n") );
}
#endif
InsertTailList(
&StreamObject->FreeQueue,
&StreamHeader->ListEntry );
#if (DBG)
StreamHeader->OnFreeList = TRUE;
#endif
}
}
}
//
// Clean up orphaned frames from the allocator and free headers
// to the pool if there was a failure.
//
if (!NT_SUCCESS( Status )) {
while (!IsListEmpty( &StreamObject->FreeQueue )) {
PLIST_ENTRY Node;
Node = RemoveHeadList( &StreamObject->FreeQueue );
StreamHeader =
CONTAINING_RECORD(
Node,
STREAM_HEADER_EX,
ListEntry );
#if (DBG)
StreamHeader->OnFreeList = FALSE;
ASSERT( StreamHeader->Data == StreamHeader->Header.Data );
#endif
FreeFrame(
StreamObject->AllocatorFileObject,
StreamHeader->Header.Data );
#if (DBG)
if (StreamHeader->OnFreeList || StreamHeader->OnActiveList) {
DebugPrint((DebugLevelTrace,
"freeing header %x still on list\n", StreamHeader) );
}
#endif
ExFreePool( StreamHeader );
}
DebugPrint((DebugLevelTrace,
"PrepareTransfer exiting, frame allocation failed: %08x\n", Status) );
return Status;
}
}
StreamObject->PinState = PinPrepared;
DebugPrint((DebugLevelTrace,"exiting PrepareTransfer\n"));
return STATUS_SUCCESS;
}
//---------------------------------------------------------------------------
NTSTATUS
BeginTransfer(
IN PFILTER_INSTANCE FilterInstance,
IN PSTREAM_OBJECT StreamObject
)
/*++
Routine Description:
Begins the data transfer from each pin by initiating stream reads
from the inflow pin. The completion routine for each read will
continue the stream processing.
Arguments:
IN PFILTER_INSTANCE FilterInstance,
pointer to the filter instance
IN PSTREAM_OBJECT StreamObject -
pointer to the transform instance
Return:
STATUS_SUCCESS or an appropriate error code.
Comments:
Not pageable, uses SpinLocks.
--*/
{
KIRQL irql0,irqlFree;
NTSTATUS Status;
PSTREAM_HEADER_EX StreamHeader;
PADDITIONAL_PIN_INFO AdditionalInfo;
DebugPrint((DebugLevelTrace,"entering BeginTransfer\n"));
//
// If the PinState is not PinPrepared, then return.
//
if (StreamObject->PinState != PinPrepared) {
DebugPrint((DebugLevelTrace,"BeginTransfer exiting, PinState != PinPrepared\n") );
return STATUS_INVALID_DEVICE_STATE;
}
AdditionalInfo = FilterInstance->PinInstanceInfo;
StreamObject->PinState = PinRunning;
//
// All preparation is complete. If this is the source pin, begin
// the actual data transfer.
//
Status = STATUS_SUCCESS;
if (StreamObject->PinType == IrpSource) {
#if (DBG)
//
// get the dataflow direction
//
DebugPrint((DebugLevelVerbose,
"BeginTransfer, DataFlow:"));
if (StreamObject->DeviceExtension->StreamDescriptor->StreamInfo.DataFlow == KSPIN_DATAFLOW_IN)
DebugPrint((DebugLevelVerbose,
"KSPIN_DATAFLOW_IN\n"));
else
DebugPrint((DebugLevelVerbose,
"KSPIN_DATAFLOW_OUT\n"));
#endif
//
// Begin the transfer by reading from the inflow pin.
//
KeAcquireSpinLock( &StreamObject->Queues[0].QueueLock, &irql0 );
KeAcquireSpinLock( &StreamObject->FreeQueueLock, &irqlFree );
while (!IsListEmpty( &StreamObject->FreeQueue )) {
PLIST_ENTRY Node;
Node = RemoveHeadList( &StreamObject->FreeQueue );
StreamHeader =
CONTAINING_RECORD(
Node,
STREAM_HEADER_EX,
ListEntry );
#if (DBG)
StreamHeader->OnFreeList = FALSE;
if (StreamHeader->OnActiveList) {
DebugPrint((DebugLevelTrace,"stream header %x already on active list.\n",StreamHeader) );
}
#endif
InterlockedIncrement (&StreamObject -> QueuedFramesPlusOne);
InsertTailList( &StreamObject->Queues[0].ActiveQueue, Node );
#if (DBG)
StreamHeader->OnActiveList = TRUE;
#endif
KeReleaseSpinLock( &StreamObject->FreeQueueLock, irqlFree );
KeReleaseSpinLock( &StreamObject->Queues[0].QueueLock, irql0 );
DebugPrint((DebugLevelTrace,
"BeginTransfer, KsStreamIo: %x\n", StreamHeader));
DebugPrint((DebugLevelTrace,
"BeginTransfer, KsStreamIo: FileObject:%x\n", StreamObject->FileObject));
DebugPrint((DebugLevelTrace,
"BeginTransfer:HeaderSize:=%x\n",StreamHeader->Header.Size));
InterlockedIncrement( &StreamHeader->ReferenceCount );
StreamHeader->NextFileObject = StreamObject->NextFileObject;
//
// send a data irp to myself, first.
//
DebugPrint((DebugLevelTrace,
"BeginTransfer:Reading:%x\n",StreamHeader->Id));
Status =
KsStreamIo(
StreamObject->FileObject,
&StreamHeader->CompletionEvent, // Event
NULL, // PortContext
IoCompletionRoutine,
StreamHeader, // CompletionContext
KsInvokeOnSuccess |
KsInvokeOnCancel |
KsInvokeOnError,
&StreamHeader->IoStatus,
&StreamHeader->Header,
StreamHeader->Header.Size,
KSSTREAM_SYNCHRONOUS | KSSTREAM_READ,
KernelMode );
if (Status != STATUS_PENDING) {
//
// If this I/O completes immediately (failure or not), the
// event is not signalled.
//
KeSetEvent( &StreamHeader->CompletionEvent, IO_NO_INCREMENT, FALSE );
}
if (!NT_SUCCESS( Status )) {
DebugPrint((DebugLevelTrace, "KsStreamIo returned %08x\n", Status ));
} else {
Status = STATUS_SUCCESS;
}
KeAcquireSpinLock( &StreamObject->Queues[0].QueueLock, &irql0 );
KeAcquireSpinLock( &StreamObject->FreeQueueLock, &irqlFree );
}
KeReleaseSpinLock( &StreamObject->FreeQueueLock, irqlFree );
KeReleaseSpinLock( &StreamObject->Queues[0].QueueLock, irql0 );
}
DebugPrint((DebugLevelTrace,"exiting BeginTransfer\n"));
return Status;
}
//---------------------------------------------------------------------------
NTSTATUS
EndTransfer(
IN PFILTER_INSTANCE FilterInstance,
IN PSTREAM_OBJECT StreamObject
)
/*++
Routine Description:
Ends the data transfer, waits for all Irps to complete
Arguments:
IN PFILTER_INSTANCE FilterInstance -
pointer to the filter instance
IN PSTREAM_OBJECT StreamObject
pointer to the Stream object
Return:
STATUS_SUCCESS or an appropriate error code.
Comments:
Not pageable, uses SpinLocks.
--*/
{
PDEVICE_EXTENSION DeviceExtension;
KIRQL irqlOld;
DeviceExtension = StreamObject->DeviceExtension;
DebugPrint((DebugLevelTrace,"entering EndTransfer!\n"));
//
// Set the marker indicating that we stop sourcing frames and then flush
// to ensure that anything blocked on the output pin at least gets
// cancelled before we block and deadlock on it.
//
StreamObject -> PinState = PinStopPending;
StreamFlushIo (DeviceExtension, StreamObject);
if (InterlockedDecrement (&StreamObject -> QueuedFramesPlusOne)) {
//
// Release the control mutex to allow the I/O thread to run.
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
DebugPrint((DebugLevelTrace,
"waiting for pin %d queue to empty\n", StreamObject->PinId));
//
// Wait for the queue to empty
//
KeWaitForSingleObject(
&StreamObject -> StopEvent,
Executive,
KernelMode,
FALSE,
NULL);
DebugPrint((DebugLevelTrace,"queue emptied\n") );
//
// Re-acquire the control object.
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
Executive,
KernelMode,
FALSE,// not alertable
NULL);
}
//
// Free the frames so that we can reprepare for new allocator
// framing, a new allocator or just general cleanup/shutdown.
//
KeAcquireSpinLock( &StreamObject->FreeQueueLock, &irqlOld );
while (!IsListEmpty( &StreamObject->FreeQueue )) {
PLIST_ENTRY Node;
PSTREAM_HEADER_EX StreamHeader;
Node = RemoveHeadList( &StreamObject->FreeQueue );
StreamHeader =
CONTAINING_RECORD(
Node,
STREAM_HEADER_EX,
ListEntry );
#if (DBG)
StreamHeader->OnFreeList = FALSE;
#endif
KeReleaseSpinLock( &StreamObject->FreeQueueLock, irqlOld );
#if (DBG)
ASSERT( StreamHeader->Data == StreamHeader->Header.Data );
#endif
FreeFrame(
StreamObject->AllocatorFileObject,
StreamHeader->Header.Data );
DebugPrint((DebugLevelTrace,
"freeing header: %08x, list: %08x\n", StreamHeader, &StreamObject->FreeQueue) );
#if (DBG)
if (StreamHeader->OnFreeList || StreamHeader->OnActiveList) {
DebugPrint((DebugLevelTrace,
"freeing header %x still on list\n", StreamHeader) );
}
#endif
ExFreePool( StreamHeader );
KeAcquireSpinLock( &StreamObject->FreeQueueLock, &irqlOld );
}
StreamObject->PinState = PinStopped;
KeReleaseSpinLock( &StreamObject->FreeQueueLock, irqlOld );
DebugPrint((DebugLevelTrace,"exiting CleanupTransfer\n"));
return STATUS_SUCCESS;
}
//---------------------------------------------------------------------------
NTSTATUS
AllocateFrame(
PFILE_OBJECT Allocator,
PVOID *Frame
)
/*++
Routine Description:
Allocates a frame from the given allocator
Arguments:
PFILE_OBJECT Allocator -
pointer to the allocator's file object
PVOID *Frame -
pointer to receive the allocated frame pointer
Return:
STATUS_SUCCESS and *Frame contains a pointer to the allocated
frame, otherwise an appropriate error code.
--*/
{
NTSTATUS Status;
KSMETHOD Method;
ULONG Returned;
DebugPrint((DebugLevelTrace,"entering AllocateFrame\n"));
Method.Set = KSMETHODSETID_StreamAllocator;
Method.Id = KSMETHOD_STREAMALLOCATOR_ALLOC;
Method.Flags = KSMETHOD_TYPE_WRITE;
Status =
KsSynchronousIoControlDevice(
Allocator,
KernelMode,
IOCTL_KS_METHOD,
&Method,
sizeof( Method ),
Frame,
sizeof( PVOID ),
&Returned );
DebugPrint((DebugLevelTrace,"exiting AllocateFrame\n"));
return Status;
}
//---------------------------------------------------------------------------
NTSTATUS
FreeFrame(
PFILE_OBJECT Allocator,
PVOID Frame
)
/*++
Routine Description:
Frees a frame to the given allocator
Arguments:
PFILE_OBJECT Allocator -
pointer to the allocator's file object
PVOID Frame -
pointer to the frame to be freed.
Return:
STATUS_SUCCESS or else an appropriate error code.
--*/
{
NTSTATUS Status;
KSMETHOD Method;
ULONG Returned;
DebugPrint((DebugLevelTrace,"entering FreeFrame\n"));
Method.Set = KSMETHODSETID_StreamAllocator;
Method.Id = KSMETHOD_STREAMALLOCATOR_FREE;
Method.Flags = KSMETHOD_TYPE_READ;
Status =
KsSynchronousIoControlDevice(
Allocator,
KernelMode,
IOCTL_KS_METHOD,
&Method,
sizeof( Method ),
&Frame,
sizeof( PVOID ),
&Returned );
DebugPrint((DebugLevelTrace,"exiting FreeFrame\n"));
return Status;
}
//---------------------------------------------------------------------------
NTSTATUS
PinCreateHandler(
IN PIRP Irp,
IN PSTREAM_OBJECT StreamObject
)
/*++
Routine Description:
This is the pin creation handler which is called by KS when a
pin create request is submitted to the filter.
Arguments:
IN PIRP Irp -
pointer to the I/O request packet
Return:
STATUS_SUCCESS or an appropriate error return code.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpStack;
PFILTER_INSTANCE FilterInstance;
PADDITIONAL_PIN_INFO AdditionalInfo;
PFILE_OBJECT NextFileObject;
IrpStack = IoGetCurrentIrpStackLocation( Irp );
DebugPrint((DebugLevelTrace,"entering PinCreateHandler\n"));
FilterInstance =
(PFILTER_INSTANCE) IrpStack->FileObject->RelatedFileObject->FsContext;
AdditionalInfo = FilterInstance->PinInstanceInfo;
Status = STATUS_SUCCESS;
StreamObject->NextFileObject = NULL;
DebugPrint((DebugLevelTrace,"PinCreateHandler:its an IrpSource\n"));
//
// Validate that we can handle this connection request
//
if (StreamObject->NextFileObject) {
DebugPrint((DebugLevelTrace,"invalid connection request\n") );
Status = STATUS_CONNECTION_REFUSED;
}
else
{
Status =
ObReferenceObjectByHandle(
StreamObject->PinToHandle,
FILE_READ_ACCESS | FILE_WRITE_ACCESS | SYNCHRONIZE,
*IoFileObjectType,
KernelMode,
&NextFileObject,
NULL );
if (!NT_SUCCESS(Status)) {
DebugPrint((DebugLevelTrace,"PinCreateHandler:error referencing PinToHandle\n"));
}
else
{
// NextFileObject must be per instance
//AdditionalInfo[ StreamObject->PinId ].NextFileObject = NextFileObject;
StreamObject->NextFileObject = NextFileObject;
//
// Add the pin's target to the list of targets for
// recalculating stack depth.
//
KsSetTargetDeviceObject(
StreamObject->ComObj.DeviceHeader,
IoGetRelatedDeviceObject(
NextFileObject ) );
}
}
DebugPrint((DebugLevelTrace,"PinCreateHandler returning %x\n", Status ));
return Status;
}
#endif