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 );
|
||
}
|
||
}
|