/* ++ * * 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 #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