|
|
/* ++
* * Copyright (c) 1996 Microsoft Corporation * * Module Name: * * codguts.c * * Abstract: * * This is the WDM streaming class driver. This module contains code related * to internal processing. * * Author: * * billpa * * Environment: * * Kernel mode only * * * Revision History: * * -- */
#include "codcls.h"
#include <stdlib.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, SCBuildRequestPacket)
#pragma alloc_text(PAGE, SCProcessDmaDataBuffers)
#pragma alloc_text(PAGE, SCProcessPioDataBuffers)
#pragma alloc_text(PAGE, SCOpenMinidriverInstance)
#pragma alloc_text(PAGE, SCMinidriverDevicePropertyHandler)
#pragma alloc_text(PAGE, SCMinidriverStreamPropertyHandler)
#pragma alloc_text(PAGE, SCUpdateMinidriverProperties)
#pragma alloc_text(PAGE, SCProcessCompletedPropertyRequest)
#pragma alloc_text(PAGE, SCLogError)
#pragma alloc_text(PAGE, SCLogErrorWithString)
#pragma alloc_text(PAGE, SCReferenceDriver)
#pragma alloc_text(PAGE, SCDereferenceDriver)
#pragma alloc_text(PAGE, SCReadRegistryValues)
#pragma alloc_text(PAGE, SCGetRegistryValue)
#pragma alloc_text(PAGE, SCSubmitRequest)
#pragma alloc_text(PAGE, SCProcessDataTransfer)
#pragma alloc_text(PAGE, SCShowIoPending)
#pragma alloc_text(PAGE, SCCheckPoweredUp)
#pragma alloc_text(PAGE, SCCheckPowerDown)
#pragma alloc_text(PAGE, SCCallNextDriver)
#pragma alloc_text(PAGE, SCSendUnknownCommand)
#pragma alloc_text(PAGE, SCMapMemoryAddress)
#pragma alloc_text(PAGE, SCUpdatePersistedProperties)
#pragma alloc_text(PAGE, SCProcessCompletedPropertyRequest)
#pragma alloc_text(PAGE, SCUpdateMinidriverEvents)
#pragma alloc_text(PAGE, SCQueryCapabilities)
#pragma alloc_text(PAGE, SCRescanStreams)
#pragma alloc_text(PAGE, SCCopyMinidriverProperties)
#pragma alloc_text(PAGE, SCCopyMinidriverEvents)
#endif
#ifdef ENABLE_KS_METHODS
#pragma alloc_text(PAGE, SCCopyMinidriverMethods)
#endif
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif
extern KSDISPATCH_TABLE FilterDispatchTable;
//
// registry string indicating that the minidriver should be paged out when
// unopened
//
static const WCHAR PageOutWhenUnopenedString[] = L"PageOutWhenUnopened";
//
// registry string indicating that the minidriver should be paged out when
// idle
//
static const WCHAR PageOutWhenIdleString[] = L"PageOutWhenIdle";
//
// registry string indicating that the device should be powered down when
// unopened
//
static const WCHAR PowerDownWhenUnopenedString[] = L"PowerDownWhenUnopened";
//
// registry string indicating that the device should not be suspended when
// pins are in run state
//
static const WCHAR DontSuspendIfStreamsAreRunning[] = L"DontSuspendIfStreamsAreRunning";
//
// This driver uses SWEnum to load, which means it is a kernel mode
// streaming driver that has no hardware associated with it. We need to
// AddRef/DeRef this driver special.
//
static const WCHAR DriverUsesSWEnumToLoad[] = L"DriverUsesSWEnumToLoad";
//
//
//
static const WCHAR OkToHibernate[] = L"OkToHibernate";
//
// array of registry settings to be read when the device is initialized
//
static const STREAM_REGISTRY_ENTRY RegistrySettings[] = { { (PWCHAR) PageOutWhenUnopenedString, sizeof(PageOutWhenUnopenedString), DEVICE_REG_FL_PAGE_CLOSED },
{ (PWCHAR) PageOutWhenIdleString, sizeof(PageOutWhenIdleString), DEVICE_REG_FL_PAGE_IDLE },
{ (PWCHAR) PowerDownWhenUnopenedString, sizeof(PowerDownWhenUnopenedString), DEVICE_REG_FL_POWER_DOWN_CLOSED },
{ (PWCHAR) DontSuspendIfStreamsAreRunning, sizeof(DontSuspendIfStreamsAreRunning), DEVICE_REG_FL_NO_SUSPEND_IF_RUNNING },
{ (PWCHAR) DriverUsesSWEnumToLoad, sizeof(DriverUsesSWEnumToLoad), DRIVER_USES_SWENUM_TO_LOAD }, { (PWCHAR) OkToHibernate, sizeof(OkToHibernate), DEVICE_REG_FL_OK_TO_HIBERNATE } };
//
// this structure indicates the handlers for CreateFile on Streams
//
static const WCHAR PinTypeName[] = KSSTRING_Pin;
static const KSOBJECT_CREATE_ITEM CreateHandlers[] = {
DEFINE_KSCREATE_ITEM(StreamDispatchCreate, PinTypeName, 0) };
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif
//
// Routines start
//
NTSTATUS SCDequeueAndStartStreamDataRequest( IN PSTREAM_OBJECT StreamObject ) /*++
Routine Description:
Start the queued data IRP for the stream. THE SPINLOCK MUST BE TAKEN ON THIS CALL AND A DATA IRP MUST BE ON THE QUEUE!
Arguments:
StreamObject - address of stream info structure.
Return Value:
NTSTATUS returned
--*/
{ PIRP Irp; PSTREAM_REQUEST_BLOCK Request; PLIST_ENTRY Entry; BOOLEAN Status; PDEVICE_EXTENSION DeviceExtension = StreamObject->DeviceExtension;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); Entry = RemoveTailList(&StreamObject->DataPendingQueue); Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
ASSERT(Irp);
// ASSERT((IoGetCurrentIrpStackLocation(Irp)->MajorFunction ==
// IOCTL_KS_READ_STREAM) ||
// (IoGetCurrentIrpStackLocation(Irp)->MajorFunction ==
// IOCTL_KS_WRITE_STREAM));
ASSERT((ULONG_PTR) Irp->Tail.Overlay.DriverContext[0] > 0x40000000);
DebugPrint((DebugLevelVerbose, "'SCStartStreamDataReq: Irp = %x, S# = %x\n", Irp, StreamObject->HwStreamObject.StreamNumber));
//
// clear the ready flag as we are going to send one down.
//
ASSERT(StreamObject->ReadyForNextDataReq);
StreamObject->ReadyForNextDataReq = FALSE;
//
// set the cancel routine to outstanding
//
IoSetCancelRoutine(Irp, StreamClassCancelOutstandingIrp);
//
// release the spinlock.
//
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
//
// get the request packet from the IRP
//
Request = Irp->Tail.Overlay.DriverContext[0];
//
// build scatter/gather list if necessary
//
if (StreamObject->HwStreamObject.Dma) {
//
// allocate the adapter channel. call cannot fail as the only
// time it would is when there aren't enough map registers, and
// we've already checked for that condition.
//
Status = SCSetUpForDMA(DeviceExtension->DeviceObject, Request); ASSERT(Status);
//
// DMA adapter allocation requires a
// callback, so just exit
//
return (STATUS_PENDING);
} // if DMA
//
// start the request for the PIO case.
//
SCStartMinidriverRequest(StreamObject, Request, (PVOID) StreamObject->HwStreamObject.ReceiveDataPacket);
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); return (STATUS_PENDING);
}
NTSTATUS SCDequeueAndStartStreamControlRequest( IN PSTREAM_OBJECT StreamObject ) /*++
Routine Description:
Start the queued control IRP for the stream. THE SPINLOCK MUST BE TAKEN ON THIS CALL AND A DATA IRP MUST BE ON THE QUEUE!
Arguments:
StreamObject - address of stream info structure.
Return Value:
NTSTATUS returned
--*/
{ PIRP Irp; PSTREAM_REQUEST_BLOCK Request; PLIST_ENTRY Entry;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); Entry = RemoveTailList(&StreamObject->ControlPendingQueue); Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
ASSERT(Irp); DebugPrint((DebugLevelTrace, "'SCStartStreamControlReq: Irp = %x, S# = %x\n", Irp, StreamObject->HwStreamObject.StreamNumber));
//
// clear the ready flag as we are going
// to send one down.
//
ASSERT(StreamObject->ReadyForNextControlReq);
StreamObject->ReadyForNextControlReq = FALSE;
//
// release the spinlock.
//
KeReleaseSpinLockFromDpcLevel(&StreamObject->DeviceExtension->SpinLock);
//
// get the request packet from the IRP
//
Request = Irp->Tail.Overlay.DriverContext[0];
//
// start the request.
//
SCStartMinidriverRequest(StreamObject, Request, (PVOID) StreamObject->HwStreamObject.ReceiveControlPacket);
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); return (STATUS_PENDING);
}
NTSTATUS SCDequeueAndStartDeviceRequest( IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
Start the queued device IRP. THE DEV SPINLOCK MUST BE TAKEN ON THIS CALL AND AN IRP MUST BE ON THE QUEUE!
Arguments:
DeviceExtension - address of device extension.
Return Value:
NTSTATUS
--*/
{ PIRP Irp; PLIST_ENTRY Entry; PSTREAM_REQUEST_BLOCK Request;
Entry = RemoveTailList(&DeviceExtension->PendingQueue); Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); ASSERT(Irp);
//
// clear the ready flag as we are going
// to send one down.
//
ASSERT(DeviceExtension->ReadyForNextReq);
DeviceExtension->ReadyForNextReq = FALSE;
//
// get the request packet from the IRP
//
Request = Irp->Tail.Overlay.DriverContext[0];
ASSERT(Request);
//
// show that the request is active.
//
Request->Flags |= SRB_FLAGS_IS_ACTIVE;
//
// place the request on the outstanding
// queue
//
InsertHeadList( &DeviceExtension->OutstandingQueue, &Request->SRBListEntry);
//
// set the cancel routine to outstanding
//
IoSetCancelRoutine(Irp, StreamClassCancelOutstandingIrp);
//
// send down the request to the
// minidriver.
//
DebugPrint((DebugLevelTrace, "'SCDequeueStartDevice: starting Irp %x, SRB = %x, Command = %x\n", Request->HwSRB.Irp, Request, Request->HwSRB.Command));
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, (PVOID) DeviceExtension->MinidriverData->HwInitData.HwReceivePacket, &Request->HwSRB);
//
// release the spinlock.
//
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
return (STATUS_PENDING); }
PSTREAM_REQUEST_BLOCK SCBuildRequestPacket( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp, IN ULONG AdditionalSize1, // scatter gather size
IN ULONG AdditionalSize2 // saved ptr array size
) /*++
Routine Description:
Routine builds an SRB and fills in generic fields
Arguments:
DeviceExtension - address of device extension. Irp - Address of I/O request packet. AdditionalSize1 - additional size needed for scatter/gather, etc. AdditionalSize2 - additional size needed for the Saved pointer array.
Return Value:
Address of the streaming request packet.
--*/
{ ULONG BlockSize; PSTREAM_REQUEST_BLOCK Request;
PAGED_CODE();
//
// compute the size of the block needed.
//
BlockSize = sizeof(STREAM_REQUEST_BLOCK) + DeviceExtension-> MinidriverData->HwInitData.PerRequestExtensionSize + AdditionalSize1+ AdditionalSize2;
Request = ExAllocatePool(NonPagedPool, BlockSize);
if (Request == NULL) { DebugPrint((DebugLevelError, "SCBuildRequestPacket: No pool for packet")); ASSERT(0); return (NULL); } //
// alloc MDL for the request.
//
// GUBGUB This a marginal performace enhancment chance.
// - should find a way to avoid allocating both an MDL and
// SRB per request. Maybe have a list of MDL's around and allocate only
// if we run out. Forrest won't like this.
//
//
Request->Mdl = IoAllocateMdl(Request, BlockSize, FALSE, FALSE, NULL );
if (Request->Mdl == NULL) { ExFreePool(Request); DebugPrint((DebugLevelError, "SCBuildRequestPacket: can't get MDL")); return (NULL); } MmBuildMdlForNonPagedPool(Request->Mdl);
//
// fill in the various SRB fields
// generically
//
Request->Length = BlockSize; Request->HwSRB.SizeOfThisPacket = sizeof(HW_STREAM_REQUEST_BLOCK);
Request->HwSRB.Status = STATUS_PENDING; Request->HwSRB.StreamObject = NULL; Request->HwSRB.HwInstanceExtension = NULL; Request->HwSRB.NextSRB = (PHW_STREAM_REQUEST_BLOCK) NULL; Request->HwSRB.SRBExtension = Request + 1; Request->HwSRB.Irp = Irp; Request->Flags = 0; Request->MapRegisterBase = 0; Request->HwSRB.Flags = 0; Request->HwSRB.TimeoutCounter = 15; Request->HwSRB.TimeoutOriginal = 15; Request->HwSRB.ScatterGatherBuffer = (PKSSCATTER_GATHER) ((ULONG_PTR) Request->HwSRB.SRBExtension + (ULONG_PTR) DeviceExtension-> MinidriverData->HwInitData.PerRequestExtensionSize);
Request->pMemPtrArray = (PVOID) (((ULONG_PTR) Request->HwSRB.SRBExtension + (ULONG_PTR) DeviceExtension-> MinidriverData->HwInitData.PerRequestExtensionSize) + AdditionalSize1); //
// point the IRP workspace to the request
// packet
//
Irp->Tail.Overlay.DriverContext[0] = Request;
return (Request);
} // end SCBuildRequestPacket()
VOID SCProcessDmaDataBuffers( IN PKSSTREAM_HEADER FirstHeader, IN ULONG NumberOfHeaders, IN PSTREAM_OBJECT StreamObject, IN PMDL FirstMdl, OUT PULONG NumberOfPages, IN ULONG StreamHeaderSize, IN BOOLEAN Write
) /*++
Routine Description:
Processes each data buffer for PIO &| DMA case
Arguments:
FirstHeader - Address of the 1st s/g packet StreamObject- pointer to stream object NumberOfPages - number of pages in the request
Return Value:
--*/
{ PKSSTREAM_HEADER CurrentHeader; PMDL CurrentMdl; ULONG i; ULONG DataBytes; PAGED_CODE();
//
// loop through each scatter/gather elements
//
CurrentHeader = FirstHeader; CurrentMdl = FirstMdl;
for (i = 0; i < NumberOfHeaders; i++) {
//
// pick up the correct data buffer, based on the xfer direction
//
if (Write) {
DataBytes = CurrentHeader->DataUsed;
} else { // if write
DataBytes = CurrentHeader->FrameExtent;
} // if write
//
// if this header has data, process it.
//
if (DataBytes) { #if DBG
if (CurrentHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID) { DebugPrint((DebugLevelVerbose, "'SCProcessData: time = %x\n", CurrentHeader->PresentationTime.Time)); } #endif
//
// add # pages to total if DMA
//
*NumberOfPages += ADDRESS_AND_SIZE_TO_SPAN_PAGES( MmGetMdlVirtualAddress(CurrentMdl), DataBytes); CurrentMdl = CurrentMdl->Next; } //
// offset to the next buffer
//
CurrentHeader = ((PKSSTREAM_HEADER) ((PBYTE) CurrentHeader + StreamHeaderSize));
} // for # elements
} // end SCProcessDmaDataBuffers()
//
// mmGetSystemAddressForMdl() is defined as a macro in wdm.h which
// calls mmMapLockedPages() which is treated as an evil by verifier.
// mmMapLockedPages is reimplemented by mm via
// mmMapLockedPagesSpecifyCache(MDL,Mode,mmCaches,NULL,TRUE,HighPriority)
// where TRUE is to indicate a bug check, should the call fails.
// I don't need the bug check, therefore, I specify FALSE below.
//
#ifdef WIN9X_STREAM
#define SCGetSystemAddressForMdl(MDL) MmGetSystemAddressForMdl(MDL)
#else
#define SCGetSystemAddressForMdl(MDL) \
(((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | \ MDL_SOURCE_IS_NONPAGED_POOL)) ? \ ((MDL)->MappedSystemVa) : \ (MmMapLockedPagesSpecifyCache((MDL), \ KernelMode, \ MmCached, \ NULL, \ FALSE, \ HighPagePriority))) #endif
BOOLEAN SCProcessPioDataBuffers( IN PKSSTREAM_HEADER FirstHeader, IN ULONG NumberOfHeaders, IN PSTREAM_OBJECT StreamObject, IN PMDL FirstMdl, IN ULONG StreamHeaderSize, IN PVOID *pDataPtrArray, IN BOOLEAN Write ) /*++
Routine Description:
Processes each data buffer for PIO &| DMA case
Arguments:
FirstHeader - Address of the 1st s/g packet StreamObject- pointer to stream object NumberOfPages - number of pages in the request
Return Value:
--*/
{ PKSSTREAM_HEADER CurrentHeader; PMDL CurrentMdl; ULONG i; ULONG DataBytes; BOOLEAN ret = FALSE;
PAGED_CODE();
//
// loop through each scatter/gather elements
//
CurrentHeader = FirstHeader; CurrentMdl = FirstMdl;
for (i = 0; i < NumberOfHeaders; i++) {
//
// pick up the correct data buffer, based on the xfer direction
//
if (Write) {
DataBytes = CurrentHeader->DataUsed;
} else { // if write
DataBytes = CurrentHeader->FrameExtent;
} // if write
//
// if this header has data, process it.
//
if (DataBytes) { //
// fill in the system virtual pointer
// to the buffer if mapping is
// needed
//
#if (DBG)
if ( 0 != ( CurrentMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL))) {
ASSERT(CurrentHeader->Data == (PVOID) ((ULONG_PTR) CurrentMdl->StartVa + CurrentMdl->ByteOffset)); } #endif
DebugPrint((DebugLevelVerbose, "Saving: Index:%x, Ptr:%x\n", i, CurrentHeader->Data));
ret = TRUE; pDataPtrArray[i] = CurrentHeader->Data; CurrentHeader->Data = SCGetSystemAddressForMdl(CurrentMdl);
DebugPrint((DebugLevelVerbose, "'SCPio: buff = %x, length = %x\n", CurrentHeader->Data, DataBytes)); CurrentMdl = CurrentMdl->Next; } //
// offset to the next buffer
//
CurrentHeader = ((PKSSTREAM_HEADER) ((PBYTE) CurrentHeader + StreamHeaderSize));
} // for # elements
return(ret); } // end SCProcessPioDataBuffers()
BOOLEAN SCSetUpForDMA( IN PDEVICE_OBJECT DeviceObject, IN PSTREAM_REQUEST_BLOCK Request
) /*++
Routine Description:
process read/write DMA request. allocate adapter channel.
Arguments:
DeviceObject - device object for the device Request - address of Codec Request Block
Return Value:
returns TRUE if channel is allocated
--*/
{ NTSTATUS status;
//
// Allocate the adapter channel with sufficient map registers
// for the transfer.
//
status = IoAllocateAdapterChannel( ((PDEVICE_EXTENSION) (DeviceObject->DeviceExtension))->DmaAdapterObject, DeviceObject, Request->HwSRB.NumberOfPhysicalPages + 1, // one more for the SRB
// extension
StreamClassDmaCallback, Request);
if (!NT_SUCCESS(status)) { return FALSE; } return TRUE;
}
IO_ALLOCATION_ACTION StreamClassDmaCallback( IN PDEVICE_OBJECT DeviceObject, IN PIRP InputIrp, IN PVOID MapRegisterBase, IN PVOID Context ) /*++
Routine Description:
continues to process read/write request after DMA adapter is allocated builds scatter/gather list from logical buffer list.
Arguments:
DeviceObject - dev object for adapter InputIrp - bogus MapRegisterBase - base address of map registers Context - address of Codec Request Block
Return Value:
returns the appropriate I/O allocation action.
--*/
{ PSTREAM_REQUEST_BLOCK Request = Context; PKSSCATTER_GATHER scatterList; BOOLEAN writeToDevice; PVOID dataVirtualAddress; ULONG totalLength, NumberOfBuffers; PIRP Irp = Request->HwSRB.Irp;
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD( Request->HwSRB.StreamObject, STREAM_OBJECT, HwStreamObject ); PMDL CurrentMdl; ULONG NumberOfElements = 0;
//
// Save the MapRegisterBase for later use
// to deallocate the map
// registers.
//
Request->MapRegisterBase = MapRegisterBase;
//
// determine whether this is a write request
//
writeToDevice = Request->HwSRB.Command == SRB_WRITE_DATA ? TRUE : FALSE;
scatterList = Request->HwSRB.ScatterGatherBuffer;
NumberOfBuffers = Request->HwSRB.NumberOfBuffers;
ASSERT(Irp);
CurrentMdl = Irp->MdlAddress;
while (CurrentMdl) {
//
// Determine the virtual address of the buffer
//
dataVirtualAddress = (PSCHAR) MmGetMdlVirtualAddress(CurrentMdl);
//
// flush the buffers since we are doing DMA.
//
KeFlushIoBuffers(CurrentMdl, (BOOLEAN) (Request->HwSRB.Command == SRB_WRITE_DATA ? TRUE : FALSE), TRUE);
//
// Build the scatter/gather list by looping through the buffers
// calling I/O map transfer.
//
totalLength = 0;
while (totalLength < CurrentMdl->ByteCount) {
NumberOfElements++;
//
// Request that the rest of the transfer be mapped.
//
scatterList->Length = CurrentMdl->ByteCount - totalLength;
//
// Since we are a master call I/O map transfer with a NULL
// adapter.
//
scatterList->PhysicalAddress = IoMapTransfer(((PDEVICE_EXTENSION) (DeviceObject->DeviceExtension)) ->DmaAdapterObject, CurrentMdl, MapRegisterBase, (PSCHAR) dataVirtualAddress + totalLength, &scatterList->Length, writeToDevice);
DebugPrint((DebugLevelVerbose, "'SCDma: seg = %x'%x, length = %x\n", scatterList->PhysicalAddress.HighPart, scatterList->PhysicalAddress.LowPart, scatterList->Length));
totalLength += scatterList->Length; scatterList++; }
CurrentMdl = CurrentMdl->Next;
} // while CurrentMdl
Request->HwSRB.NumberOfScatterGatherElements = NumberOfElements;
//
// now map the transfer for the SRB in case the minidriver needs the
// physical address of the extension.
//
// NOTE: This function changes the length field in the SRB, which
// makes it invalid. It is not used elsewhere, however.
//
// We must flush the buffers appropriately as the SRB extension
// may be DMA'ed both from and to. According to JHavens, we want to
// tell IOMapXfer and KeFlushIoBuffers that this is a write, and upon
// completion tell IoFlushAdapterBuffers that this is a read.
//
// Need to investigate if doing these extra calls add more overhead than
// maintaining a queue of SRB's & extensions. However, on x86
// platforms the KeFlush call gets compiled out and
// on PCI systems the IoFlush call doesn't get made, so there is no
// overhead on these system except the map xfer call.
//
//
// flush the SRB buffer since we are doing DMA.
//
KeFlushIoBuffers(Request->Mdl, FALSE, TRUE);
//
// get the physical address of the SRB
//
Request->PhysicalAddress = IoMapTransfer(((PDEVICE_EXTENSION) (DeviceObject->DeviceExtension)) ->DmaAdapterObject, Request->Mdl, MapRegisterBase, (PSCHAR) MmGetMdlVirtualAddress( Request->Mdl), &Request->Length, TRUE);
//
// if we are async, signal the event which will cause the request to be
// called down on the original thread; otherwise, send the request down
// now at dispatch level.
//
if (((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->NoSync) {
KeSetEvent(&Request->DmaEvent, IO_NO_INCREMENT, FALSE);
} else {
//
// send the request to the minidriver
//
SCStartMinidriverRequest(StreamObject, Request, (PVOID) StreamObject->HwStreamObject.ReceiveDataPacket);
} // if nosync
//
// keep the map registers but release the I/O adapter channel
//
return (DeallocateObjectKeepRegisters); }
VOID SCStartMinidriverRequest( IN PSTREAM_OBJECT StreamObject, IN PSTREAM_REQUEST_BLOCK Request, IN PVOID EntryPoint ) /*++
Routine Description:
adds request to outstanding queue and starts the minidriver.
Arguments:
StreamObject - Address stream info struct Request - Address of streaming data packet EntryPoint - Minidriver routine to be called
Return Value:
--*/
{ PIRP Irp = Request->HwSRB.Irp; PDEVICE_EXTENSION DeviceExtension = StreamObject->DeviceExtension;
//
// show that the request is active.
//
Request->Flags |= SRB_FLAGS_IS_ACTIVE;
//
// place the request on the outstanding queue
//
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
InsertHeadList( &DeviceExtension->OutstandingQueue, &Request->SRBListEntry);
//
// set the cancel routine to outstanding
//
IoSetCancelRoutine(Irp, StreamClassCancelOutstandingIrp);
//
// send down the request to the minidriver. Protect the call with the
// device spinlock to synchronize timers, etc.
//
#if DBG
if (DeviceExtension->NeedyStream) {
ASSERT(DeviceExtension->NeedyStream->OnNeedyQueue); } #endif
DebugPrint((DebugLevelTrace, "'SCStartMinidriverRequeest: starting Irp %x, S# = %x, SRB = %x, Command = %x\n", Request->HwSRB.Irp, StreamObject->HwStreamObject.StreamNumber, Request, Request->HwSRB.Command));
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, EntryPoint, &Request->HwSRB);
#if DBG
if (DeviceExtension->NeedyStream) {
ASSERT(DeviceExtension->NeedyStream->OnNeedyQueue); } #endif
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
return;
} // SCStartMinidriverRequest
VOID StreamClassDpc( IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++
Routine Description:
this routine processes requests and notifications from the minidriver
Arguments:
Dpc - pointer to Dpc structure DeviceObject - device object for the adapter Irp - not used Context - StreamObject structure
Return Value:
None.
--*/
{ PSTREAM_OBJECT StreamObject = Context, NeedyStream; PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; INTERRUPT_CONTEXT interruptContext; INTERRUPT_DATA SavedStreamInterruptData; INTERRUPT_DATA SavedDeviceInterruptData; PSTREAM_REQUEST_BLOCK SRB; PERROR_LOG_ENTRY LogEntry; HW_TIME_CONTEXT TimeContext;
UNREFERENCED_PARAMETER(Irp); UNREFERENCED_PARAMETER(Dpc);
interruptContext.SavedStreamInterruptData = &SavedStreamInterruptData; interruptContext.SavedDeviceInterruptData = &SavedDeviceInterruptData; interruptContext.DeviceExtension = DeviceExtension;
DebugPrint((DebugLevelVerbose, "'StreamClassDpc: enter\n"));
//
// if a stream object is passed in, first
// check if work is pending
//
if (StreamObject) {
SCStartRequestOnStream(StreamObject, DeviceExtension);
} // if streamobject
RestartDpc:
//
// Check for a ready for next packet on
// the device.
//
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
if ((DeviceExtension->ReadyForNextReq) && (!IsListEmpty(&DeviceExtension->PendingQueue))) {
//
// start the device request, which
// clears the ready flag and
// releases the spinlock. Then
// reacquire the spinloc.
//
SCDequeueAndStartDeviceRequest(DeviceExtension); KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
} //
// Get the interrupt state snapshot. This copies the interrupt state to
// saved state where it can be processed. It also clears the interrupt
// flags. We acquired the device spinlock to protect the structure as
// the minidriver could have requested a DPC call from this routine,
// which could be preempted in the middle of minidriver's changing the
// below structure, and we'd then take a snapshot of the structure while
// it was changing.
//
interruptContext.NeedyStream = NULL;
SavedDeviceInterruptData.CompletedSRB = NULL; SavedStreamInterruptData.CompletedSRB = NULL; SavedDeviceInterruptData.Flags = 0; SavedStreamInterruptData.Flags = 0;
if (!DeviceExtension->SynchronizeExecution(DeviceExtension->InterruptObject, SCGetInterruptState, &interruptContext)) {
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); return; } KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
NeedyStream = interruptContext.NeedyStream;
if (NeedyStream) {
//
// try to start a request on this
// stream
//
SCStartRequestOnStream(NeedyStream, DeviceExtension);
//
// Process any completed stream requests.
//
while (SavedStreamInterruptData.CompletedSRB != NULL) {
//
// Remove the request from the
// linked-list.
//
SRB = CONTAINING_RECORD(SavedStreamInterruptData.CompletedSRB, STREAM_REQUEST_BLOCK, HwSRB);
SavedStreamInterruptData.CompletedSRB = SRB->HwSRB.NextSRB;
DebugPrint((DebugLevelTrace, "'SCDpc: Completing stream Irp %x, S# = %x, SRB = %x, Func = %x, Callback = %x, SRB->IRP = %x\n", SRB->HwSRB.Irp, NeedyStream->HwStreamObject.StreamNumber, SRB, SRB->HwSRB.Command, SRB->Callback, SRB->HwSRB.Irp));
SCCallBackSrb(SRB, DeviceExtension);
}
//
// Check for timer requests.
//
if (SavedStreamInterruptData.Flags & INTERRUPT_FLAGS_TIMER_CALL_REQUEST) {
SCProcessTimerRequest(&NeedyStream->ComObj, &SavedStreamInterruptData); } //
// check to see if a change priority call has been requested.
//
if (SavedStreamInterruptData.Flags & INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST) {
SCProcessPriorityChangeRequest(&NeedyStream->ComObj, &SavedStreamInterruptData, DeviceExtension); } //
// Check for master clock queries.
//
if (SavedStreamInterruptData.Flags & INTERRUPT_FLAGS_CLOCK_QUERY_REQUEST) {
LARGE_INTEGER ticks; ULONGLONG rate; KIRQL SavedIrql;
//
// call the master clock's entry point then call the minidriver's
// callback procedure to report the time.
//
TimeContext.HwDeviceExtension = DeviceExtension->HwDeviceExtension; TimeContext.HwStreamObject = &NeedyStream->HwStreamObject; TimeContext.Function = SavedStreamInterruptData.HwQueryClockFunction;
//
// take the lock so MasterCliockinfo won't disapear under us
//
KeAcquireSpinLock( &NeedyStream->LockUseMasterClock, &SavedIrql );
if ( NULL == NeedyStream->MasterClockInfo ) { ASSERT( 0 && "Mini driver queries clock while we have no master clock"); //
// give a hint that something is wrong via Time, since we return void.
//
TimeContext.Time = (ULONGLONG)-1; goto callminidriver; }
switch (SavedStreamInterruptData.HwQueryClockFunction) {
case TIME_GET_STREAM_TIME:
TimeContext.Time = NeedyStream->MasterClockInfo-> FunctionTable.GetCorrelatedTime( NeedyStream->MasterClockInfo->ClockFileObject, &TimeContext.SystemTime);
goto callminidriver;
case TIME_READ_ONBOARD_CLOCK:
TimeContext.Time = NeedyStream->MasterClockInfo-> FunctionTable.GetTime( NeedyStream->MasterClockInfo->ClockFileObject);
//
// timestamp the value as close as possible
//
ticks = KeQueryPerformanceCounter((PLARGE_INTEGER) & rate);
TimeContext.SystemTime = KSCONVERT_PERFORMANCE_TIME( rate, ticks );
callminidriver:
//
// finish using MasterClockInfo.
//
KeReleaseSpinLock( &NeedyStream->LockUseMasterClock, SavedIrql );
//
// call the minidriver's callback procedure
//
if (!DeviceExtension->NoSync) {
//
// Acquire the device spinlock.
//
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
} DebugPrint((DebugLevelTrace, "'SCDPC: calling time func, S# = %x, Command = %x\n", NeedyStream->HwStreamObject.StreamNumber, TimeContext.Function));
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, (PKSYNCHRONIZE_ROUTINE) SavedStreamInterruptData.HwQueryClockRoutine, &TimeContext );
if (!DeviceExtension->NoSync) {
//
// Release the spinlock.
//
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
} break;
default: KeReleaseSpinLock( &NeedyStream->LockUseMasterClock, SavedIrql ); ASSERT(0); } // switch clock func
} // if queryclock
} // if needystream
//
// Check for an error log request.
//
if (SavedDeviceInterruptData.Flags & INTERRUPT_FLAGS_LOG_ERROR) {
//
// Process the error log request.
//
LogEntry = &SavedDeviceInterruptData.LogEntry;
SCLogError(DeviceObject, LogEntry->SequenceNumber, LogEntry->ErrorCode, LogEntry->UniqueId );
} // if log error
//
// Process any completed device requests.
//
while (SavedDeviceInterruptData.CompletedSRB != NULL) {
//
// Remove the request from the linked-list.
//
SRB = CONTAINING_RECORD(SavedDeviceInterruptData.CompletedSRB, STREAM_REQUEST_BLOCK, HwSRB);
SavedDeviceInterruptData.CompletedSRB = SRB->HwSRB.NextSRB;
DebugPrint((DebugLevelTrace, "'SCDpc: Completing device Irp %x\n", SRB->HwSRB.Irp));
SCCallBackSrb(SRB, DeviceExtension); }
//
// Check for device timer requests.
//
if (SavedDeviceInterruptData.Flags & INTERRUPT_FLAGS_TIMER_CALL_REQUEST) {
SCProcessTimerRequest(&DeviceExtension->ComObj, &SavedDeviceInterruptData); } //
// check if we have any dead events that need discarding. if so, we'll
// schedule a work item to get rid of them.
//
if ((!IsListEmpty(&DeviceExtension->DeadEventList)) && (!(DeviceExtension->DeadEventItemQueued))) {
DeviceExtension->DeadEventItemQueued = TRUE;
ExQueueWorkItem(&DeviceExtension->EventWorkItem, DelayedWorkQueue); } //
// check to see if a change priority call
// has been requested for the device.
//
if (SavedDeviceInterruptData.Flags & INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST) {
SCProcessPriorityChangeRequest(&DeviceExtension->ComObj, &SavedDeviceInterruptData, DeviceExtension);
} // if change priority
//
// Check for stream rescan request.
//
if (SavedDeviceInterruptData.Flags & INTERRUPT_FLAGS_NEED_STREAM_RESCAN) {
TRAP; ExQueueWorkItem(&DeviceExtension->RescanWorkItem, DelayedWorkQueue); } //
// Check for minidriver work requests. Note this is an unsynchronized
// test on bits that can be set by the interrupt routine; however,
// the worst that can happen is that the completion DPC checks for work
// twice.
//
if ((DeviceExtension->NeedyStream) || (DeviceExtension->ComObj.InterruptData.Flags & INTERRUPT_FLAGS_NOTIFICATION_REQUIRED)) {
//
// Start over from the top.
//
DebugPrint((DebugLevelVerbose, "'StreamClassDpc: restarting\n")); goto RestartDpc; } return;
} // end StreamClassDpc()
VOID SCStartRequestOnStream( IN PSTREAM_OBJECT StreamObject, IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
Routine tries to start either a control or data request on the specified stream.
Arguments:
StreamObject - pointer to stream object DeviceExtension - pointer to device extension.
Return Value:
None
Notes:
--*/ { //
// Check for a ready for next packet. Acquire spinlock to protect
// READY bits. Note that we don't snapshot the ready flags as we do with
// the remaining notification flags, as we don't want to clear the flags
// unconditionally in the snapshot in case there is not currently a
// request pending. Also, starting a request before the snapshot will
// give a slight perf improvement. Note that the flags can be set via
// the minidriver while we are checking them, but since the minidriver
// cannot clear them and the minidriver cannot call for a next request
// more than once before it receives one, this is not a problem.
//
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
if ((StreamObject->ReadyForNextDataReq) && (!IsListEmpty(&StreamObject->DataPendingQueue))) {
//
// start the request, which clears the ready flag and releases
// the spinlock, then reobtain the spinlock.
//
SCDequeueAndStartStreamDataRequest(StreamObject); KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
} // if ready for data
if ((StreamObject->ReadyForNextControlReq) && (!IsListEmpty(&StreamObject->ControlPendingQueue))) {
//
// start the request, which clears the ready flag and releases
// the spinlock.
//
SCDequeueAndStartStreamControlRequest(StreamObject);
} else {
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } // if ready for control
return; }
BOOLEAN SCGetInterruptState( IN PVOID ServiceContext ) /*++
Routine Description:
This routine saves the InterruptFlags, error log info, and CompletedRequests fields and clears the InterruptFlags.
Arguments:
ServiceContext - Supplies a pointer to the interrupt context which contains pointers to the interrupt data and where to save it.
Return Value:
Returns TRUE if there is new work and FALSE otherwise.
Notes:
Called via KeSynchronizeExecution with the port device extension spinlock held.
--*/ { PINTERRUPT_CONTEXT interruptContext = ServiceContext; PDEVICE_EXTENSION DeviceExtension; PSTREAM_OBJECT NeedyStream; BOOLEAN Work = FALSE;
DeviceExtension = interruptContext->DeviceExtension;
//
// get the needy streams and zero the
// link.
//
interruptContext->NeedyStream = NeedyStream = DeviceExtension->NeedyStream;
//
// capture the state of needy stream
//
if (NeedyStream) {
//
// Move the interrupt state to save
// area.
//
ASSERT(NeedyStream->NextNeedyStream != NeedyStream); ASSERT(NeedyStream->ComObj.InterruptData.Flags & INTERRUPT_FLAGS_NOTIFICATION_REQUIRED); ASSERT(NeedyStream->OnNeedyQueue);
DebugPrint((DebugLevelVerbose, "'SCGetInterruptState: Snapshot for stream %p, S# = %x, NextNeedy = %p\n", NeedyStream, NeedyStream->HwStreamObject.StreamNumber, NeedyStream->NextNeedyStream));
NeedyStream->OnNeedyQueue = FALSE;
*interruptContext->SavedStreamInterruptData = NeedyStream->ComObj.InterruptData;
//
// Clear the interrupt state.
//
NeedyStream->ComObj.InterruptData.Flags &= STREAM_FLAGS_INTERRUPT_FLAG_MASK; NeedyStream->ComObj.InterruptData.CompletedSRB = NULL;
Work = TRUE;
DeviceExtension->NeedyStream = (PSTREAM_OBJECT) NeedyStream->NextNeedyStream; NeedyStream->NextNeedyStream = NULL;
#if DBG
if (DeviceExtension->NeedyStream) {
ASSERT(DeviceExtension->NeedyStream->OnNeedyQueue); } #endif
} // if NeedyStream
//
// now copy over the device interrupt
// data if necessary
//
if (DeviceExtension->ComObj.InterruptData.Flags & INTERRUPT_FLAGS_NOTIFICATION_REQUIRED) {
*interruptContext->SavedDeviceInterruptData = DeviceExtension->ComObj.InterruptData;
//
// Clear the device interrupt state.
//
DeviceExtension->ComObj.InterruptData.Flags &= DEVICE_FLAGS_INTERRUPT_FLAG_MASK;
DeviceExtension->ComObj.InterruptData.CompletedSRB = NULL; Work = TRUE; } return (Work); }
NTSTATUS SCProcessCompletedRequest( IN PSTREAM_REQUEST_BLOCK SRB ) /*++
Routine Description:
This routine processes a request which has completed.
Arguments:
SRB- address of completed STREAM request block
Return Value:
None.
--*/
{ PIRP Irp = SRB->HwSRB.Irp;
//
// complete the IRP
//
return (SCCompleteIrp(Irp, SCDequeueAndDeleteSrb(SRB), (PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1));
}
NTSTATUS SCDequeueAndDeleteSrb( IN PSTREAM_REQUEST_BLOCK SRB ) /*++
Routine Description:
This routine dequeues and deletes a completed SRB
Arguments:
SRB- address of completed STREAM request block
Return Value:
None.
--*/
{ PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1; NTSTATUS Status = SRB->HwSRB.Status; KIRQL irql;
//
// remove the SRB from our outstanding
// queue. protect list with
// spinlock.
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql);
RemoveEntryList(&SRB->SRBListEntry);
if (SRB->HwSRB.Irp) {
IoSetCancelRoutine(SRB->HwSRB.Irp, NULL); } KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);
//
// free the SRB and MDL
//
if ( !NT_SUCCESS( Status )) { DebugPrint((DebugLevelWarning, "SCDequeueAndDeleteSrb Command:%x Status=%x\n", SRB->HwSRB.Command, Status )); } IoFreeMdl(SRB->Mdl); ExFreePool(SRB); return (Status); }
VOID SCProcessCompletedDataRequest( IN PSTREAM_REQUEST_BLOCK SRB ) /*++
Routine Description:
This routine processes a data request which has completed. It completes any pending transfers, releases the adapter objects and map registers.
Arguments:
SRB- address of completed STREAM request block
Return Value:
None.
--*/
{ PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1; PIRP Irp = SRB->HwSRB.Irp; PMDL CurrentMdl; ULONG i = 0;
if (Irp) {
PIO_STACK_LOCATION IrpStack; PKSSTREAM_HEADER CurrentHeader;
CurrentHeader = SRB->HwSRB.CommandData.DataBufferArray;
ASSERT(CurrentHeader);
IrpStack = IoGetCurrentIrpStackLocation(Irp); ASSERT(IrpStack);
#if DBG
//
// assert the MDL list.
//
CurrentMdl = Irp->MdlAddress;
while (CurrentMdl) {
CurrentMdl = CurrentMdl->Next; } // while
#endif
CurrentMdl = Irp->MdlAddress;
while (CurrentMdl) {
//
// flush the buffers if we did PIO data in
//
if (SRB->HwSRB.StreamObject->Pio) {
//
// find the first header with data...
//
while (!(CurrentHeader->DataUsed) && (!CurrentHeader->FrameExtent)) {
CurrentHeader = ((PKSSTREAM_HEADER) ((PBYTE) CurrentHeader + SRB->StreamHeaderSize)); }
//
// restore the pointer we changed
//
// CurrentHeader->Data = (PVOID) ((ULONG_PTR) CurrentMdl->StartVa +
// CurrentMdl->ByteOffset);
//
if (SRB->bMemPtrValid) { // safety first!
DebugPrint((DebugLevelVerbose, "Restoring: Index:%x, Ptr:%x\n", i, SRB->pMemPtrArray[i]));
CurrentHeader->Data = SRB->pMemPtrArray[i]; }
DebugPrint((DebugLevelVerbose, "'SCPioComplete: Irp = %x, header = %x, Data = %x\n", Irp, CurrentHeader, CurrentHeader->Data));
//
// update to the next header
//
i++; CurrentHeader = ((PKSSTREAM_HEADER) ((PBYTE) CurrentHeader + SRB->StreamHeaderSize));
if (SRB->HwSRB.Command == SRB_READ_DATA) {
KeFlushIoBuffers(CurrentMdl, TRUE, FALSE);
} // if data in
} // if PIO
//
// Flush the adapter buffers if we had map registers => DMA.
//
if (SRB->MapRegisterBase) {
//
// Since we are a master call I/O flush adapter buffers
// with a NULL adapter.
//
IoFlushAdapterBuffers(DeviceExtension->DmaAdapterObject, CurrentMdl, SRB->MapRegisterBase, MmGetMdlVirtualAddress(CurrentMdl), CurrentMdl->ByteCount, (BOOLEAN) (SRB->HwSRB.Command == SRB_READ_DATA ? FALSE : TRUE) );
} // if DMA
CurrentMdl = CurrentMdl->Next;
} // while CurrentMdl
//
// flush the buffer for the SRB extension in case the adapter DMA'ed
// to it. JHavens says we must treat this as a READ.
//
//
// Flush the adapter buffer for the SRB if we had map registers =>
// DMA.
//
if (SRB->MapRegisterBase) {
IoFlushAdapterBuffers(DeviceExtension->DmaAdapterObject, SRB->Mdl, SRB->MapRegisterBase, MmGetMdlVirtualAddress( SRB->Mdl), SRB->Length, FALSE);
//
// Free the map registers if DMA.
//
IoFreeMapRegisters(DeviceExtension->DmaAdapterObject, SRB->MapRegisterBase, SRB->HwSRB.NumberOfPhysicalPages);
} // if MapRegisterBase
//
// free the extra data, if any.
//
if (IrpStack->Parameters.Others.Argument4 != NULL) {
TRAP; ExFreePool(IrpStack->Parameters.Others.Argument4);
} // if extradata
} // if Irp
//
// call the generic completion handler
//
SCProcessCompletedRequest(SRB);
} // SCProcessCompletedDataRequest
VOID SCMinidriverStreamTimerDpc( IN struct _KDPC * Dpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++
Routine Description:
This routine calls the minidriver when its requested timer fires. It interlocks either with the port spinlock and the interrupt object.
Arguments:
Dpc - Unsed. Context - Supplies a pointer to the stream object for this adapter. SystemArgument1 - Unused. SystemArgument2 - Unused.
Return Value:
None.
--*/
{ PSTREAM_OBJECT StreamObject = ((PSTREAM_OBJECT) Context); PDEVICE_EXTENSION DeviceExtension = StreamObject->DeviceExtension;
//
// Acquire the device spinlock if synchronized.
//
if (!(DeviceExtension->NoSync)) {
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); } //
// Make sure the timer routine is still
// desired.
//
if (StreamObject->ComObj.HwTimerRoutine != NULL) {
DebugPrint((DebugLevelTrace, "'SCTimerDpc: Calling MD timer callback, S# = %x, Routine = %p\n", StreamObject->HwStreamObject.StreamNumber, StreamObject->ComObj.HwTimerRoutine));
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, (PKSYNCHRONIZE_ROUTINE) StreamObject->ComObj.HwTimerRoutine, StreamObject->ComObj.HwTimerContext );
} //
// Release the spinlock if we're synchronized.
//
if (!(DeviceExtension->NoSync)) {
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } //
// Call the DPC directly to check for work.
//
StreamClassDpc(NULL, DeviceExtension->DeviceObject, NULL, StreamObject);
}
VOID SCMinidriverDeviceTimerDpc( IN struct _KDPC * Dpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++
Routine Description:
This routine calls the minidriver when its requested timer fires. It interlocks either with the port spinlock and the interrupt object.
Arguments:
Dpc - Unsed.
Context - Supplies a pointer to the stream object for this adapter.
SystemArgument1 - Unused.
SystemArgument2 - Unused.
Return Value:
None.
--*/
{ PDEVICE_EXTENSION DeviceExtension = Context;
//
// Acquire the device spinlock.
//
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
//
// Make sure the timer routine is still
// desired.
//
if (DeviceExtension->ComObj.HwTimerRoutine != NULL) {
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, (PKSYNCHRONIZE_ROUTINE) DeviceExtension->ComObj.HwTimerRoutine, DeviceExtension->ComObj.HwTimerContext );
} //
// Release the spinlock.
//
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
//
// Call the DPC directly to check for
// work.
//
StreamClassDpc(NULL, DeviceExtension->DeviceObject, NULL, NULL);
}
VOID SCLogError( IN PDEVICE_OBJECT DeviceObject, IN ULONG SequenceNumber, IN NTSTATUS ErrorCode, IN ULONG UniqueId ) /*++
Routine Description:
This function logs an error.
Arguments:
DeviceObject - device or driver object SequenceNumber - supplies the sequence # of the error. ErrorCode - Supplies the error code for this error. UniqueId - Supplies the UniqueId for this error.
Return Value:
None.
--*/
{ PIO_ERROR_LOG_PACKET packet;
PAGED_CODE(); packet = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry(DeviceObject, sizeof(IO_ERROR_LOG_PACKET));
if (packet) { packet->ErrorCode = ErrorCode; packet->SequenceNumber = SequenceNumber; packet->MajorFunctionCode = 0; packet->RetryCount = (UCHAR) 0; packet->UniqueErrorValue = UniqueId; packet->FinalStatus = STATUS_SUCCESS; packet->DumpDataSize = 0;
IoWriteErrorLogEntry(packet); } }
VOID SCLogErrorWithString( IN PDEVICE_OBJECT DeviceObject, IN OPTIONAL PDEVICE_EXTENSION DeviceExtension, IN NTSTATUS ErrorCode, IN ULONG UniqueId, IN PUNICODE_STRING String1 ) /*++
Routine Description
This function logs an error and includes the string provided.
Arguments:
DeviceObject - device or driver object DeviceExtension - Supplies a pointer to the port device extension. ErrorCode - Supplies the error code for this error. UniqueId - Supplies the UniqueId for this error. String1 - The string to be inserted.
Return Value:
None.
--*/
{ ULONG length; PCHAR dumpData; PIO_ERROR_LOG_PACKET packet;
PAGED_CODE(); length = String1->Length + sizeof(IO_ERROR_LOG_PACKET) + 2; if (length > ERROR_LOG_MAXIMUM_SIZE) { length = ERROR_LOG_MAXIMUM_SIZE; } packet = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry(DeviceObject, (UCHAR) length); if (packet) { packet->ErrorCode = ErrorCode; packet->SequenceNumber = (DeviceExtension != NULL) ? DeviceExtension->SequenceNumber++ : 0; packet->MajorFunctionCode = 0; packet->RetryCount = (UCHAR) 0; packet->UniqueErrorValue = UniqueId; packet->FinalStatus = STATUS_SUCCESS; packet->NumberOfStrings = 1; packet->StringOffset = (USHORT) ((PUCHAR) & packet->DumpData[0] - (PUCHAR) packet); packet->DumpDataSize = (USHORT) (length - sizeof(IO_ERROR_LOG_PACKET)); packet->DumpDataSize /= sizeof(ULONG); dumpData = (PUCHAR) & packet->DumpData[0];
RtlCopyMemory(dumpData, String1->Buffer, String1->Length); dumpData += String1->Length; *dumpData++ = '\0'; *dumpData++ = '\0';
IoWriteErrorLogEntry(packet); } return; }
BOOLEAN StreamClassSynchronizeExecution( IN PKINTERRUPT Interrupt, IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine, IN PVOID SynchronizeContext ) /*++
Routine Description:
This routine calls the minidriver entry point which was passed in as a parameter. It acquires a spin lock so that all accesses to the minidriver's routines are synchronized. This routine is used as a subsitute for KeSynchronizedExecution for minidrivers which do not use hardware interrupts.
Arguments:
Interrrupt - Supplies a pointer to the port device extension. SynchronizeRoutine - Supplies a pointer to the routine to be called. SynchronizeContext - Supplies the context to pass to the SynchronizeRoutine.
Return Value:
Returns the returned by the SynchronizeRoutine.
--*/
{ PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) Interrupt; BOOLEAN returnValue;
#if DBG
ULONGLONG ticks; ULONGLONG rate; ULONGLONG StartTime, EndTime;
ticks = (ULONGLONG) KeQueryPerformanceCounter((PLARGE_INTEGER) & rate).QuadPart;
StartTime = ticks * 10000 / rate; #endif
returnValue = SynchronizeRoutine(SynchronizeContext);
#if DBG
ticks = (ULONGLONG) KeQueryPerformanceCounter((PLARGE_INTEGER) & rate).QuadPart;
EndTime = ticks * 10000 / rate;
DebugPrint((DebugLevelVerbose, "'SCDebugSync: minidriver took %d microseconds at dispatch level.\n", (EndTime - StartTime) * 10));
if ((EndTime - StartTime) > 100) {
DebugPrint((DebugLevelFatal, "Stream Class: minidriver took %I64d millisecond(s) at " "dispatch level. See dev owner. Type LN %p for the name of the minidriver\n", (EndTime - StartTime) / 100, SynchronizeRoutine)); } #endif
return (returnValue); }
#if DBG
BOOLEAN SCDebugKeSynchronizeExecution( IN PKINTERRUPT Interrupt, IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine, IN PVOID SynchronizeContext ) /*++
Routine Description:
Arguments:
Interrrupt - Supplies a pointer to the port device extension. SynchronizeRoutine - Supplies a pointer to the routine to be called. SynchronizeContext - Supplies the context to pass to the SynchronizeRoutine.
Return Value:
Returns the returned by the SynchronizeRoutine.
--*/
{ ULONGLONG ticks; ULONGLONG rate; ULONGLONG StartTime, EndTime; BOOLEAN returnValue;
ticks = (ULONGLONG) KeQueryPerformanceCounter((PLARGE_INTEGER) & rate).QuadPart;
StartTime = ticks * 10000 / rate;
returnValue = KeSynchronizeExecution(Interrupt, SynchronizeRoutine, SynchronizeContext);
ticks = (ULONGLONG) KeQueryPerformanceCounter((PLARGE_INTEGER) & rate).QuadPart;
EndTime = ticks * 10000 / rate;
DebugPrint((DebugLevelVerbose, "'SCDebugSync: minidriver took %d microseconds at raised IRQL.\n", (EndTime - StartTime) * 10));
if ((EndTime - StartTime) > 50) {
DebugPrint((DebugLevelFatal, "Stream Class: minidriver took %d%d millisecond(s) at raised IRQL. See dev owner. Type LN %x for the name of the minidriver\n", (EndTime - StartTime) / 100, SynchronizeRoutine)); } return (returnValue); }
#endif
NTSTATUS SCCompleteIrp( IN PIRP Irp, IN NTSTATUS Status, IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
Routine generically calls back a completed IRP, and shows one less I/O pending.
Arguments:
Irp - IRP to complete Status - Status to complete it with DeviceExtension - pointer to device extension
Return Value:
Returns the Status parameter
--*/
{
#if DBG
PMDL CurrentMdl; #endif
if (Irp) { Irp->IoStatus.Status = Status;
#if DBG
//
// random asserts follow...
// make sure we have not freed the system buffer.
//
if (Irp->AssociatedIrp.SystemBuffer) {
DebugPrint((DebugLevelVerbose, "'SCComplete: Irp = %p, sys buffer = %p\n", Irp, Irp->AssociatedIrp.SystemBuffer)); } //
// assert the MDL list.
//
CurrentMdl = Irp->MdlAddress;
while (CurrentMdl) {
CurrentMdl = CurrentMdl->Next; } // while
#endif
if ( Irp->CurrentLocation < Irp->StackCount+1 ) { IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { //
// we got a dummy Irp we created. IoVerifier code will pews if
// we call IoCompleteRequest because the Current Stack location
// is at the end of last stack location. We can't use
// IoBuildIoControlRequest to create the Irp becuase it will
// be added to a thread and the only way to get it off is to
// call IoCompleteRequest.
//
IoFreeIrp( Irp ); } } if (!(InterlockedDecrement(&DeviceExtension->OneBasedIoCount))) {
//
// the device is being removed and all I/O is complete. Signal the
// removal thread to wake up.
//
KeSetEvent(&DeviceExtension->RemoveEvent, IO_NO_INCREMENT, FALSE); } ASSERT(DeviceExtension->OneBasedIoCount >= 0); return (Status); }
BOOLEAN SCDummyMinidriverRoutine( IN PVOID Context ) /*++
Routine Description:
Routine used when the minidriver fills in a null for an optional routine
Arguments:
Context - unreferenced
Return Value:
TRUE
--*/
{
return (TRUE); }
#if ENABLE_MULTIPLE_FILTER_TYPES
NTSTATUS SCOpenMinidriverInstance( IN PDEVICE_EXTENSION DeviceExtension, OUT PFILTER_INSTANCE * ReturnedFilterInstance, IN PSTREAM_CALLBACK_PROCEDURE SCGlobalInstanceCallback, IN PIRP Irp) /*++
Routine Description:
Worker routine to process opening of a filter instance. Once open, we issue srb_get_stream_info.
Arguments:
DeviceExtension - pointer to device extension ReturnedFilterInstance - pointer to the filter instance structure SCGlobalInstanceCallback - callback procedure to be called if we call the minidriver Irp - pointer to the irp
Return Value:
Returns NTSTATUS and a filter instance structure if successfull
--*/
{ ULONG FilterExtensionSize; PFILTER_INSTANCE FilterInstance; PHW_STREAM_INFORMATION CurrentInfo; PADDITIONAL_PIN_INFO CurrentAdditionalInfo; ULONG i; BOOLEAN RequestIssued; PKSOBJECT_CREATE_ITEM CreateItem; ULONG FilterTypeIndex; ULONG NumberOfPins; NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
//
// The CreateItem is in Irp->Tail.Overlay.DriverContext[0] from KS
//
CreateItem = (PKSOBJECT_CREATE_ITEM)Irp->Tail.Overlay.DriverContext[0]; ASSERT( CreateItem != NULL ); FilterTypeIndex = (ULONG)(ULONG_PTR)CreateItem->Context; ASSERT( FilterTypeIndex == 0 || FilterTypeIndex < DeviceExtension->MinidriverData->HwInitData.NumNameExtensions); FilterExtensionSize = DeviceExtension->FilterExtensionSize;
ASSERT( DeviceExtension->FilterExtensionSize == DeviceExtension->MinidriverData-> HwInitData.FilterInstanceExtensionSize); FilterInstance = NULL;
NumberOfPins = DeviceExtension->FilterTypeInfos[FilterTypeIndex]. StreamDescriptor->StreamHeader.NumberOfStreams;
//
// don't call the minidriver to open the filter instance if 1x1 for backward
// compat. We do this so that minidrivers that don't support
// instancing (the vast majority) don't have to respond to this call.
//
if ( DeviceExtension->NumberOfOpenInstances > 0 && 0 == FilterExtensionSize ) { //
// Legacy 1x1 and non-1st open. assign the same
// FilterInstance and succeed it.
//
PLIST_ENTRY node; ASSERT( !IsListEmpty( &DeviceExtension->FilterInstanceList)); node = DeviceExtension->FilterInstanceList.Flink; FilterInstance = CONTAINING_RECORD(node, FILTER_INSTANCE, NextFilterInstance); ASSERT_FILTER_INSTANCE( FilterInstance ); *ReturnedFilterInstance = FilterInstance; Status = STATUS_SUCCESS; return Status; // can't goto Exit, it will insert FI again.
}
FilterInstance = ExAllocatePool(NonPagedPool, sizeof(FILTER_INSTANCE) + FilterExtensionSize + sizeof(ADDITIONAL_PIN_INFO) * NumberOfPins);
if (!FilterInstance) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; }
RtlZeroMemory(FilterInstance, sizeof(FILTER_INSTANCE) + FilterExtensionSize + sizeof(ADDITIONAL_PIN_INFO) * NumberOfPins);
FilterInstance->Signature = SIGN_FILTER_INSTANCE; FilterInstance->DeviceExtension = DeviceExtension; // keep this handy
//
// To get FilterInstance from HwInstanceExtension we need
// to arrange the memory layout
// [FilterInstnace][HwInstanceExtension][AddionalPinInfo...]
// as opposed to
// [FilterInstance][AdditionalPinInfo...][HwInstanceExtension]
//
FilterInstance->HwInstanceExtension = FilterInstance + 1; FilterInstance->PinInstanceInfo = (PADDITIONAL_PIN_INFO) ((PBYTE)(FilterInstance+1) + FilterExtensionSize);
FilterInstance->FilterTypeIndex = FilterTypeIndex; //
// initialize the filter instance list
//
InitializeListHead(&FilterInstance->FirstStream); InitializeListHead(&FilterInstance->NextFilterInstance); InitializeListHead(&FilterInstance->NotifyList);
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
Status = KsRegisterWorker( CriticalWorkQueue, &FilterInstance->WorkerRead ); if (!NT_SUCCESS( Status )) { ExFreePool(FilterInstance); FilterInstance = NULL; Status = STATUS_INSUFFICIENT_RESOURCES; ASSERT( 0 ); goto Exit; }
Status = KsRegisterWorker( CriticalWorkQueue, &FilterInstance->WorkerWrite ); if (!NT_SUCCESS( Status )) { KsUnregisterWorker( FilterInstance->WorkerRead ); ExFreePool(FilterInstance); FilterInstance = NULL; Status = STATUS_INSUFFICIENT_RESOURCES; ASSERT( 0 ); goto Exit; } DebugPrint((DebugLevelVerbose, "RegisterReadWorker %x WriteWorker %x\n", FilterInstance->WorkerRead, FilterInstance->WorkerWrite)); #endif
//
// initialize the current and max instances
//
CurrentAdditionalInfo = FilterInstance->PinInstanceInfo; CurrentInfo = &DeviceExtension->StreamDescriptor->StreamInfo;
for (i = 0; i < NumberOfPins; i++) {
CurrentAdditionalInfo[i].CurrentInstances = 0; CurrentAdditionalInfo[i].MaxInstances = CurrentInfo->NumberOfPossibleInstances;
//
// index to next streaminfo and additional info structures.
//
CurrentInfo++; }
//
// fill in the filter dispatch table pointer
//
KsAllocateObjectHeader(&FilterInstance->DeviceHeader, SIZEOF_ARRAY(CreateHandlers), (PKSOBJECT_CREATE_ITEM) CreateHandlers, Irp, (PKSDISPATCH_TABLE) & FilterDispatchTable);
if (FilterExtensionSize) {
//
// call the minidriver to open the instance if the call is supported.
// final status will be processed in the callback procedure.
//
//
// C4312 fix: This union corresponds to the _CommandData union within
// HW_STREAM_REQUEST_BLOCK. This is done to correctly align
// FilterTypeIndex for assignment on 64-bit such that it doesn't
// break on big endian machines. I don't want to waste stack
// space with an entire HW_STREAM_REQUEST_BLOCK for a 64-bit safe
// cast.
//
union { PVOID Buffer; LONG FilterTypeIndex; } u;
u.Buffer = NULL; u.FilterTypeIndex = (LONG)FilterTypeIndex;
Status = SCSubmitRequest( SRB_OPEN_DEVICE_INSTANCE, u.Buffer, 0, SCDequeueAndDeleteSrb, //SCGlobalInstanceCallback,
DeviceExtension, FilterInstance->HwInstanceExtension, NULL, Irp, &RequestIssued, &DeviceExtension->PendingQueue, (PVOID) DeviceExtension->MinidriverData->HwInitData.HwReceivePacket );
if (!RequestIssued) {
//
// if request not issued, fail the request as we could not send
// it down.
//
ASSERT(Status != STATUS_SUCCESS);
KsFreeObjectHeader(FilterInstance->DeviceHeader); #ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
KsUnregisterWorker( FilterInstance->WorkerRead ); KsUnregisterWorker( FilterInstance->WorkerWrite ); #endif
//ExFreePool(FilterInstance);
} } // if minidriver supports multiple filter
Exit: { if ( NT_SUCCESS( Status ) ) { DebugPrint((DebugLevelInfo, "Inserting FilterInstance %x\n", FilterInstance)); SCInsertFiltersInDevice( FilterInstance, DeviceExtension ); } else if ( NULL != FilterInstance) { ExFreePool( FilterInstance ); FilterInstance = NULL; } *ReturnedFilterInstance = FilterInstance; return (Status); } }
#else // ENABLE_MULTIPLE_FILTER_TYPES
#endif // ENABLE_MULTIPLE_FILTER_TYPES
NTSTATUS SCSubmitRequest( IN SRB_COMMAND Command, IN PVOID Buffer, IN ULONG DataSize, IN PSTREAM_CALLBACK_PROCEDURE Callback, IN PDEVICE_EXTENSION DeviceExtension, IN PVOID InstanceExtension, IN OPTIONAL PHW_STREAM_OBJECT HwStreamObject, IN PIRP Irp, OUT PBOOLEAN RequestIssued, IN PLIST_ENTRY Queue, IN PVOID MinidriverRoutine ) /*++
Routine Description:
This routine generically submits a non-data SRB to the minidriver. The callback procedure is called back at PASSIVE level.
Arguments:
Command - command to issue Buffer - data buffer, if any DataSize - length of transfer Callback - procedure to call back at passive level DeviceExtension - pointer to device extension InstanceExtension - pointer to instance extension, if any HwStreamObject - optional pointer to minidriver's stream object Irp - pointer to IRP RequestIssued - pointer to boolean which is set if request issued Queue - queue upon which to enqueue the request MinidriverRoutine - request routine to call with the request
Return Value:
Status --*/
{ PSTREAM_OBJECT StreamObject = 0; PSTREAM_REQUEST_BLOCK Request = SCBuildRequestPacket(DeviceExtension, Irp, 0, 0); NTSTATUS Status;
PAGED_CODE();
//
// assume request will be successfully issued.
//
*RequestIssued = TRUE;
//
// if the alloc failed, call the callback procedure with a null SRB
//
if (!Request) {
*RequestIssued = FALSE; return (STATUS_INSUFFICIENT_RESOURCES); } if (HwStreamObject) { StreamObject = CONTAINING_RECORD( HwStreamObject, STREAM_OBJECT, HwStreamObject );
//
// hack. we need to set the stream request flag if this is a stream
// request. the only case that we would NOT set this when a stream
// object is passed in is on an OPEN or CLOSE, where the stream
// object is
// passed in on a device request. special case this. if later
// this assumption changes, an assert will be hit in lowerapi.
//
if ((Command != SRB_OPEN_STREAM) && (Command != SRB_CLOSE_STREAM)) {
Request->HwSRB.Flags |= SRB_HW_FLAGS_STREAM_REQUEST; } } //
// initialize event for blocking for completion
//
KeInitializeEvent(&Request->Event, SynchronizationEvent, FALSE);
Request->HwSRB.Command = Command;
Request->Callback = SCSignalSRBEvent; Request->HwSRB.HwInstanceExtension = InstanceExtension; Request->HwSRB.StreamObject = HwStreamObject; Request->HwSRB.CommandData.StreamBuffer = Buffer; Request->HwSRB.HwDeviceExtension = DeviceExtension->HwDeviceExtension; Request->HwSRB.NumberOfBytesToTransfer = DataSize; Request->DoNotCallBack = FALSE;
//
// call routine to actually submit request to the device
//
Status = SCIssueRequestToDevice(DeviceExtension, StreamObject, Request, MinidriverRoutine, Queue, Irp);
//
// block waiting for completion if pending
//
if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Request->Event, Executive, KernelMode, FALSE, NULL); } return (Callback(Request));
}
VOID SCSignalSRBEvent( IN PSTREAM_REQUEST_BLOCK Srb ) /*++
Routine Description:
Sets the event for a completed SRB
Arguments:
Srb - pointer to the request
Return Value:
none --*/
{
KeSetEvent(&Srb->Event, IO_NO_INCREMENT, FALSE); return; }
NTSTATUS SCProcessDataTransfer( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp, IN SRB_COMMAND Command ) /*++
Routine Description:
Process a data transfer request to a stream
Arguments:
DeviceExtension - address of device extension. Irp - pointer to the IRP Command - read or write command
Return Value:
NTSTATUS returned as appropriate
--*/
{ PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp); PSTREAM_REQUEST_BLOCK Request; PSTREAM_OBJECT StreamObject = IrpStack->FileObject->FsContext; NTSTATUS Status; PKSSTREAM_HEADER OutputBuffer = NULL; ULONG NumberOfPages = 0, NumberOfBuffers = 0; ULONG Flags = KSPROBE_STREAMWRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_ALLOWFORMATCHANGE; ULONG HeaderSize=0; // prefixbug 17392
ULONG ExtraSize=0; // prefixbug 17391
#if DBG
PMDL CurrentMdl; #endif
PVOID pMemPtrArray = NULL;
PAGED_CODE();
//
// if we are flushing, we must error any I/O during this period.
//
if (StreamObject->InFlush) {
DebugPrint((DebugLevelError, "'StreamDispatchIOControl: Aborting IRP during flush!")); TRAP;
return (STATUS_DEVICE_NOT_READY);
} // if flushing
Irp->IoStatus.Information = 0;
#if DBG
DeviceExtension->NumberOfRequests++; #endif
if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength) {
//
// get the size of the header and the expansion from the minidriver.
//
HeaderSize = StreamObject->HwStreamObject.StreamHeaderMediaSpecific + sizeof(KSSTREAM_HEADER); ExtraSize = StreamObject->HwStreamObject.StreamHeaderWorkspace;
//
// we assumed this was a write. do additional processing if a read.
//
if (Command == SRB_READ_DATA) {
Flags = KSPROBE_STREAMREAD | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK;
//
// this is a read, so set the information field in the irp to
// copy back the headers when the I/O is complete.
//
Irp->IoStatus.Information = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
} //
// lock and probe the buffer
//
DebugPrint((DebugLevelVerbose, "Stream: HeaderSize:%x\n",HeaderSize)); DebugPrint((DebugLevelVerbose, "Stream: sizeof(KSSSTREAM_HEADER):%x\n",sizeof(KSSTREAM_HEADER))); DebugPrint((DebugLevelVerbose, "Stream: MediaSpecific:%x\n",StreamObject->HwStreamObject.StreamHeaderMediaSpecific)); DebugPrint((DebugLevelVerbose, "Stream: StreamHeader->Size:%x\n",((PKSSTREAM_HEADER)(Irp->UserBuffer))->Size));
if (!NT_SUCCESS(Status = KsProbeStreamIrp(Irp, Flags, HeaderSize))) {
DebugPrint((DebugLevelError, "Stream: ProbeStreamIrp failed!"));
return (Status);
} if (!ExtraSize) {
OutputBuffer = (PKSSTREAM_HEADER) Irp->AssociatedIrp.SystemBuffer;
IrpStack->Parameters.Others.Argument4 = NULL; } else {
TRAP; if (!NT_SUCCESS(Status = KsAllocateExtraData(Irp, ExtraSize, &OutputBuffer))) {
DebugPrint((DebugLevelError, "Stream: AllocExtraData failed!"));
return (Status); } // if not success
IrpStack->Parameters.Others.Argument4 = OutputBuffer;
}
#if DBG
//
// assert the MDL list.
//
CurrentMdl = Irp->MdlAddress;
while (CurrentMdl) {
CurrentMdl = CurrentMdl->Next; } // while
#endif
//
// calculate the # of buffers.
//
NumberOfBuffers = IrpStack->Parameters. DeviceIoControl.OutputBufferLength / HeaderSize;
//
// do addtional processing on the data buffers.
//
if (StreamObject->HwStreamObject.Dma) { // an optimization
SCProcessDmaDataBuffers(OutputBuffer, NumberOfBuffers, StreamObject, Irp->MdlAddress, &NumberOfPages, HeaderSize + ExtraSize, (BOOLEAN) (Command == SRB_WRITE_DATA)); } //
// if number of pages is > than the max supported, return error.
// Allow
// for one extra map register for the SRB extension.
//
// GUBGUB - This is really a workitem to make it correct.
// need to break up requests that have too many elements.
//
if (NumberOfPages > (DeviceExtension->NumberOfMapRegisters - 1)) {
return (STATUS_INSUFFICIENT_RESOURCES); } } // if BufferSize
//
// build an SRB and alloc workspace for the request. Allocate
// scatter/gather space also if needed.
//
Request = SCBuildRequestPacket(DeviceExtension, Irp, NumberOfPages * sizeof(KSSCATTER_GATHER), NumberOfBuffers * sizeof(PVOID));
if (Request == NULL) {
return (STATUS_INSUFFICIENT_RESOURCES); }
//
// do more addtional processing on the data buffers.
//
if (StreamObject->HwStreamObject.Pio) { // a small optimization
Request->bMemPtrValid = SCProcessPioDataBuffers(OutputBuffer, NumberOfBuffers, StreamObject, Irp->MdlAddress, HeaderSize + ExtraSize, Request->pMemPtrArray, (BOOLEAN) (Command == SRB_WRITE_DATA)); } //
// set # of physical pages
//
Request->HwSRB.NumberOfPhysicalPages = NumberOfPages;
//
// set # of data buffers
//
Request->HwSRB.NumberOfBuffers = NumberOfBuffers;
//
// set the command code in the packet.
//
Request->HwSRB.Command = Command;
//
// set the input and output buffers
//
Request->HwSRB.CommandData.DataBufferArray = OutputBuffer; Request->HwSRB.HwDeviceExtension = DeviceExtension->HwDeviceExtension; Request->Callback = SCProcessCompletedDataRequest; Request->HwSRB.StreamObject = &StreamObject->HwStreamObject; Request->StreamHeaderSize = HeaderSize + ExtraSize; Request->DoNotCallBack = FALSE; Request->HwSRB.Flags |= (SRB_HW_FLAGS_DATA_TRANSFER | SRB_HW_FLAGS_STREAM_REQUEST);
ASSERT_FILTER_INSTANCE( StreamObject->FilterInstance ); Request->HwSRB.HwInstanceExtension = StreamObject->FilterInstance->HwInstanceExtension;
//
// point the IRP workspace to the request
// packet
//
Irp->Tail.Overlay.DriverContext[0] = Request;
IoMarkIrpPending(Irp);
// ASSERT((IoGetCurrentIrpStackLocation(Irp)->MajorFunction ==
// IOCTL_KS_READ_STREAM) ||
// (IoGetCurrentIrpStackLocation(Irp)->MajorFunction ==
// IOCTL_KS_WRITE_STREAM));
ASSERT((ULONG_PTR) Irp->Tail.Overlay.DriverContext[0] > 0x40000000);
return (SCIssueRequestToDevice(DeviceExtension, StreamObject, Request, StreamObject->HwStreamObject.ReceiveDataPacket, &StreamObject->DataPendingQueue, Irp));
}
VOID SCErrorDataSRB( IN PHW_STREAM_REQUEST_BLOCK SRB ) /*++
Routine Description:
Dummy routine invoked when a data request is received for non-data receiving stream.
Arguments:
SRB- address of STREAM request block
Return Value:
None.
--*/
{
//
// just call the SRB back with error
//
SRB->Status = STATUS_NOT_SUPPORTED; StreamClassStreamNotification(StreamRequestComplete, SRB->StreamObject); StreamClassStreamNotification(ReadyForNextStreamDataRequest, SRB->StreamObject); } // SCErrorDataSRB
NTSTATUS SCIssueRequestToDevice( IN PDEVICE_EXTENSION DeviceExtension, IN OPTIONAL PSTREAM_OBJECT StreamObject, PSTREAM_REQUEST_BLOCK Request, IN PVOID MinidriverRoutine, IN PLIST_ENTRY Queue, IN PIRP Irp ) /*++
Routine Description:
This routine calls the minidriver's request vector with a request. Both data and non-data requests are handled by this routine. The routine either synchronizes the call or not, based on the NoSync boolean.
Arguments:
DeviceExtension - pointer to device extension StreamObject - optional pointer to stream object MinidriverRoutine - request routine to call with the request Queue - queue upon which to enqueue the request Irp - pointer to IRP
Return Value:
Status --*/
{ KIRQL irql; KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql);
if (DeviceExtension->NoSync) {
//
// place the request on the
// outstanding queue and call it down
// immediately
//
ASSERT((DeviceExtension->BeginMinidriverCallin == SCBeginSynchronizedMinidriverCallin) || (DeviceExtension->BeginMinidriverCallin == SCBeginUnsynchronizedMinidriverCallin));
Request->Flags |= SRB_FLAGS_IS_ACTIVE; InsertHeadList( &DeviceExtension->OutstandingQueue, &Request->SRBListEntry);
IoSetCancelRoutine(Irp, StreamClassCancelOutstandingIrp);
KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);
if ((StreamObject) && (StreamObject->HwStreamObject.Dma) && (Request->HwSRB.Flags & SRB_HW_FLAGS_DATA_TRANSFER)) {
//
// allocate the adapter channel. call cannot fail as the only
// time it would is when there aren't enough map registers, and
// we've already checked for that condition. Block waiting til
// it's allocated.
//
KIRQL oldIrql;
KeInitializeEvent(&Request->DmaEvent, SynchronizationEvent, FALSE);
ASSERT( PASSIVE_LEVEL == KeGetCurrentIrql());
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql ); SCSetUpForDMA(DeviceExtension->DeviceObject, Request); KeLowerIrql( oldIrql );
KeWaitForSingleObject(&Request->DmaEvent, Executive, KernelMode, FALSE, NULL);
} // this could open a race window. It should be protected in spinlock.
//Request->Flags |= SRB_FLAGS_IS_ACTIVE;
((PHW_RECEIVE_STREAM_CONTROL_SRB) (MinidriverRoutine)) (&Request->HwSRB);
} else {
//
// insert the item on the queue
//
InsertHeadList( Queue, &Irp->Tail.Overlay.ListEntry);
//
// set the cancel routine to pending
//
IoSetCancelRoutine(Irp, StreamClassCancelPendingIrp);
//
// check to see if the IRP is already cancelled.
//
if (Irp->Cancel) {
//
// the IRP is cancelled. Make sure that the cancel routine
// will be called.
//
if (IoSetCancelRoutine(Irp, NULL)) {
//
// wow, the cancel routine will not be invoked.
// dequeue the request ourselves and complete
// with cancelled status.
RemoveEntryList(&Request->SRBListEntry); KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);
//
// free the SRB and MDL
//
IoFreeMdl(Request->Mdl);
ExFreePool(Request); return (STATUS_CANCELLED);
} else { // if we must cancel
KeReleaseSpinLock(&DeviceExtension->SpinLock, irql); } // if we must cancel
return (STATUS_PENDING); } // if cancelled
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
//
// call the DPC routine directly. GUBGUB questionable performance improvement chance
// BGP - is this really
// faster than scheduling it?
//
StreamClassDpc(NULL, DeviceExtension->DeviceObject, Irp, StreamObject);
KeLowerIrql(irql); } return (STATUS_PENDING); }
BOOLEAN SCCheckFilterInstanceStreamsForIrp( IN PFILTER_INSTANCE FilterInstance, IN PIRP Irp ) /*++
Routine Description:
This routine checks all filter instance streams for the specified IRP.
Arguments:
FilterInstance - pointer to the filter instance Irp - pointer to the IRP.
Return Value:
TRUE if the IRP is found.
--*/
{
PSTREAM_OBJECT StreamObject; PLIST_ENTRY StreamListEntry, StreamObjectEntry;
StreamListEntry = StreamObjectEntry = &FilterInstance->FirstStream;
while (StreamObjectEntry->Flink != StreamListEntry) {
StreamObjectEntry = StreamObjectEntry->Flink;
//
// follow the link to the stream
// object
//
StreamObject = CONTAINING_RECORD(StreamObjectEntry, STREAM_OBJECT, NextStream);
if (SCCheckRequestsForIrp( &StreamObject->DataPendingQueue, Irp, TRUE, StreamObject->DeviceExtension)) {
return (TRUE); } if (SCCheckRequestsForIrp( &StreamObject->ControlPendingQueue, Irp, TRUE, StreamObject->DeviceExtension)) {
return (TRUE); } }
return (FALSE);
} // SCCheckFilterInstanceStreamsForIrp
BOOLEAN SCCheckRequestsForIrp( IN PLIST_ENTRY ListEntry, IN PIRP Irp, IN BOOLEAN IsIrpQueue, IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
This routine checks all requests on a queue for the specified IRP. If the IRP parameter is NULL, the first IRP on the queue is cancelled.
Arguments:
ListEntry - list to check for the IRP Irp - pointer to the IRP or NULL to cancel the first IRP. IsIrpQueue - TRUE indicates an IRP queue, FALSE indicates an SRB queue DeviceExtension - pointer to the device extension
Return Value:
TRUE if the IRP is found or if we cancel it.
--*/
{
PLIST_ENTRY IrpEntry = ListEntry; PIRP CurrentIrp;
while (IrpEntry->Flink != ListEntry) {
IrpEntry = IrpEntry->Flink;
ASSERT(IrpEntry); ASSERT(IrpEntry->Flink); ASSERT(IrpEntry->Blink);
//
// follow the link to the IRP
//
if (IsIrpQueue) {
CurrentIrp = CONTAINING_RECORD(IrpEntry, IRP, Tail.Overlay.ListEntry); } else {
CurrentIrp = ((PSTREAM_REQUEST_BLOCK) (CONTAINING_RECORD(IrpEntry, STREAM_REQUEST_BLOCK, SRBListEntry)))->HwSRB.Irp; }
//
// this routine is used to cancel irp's if IRP is null.
//
if ((!Irp) && (!CurrentIrp->Cancel)) {
//
// The IRP has not been previously cancelled, so cancel it after
// releasing the spinlock to avoid deadlock with the cancel
// routine.
//
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
//
// This code is suspicious that the CurrentIrp is not protected, i.e.
// it could be processed and freed from other thread. However, we
// are not never called with (!Irp). Therefore, we should never
// come in executing this piece of code. here is the analysis.
// 1. We are called from
// a. SCCheckFilterInstanceStreamIrp()
// b. SCCancelOutstandingIrp()
// c. StreamClassCancelPendingIrp()
// 2. Further inspection shows that a. SCCheckFilterInstanceStreamForIrp() is
// only called by StreamClassCancelPendingIrp() which always has non-null Irp.
// 3. SCCancelOutstandingIrp() is called by
// a. StreamClassCancelPendingIrp() which always has non-NULL irp.
// b. StreamClassCancelOutstandingIrp() which always has non-NULL irp.
// The concusion is that we are never called with null irp. Therefore, this
// piece code is never executed. But this driver has been thru win2k extenteded
// test cycle. I rather be conservative. Add an Assertion instead of removing
// the code for now.
//
ASSERT( 0 ); IoCancelIrp(CurrentIrp);
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); return (TRUE); } if (Irp == CurrentIrp) {
return (TRUE); } } // while list entry
return (FALSE);
} // SCCheckRequestsForIrp
VOID SCNotifyMinidriverCancel( IN PSTREAM_REQUEST_BLOCK SRB ) /*++
Routine Description:
Synchronized routine to notify minidriver that an IRP has been canceled
Arguments:
SRB - pointer to SRB that has been canceled.
Return Value:
none
--*/
{ PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
//
// if the active flag is still set in the SRB, the minidriver still
// has it so call him to abort it.
//
if (SRB->Flags & SRB_FLAGS_IS_ACTIVE) {
//
// call the minidriver with the SRB.
//
(DeviceExtension->MinidriverData->HwInitData.HwCancelPacket) (&SRB->HwSRB); } return; }
VOID SCCancelOutstandingIrp( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp ) /*++
Routine Description:
Routine to notify minidriver that an IRP has been canceled. Device spinlock NUST be taken before this routine is called.
Arguments:
DeviceExtension - pointer to device extension Irp - pointer to the IRP.
Return Value:
none
--*/
{ PSTREAM_REQUEST_BLOCK Srb;
//
// just return if the request is not on
// our queue.
//
if ((!IsListEmpty(&DeviceExtension->OutstandingQueue)) && (SCCheckRequestsForIrp( &DeviceExtension->OutstandingQueue, Irp, FALSE, DeviceExtension))) {
//
// the request is sitting on our
// outstanding queue. call the
// minidriver
// via a synchronize routine to
// cancel it.
//
Srb = Irp->Tail.Overlay.DriverContext[0];
#if DBG
if (Srb->HwSRB.StreamObject) {
DebugPrint((DebugLevelWarning, "'SCCancelOutstanding: canceling, Irp = %x, Srb = %x, S# = %x\n", Irp, Srb, Srb->HwSRB.StreamObject->StreamNumber));
} else {
DebugPrint((DebugLevelWarning, "'SCCancelOutstanding: canceling nonstream, Irp = %x\n", Irp)); } // if SO
#endif
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).
//
if (!(Srb->Flags & SRB_FLAGS_IS_ACTIVE)) { return; } Srb->DoNotCallBack = TRUE;
//
// release the spinlock temporarily since we need to call the
// minidriver. The caller won't be affected by this.
//
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
(DeviceExtension->MinidriverData->HwInitData.HwCancelPacket) (&Srb->HwSRB);
//
// reacquire the spinlock since the caller will release it
//
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
Srb->DoNotCallBack = FALSE;
//
// 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.
//
if (!(Srb->Flags & SRB_FLAGS_IS_ACTIVE)) {
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
(Srb->Callback) (Srb);
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); } // if ! active
} else {
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, (PVOID) SCNotifyMinidriverCancel, Srb); } // if nosync
} // if on our queue
return; }
NTSTATUS SCMinidriverDevicePropertyHandler( IN SRB_COMMAND Command, IN PIRP Irp, IN PKSPROPERTY Property, IN OUT PVOID PropertyInfo ) /*++
Routine Description:
Process get/set property to the device.
Arguments:
Command - either GET or SET property Irp - pointer to the IRP Property - pointer to the property structure PropertyInfo - buffer for property information
Return Value:
NTSTATUS returned as appropriate.
--*/
{ PIO_STACK_LOCATION IrpStack; PDEVICE_EXTENSION DeviceExtension; PFILTER_INSTANCE FilterInstance; PSTREAM_PROPERTY_DESCRIPTOR PropDescriptor; NTSTATUS Status; BOOLEAN RequestIssued;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp); DeviceExtension = (PDEVICE_EXTENSION) (IrpStack->DeviceObject)->DeviceExtension;
FilterInstance = IrpStack->FileObject->FsContext;
PropDescriptor = ExAllocatePool(NonPagedPool, sizeof(STREAM_PROPERTY_DESCRIPTOR)); if (PropDescriptor == NULL) { DebugPrint((DebugLevelError, "SCDevicePropHandler: No pool for descriptor")); return (STATUS_INSUFFICIENT_RESOURCES); } //
// compute the index of the property set.
//
// this value is calculated by subtracting the base property set
// pointer from the requested property set pointer.
//
// The requested property set is pointed to by Context[0] by
// KsPropertyHandler.
//
PropDescriptor->PropertySetID = (ULONG) ((ULONG_PTR) Irp->Tail.Overlay.DriverContext[0] - IFN_MF( (ULONG_PTR) DeviceExtension->DevicePropertiesArray) IF_MF( (ULONG_PTR) FilterInstance->DevicePropertiesArray) )/ sizeof(KSPROPERTY_SET);
PropDescriptor->Property = Property; PropDescriptor->PropertyInfo = PropertyInfo; PropDescriptor->PropertyInputSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; PropDescriptor->PropertyOutputSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
//
// send a get or set property SRB to the device.
//
Status = SCSubmitRequest(Command, PropDescriptor, 0, SCProcessCompletedPropertyRequest, DeviceExtension, FilterInstance->HwInstanceExtension, NULL, Irp, &RequestIssued, &DeviceExtension->PendingQueue, (PVOID) DeviceExtension-> MinidriverData->HwInitData. HwReceivePacket ); if (!RequestIssued) {
ExFreePool(PropDescriptor); } return (Status); }
NTSTATUS SCMinidriverStreamPropertyHandler( IN SRB_COMMAND Command, IN PIRP Irp, IN PKSPROPERTY Property, IN OUT PVOID PropertyInfo ) /*++
Routine Description:
Process get or set property to the device.
Arguments:
Command - either GET or SET property Irp - pointer to the IRP Property - pointer to the property structure PropertyInfo - buffer for property information
Return Value:
None.
--*/
{ PIO_STACK_LOCATION IrpStack; PDEVICE_EXTENSION DeviceExtension; PSTREAM_OBJECT StreamObject; PSTREAM_PROPERTY_DESCRIPTOR PropDescriptor; NTSTATUS Status; BOOLEAN RequestIssued;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp); DeviceExtension = (PDEVICE_EXTENSION) (IrpStack->DeviceObject)->DeviceExtension;
StreamObject = IrpStack->FileObject->FsContext;
PropDescriptor = ExAllocatePool(NonPagedPool, sizeof(STREAM_PROPERTY_DESCRIPTOR)); if (PropDescriptor == NULL) { DebugPrint((DebugLevelError, "SCDevicePropHandler: No pool for descriptor")); return (STATUS_INSUFFICIENT_RESOURCES); } //
// compute the index of the property set.
//
// this value is calculated by subtracting the base property set
// pointer from the requested property set pointer.
//
// The requested property set is pointed to by Context[0] by
// KsPropertyHandler.
//
PropDescriptor->PropertySetID = (ULONG) ((ULONG_PTR) Irp->Tail.Overlay.DriverContext[0] - (ULONG_PTR) StreamObject->PropertyInfo) / sizeof(KSPROPERTY_SET);
PropDescriptor->Property = Property; PropDescriptor->PropertyInfo = PropertyInfo; PropDescriptor->PropertyInputSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; PropDescriptor->PropertyOutputSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; //
// send a get or set property SRB to the stream.
//
Status = SCSubmitRequest(Command, PropDescriptor, 0, SCProcessCompletedPropertyRequest, DeviceExtension, StreamObject->FilterInstance->HwInstanceExtension, &StreamObject->HwStreamObject, Irp, &RequestIssued, &StreamObject->ControlPendingQueue, (PVOID) StreamObject->HwStreamObject. ReceiveControlPacket );
if (!RequestIssued) {
ExFreePool(PropDescriptor); } return (Status); }
NTSTATUS SCProcessCompletedPropertyRequest( IN PSTREAM_REQUEST_BLOCK SRB ) /*++
Routine Description:
This routine processes a property request which has completed.
Arguments:
SRB- address of completed STREAM request block
Return Value:
None.
--*/
{ PAGED_CODE();
//
// free the prop info structure and
// complete the request
//
ExFreePool(SRB->HwSRB.CommandData.PropertyInfo);
//
// set the information field from the SRB
// transferlength field
//
SRB->HwSRB.Irp->IoStatus.Information = SRB->HwSRB.ActualBytesTransferred;
return (SCDequeueAndDeleteSrb(SRB));
}
VOID SCUpdateMinidriverProperties( IN ULONG NumProps, IN PKSPROPERTY_SET MinidriverProps, IN BOOLEAN Stream ) /*++
Routine Description:
Process get property to the device.
Arguments:
NumProps - number of properties to process MinidriverProps - pointer to the array of properties to process Stream - TRUE indicates we are processing a set for the stream
Return Value:
None.
--*/
{ PKSPROPERTY_ITEM CurrentPropId; PKSPROPERTY_SET CurrentProp; ULONG i, j;
PAGED_CODE();
//
// walk the minidriver's property info to fill in the dispatch
// vectors as appropriate.
//
CurrentProp = MinidriverProps;
for (i = 0; i < NumProps; i++) {
CurrentPropId = (PKSPROPERTY_ITEM) CurrentProp->PropertyItem;
for (j = 0; j < CurrentProp->PropertiesCount; j++) {
//
// if support handler is supported, send it to the "get" handler
//
if (CurrentPropId->SupportHandler) {
if (Stream) {
CurrentPropId->SupportHandler = StreamClassMinidriverStreamGetProperty;
} else {
CurrentPropId->SupportHandler = StreamClassMinidriverDeviceGetProperty; } // if stream
} //
// if get prop routine is
// supported, add our vector.
//
if (CurrentPropId->GetPropertyHandler) {
if (Stream) {
CurrentPropId->GetPropertyHandler = StreamClassMinidriverStreamGetProperty; } else {
CurrentPropId->GetPropertyHandler = StreamClassMinidriverDeviceGetProperty; } // if stream
} // if get supported
//
// if get prop routine is
// supported, add our vector.
//
if (CurrentPropId->SetPropertyHandler) {
if (Stream) {
CurrentPropId->SetPropertyHandler = StreamClassMinidriverStreamSetProperty;
} else {
CurrentPropId->SetPropertyHandler = StreamClassMinidriverDeviceSetProperty; } // if stream
} //
// index to next property item in
// array
//
CurrentPropId++;
} // for number of property items
//
// index to next property set in
// array
//
CurrentProp++;
} // for number of property sets
}
VOID SCUpdateMinidriverEvents( IN ULONG NumEvents, IN PKSEVENT_SET MinidriverEvents, IN BOOLEAN Stream ) /*++
Routine Description:
Process get property to the device.
Arguments:
NumEvents - number of event sets to process MinidriverEvents - pointer to the array of properties to process Stream - TRUE indicates we are processing a set for the stream
Return Value:
None.
--*/
{ PKSEVENT_ITEM CurrentEventId; PKSEVENT_SET CurrentEvent; ULONG i, j;
PAGED_CODE();
//
// walk the minidriver's event info to fill in the dispatch
// vectors as appropriate.
//
CurrentEvent = MinidriverEvents;
for (i = 0; i < NumEvents; i++) {
CurrentEventId = (PKSEVENT_ITEM) CurrentEvent->EventItem;
for (j = 0; j < CurrentEvent->EventsCount; j++) {
if (Stream) {
//
// set up the add and remove handlers for the stream.
// GUBGUB - Still not see justifications.
// don't support IsSupported currently, until
// a good justification of it is made.
//
CurrentEventId->AddHandler = StreamClassEnableEventHandler; CurrentEventId->RemoveHandler = StreamClassDisableEventHandler;
} else {
//
// set up the add and remove handlers for the device.
// GUBGUB - still not see justifications
// - don't support IsSupported currently, until
// a good justification of it is made.
//
CurrentEventId->AddHandler = StreamClassEnableDeviceEventHandler; CurrentEventId->RemoveHandler = StreamClassDisableDeviceEventHandler;
} // if stream
//
// index to next property item in
// array
//
CurrentEventId++;
} // for number of event items
//
// index to next event set in array
//
CurrentEvent++;
} // for number of event sets
}
VOID SCReadRegistryValues(IN PDEVICE_EXTENSION DeviceExtension, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++
Routine Description:
Reads all registry values for the device
Arguments:
DeviceExtension - pointer to the device extension PhysicalDeviceObject - pointer to the PDO
Return Value:
None.
--*/
{ ULONG i; NTSTATUS Status; HANDLE handle; ULONG DataBuffer;
PAGED_CODE();
Status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, PLUGPLAY_REGKEY_DRIVER, STANDARD_RIGHTS_ALL, &handle);
//
// loop through our table of strings,
// reading the registry for each.
//
if (NT_SUCCESS(Status)) {
for (i = 0; i < SIZEOF_ARRAY(RegistrySettings); i++) {
//
// read the registry value and set
// the flag if the setting is true.
//
//
// Need to init each time besides
// we only obtain one byte in the DataBuffer
//
DataBuffer = 0; Status = SCGetRegistryValue(handle, RegistrySettings[i].String, RegistrySettings[i].StringLength, &DataBuffer, 1);
DebugPrint((DebugLevelInfo, "Reg Key %S value %x\n", RegistrySettings[i].String, (BYTE)DataBuffer)); if ((NT_SUCCESS(Status)) && DataBuffer) {
//
// setting is true, so or in the
// appropriate flag
//
DeviceExtension->RegistryFlags |= RegistrySettings[i].Flags; } // if true
} // while strings
DebugPrint((DebugLevelInfo,"====DeviceObject %x DeviceExtenion %x has RegFlags %x\n", DeviceExtension->DeviceObject, DeviceExtension, DeviceExtension->RegistryFlags ));
//
// close the registry handle.
//
ZwClose(handle);
} // status = success
}
NTSTATUS SCGetRegistryValue( IN HANDLE Handle, IN PWCHAR KeyNameString, IN ULONG KeyNameStringLength, IN PVOID Data, IN ULONG DataLength ) /*++
Routine Description:
Reads the specified registry value
Arguments:
Handle - handle to the registry key KeyNameString - value to read KeyNameStringLength - length of string Data - buffer to read data into DataLength - length of data buffer
Return Value:
NTSTATUS returned as appropriate
--*/ { NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES; UNICODE_STRING KeyName; ULONG Length; PKEY_VALUE_FULL_INFORMATION FullInfo;
PAGED_CODE();
RtlInitUnicodeString(&KeyName, KeyNameString);
Length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength + DataLength;
FullInfo = ExAllocatePool(PagedPool, Length);
if (FullInfo) { Status = ZwQueryValueKey(Handle, &KeyName, KeyValueFullInformation, FullInfo, Length, &Length);
if (NT_SUCCESS(Status)) {
if (DataLength >= FullInfo->DataLength) { RtlCopyMemory(Data, ((PUCHAR) FullInfo) + FullInfo->DataOffset, FullInfo->DataLength);
} else {
Status = STATUS_BUFFER_TOO_SMALL; } // buffer right length
} // if success
ExFreePool(FullInfo);
} // if fullinfo
return Status;
}
NTSTATUS SCReferenceSwEnumDriver( IN PDEVICE_EXTENSION DeviceExtension, IN BOOLEAN Reference // AddRef or DeRef
) /*++
Routine Description:
This routine shows one more reference to the minidriver, and pages in the minidriver if the count was zero
Arguments:
DeviceExtension - pointer to device extension
Return Value:
none.
--*/
{ NTSTATUS Status; KEVENT Event; IO_STATUS_BLOCK IoStatusBlock; PIRP Irp; PIO_STACK_LOCATION IrpStackNext; PBUS_INTERFACE_REFERENCE BusInterface;
PMINIDRIVER_INFORMATION MinidriverInfo = DeviceExtension->DriverInfo;
PAGED_CODE();
BusInterface = ExAllocatePool(NonPagedPool, sizeof(BUS_INTERFACE_REFERENCE)); if (BusInterface == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } //
// There is no file object associated with this Irp, so the event may be located
// on the stack as a non-object manager object.
//
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, DeviceExtension->AttachedPdo, NULL, 0, NULL, &Event, &IoStatusBlock); if (Irp != NULL) { Irp->RequestorMode = KernelMode; Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; IrpStackNext = IoGetNextIrpStackLocation(Irp); //
// Create an interface query out of the Irp.
//
IrpStackNext->MinorFunction = IRP_MN_QUERY_INTERFACE; IrpStackNext->Parameters.QueryInterface.InterfaceType = (GUID*)&REFERENCE_BUS_INTERFACE; IrpStackNext->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_REFERENCE); IrpStackNext->Parameters.QueryInterface.Version = BUS_INTERFACE_REFERENCE_VERSION; IrpStackNext->Parameters.QueryInterface.Interface = (PINTERFACE)BusInterface; IrpStackNext->Parameters.QueryInterface.InterfaceSpecificData = NULL; Status = IoCallDriver(DeviceExtension->AttachedPdo, Irp); if (Status == STATUS_PENDING) { //
// This waits using KernelMode, so that the stack, and therefore the
// event on that stack, is not paged out.
//
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } } else { Status = STATUS_INSUFFICIENT_RESOURCES; }
if (Status == STATUS_SUCCESS) { if (Reference) BusInterface->ReferenceDeviceObject(BusInterface->Interface.Context); else BusInterface->DereferenceDeviceObject(BusInterface->Interface.Context); }
ExFreePool(BusInterface);
return Status;
}
VOID SCDereferenceDriver( IN PDEVICE_EXTENSION DeviceExtension
) /*++
Routine Description:
This routine shows one fewer reference to the minidriver, and pages out the minidriver if the count goes to zero
Arguments:
DeviceExtension - pointer to device extension
Return Value:
none.
--*/
{
PMINIDRIVER_INFORMATION MinidriverInfo; PDEVICE_EXTENSION CurrentDeviceExtension; BOOLEAN RequestIssued, DontPage = FALSE; KEVENT Event; IO_STATUS_BLOCK IoStatusBlock; PIRP Irp; PDEVICE_OBJECT DeviceObject; NTSTATUS Status;
PAGED_CODE();
//
// if the driver said it was a SWENUM driver, dereference it.
//
if (DeviceExtension->RegistryFlags & DRIVER_USES_SWENUM_TO_LOAD) { SCReferenceSwEnumDriver(DeviceExtension,FALSE); }
MinidriverInfo = IoGetDriverObjectExtension(DeviceExtension->DeviceObject->DriverObject, (PVOID) StreamClassPnP);
DebugPrint(( DebugLevelVerbose, "DerefernceDriver %x Count %x DriverFlags=%x\n", DeviceExtension->DeviceObject->DriverObject, MinidriverInfo->UseCount, MinidriverInfo->Flags)); if (!(MinidriverInfo->Flags & DRIVER_FLAGS_NO_PAGEOUT)) {
KeWaitForSingleObject(&MinidriverInfo->ControlEvent, Executive, KernelMode, FALSE, // not alertable
NULL);
//
// dec the refcount and see if we can page out.
//
DebugPrint(( DebugLevelVerbose, "DerefernceDriver CountDown\n"));
ASSERT((LONG) MinidriverInfo->UseCount > 0);
if (!(--MinidriverInfo->UseCount)) {
//
// page out the minidriver after alerting it that we are going to.
// PNP is supposed to be serialized, so there should be
// no need to protect this list. I'm worried about this, tho.
// need to research.
// My unstderstanding is that PnP is serialized.
//
// This is by-design, not a bug.
// This code assumes that the minidriver will bind only
// with the stream class. this needs to be doc'ed in the spec
// that only single binders will be able to use autopage.
//
//
// find the first device object chained to the driver object.
//
DeviceObject = DeviceExtension->DeviceObject->DriverObject->DeviceObject;
while (DeviceObject) {
CurrentDeviceExtension = DeviceObject->DeviceExtension;
DebugPrint((DebugLevelVerbose, "DerefernceDriver Checking Device=%x\n", DeviceObject));
//
// if the device is not started, don't call the minidriver
// also don't process a child device
//
if ((CurrentDeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) && (!(CurrentDeviceExtension->Flags & DEVICE_FLAGS_CHILD))) {
KeInitializeEvent(&Event, NotificationEvent, FALSE);
//
// allocate IRP for issuing the pageout. Since this IRP
// should not really be referenced, use dummy IOCTL code.
// I chose this one since it will always fail in the KS
// property handler if someone is silly enough to try to
// process it. Also make the irp internal i/o control.
//
// IoVerifier.c test code does not check IrpStack bound like
// the formal production code. And the owner does not want to
// fix it. It's more productive just work around here.
//Irp = IoBuildDeviceIoControlRequest(
// IOCTL_KS_PROPERTY,
// DeviceObject,
// NULL,
// 0,
// NULL,
// 0,
// TRUE,
// &Event,
// &IoStatusBlock);
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if (!Irp) {
//
// could not allocate IRP. don't page out.
//
DontPage = TRUE;
break; }
else { PIO_STACK_LOCATION NextStack; //
// This is a dummy Irp, the MJ/MN are arbitrary
//
NextStack = IoGetNextIrpStackLocation(Irp); ASSERT(NextStack != NULL); NextStack->MajorFunction = IRP_MJ_PNP; NextStack->MinorFunction = IRP_MN_CANCEL_STOP_DEVICE; Irp->UserIosb = &IoStatusBlock; Irp->UserEvent = &Event; }
//
// show one more I/O pending on the device.
//
DebugPrint((DebugLevelVerbose, "Sending SRB_PAGING_OUT_DRIVER to Device=%x\n", DeviceObject));
InterlockedIncrement(&CurrentDeviceExtension->OneBasedIoCount);
Status = SCSubmitRequest(SRB_PAGING_OUT_DRIVER, (PVOID) NULL, 0, SCProcessCompletedRequest, CurrentDeviceExtension, NULL, NULL, Irp, &RequestIssued, &CurrentDeviceExtension->PendingQueue, (PVOID) CurrentDeviceExtension-> MinidriverData->HwInitData. HwReceivePacket );
if (!RequestIssued) {
//
// could not issue SRB. complete IRP and don't page
// out.
//
DontPage = TRUE; SCCompleteIrp(Irp, Status, CurrentDeviceExtension); break;
} // if ! requestissued
//
// check status. note that we do not check for pending,
// since the above call is sync and won't return til the
// request is complete.
//
if (!NT_SUCCESS(Status)) {
//
// if the minidriver did not OK the pageout, don't
// page
// out.
//
DontPage = TRUE; break;
} // if !success
} // if started
DeviceObject = DeviceObject->NextDevice; } // while deviceobject
//
// if we were able to alert each device controlled by the driver
// that a pageout is emminent, page the driver out.
//
if (!DontPage) {
DebugPrint((DebugLevelVerbose, "mmPageEntireDriver %x\n", DeviceExtension->DeviceObject->DriverObject)); MinidriverInfo->Flags |= DRIVER_FLAGS_PAGED_OUT; MmPageEntireDriver(MinidriverInfo->HwInitData.HwReceivePacket);
} // if ! dontpage
} // if !usecount
//
// release the control event.
//
KeSetEvent(&MinidriverInfo->ControlEvent, IO_NO_INCREMENT, FALSE);
} // if pageable
}
VOID SCReferenceDriver( IN PDEVICE_EXTENSION DeviceExtension
) /*++
Routine Description:
This routine shows one more reference to the minidriver, and pages in the minidriver if the count was zero
Arguments:
DeviceExtension - pointer to device extension
Return Value:
none.
--*/
{
PMINIDRIVER_INFORMATION MinidriverInfo = DeviceExtension->DriverInfo;
PAGED_CODE();
//
// if the driver said it was a SWENUM driver, reference it.
//
if (DeviceExtension->RegistryFlags & DRIVER_USES_SWENUM_TO_LOAD) { SCReferenceSwEnumDriver(DeviceExtension,TRUE); } DebugPrint(( DebugLevelVerbose, "ReferenceDriver %x Count %x DriverFlags=%x\n", DeviceExtension->DeviceObject->DriverObject, MinidriverInfo->UseCount, MinidriverInfo->Flags));
if (!(MinidriverInfo->Flags & DRIVER_FLAGS_NO_PAGEOUT)) {
KeWaitForSingleObject(&MinidriverInfo->ControlEvent, Executive, KernelMode, FALSE, // not alertable
NULL);
DebugPrint(( DebugLevelVerbose, "RefernceDriver Countup\n"));
//
// inc the refcount and see if we
// need to page in.
//
ASSERT((LONG) MinidriverInfo->UseCount >= 0);
if (!(MinidriverInfo->UseCount++)) {
//
// page in the minidriver
//
MmResetDriverPaging(MinidriverInfo->HwInitData.HwReceivePacket); MinidriverInfo->Flags &= ~(DRIVER_FLAGS_PAGED_OUT);
} // if !usecount
KeSetEvent(&MinidriverInfo->ControlEvent, IO_NO_INCREMENT, FALSE);
} // if pageable
}
VOID SCInsertStreamInFilter( IN PSTREAM_OBJECT StreamObject, IN PDEVICE_EXTENSION DeviceExtension
) /*++
Routine Description:
Inserts a new stream in the stream queue on the filter instance
Arguments:
StreamObject = pointer to stream object
Return Value:
none.
--*/ {
KIRQL Irql;
//
// insert the stream object in the filter
// instance list
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
InsertHeadList(&((PFILTER_INSTANCE) (StreamObject->FilterInstance))-> FirstStream, &StreamObject->NextStream);
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
return; }
VOID SCInsertFiltersInDevice( IN PFILTER_INSTANCE FilterInstance, IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
Inserts a new filter in the device list at DPC level
Arguments:
Return Value:
none.
--*/ { KIRQL Irql;
//
// insert the filter instance in the global list
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
InsertHeadList( &DeviceExtension->FilterInstanceList, &FilterInstance->NextFilterInstance);
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); }
VOID SCInterlockedRemoveEntryList( PDEVICE_EXTENSION DeviceExtension, PLIST_ENTRY List ) /*++
Routine Description:
Removes the specified entry under spinlock
Arguments:
Return Value:
none.
--*/ { KIRQL Irql;
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
RemoveEntryList(List);
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
}
VOID SCProcessTimerRequest( IN PCOMMON_OBJECT CommonObject, IN PINTERRUPT_DATA SavedInterruptData
) /*++
Routine Description:
This routine handles a minidriver request to either set or clear a timer
Arguments:
CommonObject - pointer to common object SavedInterruptData - captured interrupt data
Return Value:
none.
--*/ { LARGE_INTEGER timeValue;
CommonObject->HwTimerRoutine = SavedInterruptData->HwTimerRoutine;
CommonObject->HwTimerContext = SavedInterruptData->HwTimerContext;
//
// The minidriver wants a timer request.
// If the requested timer value is zero,
// then cancel the timer.
//
if (SavedInterruptData->HwTimerValue == 0) {
KeCancelTimer(&CommonObject->MiniDriverTimer);
} else {
//
// Convert the timer value from
// microseconds to a negative
// 100
// nanoseconds.
//
// timeValue.QuadPart = Int32x32To64(
// SavedInterruptData->HwTimerValue,
// -10);
timeValue.LowPart = SavedInterruptData->HwTimerValue * -10; timeValue.HighPart = -1;
//
// Set the timer.
//
KeSetTimer(&CommonObject->MiniDriverTimer, timeValue, &CommonObject->MiniDriverTimerDpc); } }
VOID SCProcessPriorityChangeRequest( IN PCOMMON_OBJECT CommonObject, IN PINTERRUPT_DATA SavedInterruptData, IN PDEVICE_EXTENSION DeviceExtension
) /*++
Routine Description:
Routine handles priority change requests from the minidriver
Arguments:
CommonObject - pointer to common object SavedInterruptData - captured interrupt data DeviceExtension - pointer to device extension
Return Value:
none.
--*/ {
#if DBG
PDEBUG_WORK_ITEM DbgWorkItemStruct; #endif
if (SavedInterruptData->HwPriorityLevel == Dispatch) {
DebugPrint((DebugLevelVerbose, "'SCDpc: Dispatch priority callout\n"));
//
// Acquire the device spinlock so
// nothing else starts.
//
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
//
// call the minidriver at dispatch
// level.
//
SavedInterruptData->HwPriorityRoutine(SavedInterruptData->HwPriorityContext);
if ((CommonObject->InterruptData.Flags & INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST) && (CommonObject->InterruptData.HwPriorityLevel == High)) {
DebugPrint((DebugLevelVerbose, "'SCDpc: High priority callout\n"));
//
// if the minidriver now wants a high priority callback,
// do so now. This is safe since we have the device
// spinlock and the minidriver cannot make
// another priority request for this stream while one is
// requested.
//
CommonObject->InterruptData.Flags &= ~(INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST);
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, (PVOID) CommonObject->InterruptData.HwPriorityRoutine, CommonObject->InterruptData.HwPriorityContext);
} // if high requested
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
} else if (SavedInterruptData->HwPriorityLevel == Low) {
#if DBG
//
// make sure that the minidriver is not misusing this function.
//
if (DeviceExtension->NumberOfRequests > 0xFFFFFFF0) { DeviceExtension->Flags |= DEVICE_FLAGS_PRI_WARN_GIVEN;
} if ((++DeviceExtension->NumberOfLowPriCalls > 100) && ((DeviceExtension->NumberOfLowPriCalls) > DeviceExtension->NumberOfRequests / 4) && (!(DeviceExtension->Flags & DEVICE_FLAGS_PRI_WARN_GIVEN))) {
DeviceExtension->Flags |= DEVICE_FLAGS_PRI_WARN_GIVEN;
DebugPrint((DebugLevelFatal, "Stream Class has determined that a minidriver is scheduling\n")); DebugPrint((DebugLevelFatal, "a low priority callback for more than 25 percent of the requests\n")); DebugPrint((DebugLevelFatal, "it has received. This driver should probably be setting the\n")); DebugPrint((DebugLevelFatal, "TurnOffSynchronization boolean and doing its own synchronization.\n")); DebugPrint((DebugLevelFatal, "Please open a bug against the dev owner of this minidriver.\n")); DebugPrint((DebugLevelFatal, "Do an LN of %x to determine the name of the minidriver.\n", SavedInterruptData->HwPriorityRoutine)); TRAP; } // if bad pri
if (CommonObject->InterruptData.Flags & INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST) {
DebugPrint((DebugLevelFatal, "Stream Minidriver scheduled priority twice!\n")); ASSERT(1 == 0); } // if scheduled twice
DbgWorkItemStruct = ExAllocatePool(NonPagedPool, sizeof(DEBUG_WORK_ITEM)); // DebugPrint((DebugLevelFatal, "A %x\n", DbgWorkItemStruct));
if (DbgWorkItemStruct) {
DbgWorkItemStruct->HwPriorityRoutine = SavedInterruptData->HwPriorityRoutine; DbgWorkItemStruct->HwPriorityContext = SavedInterruptData->HwPriorityContext; DbgWorkItemStruct->Object = CommonObject;
ExInitializeWorkItem(&CommonObject->WorkItem, SCDebugPriorityWorkItem, DbgWorkItemStruct); } else {
ExInitializeWorkItem(&CommonObject->WorkItem, SavedInterruptData->HwPriorityRoutine, SavedInterruptData->HwPriorityContext); }
#else
ExInitializeWorkItem(&CommonObject->WorkItem, SavedInterruptData->HwPriorityRoutine, SavedInterruptData->HwPriorityContext); #endif
ExQueueWorkItem(&CommonObject->WorkItem, DelayedWorkQueue); } // if priority
}
VOID SCBeginSynchronizedMinidriverCallin( IN PDEVICE_EXTENSION DeviceExtension, IN PKIRQL Irql) /*++
Routine Description:
This routine handles begin processing of a synchronized minidriver callin
Arguments:
DeviceExtension - pointer to the device extension Irql - POINTER to a KIRQL structure
Return Value:
none.
--*/ { return; }
VOID SCBeginUnsynchronizedMinidriverCallin( IN PDEVICE_EXTENSION DeviceExtension, IN PKIRQL Irql) /*++
Routine Description:
This routine handles begin processing of an unsynchronized minidriver callin
Arguments:
DeviceExtension - pointer to the device extension Irql - POINTER to a KIRQL structure
Return Value:
none.
--*/
{ KeAcquireSpinLock(&DeviceExtension->SpinLock, Irql); \ return; }
VOID SCEndSynchronizedMinidriverStreamCallin( IN PSTREAM_OBJECT StreamObject, IN PKIRQL Irql) /*++
Routine Description:
This routine handles end processing of a synchronized minidriver stream callin
Arguments:
DeviceExtension - pointer to the device extension Irql - POINTER to a KIRQL structure
Return Value:
none.
--*/
{ SCRequestDpcForStream(StreamObject); return; }
VOID SCEndSynchronizedMinidriverDeviceCallin( IN PDEVICE_EXTENSION DeviceExtension, IN PKIRQL Irql) /*++
Routine Description:
This routine handles end processing of a synchronized minidriver device callin
Arguments:
DeviceExtension - pointer to the device extension Irql - POINTER to a KIRQL structure
Return Value:
none.
--*/
{
DeviceExtension->ComObj.InterruptData.Flags |= INTERRUPT_FLAGS_NOTIFICATION_REQUIRED; return; }
VOID SCEndUnsynchronizedMinidriverDeviceCallin( IN PDEVICE_EXTENSION DeviceExtension, IN PKIRQL Irql) /*++
Routine Description:
This routine handles end processing of an unsynchronized minidriver device callin
Arguments:
DeviceExtension - pointer to the device extension Irql - POINTER to a KIRQL structure
Return Value:
none.
--*/
{ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); DeviceExtension->ComObj.InterruptData.Flags |= INTERRUPT_FLAGS_NOTIFICATION_REQUIRED; StreamClassDpc(NULL, DeviceExtension->DeviceObject, NULL, NULL); KeLowerIrql(*Irql); return; }
VOID SCEndUnsynchronizedMinidriverStreamCallin( IN PSTREAM_OBJECT StreamObject, IN PKIRQL Irql) /*++
Routine Description:
This routine handles end processing of an unsynchronized minidriver stream callin
Arguments:
DeviceExtension - pointer to the device extension Irql - POINTER to a KIRQL structure
Return Value:
none.
--*/
{ KeReleaseSpinLockFromDpcLevel(&StreamObject->DeviceExtension->SpinLock); SCRequestDpcForStream(StreamObject);
StreamClassDpc(NULL, StreamObject->DeviceExtension->DeviceObject, NULL, StreamObject); KeLowerIrql(*Irql); return; }
VOID SCCheckPoweredUp( IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
This routine powers up the HW if necessary
Arguments:
DeviceExtension - pointer to the device extension
Return Value:
none.
--*/ {
NTSTATUS Status; POWER_STATE PowerState; POWER_CONTEXT PowerContext;
PAGED_CODE();
//
// check to see if we are powered down
//
if (DeviceExtension->RegistryFlags & DEVICE_REG_FL_POWER_DOWN_CLOSED) { while (DeviceExtension->CurrentPowerState != PowerDeviceD0) {
//
// release the event to avoid deadlocks with the power up code.
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
//
// tell the power manager to power up the device.
//
PowerState.DeviceState = PowerDeviceD0;
//
// now send down a set power based on this info.
//
KeInitializeEvent(&PowerContext.Event, NotificationEvent, FALSE);
Status = PoRequestPowerIrp(DeviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, PowerState, SCBustedSynchPowerCompletionRoutine, &PowerContext, NULL);
if (Status == STATUS_PENDING) {
//
// wait for the IRP to complete
//
KeWaitForSingleObject( &PowerContext.Event, Suspended, KernelMode, FALSE, NULL); } //
// reacquire the event and loop if good status. The only reason
// we would get a good status here is if the HW powered up, but
// some
// policy maker instantly powered it down again. This should
// never
// happen more than once, but if it does this thread could be
// stuck.
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent, Executive, KernelMode, FALSE, // not alertable
NULL);
if (!NT_SUCCESS(PowerContext.Status)) {
//
// if we could not power up, go ahead and let the request go
// through. The worst that will happen is that the request
// will fail at the HW level.
//
break; } }
} // if power down when closed
return; }
VOID SCCheckPowerDown( IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
This routine powers down the hardware if possible
Arguments:
DeviceExtension - pointer to the device extension
Return Value:
none.
--*/ { NTSTATUS Status; POWER_STATE PowerState; POWER_CONTEXT PowerContext;
PAGED_CODE();
//
// only power down if there are not open files
//
if (DeviceExtension->RegistryFlags & DEVICE_REG_FL_POWER_DOWN_CLOSED) { if (!DeviceExtension->NumberOfOpenInstances) {
//
// release the event to avoid deadlocks with the power up code.
//
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
//
// tell the power manager to power down the device.
//
PowerState.DeviceState = PowerDeviceD3;
//
// now send down a set power based on this info.
//
KeInitializeEvent(&PowerContext.Event, NotificationEvent, FALSE);
Status = PoRequestPowerIrp(DeviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, PowerState, SCBustedSynchPowerCompletionRoutine, &PowerContext, NULL);
if (Status == STATUS_PENDING) {
//
// wait for the IRP to complete
//
KeWaitForSingleObject( &PowerContext.Event, Suspended, KernelMode, FALSE, NULL); } //
// reacquire the event.
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent, Executive, KernelMode, FALSE, // not alertable
NULL); } } // if power down closed
return; }
VOID SCWaitForOutstandingIo( IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
This routine decs the one based I/O counter and blocks until the counter goes to zero.
Arguments:
DeviceExtension - pointer to the device extension
Return Value:
none.
--*/ { KIRQL Irql; KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
DeviceExtension->Flags |= DEVICE_FLAGS_DEVICE_INACCESSIBLE;
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
if (InterlockedDecrement(&DeviceExtension->OneBasedIoCount)) {
#ifdef wecandothis
PFILTER_INSTANCE FilterInstance; KIRQL Irql; PLIST_ENTRY FilterEntry, FilterListEntry;
//
// there is I/O outstanding. Cancel all outstanding IRP's.
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
checkfilters: FilterInstance = DeviceExtension->GlobalFilterInstance;
if (FilterInstance) {
if (SCCheckFilterInstanceStreamsForIrp(FilterInstance, NULL)) {
DebugPrint((DebugLevelWarning, "'SCCancelPending: found Irp on global instance\n"));
//
// we found one. jump back to loop back through since the
// spinlock
// had to be released and reaquired to cancel the irp.
//
goto checkfilters; } } FilterListEntry = FilterEntry = &DeviceExtension->FilterInstanceList;
while (FilterEntry->Flink != FilterListEntry->Blink) {
FilterEntry = FilterEntry->Flink;
//
// follow the link to the instance
//
FilterInstance = CONTAINING_RECORD(FilterListEntry, FILTER_INSTANCE, NextFilterInstance);
//
// process the streams on this list
//
if (SCCheckFilterInstanceStreamsForIrp(FilterInstance, NULL)) {
//
// we found one. jump back to loop back through since the
// spinlock
// had to be released and reaquired to cancel the irp.
//
goto checkfilters;
} //
// get the list entry for this instance
//
FilterListEntry = &FilterInstance->NextFilterInstance; }
//
// now process any requests on the device itself
//
while (SCCheckRequestsForIrp( &DeviceExtension->OutstandingQueue, NULL, TRUE, DeviceExtension)) {
}
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
#endif
//
// Block on the removal event which is signaled as the last I/O
// completes.
//
KeWaitForSingleObject(&DeviceExtension->RemoveEvent, Executive, KernelMode, FALSE, // not alertable
NULL); } //
// restore the counter to 1-based, since we've now assured that all
// I/O to the device has completed.
//
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
return; }
NTSTATUS SCShowIoPending( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp ) /*++
Routine Description:
This routine shows that one more I/O is outstanding, or errors the I/O if the device is inaccessible.
Arguments:
DeviceExtension - pointer to device extension Irp - pointer to IRP
Return Value:
TRUE if I/O can be submitted.
--*/ { PAGED_CODE();
//
// assume that the device is accessible and show one more request.
// if it's not accessible, we'll show one less. do it in this order
// to prevent a race where the inaccessible flag has been set, but the
// the i/o count has not been dec'd yet.
//
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
if (DeviceExtension->Flags & DEVICE_FLAGS_DEVICE_INACCESSIBLE) {
NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
InterlockedDecrement(&DeviceExtension->OneBasedIoCount);
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return (Status); } return (STATUS_SUCCESS);
}
NTSTATUS SCCallNextDriver( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp ) /*++
Routine Description:
Arguments:
DeviceExtension - pointer to device extension Irp - pointer to IRP
Return Value:
none.
--*/ { KEVENT Event; PIO_STACK_LOCATION IrpStack, NextStack; NTSTATUS Status;
PAGED_CODE();
if ( NULL == DeviceExtension->AttachedPdo ) { //
// DO has been detached, return success directly.
//
return STATUS_SUCCESS; }
IrpStack = IoGetCurrentIrpStackLocation(Irp);
NextStack = IoGetNextIrpStackLocation(Irp); ASSERT(NextStack != NULL); RtlCopyMemory(NextStack, IrpStack, sizeof(IO_STACK_LOCATION));
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
IoSetCompletionRoutine(Irp, SCSynchCompletionRoutine, &Event, TRUE, TRUE, TRUE);
if ( IRP_MJ_POWER != IrpStack->MajorFunction ) { Status = IoCallDriver(DeviceExtension->AttachedPdo, Irp); } else {
//
// power Irp, use PoCallDriver()
//
Status = PoCallDriver( DeviceExtension->AttachedPdo, Irp ); }
if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = Irp->IoStatus.Status; } return (Status); }
VOID SCMinidriverTimeFunction( IN PHW_TIME_CONTEXT TimeContext ) /*++
Routine Description:
Arguments:
Return Value:
Notes:
--*/ {
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) TimeContext->HwDeviceExtension - 1; KIRQL Irql; PSTREAM_OBJECT StreamObject = CONTAINING_RECORD( TimeContext->HwStreamObject, STREAM_OBJECT, HwStreamObject);
//
// call the minidriver to process the time function
//
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, (PVOID) StreamObject-> HwStreamObject.HwClockObject.HwClockFunction, TimeContext);
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
}
ULONGLONG SCGetStreamTime( IN PFILE_OBJECT FileObject
) /*++
Routine Description:
Arguments:
Return Value:
Notes:
--*/ { HW_TIME_CONTEXT TimeContext;
PCLOCK_INSTANCE ClockInstance = (PCLOCK_INSTANCE) FileObject->FsContext;
TimeContext.HwStreamObject = &ClockInstance->StreamObject->HwStreamObject;
TimeContext.HwDeviceExtension = ClockInstance->StreamObject-> DeviceExtension->HwDeviceExtension;
TimeContext.Function = TIME_GET_STREAM_TIME;
SCMinidriverTimeFunction(&TimeContext);
return (TimeContext.Time); }
ULONGLONG FASTCALL SCGetPhysicalTime( IN PFILE_OBJECT FileObject
) /*++
Routine Description:
Arguments:
Return Value:
Notes:
--*/ { HW_TIME_CONTEXT TimeContext;
PCLOCK_INSTANCE ClockInstance = (PCLOCK_INSTANCE) FileObject->FsContext;
TimeContext.HwStreamObject = &ClockInstance->StreamObject->HwStreamObject;
TimeContext.HwDeviceExtension = ClockInstance->StreamObject-> DeviceExtension->HwDeviceExtension;
TimeContext.Function = TIME_READ_ONBOARD_CLOCK;
SCMinidriverTimeFunction(&TimeContext);
return (TimeContext.Time); }
ULONGLONG FASTCALL SCGetSynchronizedTime( IN PFILE_OBJECT FileObject, IN PULONGLONG SystemTime
) /*++
Routine Description:
Arguments:
Return Value:
Notes:
--*/ { HW_TIME_CONTEXT TimeContext;
PCLOCK_INSTANCE ClockInstance = (PCLOCK_INSTANCE) FileObject->FsContext;
TimeContext.HwStreamObject = &ClockInstance->StreamObject->HwStreamObject;
TimeContext.HwDeviceExtension = ClockInstance->StreamObject-> DeviceExtension->HwDeviceExtension;
TimeContext.Function = TIME_GET_STREAM_TIME;
SCMinidriverTimeFunction(&TimeContext);
*SystemTime = TimeContext.SystemTime; return (TimeContext.Time); }
NTSTATUS SCSendUnknownCommand( IN PIRP Irp, IN PDEVICE_EXTENSION DeviceExtension, IN PVOID Callback, OUT PBOOLEAN RequestIssued ) /*++
Routine Description:
Arguments:
Irp - pointer to the IRP
Return Value:
NTSTATUS returned as appropriate.
--*/
{
PAGED_CODE();
//
// send an UNKNOWN_COMMAND SRB to the minidriver.
//
return (SCSubmitRequest(SRB_UNKNOWN_DEVICE_COMMAND, NULL, 0, Callback, DeviceExtension, NULL, NULL, Irp, RequestIssued, &DeviceExtension->PendingQueue, (PVOID) DeviceExtension-> MinidriverData->HwInitData. HwReceivePacket ));
}
BOOLEAN SCMapMemoryAddress(PACCESS_RANGE AccessRanges, PHYSICAL_ADDRESS TranslatedAddress, PPORT_CONFIGURATION_INFORMATION ConfigInfo, PDEVICE_EXTENSION DeviceExtension, PCM_RESOURCE_LIST ResourceList, PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor) /*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{ PMAPPED_ADDRESS newMappedAddress;
PAGED_CODE();
//
// Now we need to map a linear address to the physical
// address that HalTranslateBusAddress provided us.
//
//
// set the access range in the structure.
//
AccessRanges->RangeLength = PartialResourceDescriptor->u.Memory.Length;
AccessRanges->RangeInMemory = TRUE;
AccessRanges->RangeStart.QuadPart = (ULONG_PTR) MmMapIoSpace( TranslatedAddress, AccessRanges->RangeLength, FALSE // No caching
);
if (AccessRanges->RangeStart.QuadPart == 0) {
//
// Couldn't translate the resources, return an error
// status
//
DebugPrint((DebugLevelFatal, "StreamClassPnP: Couldn't translate Memory Slot Resources\n")); return FALSE;
} //
// Allocate memory to store mapped address for unmap.
//
newMappedAddress = ExAllocatePool(NonPagedPool, sizeof(MAPPED_ADDRESS));
//
// save a link to the resources if the alloc succeeded.
// if it failed, don't worry about it.
//
if (newMappedAddress != NULL) {
//
// Store mapped address, bytes count, etc.
//
newMappedAddress->MappedAddress = (PVOID) AccessRanges->RangeStart.QuadPart; newMappedAddress->NumberOfBytes = AccessRanges->RangeLength; newMappedAddress->IoAddress = PartialResourceDescriptor->u.Memory.Start; newMappedAddress->BusNumber = ConfigInfo->SystemIoBusNumber;
//
// Link current list to new entry.
//
newMappedAddress->NextMappedAddress = DeviceExtension->MappedAddressList;
//
// Point anchor at new list.
//
DeviceExtension->MappedAddressList = newMappedAddress;
} // if newmappedaddress
return TRUE; }
VOID SCUpdatePersistedProperties(IN PSTREAM_OBJECT StreamObject, IN PDEVICE_EXTENSION DeviceExtension, IN PFILE_OBJECT FileObject ) /*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{ NTSTATUS Status; HANDLE handle; CHAR AsciiKeyName[32]; ANSI_STRING AnsiKeyName; UNICODE_STRING UnicodeKeyName;
PAGED_CODE();
Status = IoOpenDeviceRegistryKey(DeviceExtension->PhysicalDeviceObject, PLUGPLAY_REGKEY_DRIVER, STANDARD_RIGHTS_ALL, &handle);
//
// loop through our table of strings,
// reading the registry for each.
//
if (NT_SUCCESS(Status)) {
//
// create the subkey for the pin, in the form of "Pin0\Properties",
// etc.
//
sprintf(AsciiKeyName, "Pin%d\\Properties", StreamObject->HwStreamObject.StreamNumber); RtlInitAnsiString(&AnsiKeyName, AsciiKeyName);
if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeKeyName, &AnsiKeyName, TRUE))) { //
// call KS to unserialize the properties.
//
KsUnserializeObjectPropertiesFromRegistry(FileObject, handle, &UnicodeKeyName); //
// free the unicode string
//
RtlFreeUnicodeString(&UnicodeKeyName);
} // if rtl..
//
// close the registry handle.
//
ZwClose(handle);
} // status = success
}
NTSTATUS SCQueryCapabilities( IN PDEVICE_OBJECT PdoDeviceObject, IN PDEVICE_CAPABILITIES DeviceCapabilities ) /*++
Routine Description:
This routine reads the capabilities of our parent.
Arguments:
DeviceObject - "Real" physical device object
Return Value:
None.
--*/
{ PIO_STACK_LOCATION NextStack; PIRP Irp; NTSTATUS Status; KEVENT Event;
PAGED_CODE();
//
// allocate an IRP for the call.
//
Irp = IoAllocateIrp(PdoDeviceObject->StackSize, FALSE);
if (!Irp) {
return STATUS_INSUFFICIENT_RESOURCES; } NextStack = IoGetNextIrpStackLocation(Irp);
ASSERT(NextStack != NULL); NextStack->MajorFunction = IRP_MJ_PNP; NextStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
//
// Initialize the capabilities that we will send down
//
RtlZeroMemory(DeviceCapabilities, sizeof(DEVICE_CAPABILITIES) ); DeviceCapabilities->Size = sizeof(DEVICE_CAPABILITIES); DeviceCapabilities->Version = 1; DeviceCapabilities->Address = -1; DeviceCapabilities->UINumber = -1;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
IoSetCompletionRoutine(Irp, SCSynchCompletionRoutine, &Event, TRUE, TRUE, TRUE);
NextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
DebugPrint((DebugLevelInfo, "Capabilities Version %x Flags %x\n", (ULONG)DeviceCapabilities->Version, *(UNALIGNED ULONG*)(&DeviceCapabilities->Version+1)));
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; // bug #282910
Status = IoCallDriver(PdoDeviceObject, Irp);
if (Status == STATUS_PENDING) {
//
// block waiting for completion
//
KeWaitForSingleObject( &Event, Suspended, KernelMode, FALSE, NULL); } //
// obtain final status and free IRP.
//
Status = Irp->IoStatus.Status;
IoFreeIrp(Irp);
return (Status);
}
NTSTATUS SCEnableEventSynchronized( IN PVOID ServiceContext ) /*++
Routine Description:
This routine inserts the new event on the queue, and calls the minidriver with the event.
Arguments:
ServiceContext - Supplies a pointer to the interrupt context which contains pointers to the interrupt data and where to save it.
Return Value:
Returns TRUE if there is new work and FALSE otherwise.
Notes:
Called via KeSynchronizeExecution with the port device extension spinlock held.
--*/ { PHW_EVENT_DESCRIPTOR Event = ServiceContext; NTSTATUS Status = STATUS_SUCCESS;
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD( Event->StreamObject, STREAM_OBJECT, HwStreamObject);
PDEVICE_EXTENSION DeviceExtension = StreamObject->DeviceExtension;
//
// insert the event on our list, in case the minidriver decides to signal
// from within this call.
//
InsertHeadList(&StreamObject->NotifyList, &Event->EventEntry->ListEntry);
//
// call the minidriver's event routine, if present.
//
if (StreamObject->HwStreamObject.HwEventRoutine) {
Status = StreamObject->HwStreamObject.HwEventRoutine(Event);
} // if eventroutine
if (!NT_SUCCESS(Status)) {
//
// minidriver did not like it. remove the entry from the list.
//
DebugPrint((DebugLevelError, "StreamEnableEvent: minidriver failed enable!\n"));
RemoveEntryList(&Event->EventEntry->ListEntry); } return (Status); }
NTSTATUS SCEnableDeviceEventSynchronized( IN PVOID ServiceContext ) /*++
Routine Description:
This routine inserts the new event on the queue, and calls the minidriver with the event.
Arguments:
ServiceContext - Supplies a pointer to the interrupt context which contains pointers to the interrupt data and where to save it.
Return Value:
Returns TRUE if there is new work and FALSE otherwise.
Notes:
--*/ { PHW_EVENT_DESCRIPTOR Event = ServiceContext; NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)Event->DeviceExtension - 1; IF_MF( PFILTER_INSTANCE FilterInstance = (PFILTER_INSTANCE)Event->HwInstanceExtension -1;)
//
// insert the event on our list, in case the minidriver decides to signal
// from within this call.
//
IFN_MF(InsertHeadList(&DeviceExtension->NotifyList,&Event->EventEntry->ListEntry);) IF_MF(InsertHeadList(&FilterInstance->NotifyList,&Event->EventEntry->ListEntry);)
//
// call the minidriver's event routine, if present.
//
IFN_MF( if (DeviceExtension->HwEventRoutine) {
Status = DeviceExtension->HwEventRoutine(Event);
} // if eventroutine
) IF_MF( if (FilterInstance->HwEventRoutine) {
Status = FilterInstance->HwEventRoutine(Event);
} // if eventroutine
)
if (!NT_SUCCESS(Status)) {
//
// minidriver did not like it. remove the entry from the list.
//
DebugPrint((DebugLevelError, "DeviceEnableEvent: minidriver failed enable!\n"));
RemoveEntryList(&Event->EventEntry->ListEntry); } return (Status); }
VOID SCFreeDeadEvents( IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
Free dead events at passive level
Arguments:
DeviceExtension - address of device extension.
Return Value:
None
--*/
{ LIST_ENTRY EventList; PLIST_ENTRY EventListEntry; PKSEVENT_ENTRY EventEntry; KIRQL Irql;
//
// capture the dead list at the appropriate synchronization level.
//
// hack to save code. store the DeviceExtension* in the list entry.
EventList.Flink = (PLIST_ENTRY) DeviceExtension;
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
DeviceExtension->SynchronizeExecution( DeviceExtension->InterruptObject, (PVOID) SCGetDeadListSynchronized, &EventList);
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
//
// discard each event on the captured list
//
while (!IsListEmpty(&EventList)) {
EventListEntry = RemoveHeadList(&EventList);
EventEntry = CONTAINING_RECORD(EventListEntry, KSEVENT_ENTRY, ListEntry);
KsDiscardEvent(EventEntry); } // while not empty
//
// show event has been run
//
DeviceExtension->DeadEventItemQueued = FALSE;
return; }
VOID SCGetDeadListSynchronized( IN PLIST_ENTRY NewEventList ) /*++
Routine Description:
Get the list of dead events at the appropriate sync level
Arguments:
NewListEntry - list head to add the event list.
Return Value:
None
--*/
{
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) NewEventList->Flink; PLIST_ENTRY ListEntry;
InitializeListHead(NewEventList);
//
// capture the dead list to our temp list head
//
while (!IsListEmpty(&DeviceExtension->DeadEventList)) {
ListEntry = RemoveTailList(&DeviceExtension->DeadEventList);
InsertHeadList(NewEventList, ListEntry);
} // while dead list not empty
InitializeListHead(&DeviceExtension->DeadEventList); return;
}
#if SUPPORT_MULTIPLE_FILTER_TYPES
VOID SCRescanStreams( IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
Rescan minidriver streams of all filters with the request
Arguments:
DeviceExtension - address of device extension.
Return Value:
None
--*/
{ PHW_STREAM_DESCRIPTOR StreamBuffer; PDEVICE_OBJECT DeviceObject = DeviceExtension->DeviceObject; PFILTER_INSTANCE FilterInstance; BOOLEAN RequestIssued; KEVENT Event; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PIRP Irp; ULONG ul; PLIST_ENTRY Node;
PAGED_CODE();
TRAP; DebugPrint((DebugLevelVerbose, "'RescanStreams: enter\n"));
//
// take the control event to avoid race
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent, Executive, KernelMode, FALSE,// not alertable
NULL);
ASSERT( !IsListEmpty( DeviceExtension->FilterInstanceList ));
Node = &DeviceExtension->FilterInstanceList;
while ( Node != Node->Flink ) { FilterInstance = CONTAINING_RECORD(Node, FILTER_INSTANCE, NextFilterInstance); if ( InterlockedExchange( &FilterInstance->NeedReenumeration, 0)) { //
// send an SRB to retrieve the stream information
//
ASSERT( FilterInstance->StreamDescriptorSize ); StreamBuffer = ExAllocatePool(NonPagedPool, FilterInstance->StreamDescriptorSize);
if (!StreamBuffer) { DebugPrint((DebugLevelError, "RescanStreams: couldn't allocate!\n")); TRAP; KeSetEvent( &DeviceExtension->ControlEvent,IO_NO_INCREMENT, FALSE); return; } //
// zero-init the buffer
//
RtlZeroMemory(StreamBuffer, ConfigInfo->StreamDescriptorSize);
//
// allocate IRP for issuing the get stream info.
// Since this IRP
// should not really be referenced, use dummy IOCTL code.
// I chose this one since it will always fail in the KS
// property handler if someone is silly enough to try to
// process it. Also make the irp internal i/o control.
//
// IoVerifier.c test code does not check IrpStack bound like
// the formal production code. And the owner does not want to
// fix it. It's more productive just work around here.
//Irp = IoBuildDeviceIoControlRequest(
// IOCTL_KS_PROPERTY,
// DeviceObject,
// NULL,
// 0,
// NULL,
// 0,
// TRUE,
// &Event,
// &IoStatusBlock);
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (!Irp) { //
// could not allocate IRP. fail.
//
ExFreePool( StreamBuffer ); DebugPrint((DebugLevelError, "RescanStreams: couldn't allocate!\n")); TRAP; return; } else { PIO_STACK_LOCATION NextStack; //
// This is a dummy Ir, the MJ is arbitrary
//
NextStack = IoGetNextIrpStackLocation(Irp); ASSERT(NextStack != NULL); NextStack->MajorFunction = IRP_MJ_PNP; NextStack->MinorFunction = IRP_MN_CANCEL_STOP_DEVICE; Irp->UserIosb = &IoStatusBlock; Irp->UserEvent = &Event; }
//
// show one more I/O pending on the device.
//
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
//
// submit the command to retrieve the stream info.
// additional processing will be done by the callback
// procedure.
//
Status = SCSubmitRequest(SRB_GET_STREAM_INFO, StreamBuffer, ConfigInfo->StreamDescriptorSize, SCStreamInfoCallback, DeviceExtension, FilterInstance->HwInstanceExtension, NULL, Irp, &RequestIssued, &DeviceExtension->PendingQueue, (PVOID) DeviceExtension-> MinidriverData->HwInitData. HwReceivePacket );
if (!RequestIssued) { KeSetEvent( &DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE); ExFreePool(StreamBuffer); DebugPrint((DebugLevelError, "RescanStreams: couldn't issue request!\n")); TRAP; SCCompleteIrp(Irp, Status, DeviceExtension); return; } } // check all filterinstances
//
// processing will continue in callback procedure.
//
return; }
#else // SUPPORT_MULTIPLE_FILTER_TYPES
VOID SCRescanStreams( IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
Rescan minidriver streams
Arguments:
DeviceExtension - address of device extension.
Return Value:
None
--*/
{ PHW_STREAM_DESCRIPTOR StreamBuffer; PDEVICE_OBJECT DeviceObject = DeviceExtension->DeviceObject; PPORT_CONFIGURATION_INFORMATION ConfigInfo = DeviceExtension->ConfigurationInformation; BOOLEAN RequestIssued; KEVENT Event; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PIRP Irp;
PAGED_CODE();
TRAP; DebugPrint((DebugLevelVerbose, "'RescanStreams: enter\n"));
//
// send an SRB to retrieve the stream information
//
ASSERT(ConfigInfo->StreamDescriptorSize);
StreamBuffer = ExAllocatePool(NonPagedPool, ConfigInfo->StreamDescriptorSize );
if (!StreamBuffer) {
DebugPrint((DebugLevelError, "RescanStreams: couldn't allocate!\n")); TRAP; return; } //
// take the control event to avoid race
//
KeWaitForSingleObject(&DeviceExtension->ControlEvent, Executive, KernelMode, FALSE,// not alertable
NULL);
//
// zero-init the buffer
//
RtlZeroMemory(StreamBuffer, ConfigInfo->StreamDescriptorSize);
//
// allocate IRP for issuing the get stream info.
// Since this IRP
// should not really be referenced, use dummy IOCTL code.
// I chose this one since it will always fail in the KS
// property handler if someone is silly enough to try to
// process it. Also make the irp internal i/o control.
//
Irp = IoBuildDeviceIoControlRequest( IOCTL_KS_PROPERTY, DeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatusBlock);
if (!Irp) {
//
// could not allocate IRP. fail.
//
ExFreePool( StreamBuffer ); DebugPrint((DebugLevelError, "RescanStreams: couldn't allocate!\n")); TRAP; return;
} // if ! irp
//
// show one more I/O pending on the device.
//
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
//
// submit the command to retrieve the stream info.
// additional processing will be done by the callback
// procedure.
//
Status = SCSubmitRequest(SRB_GET_STREAM_INFO, StreamBuffer, ConfigInfo->StreamDescriptorSize, SCStreamInfoCallback, DeviceExtension, NULL, NULL, Irp, &RequestIssued, &DeviceExtension->PendingQueue, (PVOID) DeviceExtension-> MinidriverData->HwInitData. HwReceivePacket );
if (!RequestIssued) {
ExFreePool(StreamBuffer); DebugPrint((DebugLevelError, "RescanStreams: couldn't issue request!\n")); TRAP; SCCompleteIrp(Irp, Status, DeviceExtension); return;
} //
// processing will continue in callback procedure.
//
return;
} #endif // SUPPORT_MULTIPLE_FILTER_TYPES
BOOLEAN SCCheckIfStreamsRunning( IN PFILTER_INSTANCE FilterInstance ) /*++
Routine Description:
Arguments:
Return Value:
--*/
{
PSTREAM_OBJECT StreamObject; PLIST_ENTRY StreamListEntry, StreamObjectEntry;
//
// process the streams on this list
//
StreamListEntry = StreamObjectEntry = &FilterInstance->FirstStream;
while (StreamObjectEntry->Flink != StreamListEntry) {
StreamObjectEntry = StreamObjectEntry->Flink;
//
// follow the link to the stream
// object
//
StreamObject = CONTAINING_RECORD(StreamObjectEntry, STREAM_OBJECT, NextStream);
if (StreamObject->CurrentState == KSSTATE_RUN) {
return (TRUE);
} // if running
} // while streams
return (FALSE);
}
VOID SCCallBackSrb( IN PSTREAM_REQUEST_BLOCK Srb, IN PDEVICE_EXTENSION DeviceExtension ) /*++
Routine Description:
Arguments:
Return Value:
--*/
{ KIRQL Irql;
if (DeviceExtension->NoSync) {
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
if (Srb->DoNotCallBack) { TRAP; DebugPrint((DebugLevelError, "'ScCallback: NOT calling back request - Irp = %x", Srb->HwSRB.Irp)); KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); return;
} // if NoCallback
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); } // if NoSync
(Srb->Callback) (Srb);
}
#if DBG
VOID SCDebugPriorityWorkItem( IN PDEBUG_WORK_ITEM WorkItemStruct ) /*++
Routine Description:
Arguments:
Return Value:
None
--*/
{ PCOMMON_OBJECT Object = WorkItemStruct->Object; PHW_PRIORITY_ROUTINE Routine = WorkItemStruct->HwPriorityRoutine; PVOID Context = WorkItemStruct->HwPriorityContext;
// DebugPrint((DebugLevelFatal, "F %x\n", WorkItemStruct));
ExFreePool(WorkItemStruct);
Object->PriorityWorkItemScheduled = FALSE;
if (Object->InterruptData.Flags & INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST) {
DebugPrint((DebugLevelFatal, "Stream Minidriver scheduled priority twice!\n")); ASSERT(1 == 0); } // if scheduled twice
Routine(Context);
if (Object->InterruptData.Flags & INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST) {
DebugPrint((DebugLevelFatal, "Stream Minidriver scheduled priority twice!\n")); ASSERT(1 == 0); } // if scheduled twice
}
#endif
PKSPROPERTY_SET SCCopyMinidriverProperties( IN ULONG NumProps, IN PKSPROPERTY_SET MinidriverProps ) /*++
Routine Description:
Makes a private copy of the minidriver's properties
Arguments:
NumProps - number of properties to process MinidriverProps - pointer to the array of properties to process
Return Value:
None.
--*/
{ PKSPROPERTY_ITEM CurrentPropItem; PKSPROPERTY_SET CurrentProp; ULONG i, BufferSize; PVOID NewPropertyBuffer;
#if DBG
ULONG TotalBufferUsed; #endif
PAGED_CODE();
CurrentProp = MinidriverProps; BufferSize = NumProps * sizeof(KSPROPERTY_SET);
//
// walk the minidriver's property sets to determine the size of the
// buffer
// needed. Size computed from # of sets from above, + # of items.
//
for (i = 0; i < NumProps; i++) {
BufferSize += CurrentProp->PropertiesCount * sizeof(KSPROPERTY_ITEM);
//
// index to next property set in
// array
//
CurrentProp++;
} // for number of property sets
if (!(NewPropertyBuffer = ExAllocatePool(NonPagedPool, BufferSize))) {
TRAP; return (NULL); } //
// copy the array of sets over to the 1st part of the buffer.
//
RtlCopyMemory(NewPropertyBuffer, MinidriverProps, sizeof(KSPROPERTY_SET) * NumProps);
//
// walk thru the sets, copying the items for each set, and updating the
// pointer to each item array in each set as we go.
//
CurrentProp = (PKSPROPERTY_SET) NewPropertyBuffer; CurrentPropItem = (PKSPROPERTY_ITEM) ((ULONG_PTR) NewPropertyBuffer + sizeof(KSPROPERTY_SET) * NumProps);
#if DBG
TotalBufferUsed = sizeof(KSPROPERTY_SET) * NumProps; #endif
for (i = 0; i < NumProps; i++) {
RtlCopyMemory(CurrentPropItem, CurrentProp->PropertyItem, CurrentProp->PropertiesCount * sizeof(KSPROPERTY_ITEM));
#if DBG
TotalBufferUsed += CurrentProp->PropertiesCount * sizeof(KSPROPERTY_ITEM); ASSERT(TotalBufferUsed <= BufferSize); #endif
CurrentProp->PropertyItem = CurrentPropItem;
CurrentPropItem += CurrentProp->PropertiesCount; CurrentProp++;
}
return ((PKSPROPERTY_SET) NewPropertyBuffer);
}
PKSEVENT_SET SCCopyMinidriverEvents( IN ULONG NumEvents, IN PKSEVENT_SET MinidriverEvents ) /*++
Routine Description:
Makes a private copy of the minidriver's properties
Arguments:
NumEvents - number of event sets to process MinidriverEvents - pointer to the array of properties to process
Return Value:
None.
--*/
{ PKSEVENT_ITEM CurrentEventItem; PKSEVENT_SET CurrentEvent; ULONG i, BufferSize; PVOID NewEventBuffer;
#if DBG
ULONG TotalBufferUsed; #endif
PAGED_CODE();
CurrentEvent = MinidriverEvents; BufferSize = NumEvents * sizeof(KSEVENT_SET);
//
// walk the minidriver's property sets to determine the size of the
// buffer
// needed. Size computed from # of sets from above, + # of items.
//
for (i = 0; i < NumEvents; i++) {
BufferSize += CurrentEvent->EventsCount * sizeof(KSEVENT_ITEM);
//
// index to next property set in
// array
//
CurrentEvent++;
} // for number of property sets
if (!(NewEventBuffer = ExAllocatePool(NonPagedPool, BufferSize))) {
TRAP; return (NULL); } //
// copy the array of sets over to the 1st part of the buffer.
//
RtlCopyMemory(NewEventBuffer, MinidriverEvents, sizeof(KSEVENT_SET) * NumEvents);
//
// walk thru the sets, copying the items for each set, and updating the
// pointer to each item array in each set as we go.
//
CurrentEvent = (PKSEVENT_SET) NewEventBuffer; CurrentEventItem = (PKSEVENT_ITEM) ((ULONG_PTR) NewEventBuffer + sizeof(KSEVENT_SET) * NumEvents);
#if DBG
TotalBufferUsed = sizeof(KSEVENT_SET) * NumEvents; #endif
for (i = 0; i < NumEvents; i++) {
RtlCopyMemory(CurrentEventItem, CurrentEvent->EventItem, CurrentEvent->EventsCount * sizeof(KSEVENT_ITEM));
#if DBG
TotalBufferUsed += CurrentEvent->EventsCount * sizeof(KSEVENT_ITEM); ASSERT(TotalBufferUsed <= BufferSize); #endif
CurrentEvent->EventItem = CurrentEventItem;
CurrentEventItem += CurrentEvent->EventsCount; CurrentEvent++;
}
return ((PKSEVENT_SET) NewEventBuffer);
}
#ifdef ENABLE_KS_METHODS
PKSMETHOD_SET SCCopyMinidriverMethods( IN ULONG NumMethods, IN PKSMETHOD_SET MinidriverMethods ) /*++
Routine Description:
Makes a private copy of the minidriver's properties
Arguments:
NumMethods - number of properties to process MinidriverMethods - pointer to the array of properties to process
Return Value:
None.
--*/
{ PKSMETHOD_ITEM CurrentMethodItem; PKSMETHOD_SET CurrentMethod; ULONG i, BufferSize; PVOID NewMethodBuffer;
#if DBG
ULONG TotalBufferUsed; #endif
PAGED_CODE();
CurrentMethod = MinidriverMethods; BufferSize = NumMethods * sizeof(KSMETHOD_SET);
//
// walk the minidriver's property sets to determine the size of the
// buffer
// needed. Size computed from # of sets from above, + # of items.
//
for (i = 0; i < NumMethods; i++) {
BufferSize += CurrentMethod->MethodsCount * sizeof(KSMETHOD_ITEM);
//
// index to next property set in
// array
//
CurrentMethod++;
} // for number of property sets
if (!(NewMethodBuffer = ExAllocatePool(NonPagedPool, BufferSize))) {
TRAP; return (NULL); } //
// copy the array of sets over to the 1st part of the buffer.
//
RtlCopyMemory(NewMethodBuffer, MinidriverMethods, sizeof(KSMETHOD_SET) * NumMethods);
//
// walk thru the sets, copying the items for each set, and updating the
// pointer to each item array in each set as we go.
//
CurrentMethod = (PKSMETHOD_SET) NewMethodBuffer; CurrentMethodItem = (PKSMETHOD_ITEM) ((ULONG_PTR) NewMethodBuffer + sizeof(KSMETHOD_SET) * NumMethods);
#if DBG
TotalBufferUsed = sizeof(KSMETHOD_SET) * NumMethods; #endif
for (i = 0; i < NumMethods; i++) {
RtlCopyMemory(CurrentMethodItem, CurrentMethod->MethodItem, CurrentMethod->MethodsCount * sizeof(KSMETHOD_ITEM));
#if DBG
TotalBufferUsed += CurrentMethod->MethodsCount * sizeof(KSMETHOD_ITEM); ASSERT(TotalBufferUsed <= BufferSize); #endif
CurrentMethod->MethodItem = CurrentMethodItem;
CurrentMethodItem += CurrentMethod->MethodsCount; CurrentMethod++;
}
return ((PKSMETHOD_SET) NewMethodBuffer);
}
VOID SCUpdateMinidriverMethods( IN ULONG NumMethods, IN PKSMETHOD_SET MinidriverMethods, IN BOOLEAN Stream ) /*++
Routine Description:
Process method to the device.
Arguments:
NumMethods - number of methods to process MinidriverMethods - pointer to the array of methods to process Stream - TRUE indicates we are processing a set for the stream
Return Value:
None.
--*/
{ PKSMETHOD_ITEM CurrentMethodId; PKSMETHOD_SET CurrentMethod; ULONG i, j;
PAGED_CODE();
//
// walk the minidriver's property info to fill in the dispatch
// vectors as appropriate.
//
CurrentMethod = MinidriverMethods;
for (i = 0; i < NumMethods; i++) {
CurrentMethodId = (PKSMETHOD_ITEM) CurrentMethod->MethodItem;
for (j = 0; j < CurrentMethod->MethodsCount; j++) {
//
// if support handler is supported, send it to the handler
//
if (CurrentMethodId->SupportHandler) {
if (Stream) {
CurrentMethodId->SupportHandler = StreamClassMinidriverStreamMethod;
} else {
CurrentMethodId->SupportHandler = StreamClassMinidriverDeviceMethod; } // if stream
} //
// if method routine is
// supported, add our vector.
//
if (CurrentMethodId->MethodHandler) {
if (Stream) {
CurrentMethodId->MethodHandler = StreamClassMinidriverStreamMethod; } else {
CurrentMethodId->MethodHandler = StreamClassMinidriverDeviceMethod; } // if stream
} // if supported
//
// index to next method item in
// array
//
CurrentMethodId++;
} // for number of property items
//
// index to next method set in
// array
//
CurrentMethod++;
} // for number of method sets
}
NTSTATUS SCMinidriverDeviceMethodHandler( IN SRB_COMMAND Command, IN PIRP Irp, IN PKSMETHOD Method, IN OUT PVOID MethodInfo ) /*++
Routine Description:
Process get/set method to the device.
Arguments:
Command - either GET or SET method Irp - pointer to the IRP Method - pointer to the method structure MethodInfo - buffer for method information
Return Value:
NTSTATUS returned as appropriate.
--*/
{ PIO_STACK_LOCATION IrpStack; PDEVICE_EXTENSION DeviceExtension; PFILTER_INSTANCE FilterInstance; PSTREAM_METHOD_DESCRIPTOR MethodDescriptor; NTSTATUS Status; BOOLEAN RequestIssued;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp); DeviceExtension = (PDEVICE_EXTENSION) (IrpStack->DeviceObject)->DeviceExtension;
FilterInstance = IrpStack->FileObject->FsContext;
MethodDescriptor = ExAllocatePool(NonPagedPool, sizeof(STREAM_METHOD_DESCRIPTOR)); if (MethodDescriptor == NULL) { DEBUG_BREAKPOINT(); DebugPrint((DebugLevelError, "SCDeviceMethodHandler: No pool for descriptor")); return (STATUS_INSUFFICIENT_RESOURCES); } //
// compute the index of the method set.
//
// this value is calculated by subtracting the base method set
// pointer from the requested method set pointer.
//
// The requested method set is pointed to by Context[0] by
// KsMethodHandler.
//
MethodDescriptor->MethodSetID = (ULONG) ((ULONG_PTR) Irp->Tail.Overlay.DriverContext[0] - IFN_MF((ULONG_PTR) DeviceExtension->DeviceMethodsArray) IF_MF((ULONG_PTR) FilterInstance->DeviceMethodsArray) ) / sizeof(KSMETHOD_SET);
MethodDescriptor->Method = Method; MethodDescriptor->MethodInfo = MethodInfo; MethodDescriptor->MethodInputSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; MethodDescriptor->MethodOutputSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
//
// send a get or set method SRB to the device.
//
Status = SCSubmitRequest(Command, MethodDescriptor, 0, SCProcessCompletedMethodRequest, DeviceExtension, FilterInstance->HwInstanceExtension, NULL, Irp, &RequestIssued, &DeviceExtension->PendingQueue, (PVOID) DeviceExtension-> MinidriverData->HwInitData. HwReceivePacket ); if (!RequestIssued) {
DEBUG_BREAKPOINT(); ExFreePool(MethodDescriptor); } return (Status); }
NTSTATUS SCMinidriverStreamMethodHandler( IN SRB_COMMAND Command, IN PIRP Irp, IN PKSMETHOD Method, IN OUT PVOID MethodInfo ) /*++
Routine Description:
Process get or set method to the device.
Arguments:
Command - either GET or SET method Irp - pointer to the IRP Method - pointer to the method structure MethodInfo - buffer for method information
Return Value:
None.
--*/
{ PIO_STACK_LOCATION IrpStack; PDEVICE_EXTENSION DeviceExtension; PSTREAM_OBJECT StreamObject; PSTREAM_METHOD_DESCRIPTOR MethodDescriptor; NTSTATUS Status; BOOLEAN RequestIssued;
PAGED_CODE();
IrpStack = IoGetCurrentIrpStackLocation(Irp); DeviceExtension = (PDEVICE_EXTENSION) (IrpStack->DeviceObject)->DeviceExtension;
StreamObject = IrpStack->FileObject->FsContext;
MethodDescriptor = ExAllocatePool(NonPagedPool, sizeof(STREAM_METHOD_DESCRIPTOR)); if (MethodDescriptor == NULL) { DEBUG_BREAKPOINT(); DebugPrint((DebugLevelError, "SCDeviceMethodHandler: No pool for descriptor")); return (STATUS_INSUFFICIENT_RESOURCES); } //
// compute the index of the method set.
//
// this value is calculated by subtracting the base method set
// pointer from the requested method set pointer.
//
// The requested method set is pointed to by Context[0] by
// KsMethodHandler.
//
MethodDescriptor->MethodSetID = (ULONG) ((ULONG_PTR) Irp->Tail.Overlay.DriverContext[0] - (ULONG_PTR) StreamObject->MethodInfo) / sizeof(KSMETHOD_SET);
MethodDescriptor->Method = Method; MethodDescriptor->MethodInfo = MethodInfo; MethodDescriptor->MethodInputSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; MethodDescriptor->MethodOutputSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; //
// send a get or set method SRB to the stream.
//
Status = SCSubmitRequest(Command, MethodDescriptor, 0, SCProcessCompletedMethodRequest, DeviceExtension, StreamObject->FilterInstance->HwInstanceExtension, &StreamObject->HwStreamObject, Irp, &RequestIssued, &StreamObject->ControlPendingQueue, (PVOID) StreamObject->HwStreamObject. ReceiveControlPacket );
if (!RequestIssued) {
DEBUG_BREAKPOINT(); ExFreePool(MethodDescriptor); } return (Status); }
NTSTATUS SCProcessCompletedMethodRequest( IN PSTREAM_REQUEST_BLOCK SRB ) /*++
Routine Description:
This routine processes a method request which has completed.
Arguments:
SRB- address of completed STREAM request block
Return Value:
None.
--*/
{ PAGED_CODE();
//
// free the method info structure and
// complete the request
//
ExFreePool(SRB->HwSRB.CommandData.MethodInfo);
//
// set the information field from the SRB
// transferlength field
//
SRB->HwSRB.Irp->IoStatus.Information = SRB->HwSRB.ActualBytesTransferred;
return (SCDequeueAndDeleteSrb(SRB));
} #endif // ENABLE_KS_METHODS
|