windows-nt/Source/XPSP1/NT/drivers/wdm/capture/mini/avcstrm/pnp.c
2020-09-26 16:20:57 +08:00

603 lines
19 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
pnp.c
Abstract: NULL filter driver -- boilerplate code
Author:
ervinp
Environment:
Kernel mode
Revision History:
--*/
#include <WDM.H>
#include "filter.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, VA_PnP)
#pragma alloc_text(PAGE, GetDeviceCapabilities)
#ifdef HANDLE_DEVICE_USAGE
#pragma alloc_text(PAGE, VA_DeviceUsageNotification)
#endif // HANDLE_DEVICE_USAGE
#endif
NTSTATUS VA_PnP(struct DEVICE_EXTENSION *devExt, PIRP irp)
/*++
Routine Description:
Dispatch routine for PnP IRPs (MajorFunction == IRP_MJ_PNP)
Arguments:
devExt - device extension for the targetted device object
irp - IO Request Packet
Return Value:
NT status code
--*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN completeIrpHere = FALSE;
BOOLEAN justReturnStatus = FALSE;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation(irp);
TRACE(TL_PNP_TRACE,("VA_PnP, minorFunc = %d \n", (ULONG)irpSp->MinorFunction));
switch (irpSp->MinorFunction){
case IRP_MN_START_DEVICE:
TRACE(TL_PNP_WARNING,("START_DEVICE\n"));
devExt->state = STATE_STARTING;
/*
* First, send the START_DEVICE irp down the stack
* synchronously to start the lower stack.
* We cannot do anything with our device object
* before propagating the START_DEVICE this way.
*/
IoCopyCurrentIrpStackLocationToNext(irp);
status = CallNextDriverSync(devExt, irp);
if (NT_SUCCESS(status)){
/*
* Now that the lower stack is started,
* do any initialization required by this device object.
*/
status = GetDeviceCapabilities(devExt);
if (NT_SUCCESS(status)){
devExt->state = STATE_STARTED;
}
else {
devExt->state = STATE_START_FAILED;
}
}
else {
devExt->state = STATE_START_FAILED;
}
completeIrpHere = TRUE;
break;
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
TRACE(TL_PNP_WARNING,("QUERY_STOP_DEVICE (%d)or QUERY_REMOVE_DEVICE(%d)\n", IRP_MN_QUERY_STOP_DEVICE, IRP_MN_QUERY_REMOVE_DEVICE));
#ifdef HANDLE_DEVICE_USAGE
//
// Need to fail these IRPs if a paging, hibernation, or crashdump
// file is currently open on this device
//
if( devExt->pagingFileCount != 0
|| devExt->hibernationFileCount != 0
|| devExt->crashdumpFileCount != 0 )
{
// Fail the IRP
TRACE(TL_PNP_WARNING,("Failing QUERY_(STOP,REMOVE)_DEVICE request b/c \n"
"paging, hiber, or crashdump file is present on device." ));
status = STATUS_UNSUCCESSFUL;
completeIrpHere = TRUE;
}
else
{
// We'll just pass this IRP down the driver stack. But
// first, must change the IRP's status to STATUS_SUCCESS
// (default is STATUS_NOT_SUPPORTED)
irp->IoStatus.Status = STATUS_SUCCESS;
}
#else
/*
* We will pass this IRP down the driver stack.
* However, we need to change the default status
* from STATUS_NOT_SUPPORTED to STATUS_SUCCESS.
*/
irp->IoStatus.Status = STATUS_SUCCESS;
#endif
break;
case IRP_MN_STOP_DEVICE:
TRACE(TL_PNP_WARNING,("STOP_DEVICE\n"));
if (devExt->state == STATE_SUSPENDED){
status = STATUS_DEVICE_POWER_FAILURE;
completeIrpHere = TRUE;
}
else {
/*
* Only set state to STOPPED if the device was
* previously started successfully.
*/
if (devExt->state == STATE_STARTED){
devExt->state = STATE_STOPPED;
}
}
break;
case IRP_MN_SURPRISE_REMOVAL: // Win2000 code base only
TRACE(TL_PNP_WARNING,("SURPRISE_REMOVAL\n"));
/*
* We will pass this IRP down the driver stack.
* However, we need to change the default status
* from STATUS_NOT_SUPPORTED to STATUS_SUCCESS.
*/
irp->IoStatus.Status = STATUS_SUCCESS;
/*
* For now just set the STATE_REMOVING state so that
* we don't do any more IO. We are guaranteed to get
* IRP_MN_REMOVE_DEVICE soon; we'll do the rest of
* the remove processing there.
*/
devExt->state = STATE_REMOVING;
/*
* Clean up and make sure to cancel pending request
*/
AVCStreamSurpriseRemoval(devExt);
break;
case IRP_MN_REMOVE_DEVICE:
/*
* Check the current state to guard against multiple
* REMOVE_DEVICE IRPs.
*/
TRACE(TL_PNP_WARNING,("REMOVE_DEVICE\n"));
if (devExt->state != STATE_REMOVED){
devExt->state = STATE_REMOVED;
/*
* Clean up and make sure to cancel pending request
* Note: there is no IRP_MN_SURPRISE_REMOVAL for Win9X
*/
AVCStreamSurpriseRemoval(devExt);
/*
* Send the REMOVE IRP down the stack asynchronously.
* Do not synchronize sending down the REMOVE_DEVICE
* IRP, because the REMOVE_DEVICE IRP must be sent
* down and completed all the way back up to the sender
* before we continue.
*/
IoCopyCurrentIrpStackLocationToNext(irp);
status = IoCallDriver(devExt->topDevObj, irp);
justReturnStatus = TRUE;
TRACE(TL_PNP_WARNING,("REMOVE_DEVICE - waiting for %d irps to complete...\n",
devExt->pendingActionCount));
/*
* We must for all outstanding IO to complete before
* completing the REMOVE_DEVICE IRP.
*
* First do an extra decrement on the pendingActionCount.
* This will cause pendingActionCount to eventually
* go to -1 once all asynchronous actions on this
* device object are complete.
* Then wait on the event that gets set when the
* pendingActionCount actually reaches -1.
*/
DecrementPendingActionCount(devExt);
KeWaitForSingleObject( &devExt->removeEvent,
Executive, // wait reason
KernelMode,
FALSE, // not alertable
NULL ); // no timeout
TRACE(TL_PNP_WARNING,("REMOVE_DEVICE - ... DONE waiting. \n"));
#ifdef HANDLE_DEVICE_USAGE
/*
* If we locked-down certain paged code sections earlier
* because of this device, then need to unlock them now
* (before calling IoDeleteDevice)
*/
if( NULL != devExt->pagingPathUnlockHandle )
{
TRACE(TL_PNP_WARNING,("UNLOCKing some driver code (non-pageable) (b/c paging path)\n" ));
MmUnlockPagableImageSection( devExt->pagingPathUnlockHandle );
devExt->pagingPathUnlockHandle = NULL;
}
if( NULL != devExt->initUnlockHandle )
{
TRACE(TL_PNP_WARNING,("UNLOCKing some driver code (non-pageable) (b/c init conditions)\n" ));
MmUnlockPagableImageSection( devExt->initUnlockHandle );
devExt->initUnlockHandle = NULL;
}
#endif // HANDLE_DEVICE_USAGE
/*
* Detach our device object from the lower
* device object stack.
*/
IoDetachDevice(devExt->topDevObj);
/*
* Delete our device object.
* This will also delete the associated device extension.
*/
IoDeleteDevice(devExt->filterDevObj);
}
break;
#ifdef HANDLE_DEVICE_USAGE
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
//
// Make sure the Type of this UsageNotification is one that we handle
//
if( irpSp->Parameters.UsageNotification.Type != DeviceUsageTypePaging
&& irpSp->Parameters.UsageNotification.Type != DeviceUsageTypeHibernation
&& irpSp->Parameters.UsageNotification.Type != DeviceUsageTypeDumpFile )
{
break; // out of the big switch statement (and just forward this IRP)
}
status = VA_DeviceUsageNotification(devExt, irp);
justReturnStatus = TRUE;
break;
#endif // HANDLE_DEVICE_USAGE
#ifdef HANDLE_DEVICE_USAGE
case IRP_MN_QUERY_PNP_DEVICE_STATE:
//
// If a paging, hibernation, or crashdump file is currently open
// on this device, must set NOT_DISABLEABLE flag in DeviceState
//
if( devExt->pagingFileCount != 0
|| devExt->hibernationFileCount != 0
|| devExt->crashdumpFileCount != 0 )
{
// Mark the device as not disableable
PPNP_DEVICE_STATE pDeviceState;
pDeviceState = (PPNP_DEVICE_STATE) &irp->IoStatus.Information;
*pDeviceState |= PNP_DEVICE_NOT_DISABLEABLE;
}
//
// We _did_ handle this IRP (as best we could), so set IRP's
// status to STATUS_SUCCESS (default is STATUS_NOT_SUPPORTED)
// before passing it down the driver stack
//
irp->IoStatus.Status = STATUS_SUCCESS;
break;
#endif // HANDLE_DEVICE_USAGE
case IRP_MN_QUERY_DEVICE_RELATIONS:
TRACE(TL_PNP_WARNING,("QUERY_DEVICE_RELATIONS\n"));
break;
default:
TRACE(TL_PNP_WARNING,("Unprocessed PnP minorFunc (%d)\n", irpSp->MinorFunction));
break;
}
if (justReturnStatus){
/*
* We've already sent this IRP down the stack.
*/
TRACE(TL_PNP_WARNING,("VA_PnP: St:%x; minor:%d; Already sent down the irp.\n", status, (ULONG)irpSp->MinorFunction));
}
else if (completeIrpHere){
TRACE(TL_PNP_WARNING,("VA_PnP: St:%x; minor:%d; Completed Status:%x\n", status, (ULONG)irpSp->MinorFunction, status));
irp->IoStatus.Status = status;
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
else {
TRACE(TL_PNP_WARNING,("VA_PnP: ST:%x; minor:%d; Pass down irp:%x; devObj:%x\n", status, (ULONG)irpSp->MinorFunction, irp, devExt->topDevObj));
IoCopyCurrentIrpStackLocationToNext(irp);
status = IoCallDriver(devExt->topDevObj, irp);
}
EXIT("VA_PnP",status);
return status;
}
#ifdef HANDLE_DEVICE_USAGE
NTSTATUS
VA_DeviceUsageNotification(struct DEVICE_EXTENSION *devExt, PIRP irp)
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
BOOLEAN fSetPagable = FALSE; // whether we set the PAGABLE bit
/// before we passed-on this IRP
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation(irp);
TRACE(TL_PNP_WARNING,("DEVICE_USAGE_NOTIFICATION (Type==%d , InPath==%d)\n"
, irpSp->Parameters.UsageNotification.Type
, irpSp->Parameters.UsageNotification.InPath
));
TRACE(TL_PNP_WARNING,(" [devExt=0x%08X fltrDevObj=0x%08X]\n", devExt, devExt->filterDevObj ));
//
// Wait on the paging path event (to prevent several instances of
// this IRP from being processed at once)
//
status = KeWaitForSingleObject( &devExt->deviceUsageNotificationEvent
, Executive // wait reason
, KernelMode
, FALSE // not alertable
, NULL // no timeout
);
/*
* IMPORTANT NOTE: When to modify our DO_POWER_PAGABLE bit depends
* on whether it needs to be set or cleared. If the IRP indicates
* our PAGABLE bit should be set, then we must set it _before_
* forwarding the IRP down the driver stack (and possibly clear it
* afterward, if lower drivers fail the IRP). But if the IRP
* indicates that our PAGABLE bit should be cleared, then we must
* first forward the IRP to lower drivers, and then clear our bit
* only if the lower drivers return STATUS_SUCCESS.
*/
//
// If removing last paging file from this device...
//
if( irpSp->Parameters.UsageNotification.Type == DeviceUsageTypePaging
&& !irpSp->Parameters.UsageNotification.InPath
&& devExt->pagingFileCount == 1 )
{
//
// Set DO_POWER_PAGABLE bit (if it was set at startup).
// If lower drivers fail this IRP, we'll clear it later.
//
TRACE(TL_PNP_WARNING,("Removing last paging file...\n" ));
if( devExt->initialFlags & DO_POWER_PAGABLE )
{
TRACE(TL_PNP_WARNING,( "...so RE-setting PAGABLE bit\n" ));
devExt->filterDevObj->Flags |= DO_POWER_PAGABLE;
fSetPagable = TRUE;
}
else
{
TRACE(TL_PNP_WARNING,( "...but PAGABLE bit wasn't set initially, so not setting it now.\n" ));
}
}
//
// Forward the irp synchronously
//
IoCopyCurrentIrpStackLocationToNext( irp );
status = CallNextDriverSync( devExt, irp );
//
// Now deal with the failure and success cases.
//
if( ! NT_SUCCESS(status) )
{
//
// Lower drivers failed the IRP, so _undo_ any changes we
// made before passing-on the IRP to those drivers.
//
if( fSetPagable )
{
TRACE(TL_PNP_WARNING,("IRP was failed, so UN-setting PAGABLE bit\n" ));
devExt->filterDevObj->Flags &= ~DO_POWER_PAGABLE;
}
}
else
{
//
// Lower drivers returned SUCCESS, so we can do everything
// that must be done in response to this IRP...
//
switch( irpSp->Parameters.UsageNotification.Type )
{
case DeviceUsageTypeHibernation:
// Adjust counter
IoAdjustPagingPathCount( &devExt->hibernationFileCount,
irpSp->Parameters.UsageNotification.InPath );
TRACE(TL_PNP_WARNING,("Num. Hibernation files is now %d\n", devExt->hibernationFileCount ));
ASSERT( devExt->hibernationFileCount >= 0 );
break;
case DeviceUsageTypeDumpFile:
// Adjust counter
IoAdjustPagingPathCount( &devExt->crashdumpFileCount,
irpSp->Parameters.UsageNotification.InPath );
TRACE(TL_PNP_WARNING,("Num. Crashdump files is now %d\n", devExt->crashdumpFileCount ));
ASSERT( devExt->crashdumpFileCount >= 0 );
break;
case DeviceUsageTypePaging:
// Adjust counter
IoAdjustPagingPathCount( &devExt->pagingFileCount,
irpSp->Parameters.UsageNotification.InPath );
TRACE(TL_PNP_WARNING,("Num. Paging files is now %d\n", devExt->pagingFileCount ));
ASSERT( devExt->pagingFileCount >= 0 );
//
// If we've just switched between being pageable<->nonpageable...
//
if( irpSp->Parameters.UsageNotification.InPath
&& devExt->pagingFileCount == 1 )
{
//
// Just added a paging file, so clear the PAGABLE
// flag, and lock-down the code for all routines
// that could be called at IRQL >= DISPATCH_LEVEL
// (so that they're _non-pageable_).
//
TRACE(TL_PNP_WARNING,("Just added first paging file...\n" ));
TRACE(TL_PNP_WARNING,("...so clearing PAGABLE bit\n" ));
devExt->filterDevObj->Flags &= ~DO_POWER_PAGABLE;
TRACE(TL_PNP_WARNING,("LOCKing some driver code (non-pageable) (b/c paging path)\n" ));
devExt->pagingPathUnlockHandle = MmLockPagableCodeSection( VA_Power ); // some func that's inside the code section that we want to lock
ASSERT( NULL != devExt->pagingPathUnlockHandle );
}
else if ( !irpSp->Parameters.UsageNotification.InPath
&& devExt->pagingFileCount == 0 )
{
//
// Just removed the last paging file, but we
// already set the PAGABLE flag (if necessary)
// before forwarding IRP, so just remove the
// _paging-path_ lock from this driver. (NOTE:
// initial-condition lock might still be in place,
// but that's what we want.)
//
TRACE(TL_PNP_WARNING,("UNLOCKing some driver code (pageable) (b/c paging path)\n" ));
ASSERT( NULL != devExt->pagingPathUnlockHandle );
MmUnlockPagableImageSection( devExt->pagingPathUnlockHandle );
devExt->pagingPathUnlockHandle = NULL;
}
break;
default:
ASSERT( FALSE ); // should never get here (b/c checked for invalid Type earlier)
} //END: switch on Type of special-file
//
// Invalidate state, so that certain flags will get updated
//
IoInvalidateDeviceState( devExt->physicalDevObj );
}//END: handling of irp success/failure cases
//
// Set event so that the next DEVICE_USAGE_NOTIFICATION IRP that
// comes along can be processed.
//
KeSetEvent( &devExt->deviceUsageNotificationEvent
, IO_NO_INCREMENT
, FALSE
);
//
// Complete the irp
//
IoCompleteRequest( irp, IO_NO_INCREMENT );
return status;
}
#endif // HANDLE_DEVICE_USAGE
NTSTATUS GetDeviceCapabilities(struct DEVICE_EXTENSION *devExt)
/*++
Routine Description:
Function retrieves the DEVICE_CAPABILITIES descriptor from the device
Arguments:
devExt - device extension for targetted device object
Return Value:
NT status code
--*/
{
NTSTATUS status;
PIRP irp;
PAGED_CODE();
irp = IoAllocateIrp(devExt->topDevObj->StackSize, FALSE);
if (irp){
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp);
// must initialize DeviceCapabilities before sending...
RtlZeroMemory( &devExt->deviceCapabilities,
sizeof(DEVICE_CAPABILITIES));
devExt->deviceCapabilities.Size = sizeof(DEVICE_CAPABILITIES);
devExt->deviceCapabilities.Version = 1;
devExt->deviceCapabilities.Address = -1;
devExt->deviceCapabilities.UINumber= -1;
// setup irp stack location...
nextSp->MajorFunction = IRP_MJ_PNP;
nextSp->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
nextSp->Parameters.DeviceCapabilities.Capabilities =
&devExt->deviceCapabilities;
/*
* For any IRP you create, you must set the default status
* to STATUS_NOT_SUPPORTED before sending it.
*/
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
status = CallNextDriverSync(devExt, irp);
IoFreeIrp(irp);
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
ASSERT(NT_SUCCESS(status));
return status;
}