/*++ Copyright (c) 1996 Microsoft Corporation Module Name: pnp.c Abstract: NULL filter driver -- boilerplate code Author: ervinp Environment: Kernel mode Revision History: --*/ #include #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; }