621 lines
17 KiB
C
621 lines
17 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1998 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
pnp.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the code for a serial imaging devices driver
|
|||
|
supporting PnP functionality
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Vlad Sadovsky vlads 10-April-1998
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History :
|
|||
|
|
|||
|
vlads 04/10/1998 Created first draft
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "serscan.h"
|
|||
|
#include "serlog.h"
|
|||
|
|
|||
|
//#include <ntpoapi.h>
|
|||
|
|
|||
|
extern ULONG SerScanDebugLevel;
|
|||
|
|
|||
|
extern const PHYSICAL_ADDRESS PhysicalZero ;
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, SerScanPnp)
|
|||
|
#pragma alloc_text(PAGE, SerScanPower)
|
|||
|
#endif
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SerScanPnp (
|
|||
|
IN PDEVICE_OBJECT pDeviceObject,
|
|||
|
IN PIRP pIrp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine handles all PNP IRPs, dispatching them as appropriate .
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceObject - represents a device
|
|||
|
|
|||
|
pIrp - PNP Irp
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - if successful.
|
|||
|
STATUS_UNSUCCESSFUL - otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status ;
|
|||
|
PDEVICE_EXTENSION Extension;
|
|||
|
PIO_STACK_LOCATION pIrpStack;
|
|||
|
PVOID pObject;
|
|||
|
ULONG NewReferenceCount;
|
|||
|
NTSTATUS ReturnStatus;
|
|||
|
|
|||
|
pIrpStack = IoGetCurrentIrpStackLocation( pIrp );
|
|||
|
|
|||
|
Extension = pDeviceObject->DeviceExtension;
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
DebugDump(SERINITDEV,("Entering PnP Dispatcher\n"));
|
|||
|
|
|||
|
switch (pIrpStack->MinorFunction) {
|
|||
|
|
|||
|
case IRP_MN_START_DEVICE:
|
|||
|
|
|||
|
//
|
|||
|
// Initialize PendingIoEvent. Set the number of pending i/o requests for this device to 1.
|
|||
|
// When this number falls to zero, it is okay to remove, or stop the device.
|
|||
|
//
|
|||
|
|
|||
|
DebugDump(SERINITDEV,("Entering Start Device \n"));
|
|||
|
|
|||
|
KeInitializeEvent(&Extension -> PdoStartEvent, SynchronizationEvent, FALSE);
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext(pIrp);
|
|||
|
|
|||
|
Status = WaitForLowerDriverToCompleteIrp(
|
|||
|
Extension->LowerDevice,
|
|||
|
pIrp,
|
|||
|
&Extension->PdoStartEvent);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
pIrp->IoStatus.Status = Status;
|
|||
|
pIrp->IoStatus.Information = 0;
|
|||
|
|
|||
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|||
|
return (Status);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#ifdef CREATE_SYMBOLIC_NAME
|
|||
|
|
|||
|
//
|
|||
|
// Now setup the symbolic link for windows.
|
|||
|
//
|
|||
|
|
|||
|
Status = IoCreateUnprotectedSymbolicLink(&Extension->SymbolicLinkName, &Extension->ClassName);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
// We were able to create the symbolic link, so record this
|
|||
|
// value in the extension for cleanup at unload time.
|
|||
|
|
|||
|
Extension->CreatedSymbolicLink = TRUE;
|
|||
|
|
|||
|
// Write out the result of the symbolic link to the registry.
|
|||
|
|
|||
|
Status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
|
|||
|
L"Serial Scanners",
|
|||
|
Extension->ClassName.Buffer,
|
|||
|
REG_SZ,
|
|||
|
Extension->SymbolicLinkName.Buffer,
|
|||
|
Extension->SymbolicLinkName.Length + sizeof(WCHAR));
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
//
|
|||
|
// It didn't work. Just go to cleanup.
|
|||
|
//
|
|||
|
|
|||
|
DebugDump(SERERRORS,
|
|||
|
("SerScan: Couldn't create the device map entry\n"
|
|||
|
"-------- for port %wZ\n",
|
|||
|
&Extension->ClassName));
|
|||
|
|
|||
|
SerScanLogError(pDeviceObject->DriverObject,
|
|||
|
pDeviceObject,
|
|||
|
PhysicalZero,
|
|||
|
PhysicalZero,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
6,
|
|||
|
Status,
|
|||
|
SER_NO_DEVICE_MAP_CREATED);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Couldn't create the symbolic link.
|
|||
|
//
|
|||
|
|
|||
|
Extension->CreatedSymbolicLink = FALSE;
|
|||
|
|
|||
|
ExFreePool(Extension->SymbolicLinkName.Buffer);
|
|||
|
Extension->SymbolicLinkName.Buffer = NULL;
|
|||
|
|
|||
|
DebugDump(SERERRORS,
|
|||
|
("SerScan: Couldn't create the symbolic link\n"
|
|||
|
"-------- for port %wZ\n",
|
|||
|
&Extension->ClassName));
|
|||
|
|
|||
|
SerScanLogError(pDeviceObject->DriverObject,
|
|||
|
pDeviceObject,
|
|||
|
PhysicalZero,
|
|||
|
PhysicalZero,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
5,
|
|||
|
Status,
|
|||
|
SER_NO_SYMLINK_CREATED);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
ExFreePool(Extension->ClassName.Buffer);
|
|||
|
Extension->ClassName.Buffer = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Ignore status of link registry write - always succeed
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Clear InInit flag to indicate device object can be used
|
|||
|
//
|
|||
|
pDeviceObject->Flags &= ~(DO_DEVICE_INITIALIZING);
|
|||
|
|
|||
|
pIrp->IoStatus.Status = Status;
|
|||
|
pIrp->IoStatus.Information = 0;
|
|||
|
|
|||
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|||
|
return (Status);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|||
|
//
|
|||
|
// Always pass to lower device in stack after indicating that we don't object
|
|||
|
//
|
|||
|
DebugDump(SERALWAYS,("IRP_MN_QUERY_REMOVE_DEVICE\n"));
|
|||
|
|
|||
|
Extension->Removing = TRUE;
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext( pIrp );
|
|||
|
return (IoCallDriver(Extension->LowerDevice, pIrp));
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|||
|
//
|
|||
|
// Always pass to lower device in stack , reset indicator as somebody canceled
|
|||
|
//
|
|||
|
DebugDump(SERALWAYS,("IRP_MN_CANCEL_REMOVE_DEVICE\n"));
|
|||
|
|
|||
|
Extension->Removing = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Kill symbolic link
|
|||
|
//
|
|||
|
if (Extension->CreatedSymbolicLink) {
|
|||
|
IoDeleteSymbolicLink(&Extension->SymbolicLinkName);
|
|||
|
Extension->CreatedSymbolicLink = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext( pIrp );
|
|||
|
return (IoCallDriver(Extension->LowerDevice, pIrp));
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case IRP_MN_SURPRISE_REMOVAL:
|
|||
|
//
|
|||
|
// Should not ever happen with us, but still process
|
|||
|
//
|
|||
|
|
|||
|
DebugDump(SERALWAYS,("IRP_MN_SURPRISE_REMOVAL\n"));
|
|||
|
|
|||
|
Extension->Removing = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Get rid of the symbolic link
|
|||
|
//
|
|||
|
SerScanHandleSymbolicLink(
|
|||
|
Extension->Pdo,
|
|||
|
&Extension->InterfaceNameString,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
#ifdef USE_EXECUTIVE_RESOURCE
|
|||
|
ExAcquireResourceExclusiveLite(
|
|||
|
&Extension->Resource,
|
|||
|
TRUE
|
|||
|
);
|
|||
|
#else
|
|||
|
ExAcquireFastMutex(&Extension->Mutex);
|
|||
|
#endif
|
|||
|
|
|||
|
pObject = InterlockedExchangePointer(&Extension->AttachedFileObject,NULL);
|
|||
|
if (pObject) {
|
|||
|
ObDereferenceObject(pObject);
|
|||
|
}
|
|||
|
|
|||
|
pObject = InterlockedExchangePointer(&Extension->AttachedDeviceObject,NULL);
|
|||
|
if (pObject) {
|
|||
|
ObDereferenceObject(pObject);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef USE_EXECUTIVE_RESOURCE
|
|||
|
ExReleaseResourceLite(&Extension->Resource);
|
|||
|
#else
|
|||
|
ExReleaseFastMutex(&Extension->Mutex);
|
|||
|
#endif
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext( pIrp );
|
|||
|
return (IoCallDriver(Extension->LowerDevice, pIrp));
|
|||
|
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_REMOVE_DEVICE:
|
|||
|
|
|||
|
DebugDump(SERALWAYS,("IRP_MN_REMOVE_DEVICE\n"));
|
|||
|
|
|||
|
DebugDump(SERINITDEV,("Entering PnP Remove Device\n"));
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Stop new requests - device is being removed
|
|||
|
//
|
|||
|
Extension->Removing = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Get rid of the symbolic link
|
|||
|
//
|
|||
|
SerScanHandleSymbolicLink(
|
|||
|
Extension->Pdo,
|
|||
|
&Extension->InterfaceNameString,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
#ifdef USE_EXECUTIVE_RESOURCE
|
|||
|
ExAcquireResourceExclusiveLite(
|
|||
|
&Extension->Resource,
|
|||
|
TRUE
|
|||
|
);
|
|||
|
#else
|
|||
|
ExAcquireFastMutex(&Extension->Mutex);
|
|||
|
#endif
|
|||
|
|
|||
|
pObject = InterlockedExchangePointer(&Extension->AttachedFileObject,NULL);
|
|||
|
if (pObject) {
|
|||
|
ObDereferenceObject(pObject);
|
|||
|
}
|
|||
|
|
|||
|
pObject = InterlockedExchangePointer(&Extension->AttachedDeviceObject,NULL);
|
|||
|
if (pObject) {
|
|||
|
ObDereferenceObject(pObject);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef USE_EXECUTIVE_RESOURCE
|
|||
|
ExReleaseResourceLite(&Extension->Resource);
|
|||
|
#else
|
|||
|
ExReleaseFastMutex(&Extension->Mutex);
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Send IRP down to lower device
|
|||
|
//
|
|||
|
IoCopyCurrentIrpStackLocationToNext( pIrp );
|
|||
|
ReturnStatus = IoCallDriver(Extension->LowerDevice, pIrp);
|
|||
|
|
|||
|
//
|
|||
|
// Decrement ref count
|
|||
|
//
|
|||
|
NewReferenceCount = InterlockedDecrement(&Extension->ReferenceCount);
|
|||
|
|
|||
|
if (NewReferenceCount != 0) {
|
|||
|
//
|
|||
|
// Wait for any io requests pending in our driver to
|
|||
|
// complete before finishing the remove
|
|||
|
//
|
|||
|
KeWaitForSingleObject(&Extension -> RemoveEvent,
|
|||
|
Suspended,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
|
|||
|
// ASSERT(&Extension->ReferenceCount == 0);
|
|||
|
#ifdef USE_EXECUTIVE_RESOURCE
|
|||
|
ExDeleteResourceLite(&Extension->Resource);
|
|||
|
#endif
|
|||
|
|
|||
|
DebugDump(SERALWAYS,("IRP_MN_QUERY_REMOVE_DEVICE - Calling IoDeleteDevice - gone\n"));
|
|||
|
|
|||
|
IoDetachDevice(Extension->LowerDevice);
|
|||
|
|
|||
|
//
|
|||
|
// Free allocated resource.
|
|||
|
//
|
|||
|
|
|||
|
if(NULL != Extension->ClassName.Buffer){
|
|||
|
ExFreePool(Extension->ClassName.Buffer);
|
|||
|
} // if(NULL != Extension->ClassName.Buffer)
|
|||
|
|
|||
|
if(NULL != Extension->SymbolicLinkName.Buffer){
|
|||
|
ExFreePool(Extension->SymbolicLinkName.Buffer);
|
|||
|
} // if(NULL != Extension->SymbolicLinkName.Buffer)
|
|||
|
|
|||
|
IoDeleteDevice(pDeviceObject);
|
|||
|
|
|||
|
return ReturnStatus;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_STOP_DEVICE:
|
|||
|
//
|
|||
|
// Pass down
|
|||
|
//
|
|||
|
DebugDump(SERALWAYS,("IRP_MN_STOP_DEVICE\n"));
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext( pIrp );
|
|||
|
return (IoCallDriver(Extension->LowerDevice, pIrp));
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|||
|
//
|
|||
|
// Check open counts
|
|||
|
//
|
|||
|
DebugDump(SERALWAYS,("IRP_MN_QUERY_STOP_DEVICE\n"));
|
|||
|
|
|||
|
if (Extension->OpenCount > 0 ) {
|
|||
|
DebugDump(SERALWAYS,("Rejecting QUERY_STOP_DEVICE\n"));
|
|||
|
|
|||
|
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|||
|
|
|||
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext( pIrp );
|
|||
|
return (IoCallDriver(Extension->LowerDevice, pIrp));
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|||
|
//
|
|||
|
// Nothing to do here, but pass to lower
|
|||
|
//
|
|||
|
DebugDump(SERALWAYS,("IRP_MN_CANCEL_STOP_DEVICE\n"));
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext( pIrp );
|
|||
|
return (IoCallDriver(Extension->LowerDevice, pIrp));
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case IRP_MN_QUERY_CAPABILITIES:
|
|||
|
{
|
|||
|
|
|||
|
ULONG i;
|
|||
|
KEVENT WaitEvent;
|
|||
|
|
|||
|
//
|
|||
|
// Send this down to the PDO first
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent(&WaitEvent, SynchronizationEvent, FALSE);
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext(pIrp);
|
|||
|
|
|||
|
Status=WaitForLowerDriverToCompleteIrp(
|
|||
|
Extension->LowerDevice,
|
|||
|
pIrp,
|
|||
|
&WaitEvent
|
|||
|
);
|
|||
|
|
|||
|
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
|
|||
|
|
|||
|
for (i = PowerSystemUnspecified; i < PowerSystemMaximum; i++) {
|
|||
|
|
|||
|
Extension->SystemPowerStateMap[i]=PowerDeviceD3;
|
|||
|
}
|
|||
|
|
|||
|
for (i = PowerSystemUnspecified; i < PowerSystemHibernate; i++) {
|
|||
|
|
|||
|
Extension->SystemPowerStateMap[i]=pIrpStack->Parameters.DeviceCapabilities.Capabilities->DeviceState[i];
|
|||
|
}
|
|||
|
|
|||
|
Extension->SystemPowerStateMap[PowerSystemWorking]=PowerDeviceD0;
|
|||
|
|
|||
|
Extension->SystemWake=pIrpStack->Parameters.DeviceCapabilities.Capabilities->SystemWake;
|
|||
|
Extension->DeviceWake=pIrpStack->Parameters.DeviceCapabilities.Capabilities->DeviceWake;
|
|||
|
|
|||
|
IoCompleteRequest(
|
|||
|
pIrp,
|
|||
|
IO_NO_INCREMENT
|
|||
|
);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
//
|
|||
|
// Unknown function - pass down
|
|||
|
//
|
|||
|
DebugDump(SERALWAYS,("Passing Pnp Irp down. MnFunc=%x , status = %x\n",pIrpStack->MinorFunction, Status));
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext( pIrp );
|
|||
|
return (IoCallDriver(Extension->LowerDevice, pIrp));
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Complete the IRP...
|
|||
|
//
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
pIrp -> IoStatus.Status = Status;
|
|||
|
pIrp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
DebugDump(SERALWAYS,("Passing Pnp Irp down, status = %x\n", Status));
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext(pIrp);
|
|||
|
Status = IoCallDriver(Extension->LowerDevice, pIrp);
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DevicePowerCompleteRoutine(
|
|||
|
PDEVICE_OBJECT DeviceObject,
|
|||
|
IN UCHAR MinorFunction,
|
|||
|
IN POWER_STATE PowerState,
|
|||
|
IN PVOID Context,
|
|||
|
IN PIO_STATUS_BLOCK IoStatus
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SerScanPower(
|
|||
|
IN PDEVICE_OBJECT pDeviceObject,
|
|||
|
IN PIRP pIrp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Process the Power IRPs sent to the PDO for this device.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceObject - pointer to the functional device object (FDO) for this device.
|
|||
|
pIrp - pointer to an I/O Request Packet
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NT status code
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PDEVICE_EXTENSION Extension = pDeviceObject->DeviceExtension;
|
|||
|
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
|
|||
|
POWER_STATE PowerState;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
switch (pIrpStack->MinorFunction) {
|
|||
|
|
|||
|
case IRP_MN_SET_POWER:
|
|||
|
|
|||
|
if (pIrpStack->Parameters.Power.Type == SystemPowerState) {
|
|||
|
//
|
|||
|
// system power state change
|
|||
|
//
|
|||
|
//
|
|||
|
// request the change in device power state based on systemstate map
|
|||
|
//
|
|||
|
PowerState.DeviceState=Extension->SystemPowerStateMap[pIrpStack->Parameters.Power.State.SystemState];
|
|||
|
|
|||
|
PoRequestPowerIrp(
|
|||
|
Extension->Pdo,
|
|||
|
IRP_MN_SET_POWER,
|
|||
|
PowerState,
|
|||
|
DevicePowerCompleteRoutine,
|
|||
|
pIrp,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// changing device state
|
|||
|
//
|
|||
|
PoSetPowerState(
|
|||
|
Extension->Pdo,
|
|||
|
pIrpStack->Parameters.Power.Type,
|
|||
|
pIrpStack->Parameters.Power.State
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_POWER:
|
|||
|
|
|||
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
PoStartNextPowerIrp(pIrp);
|
|||
|
|
|||
|
IoSkipCurrentIrpStackLocation(pIrp);
|
|||
|
|
|||
|
Status=PoCallDriver(Extension->LowerDevice, pIrp);
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|