1054 lines
31 KiB
C
1054 lines
31 KiB
C
|
#include "pch.h"
|
|||
|
|
|||
|
VOID
|
|||
|
PptDellNationalPC87364WorkAround( PUCHAR EcpController )
|
|||
|
{
|
|||
|
PUCHAR ecr = EcpController+2; // generic chipset Extended Control Register
|
|||
|
PUCHAR eir = EcpController+3; // PC87364 chipset Extended Index Register
|
|||
|
PUCHAR edr = EcpController+4; // PC87364 chipset Extended Data Register
|
|||
|
ULONG delay = 5; // in microseconds (arbitrary - this seems to work)
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to prevent BIOS from touching the registers at the
|
|||
|
// same time that we're updating them. This is a complete hack
|
|||
|
// since according to PnP we own the registers, but do it anyway
|
|||
|
// since we know that BIOS touches these same registers.
|
|||
|
//
|
|||
|
KeRaiseIrql( HIGH_LEVEL, &oldIrql );
|
|||
|
|
|||
|
KeStallExecutionProcessor( delay );
|
|||
|
P5WritePortUchar( ecr, 0x15 );
|
|||
|
KeStallExecutionProcessor( delay );
|
|||
|
P5WritePortUchar( eir, 0x02 );
|
|||
|
KeStallExecutionProcessor( delay );
|
|||
|
P5WritePortUchar( edr, 0x90 );
|
|||
|
KeStallExecutionProcessor( delay );
|
|||
|
|
|||
|
KeLowerIrql( oldIrql );
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoStartDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_START IRPs.
|
|||
|
|
|||
|
- Wait for the bus driver and any drivers beneath
|
|||
|
us in the driver stack to handle this first.
|
|||
|
- Get, validate, and save the resources given to us by PnP.
|
|||
|
- Assign IDs to and get a count of 1284.3 daisy chain devices
|
|||
|
connected to the port.
|
|||
|
- Determine the capabilities of the chipset (BYTE, EPP, ECP).
|
|||
|
- Set our PnP device interface state to trigger
|
|||
|
an interface arrival callback to anyone listening
|
|||
|
on our GUID.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - on success,
|
|||
|
an appropriate error status - otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN foundPort = FALSE;
|
|||
|
BOOLEAN foundIrq = FALSE;
|
|||
|
BOOLEAN foundDma = FALSE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// This IRP must be handled first by the parent bus driver
|
|||
|
// and then by each higher driver in the device stack.
|
|||
|
//
|
|||
|
status = PptPnpBounceAndCatchPnpIrp(fdx, Irp);
|
|||
|
if( !NT_SUCCESS( status ) && ( status != STATUS_NOT_SUPPORTED ) ) {
|
|||
|
// Someone below us in the driver stack explicitly failed the START.
|
|||
|
goto targetExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Extract resources from CM_RESOURCE_LIST and save them in our extension.
|
|||
|
//
|
|||
|
status = PptPnpStartScanCmResourceList(fdx, Irp, &foundPort, &foundIrq, &foundDma);
|
|||
|
if( !NT_SUCCESS( status ) ) {
|
|||
|
goto targetExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do our resources appear to be valid?
|
|||
|
//
|
|||
|
status = PptPnpStartValidateResources(DeviceObject, foundPort, foundIrq, foundDma);
|
|||
|
if( !NT_SUCCESS( status ) ) {
|
|||
|
goto targetExit;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Check if ACPI set a flag for us based on entries in
|
|||
|
// BIOSINFO.INF to indicate that we are running on a Dell machine
|
|||
|
// with an incorrectly programmed National PC87364 SuperIO
|
|||
|
// chipset. If so try to work around the problem here so that the
|
|||
|
// user doesn't need to flash the BIOS to get the parallel port to
|
|||
|
// work.
|
|||
|
//
|
|||
|
// Symptoms of the problem are that the parallel port Data Lines
|
|||
|
// are wedged to all zeros regardless of the setting of the bits
|
|||
|
// in the parallel port data register or the Direction bit in the
|
|||
|
// control register.
|
|||
|
//
|
|||
|
// If the port base address is 0x3BC then this won't work and the
|
|||
|
// user will need to go to Device Manager and change the LPT port
|
|||
|
// resource settings to either 0x378 or 0x278 for the base
|
|||
|
// register address. We believe that ACPI defaults to a port base
|
|||
|
// address of 0x378 so this workaround should generally work.
|
|||
|
//
|
|||
|
{
|
|||
|
ULONG DellNationalPC87364 = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Check registry to see if ACPI set the flag based on
|
|||
|
// BIOSINFO.INF to indicate that we should try the workaround.
|
|||
|
//
|
|||
|
PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DellNationalPC87364", &DellNationalPC87364 );
|
|||
|
|
|||
|
if( DellNationalPC87364 ) {
|
|||
|
|
|||
|
//
|
|||
|
// we have a Dell machine with a National PC87364 chipset
|
|||
|
// and a version of BIOS that we believe doesn't
|
|||
|
// initialize the parallel port so that it works under
|
|||
|
// Win2k or WinXP.
|
|||
|
//
|
|||
|
|
|||
|
if( fdx->PnpInfo.SpanOfEcpController > 4 ) {
|
|||
|
|
|||
|
//
|
|||
|
// We have the extra Ecp registers needed to try the
|
|||
|
// workaround without stepping on I/O register space
|
|||
|
// owned by someone else.
|
|||
|
//
|
|||
|
|
|||
|
if( ( (PUCHAR)0x678 == fdx->PnpInfo.EcpController ) ||
|
|||
|
( (PUCHAR)0x778 == fdx->PnpInfo.EcpController ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// The parallel port base register and ECP
|
|||
|
// registers are located at one of the two
|
|||
|
// traditional address ranges: ECP at 0x400 offset
|
|||
|
// from base register address of 0x278 or 0x378,
|
|||
|
// so let's try the workaround to try to unwedge
|
|||
|
// the port data lines.
|
|||
|
//
|
|||
|
|
|||
|
PptDellNationalPC87364WorkAround( fdx->PnpInfo.EcpController );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} // end new block scope for Dell/National chipset workaround
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the IEEE 1284.3 "bus" by assigning IDs [0..3] to
|
|||
|
// the 1284.3 daisy chain devices connected to the port. This
|
|||
|
// function also gives us a count of the number of such
|
|||
|
// devices connected to the port.
|
|||
|
//
|
|||
|
fdx->PnpInfo.Ieee1284_3DeviceCount = PptInitiate1284_3( fdx );
|
|||
|
|
|||
|
//
|
|||
|
// Determine the hardware modes supported (BYTE, ECP, EPP) by
|
|||
|
// the parallel port chipset and save this information in our extension.
|
|||
|
//
|
|||
|
|
|||
|
// Check to see if the filter parchip is there and use the modes it can set
|
|||
|
status = PptDetectChipFilter( fdx );
|
|||
|
|
|||
|
// if filter driver was not found use our own generic port detection
|
|||
|
if ( !NT_SUCCESS( status ) ) {
|
|||
|
PptDetectPortType( fdx );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Register w/WMI
|
|||
|
//
|
|||
|
status = PptWmiInitWmi( DeviceObject );
|
|||
|
if( !NT_SUCCESS( status ) ) {
|
|||
|
goto targetExit;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Signal those who registered for PnP interface change notification
|
|||
|
// on our GUID that we have STARTED (trigger an INTERFACE_ARRIVAL
|
|||
|
// PnP callback).
|
|||
|
//
|
|||
|
status = IoSetDeviceInterfaceState( &fdx->DeviceInterface, TRUE );
|
|||
|
if( !NT_SUCCESS(status) ) {
|
|||
|
status = STATUS_NOT_SUPPORTED;
|
|||
|
} else {
|
|||
|
fdx->DeviceInterfaceState = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
targetExit:
|
|||
|
|
|||
|
if( NT_SUCCESS( status ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Note in our extension that we have successfully STARTED.
|
|||
|
//
|
|||
|
ExAcquireFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
PptSetFlags( fdx->PnpState, PPT_DEVICE_STARTED );
|
|||
|
ExReleaseFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
|
|||
|
// create warm poll thread to poll for printer arrivals
|
|||
|
if( NULL == fdx->ThreadObjectPointer ) {
|
|||
|
|
|||
|
ULONG DisableWarmPoll;
|
|||
|
|
|||
|
fdx->PollingFailureCounter = 0; // reset counter
|
|||
|
|
|||
|
// check for registry flag to disable "polling for printers"
|
|||
|
DisableWarmPoll = 0; // if non-zero then do not poll for printer arrivals
|
|||
|
PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DisableWarmPoll", &DisableWarmPoll );
|
|||
|
|
|||
|
if( 0 == DisableWarmPoll ) {
|
|||
|
|
|||
|
// how frequently should we check for printer arrivals? (in seconds)
|
|||
|
// (WarmPollPeriod is a driver global)
|
|||
|
PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"WarmPollPeriod", &WarmPollPeriod );
|
|||
|
if( WarmPollPeriod < 5 ) {
|
|||
|
WarmPollPeriod = 5;
|
|||
|
} else {
|
|||
|
if( WarmPollPeriod > 20 ) {
|
|||
|
WarmPollPeriod = 20;
|
|||
|
}
|
|||
|
}
|
|||
|
DD((PCE)fdx,DDT,"P5FdoThread - WarmPollPeriod = %d seconds\n",WarmPollPeriod);
|
|||
|
|
|||
|
// side effect: set fdx->ThreadObjectPointer on SUCCESS
|
|||
|
P5FdoCreateThread( fdx );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
P4CompleteRequest( Irp, status, 0 );
|
|||
|
|
|||
|
PptReleaseRemoveLock( &fdx->RemoveLock, Irp );
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoQueryRemove(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_QUERY_REMOVE_DEVICE.
|
|||
|
|
|||
|
FAIL the request if there are open handles, SUCCEED otherwise.
|
|||
|
|
|||
|
This function is identical to PptPnpQueryStopDevice() except
|
|||
|
for the flag that gets set in fdx->PnpState.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - No open handles - SUCCEED IRP
|
|||
|
STATUS_DEVICE_BUSY - Open handles - FAIL IRP
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Always succeed query - PnP will veto Query Remove on our behalf if
|
|||
|
// there are open handles
|
|||
|
//
|
|||
|
|
|||
|
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
ExAcquireFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
PptSetFlags( fdx->PnpState, ( PPT_DEVICE_REMOVE_PENDING | PPT_DEVICE_PAUSED ) );
|
|||
|
ExReleaseFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
return PptPnpPassThroughPnpIrpAndReleaseRemoveLock( fdx, Irp );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoRemoveDevice(
|
|||
|
IN PDEVICE_OBJECT Fdo,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_REMOVE_DEVICE.
|
|||
|
|
|||
|
Notify those listening on our device interface GUID that
|
|||
|
we have gone away, wait until all other IRPs that the
|
|||
|
device is processing have drained, and clean up.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Fdo - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status returned from IoCallDriver.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PFDO_EXTENSION fdx = Fdo->DeviceExtension;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// clean up any child PDOs that are still here
|
|||
|
//
|
|||
|
if( fdx->RawPortPdo ) {
|
|||
|
PDEVICE_OBJECT pdo = fdx->RawPortPdo;
|
|||
|
DD((PCE)fdx,DDT,"PptFdoRemoveDevice - have RawPortPdo - cleaning up\n");
|
|||
|
P4DestroyPdo( pdo );
|
|||
|
fdx->RawPortPdo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if( fdx->EndOfChainPdo ) {
|
|||
|
PDEVICE_OBJECT pdo = fdx->EndOfChainPdo;
|
|||
|
DD((PCE)fdx,DDT,"PptFdoRemoveDevice - have EndOfChainPdo - cleaning up\n");
|
|||
|
P4DestroyPdo( pdo );
|
|||
|
fdx->EndOfChainPdo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
{
|
|||
|
LONG daisyChainId;
|
|||
|
const LONG daisyChainMaxId = 1;
|
|||
|
|
|||
|
for( daisyChainId = 0 ; daisyChainId <= daisyChainMaxId ; ++daisyChainId ) {
|
|||
|
|
|||
|
if( fdx->DaisyChainPdo[ daisyChainId ] ) {
|
|||
|
PDEVICE_OBJECT pdo = fdx->DaisyChainPdo[ daisyChainId ];
|
|||
|
DD((PCE)fdx,DDT,"PptFdoRemoveDevice - have DaisyChainPdo[%d] - cleaning up\n",daisyChainId);
|
|||
|
P4DestroyPdo( pdo );
|
|||
|
fdx->DaisyChainPdo[ daisyChainId ] = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// RMT - if fdx->DevDeletionListHead non-empty - clean it up?
|
|||
|
//
|
|||
|
PptAssert( IsListEmpty( &fdx->DevDeletionListHead) );
|
|||
|
|
|||
|
//
|
|||
|
// Set flags in our extension to indicate that we have received
|
|||
|
// IRP_MN_REMOVE_DEVICE so that we can fail new requests as appropriate.
|
|||
|
//
|
|||
|
ExAcquireFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
PptSetFlags( fdx->PnpState, PPT_DEVICE_REMOVED );
|
|||
|
ExReleaseFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
|
|||
|
//
|
|||
|
// if we still have a worker thread, kill it
|
|||
|
//
|
|||
|
{
|
|||
|
PVOID threadObjPointer = InterlockedExchangePointer( &fdx->ThreadObjectPointer, NULL );
|
|||
|
|
|||
|
if( threadObjPointer ) {
|
|||
|
|
|||
|
// set the flag for the worker thread to kill itself
|
|||
|
fdx->TimeToTerminateThread = TRUE;
|
|||
|
|
|||
|
// wake thread so it can kill self
|
|||
|
KeSetEvent( &fdx->FdoThreadEvent, 0, TRUE );
|
|||
|
|
|||
|
// wait for the thread to die
|
|||
|
KeWaitForSingleObject( threadObjPointer, Executive, KernelMode, FALSE, NULL );
|
|||
|
|
|||
|
// allow the system to release the thread object
|
|||
|
ObDereferenceObject( threadObjPointer );
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unregister w/WMI
|
|||
|
//
|
|||
|
IoWMIRegistrationControl(Fdo, WMIREG_ACTION_DEREGISTER);
|
|||
|
|
|||
|
//
|
|||
|
// Tell those listening on our device interface GUID that we have
|
|||
|
// gone away. Ignore status from the call since we can do
|
|||
|
// nothing on failure.
|
|||
|
//
|
|||
|
IoSetDeviceInterfaceState( &fdx->DeviceInterface, FALSE );
|
|||
|
fdx->DeviceInterfaceState = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Pass the IRP down the stack and wait for all other IRPs
|
|||
|
// that are being processed by the device to drain.
|
|||
|
//
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
IoSkipCurrentIrpStackLocation( Irp );
|
|||
|
status = IoCallDriver( fdx->ParentDeviceObject, Irp );
|
|||
|
PptReleaseRemoveLockAndWait( &fdx->RemoveLock, Irp );
|
|||
|
|
|||
|
//
|
|||
|
// Clean up pool allocations
|
|||
|
//
|
|||
|
RtlFreeUnicodeString( &fdx->DeviceName);
|
|||
|
RtlFreeUnicodeString( &fdx->DeviceInterface );
|
|||
|
if( fdx->PnpInfo.PortName ) {
|
|||
|
ExFreePool( fdx->PnpInfo.PortName );
|
|||
|
fdx->PnpInfo.PortName = NULL;
|
|||
|
}
|
|||
|
if( fdx->Location ) {
|
|||
|
ExFreePool( fdx->Location );
|
|||
|
fdx->Location = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Detach and delete our device object.
|
|||
|
//
|
|||
|
IoDetachDevice( fdx->ParentDeviceObject );
|
|||
|
IoDeleteDevice( Fdo );
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoCancelRemove(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_CANCEL_REMOVE_DEVICE.
|
|||
|
|
|||
|
If we previously SUCCEEDed a QUERY_REMOVE (PPT_DEVICE_REMOVE_PENDING
|
|||
|
flag is set) then we reset the appropriate device state flags
|
|||
|
and resume normal operation. Otherwise treat this as an
|
|||
|
informational message.
|
|||
|
|
|||
|
This function is identical to PptPnpCancelStopDevice() except for
|
|||
|
the fdx->PnpState.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status returned from IoCallDriver.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
ExAcquireFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
if( fdx->PnpState & PPT_DEVICE_REMOVE_PENDING ) {
|
|||
|
PptClearFlags( fdx->PnpState, ( PPT_DEVICE_REMOVE_PENDING | PPT_DEVICE_PAUSED ) );
|
|||
|
}
|
|||
|
ExReleaseFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
return PptPnpPassThroughPnpIrpAndReleaseRemoveLock( fdx, Irp );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoStopDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_STOP_DEVICE.
|
|||
|
|
|||
|
We previously SUCCEEDed QUERY_STOP. Set flags
|
|||
|
to indicate that we are now STOPPED.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status returned from IoCallDriver.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
ExAcquireFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
|
|||
|
//
|
|||
|
// Assert that we are in a STOP_PENDING state.
|
|||
|
//
|
|||
|
ASSERT( fdx->PnpState & PPT_DEVICE_STOP_PENDING );
|
|||
|
ASSERT( fdx->PnpState & PPT_DEVICE_PAUSED );
|
|||
|
|
|||
|
//
|
|||
|
// PPT_DEVICE_PAUSED remains set
|
|||
|
//
|
|||
|
PptSetFlags( fdx->PnpState, PPT_DEVICE_STOPPED );
|
|||
|
PptClearFlags( fdx->PnpState, ( PPT_DEVICE_STOP_PENDING | PPT_DEVICE_STARTED ) );
|
|||
|
|
|||
|
ExReleaseFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
return PptPnpPassThroughPnpIrpAndReleaseRemoveLock(fdx, Irp);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoQueryStop(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_QUERY_STOP_DEVICE.
|
|||
|
|
|||
|
FAIL the request if there are open handles, SUCCEED otherwise.
|
|||
|
|
|||
|
Other drivers may cache pointers to the parallel port registers that
|
|||
|
they obtained via IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO and there
|
|||
|
is currently no mechanism to find and inform all such drivers that the
|
|||
|
parallel port registers have changed and their their cached pointers are
|
|||
|
now invalid without breaking legacy drivers.
|
|||
|
|
|||
|
This function is identical to PptPnpQueryStopDevice() except
|
|||
|
for the flag that gets set in fdx->PnpState.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - No open handles - SUCCEED IRP
|
|||
|
STATUS_DEVICE_BUSY - Open handles - FAIL IRP
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
|||
|
BOOLEAN handlesOpen;
|
|||
|
|
|||
|
//
|
|||
|
// RMT - dvdf - race condition - small timing window - sequence:
|
|||
|
// 1. Test indicates no open handles - decide to SUCCEED QUERY_STOP
|
|||
|
// 2. CREATE arrives and is SUCCEEDED - open handle
|
|||
|
// 3. We SUCCEED QUERY_STOP
|
|||
|
// 4. Client obtains register addresses via IOCTL
|
|||
|
// 5. PnP Rebalances us - registers change
|
|||
|
// 6. Client acquires port via IOCTL
|
|||
|
// 7. Client tries to access registers at pre-rebalance location
|
|||
|
// 8. BOOM!!!
|
|||
|
//
|
|||
|
|
|||
|
ExAcquireFastMutex( &fdx->OpenCloseMutex );
|
|||
|
handlesOpen = (BOOLEAN)( fdx->OpenCloseRefCount > 0 );
|
|||
|
ExReleaseFastMutex( &fdx->OpenCloseMutex );
|
|||
|
|
|||
|
if( handlesOpen ) {
|
|||
|
|
|||
|
status = STATUS_DEVICE_BUSY;
|
|||
|
P4CompleteRequest( Irp, status, Irp->IoStatus.Information );
|
|||
|
PptReleaseRemoveLock( &fdx->RemoveLock, Irp );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
status = PptPnpPassThroughPnpIrpAndReleaseRemoveLock( fdx, Irp );
|
|||
|
|
|||
|
ExAcquireFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
PptSetFlags( fdx->PnpState, ( PPT_DEVICE_STOP_PENDING | PPT_DEVICE_PAUSED ) );
|
|||
|
ExReleaseFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoCancelStop(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_CANCEL_STOP_DEVICE.
|
|||
|
|
|||
|
If we previously SUCCEEDed a QUERY_STOP (PPT_DEVICE_STOP_PENDING
|
|||
|
flag is set) then we reset the appropriate device state flags
|
|||
|
and resume normal operation. Otherwise treat this as an
|
|||
|
informational message.
|
|||
|
|
|||
|
This function is identical to PptPnpCancelRemoveDevice() except for
|
|||
|
the fdx->PnpState.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status returned from IoCallDriver.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
ExAcquireFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
if( fdx->PnpState & PPT_DEVICE_STOP_PENDING ) {
|
|||
|
PptClearFlags( fdx->PnpState, ( PPT_DEVICE_STOP_PENDING | PPT_DEVICE_PAUSED ) );
|
|||
|
}
|
|||
|
ExReleaseFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
return PptPnpPassThroughPnpIrpAndReleaseRemoveLock( fdx, Irp );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoQueryDeviceRelations(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_QUERY_DEVICE_RELATIONS.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - on success,
|
|||
|
an appropriate error status - otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
DEVICE_RELATION_TYPE type = irpSp->Parameters.QueryDeviceRelations.Type;
|
|||
|
|
|||
|
if( BusRelations == type ) {
|
|||
|
return PptFdoHandleBusRelations( DeviceObject, Irp );
|
|||
|
} else {
|
|||
|
return PptPnpPassThroughPnpIrpAndReleaseRemoveLock(DeviceObject->DeviceExtension, Irp);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoFilterResourceRequirements(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_FILTER_RESOURCE_REQUIREMENTS IRPs.
|
|||
|
|
|||
|
- Wait for the bus driver and any drivers beneath
|
|||
|
us in the driver stack to handle this first.
|
|||
|
- Query the registry to find the type of filtering desired.
|
|||
|
- Filter out IRQ resources as specified by the registry setting.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - on success,
|
|||
|
an appropriate error status - otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
|||
|
ULONG filterResourceMethod = PPT_FORCE_USE_NO_IRQ;
|
|||
|
PIO_RESOURCE_REQUIREMENTS_LIST pResourceRequirementsIn;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// DDK Rule: Add on the way down, modify on the way up. We are modifying
|
|||
|
// the resource list so let the drivers beneath us handle this IRP first.
|
|||
|
//
|
|||
|
status = PptPnpBounceAndCatchPnpIrp(fdx, Irp);
|
|||
|
if( !NT_SUCCESS(status) && (status != STATUS_NOT_SUPPORTED) ) {
|
|||
|
// Someone below us in the driver stack explicitly failed the IRP.
|
|||
|
goto targetExit;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Find the "real" resource requirments list, either the PnP list
|
|||
|
// or the list created by another driver in the stack.
|
|||
|
//
|
|||
|
if ( Irp->IoStatus.Information == 0 ) {
|
|||
|
//
|
|||
|
// No one else has created a new resource list. Use the original
|
|||
|
// list from the PnP Manager.
|
|||
|
//
|
|||
|
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
pResourceRequirementsIn = IrpStack->Parameters.FilterResourceRequirements.IoResourceRequirementList;
|
|||
|
|
|||
|
if (pResourceRequirementsIn == NULL) {
|
|||
|
//
|
|||
|
// NULL list, nothing to do.
|
|||
|
//
|
|||
|
goto targetExit;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Another driver has created a new resource list. Use the list that they created.
|
|||
|
//
|
|||
|
pResourceRequirementsIn = (PIO_RESOURCE_REQUIREMENTS_LIST)Irp->IoStatus.Information;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Check the registry to find out the desired type of resource filtering.
|
|||
|
//
|
|||
|
// The following call sets the default value for filterResourceMethod
|
|||
|
// if the registry query fails.
|
|||
|
//
|
|||
|
PptRegGetDeviceParameterDword( fdx->PhysicalDeviceObject,
|
|||
|
(PWSTR)L"FilterResourceMethod",
|
|||
|
&filterResourceMethod );
|
|||
|
|
|||
|
DD((PCE)fdx,DDT,"filterResourceMethod=%x\n",filterResourceMethod);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Do filtering based on registry setting.
|
|||
|
//
|
|||
|
switch( filterResourceMethod ) {
|
|||
|
|
|||
|
case PPT_FORCE_USE_NO_IRQ:
|
|||
|
//
|
|||
|
// Registry setting dictates that we should refuse to accept IRQ resources.
|
|||
|
//
|
|||
|
// * This is the default behavior which means that we make the IRQ available
|
|||
|
// for legacy net and sound cards that may not work if they cannot get
|
|||
|
// the IRQ.
|
|||
|
//
|
|||
|
// - If we find a resource alternative that does not contain an IRQ resource
|
|||
|
// then we remove those resource alternatives that do contain IRQ
|
|||
|
// resources from the list of alternatives.
|
|||
|
//
|
|||
|
// - Otherwise we have to play hardball. Since all resource alternatives
|
|||
|
// contain IRQ resources we simply "nuke" the IRQ resource descriptors
|
|||
|
// by changing their resource Type from CmResourceTypeInterrupt to
|
|||
|
// CmResourceTypeNull.
|
|||
|
//
|
|||
|
|
|||
|
DD((PCE)fdx,DDT,"PPT_FORCE_USE_NO_IRQ\n");
|
|||
|
|
|||
|
if( PptPnpFilterExistsNonIrqResourceList( pResourceRequirementsIn ) ) {
|
|||
|
|
|||
|
DD((PCE)fdx,DDT,"Found Resource List with No IRQ - Filtering\n");
|
|||
|
PptPnpFilterRemoveIrqResourceLists( pResourceRequirementsIn );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DD((PCE)fdx,DDT,"Did not find Resource List with No IRQ - Nuking IRQ resource descriptors\n");
|
|||
|
PptPnpFilterNukeIrqResourceDescriptorsFromAllLists( pResourceRequirementsIn );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case PPT_TRY_USE_NO_IRQ:
|
|||
|
//
|
|||
|
// Registry setting dictates that we should TRY to give up IRQ resources.
|
|||
|
//
|
|||
|
// - If we find a resource alternative that does not contain an IRQ resource
|
|||
|
// then we remove those resource alternatives that do contain IRQ
|
|||
|
// resources from the list of alternatives.
|
|||
|
//
|
|||
|
// - Otherwise we do nothing.
|
|||
|
//
|
|||
|
|
|||
|
DD((PCE)fdx,DDT,"PPT_TRY_USE_NO_IRQ\n");
|
|||
|
if( PptPnpFilterExistsNonIrqResourceList(pResourceRequirementsIn) ) {
|
|||
|
|
|||
|
DD((PCE)fdx,DDT,"Found Resource List with No IRQ - Filtering\n");
|
|||
|
PptPnpFilterRemoveIrqResourceLists(pResourceRequirementsIn);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// leave the IO resource list as is
|
|||
|
DD((PCE)fdx,DDT,"Did not find Resource List with No IRQ - Do nothing\n");
|
|||
|
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case PPT_ACCEPT_IRQ:
|
|||
|
//
|
|||
|
// Registry setting dictates that we should NOT filter out IRQ resources.
|
|||
|
//
|
|||
|
// - Do nothing.
|
|||
|
//
|
|||
|
DD((PCE)fdx,DDT,"PPT_ACCEPT_IRQ\n");
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
default:
|
|||
|
//
|
|||
|
// Invalid registry setting.
|
|||
|
//
|
|||
|
// - Do nothing.
|
|||
|
//
|
|||
|
// RMT dvdf - May be desirable to write an error log entry here.
|
|||
|
//
|
|||
|
DD((PCE)fdx,DDE,"ERROR:IGNORED: bad filterResourceMethod=%x\n", filterResourceMethod);
|
|||
|
}
|
|||
|
|
|||
|
targetExit:
|
|||
|
|
|||
|
//
|
|||
|
// Preserve Irp->IoStatus.Information because it may point to a
|
|||
|
// buffer and we don't want to cause a memory leak.
|
|||
|
//
|
|||
|
P4CompleteRequest( Irp, status, Irp->IoStatus.Information );
|
|||
|
|
|||
|
PptReleaseRemoveLock(&fdx->RemoveLock, Irp);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoSurpriseRemoval(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf6
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function handles PnP IRP_MN_SURPRISE_REMOVAL.
|
|||
|
|
|||
|
Set flags accordingly in our extension, notify those
|
|||
|
listening on our device interface GUID that
|
|||
|
we have gone away, and pass the IRP down the
|
|||
|
driver stack.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status returned from IoCallDriver.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Set flags in our extension to indicate that we have received
|
|||
|
// IRP_MN_SURPRISE_REMOVAL so that we can fail new requests
|
|||
|
// as appropriate.
|
|||
|
//
|
|||
|
ExAcquireFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
PptSetFlags( fdx->PnpState, PPT_DEVICE_SURPRISE_REMOVED );
|
|||
|
ExReleaseFastMutex( &fdx->ExtensionFastMutex );
|
|||
|
|
|||
|
//
|
|||
|
// Fail outstanding allocate/select requests for the port
|
|||
|
//
|
|||
|
{
|
|||
|
PIRP nextIrp;
|
|||
|
KIRQL cancelIrql;
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
|
|||
|
while( !IsListEmpty( &fdx->WorkQueue ) ) {
|
|||
|
|
|||
|
nextIrp = CONTAINING_RECORD( fdx->WorkQueue.Blink, IRP, Tail.Overlay.ListEntry );
|
|||
|
nextIrp->Cancel = TRUE;
|
|||
|
nextIrp->CancelIrql = cancelIrql;
|
|||
|
nextIrp->CancelRoutine = NULL;
|
|||
|
PptCancelRoutine( DeviceObject, nextIrp );
|
|||
|
|
|||
|
// PptCancelRoutine() releases the cancel SpinLock so we need to reaquire
|
|||
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|||
|
}
|
|||
|
|
|||
|
IoReleaseCancelSpinLock( cancelIrql );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Tell those listening on our device interface GUID that we have
|
|||
|
// gone away. Ignore status from the call since we can do
|
|||
|
// nothing on failure.
|
|||
|
//
|
|||
|
IoSetDeviceInterfaceState( &fdx->DeviceInterface, FALSE );
|
|||
|
fdx->DeviceInterfaceState = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Succeed, pass the IRP down the stack, and release the RemoveLock.
|
|||
|
//
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
return PptPnpPassThroughPnpIrpAndReleaseRemoveLock( fdx, Irp );
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoDefaultPnpHandler(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++dvdf8
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is the default handler for PnP IRPs.
|
|||
|
All PnP IRPs that are not explicitly handled by another
|
|||
|
routine (via an entry in the PptPnpDispatchFunctionTable[]) are
|
|||
|
handled by this routine.
|
|||
|
|
|||
|
- Pass the IRP down the stack to the device below us in the
|
|||
|
driver stack and release our device RemoveLock.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device for the IRP
|
|||
|
Irp - The IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - on success,
|
|||
|
an appropriate error status - otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return PptPnpPassThroughPnpIrpAndReleaseRemoveLock(DeviceObject->DeviceExtension, Irp);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PDRIVER_DISPATCH
|
|||
|
PptFdoPnpDispatchTable[] =
|
|||
|
{
|
|||
|
PptFdoStartDevice, // IRP_MN_START_DEVICE 0x00
|
|||
|
PptFdoQueryRemove, // IRP_MN_QUERY_REMOVE_DEVICE 0x01
|
|||
|
PptFdoRemoveDevice, // IRP_MN_REMOVE_DEVICE 0x02
|
|||
|
PptFdoCancelRemove, // IRP_MN_CANCEL_REMOVE_DEVICE 0x03
|
|||
|
PptFdoStopDevice, // IRP_MN_STOP_DEVICE 0x04
|
|||
|
PptFdoQueryStop, // IRP_MN_QUERY_STOP_DEVICE 0x05
|
|||
|
PptFdoCancelStop, // IRP_MN_CANCEL_STOP_DEVICE 0x06
|
|||
|
PptFdoQueryDeviceRelations, // IRP_MN_QUERY_DEVICE_RELATIONS 0x07
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_QUERY_INTERFACE 0x08
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_QUERY_CAPABILITIES 0x09
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_QUERY_RESOURCES 0x0A
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_QUERY_RESOURCE_REQUIREMENTS 0x0B
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_QUERY_DEVICE_TEXT 0x0C
|
|||
|
PptFdoFilterResourceRequirements, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS 0x0D
|
|||
|
PptFdoDefaultPnpHandler, // no such PnP request 0x0E
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_READ_CONFIG 0x0F
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_WRITE_CONFIG 0x10
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_EJECT 0x11
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_SET_LOCK 0x12
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_QUERY_ID 0x13
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_QUERY_PNP_DEVICE_STATE 0x14
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_QUERY_BUS_INFORMATION 0x15
|
|||
|
PptFdoDefaultPnpHandler, // IRP_MN_DEVICE_USAGE_NOTIFICATION 0x16
|
|||
|
PptFdoSurpriseRemoval, // IRP_MN_SURPRISE_REMOVAL 0x17
|
|||
|
PptFdoDefaultPnpHandler // IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptFdoPnp(
|
|||
|
IN PDEVICE_OBJECT Fdo,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PFDO_EXTENSION fdx = Fdo->DeviceExtension;
|
|||
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
// diagnostic
|
|||
|
PptFdoDumpPnpIrpInfo( Fdo, Irp );
|
|||
|
|
|||
|
//
|
|||
|
// Acquire RemoveLock to prevent DeviceObject from being REMOVED
|
|||
|
// while we are using it. If we are unable to acquire the RemoveLock
|
|||
|
// then the DeviceObject has already been REMOVED.
|
|||
|
//
|
|||
|
status = PptAcquireRemoveLock( &fdx->RemoveLock, Irp);
|
|||
|
if( STATUS_SUCCESS != status ) {
|
|||
|
return P4CompleteRequest( Irp, STATUS_DELETE_PENDING, Irp->IoStatus.Information );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// RemoveLock is held. Forward the request to the appropriate handler.
|
|||
|
//
|
|||
|
// Note that the handler must release the RemoveLock prior to returning
|
|||
|
// control to this function.
|
|||
|
//
|
|||
|
|
|||
|
if( irpSp->MinorFunction < arraysize(PptFdoPnpDispatchTable) ) {
|
|||
|
return PptFdoPnpDispatchTable[ irpSp->MinorFunction ]( Fdo, Irp );
|
|||
|
} else {
|
|||
|
return PptFdoDefaultPnpHandler( Fdo, Irp );
|
|||
|
}
|
|||
|
}
|