windows-nt/Source/XPSP1/NT/base/ntos/io/pnpmgr/pphotswap.c

436 lines
11 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1998-2001 Microsoft Corporation
Module Name:
PpHotSwap.c
Abstract:
This file implements support for hotswap devices.
Author:
Adrian J. Oney (AdriaO) Feb 2001
Revision History:
--*/
#include "pnpmgrp.h"
#include "pihotswap.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PpHotSwapInitRemovalPolicy)
#pragma alloc_text(PAGE, PpHotSwapUpdateRemovalPolicy)
#pragma alloc_text(PAGE, PpHotSwapGetDevnodeRemovalPolicy)
#pragma alloc_text(PAGE, PiHotSwapGetDefaultBusRemovalPolicy)
#pragma alloc_text(PAGE, PiHotSwapGetDetachableNode)
#endif
VOID
PpHotSwapInitRemovalPolicy(
OUT PDEVICE_NODE DeviceNode
)
/*++
Routine Description:
This function initializes the removal policy information for a device node.
Arguments:
DeviceNode - DevNode to update policy.
Return Value:
Nothing.
--*/
{
PAGED_CODE();
DeviceNode->RemovalPolicy = (UCHAR) RemovalPolicyNotDetermined;
DeviceNode->HardwareRemovalPolicy = (UCHAR) RemovalPolicyNotDetermined;
}
VOID
PpHotSwapUpdateRemovalPolicy(
IN PDEVICE_NODE DeviceNode
)
/*++
Routine Description:
This function updates the removal policy by retrieving the appropriate
data from the registry or drivers.
Arguments:
DeviceNode - DevNode to update policy on.
Return Value:
Nothing.
--*/
{
NTSTATUS status;
DEVICE_REMOVAL_POLICY deviceRemovalPolicy, parentPolicy;
PDEVICE_NODE detachableNode;
ULONG policyLength, policyCharacteristics;
PAGED_CODE();
PPDEVNODE_ASSERT_LOCK_HELD(PPL_TREEOP_ALLOW_READS);
//
// First find the detachable node - it holds our policy data, and is
// special as it may make suggestions.
//
PiHotSwapGetDetachableNode(DeviceNode, &detachableNode);
//
// We aren't in fact removable. Finish now.
//
if (detachableNode == NULL) {
DeviceNode->RemovalPolicy = (UCHAR) RemovalPolicyExpectNoRemoval;
DeviceNode->HardwareRemovalPolicy = (UCHAR) RemovalPolicyExpectNoRemoval;
return;
}
//
// Check the stack for an explicit policy...
//
policyCharacteristics =
((DeviceNode->PhysicalDeviceObject->Characteristics) &
FILE_CHARACTERISTICS_REMOVAL_POLICY_MASK);
if (policyCharacteristics == FILE_CHARACTERISTICS_EXPECT_ORDERLY_REMOVAL) {
deviceRemovalPolicy = RemovalPolicyExpectOrderlyRemoval;
} else if (policyCharacteristics == FILE_CHARACTERISTICS_EXPECT_SURPRISE_REMOVAL) {
deviceRemovalPolicy = RemovalPolicyExpectSurpriseRemoval;
} else if (DeviceNode != detachableNode) {
//
// We didn't get any good guesses. Therefore use the weakest policy.
//
deviceRemovalPolicy = RemovalPolicyUnspecified;
} else {
//
// If we're the detach point, then we win.
//
PiHotSwapGetDefaultBusRemovalPolicy(DeviceNode, &deviceRemovalPolicy);
}
if (DeviceNode != detachableNode) {
//
// Do we have a winning policy? There are two possible algorithms for
// coming to such a decision.
// 1) Best policy is stored back at the detach point. If a child has a
// better policy, the detach point is updated.
// 2) Policy is inherited downwards from the parent.
//
// We choose the second algorithm because devnode start orders may
// change scenario to scenario, and we favor determinism (same results
// each time) over opportunism (nonmarked child gets write caching
// enabled only on Tuesdays.)
//
parentPolicy = DeviceNode->Parent->RemovalPolicy;
if (deviceRemovalPolicy > parentPolicy) {
//
// Seems dad was right afterall...
//
deviceRemovalPolicy = parentPolicy;
}
}
//
// Update the policy hardware policy and the overall policy in case there's
// no registry override.
//
DeviceNode->RemovalPolicy = (UCHAR) deviceRemovalPolicy;
DeviceNode->HardwareRemovalPolicy = (UCHAR) deviceRemovalPolicy;
//
// We might not have to ask the stack anything. Check for a registry
// override.
//
policyLength = sizeof(DEVICE_REMOVAL_POLICY);
status = PiGetDeviceRegistryProperty(
DeviceNode->PhysicalDeviceObject,
REG_DWORD,
REGSTR_VALUE_REMOVAL_POLICY,
NULL,
&deviceRemovalPolicy,
&policyLength
);
//
// If we have an override, set that as the policy.
//
if (NT_SUCCESS(status) &&
((deviceRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
(deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval))) {
DeviceNode->RemovalPolicy = (UCHAR) deviceRemovalPolicy;
}
}
VOID
PpHotSwapGetDevnodeRemovalPolicy(
IN PDEVICE_NODE DeviceNode,
IN BOOLEAN IncludeRegistryOverride,
OUT PDEVICE_REMOVAL_POLICY RemovalPolicy
)
/*++
Routine Description:
This function retrieves the removal policy for a device node.
Arguments:
DeviceNode - DevNode to retrieve policy from.
IncludeRegistryOverride - TRUE if a registry override should be taken into
account if present. FALSE if the check should be
restricted to the hardware.
RemovalPolicy - Receives removal policy.
Return Value:
Nothing.
--*/
{
PDEVICE_NODE detachableNode;
DEVICE_REMOVAL_POLICY reportedPolicy;
PAGED_CODE();
//
// Ensure the tree won't be edited while we examine it.
//
PpDevNodeLockTree(PPL_SIMPLE_READ);
if (IncludeRegistryOverride) {
reportedPolicy = DeviceNode->RemovalPolicy;
} else {
reportedPolicy = DeviceNode->HardwareRemovalPolicy;
}
if (reportedPolicy == RemovalPolicyNotDetermined) {
//
// We haven't started yet or asked the bus. Our policy is based on
// whether the device is removable or ejectable.
//
PiHotSwapGetDetachableNode(DeviceNode, &detachableNode);
if (detachableNode == NULL) {
reportedPolicy = RemovalPolicyExpectNoRemoval;
} else if (IopDeviceNodeFlagsToCapabilities(detachableNode)->EjectSupported) {
//
// Ejectable devices require orderly removal. We will assume the
// user knows this.
//
reportedPolicy = RemovalPolicyExpectOrderlyRemoval;
} else {
ASSERT(IopDeviceNodeFlagsToCapabilities(detachableNode)->Removable);
//
// Removal nonstarted devices can be pulled at any instant.
//
reportedPolicy = RemovalPolicyExpectSurpriseRemoval;
}
} else {
//
// The devnode has a cached policy. Cut down on the options.
//
switch(reportedPolicy) {
case RemovalPolicyExpectNoRemoval:
case RemovalPolicyExpectOrderlyRemoval:
case RemovalPolicyExpectSurpriseRemoval:
//
// Leave unchanged.
//
break;
case RemovalPolicySuggestSurpriseRemoval:
reportedPolicy = RemovalPolicyExpectSurpriseRemoval;
break;
default:
ASSERT(0);
//
// Fall through.
//
case RemovalPolicyUnspecified:
//
// Unspecified is treated as orderly since the diversity of
// busses favor high-speed orderly connections over consumer
// connections.
//
// Fall through
//
case RemovalPolicySuggestOrderlyRemoval:
reportedPolicy = RemovalPolicyExpectOrderlyRemoval;
break;
}
}
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
*RemovalPolicy = reportedPolicy;
}
VOID
PiHotSwapGetDefaultBusRemovalPolicy(
IN PDEVICE_NODE DeviceNode,
OUT PDEVICE_REMOVAL_POLICY RemovalPolicy
)
/*++
Routine Description:
This function gets the default removal policy for a bus. This should be
turned into a query in future designs.
Arguments:
DeviceNode - DevNode to examine. This devnode should be the detach point.
RemovalPolicy - Receives removal policy for the node.
Return Value:
None.
--*/
{
DEVICE_REMOVAL_POLICY deviceRemovalPolicy;
PAGED_CODE();
PPDEVNODE_ASSERT_LOCK_HELD(PPL_TREEOP_ALLOW_READS);
if ((DeviceNode->InstancePath.Length > 8) &&
(!_wcsnicmp(DeviceNode->InstancePath.Buffer, L"USB\\", 4))) {
deviceRemovalPolicy = RemovalPolicySuggestSurpriseRemoval;
} else if ((DeviceNode->InstancePath.Length > 10) &&
(!_wcsnicmp(DeviceNode->InstancePath.Buffer, L"1394\\", 5))) {
deviceRemovalPolicy = RemovalPolicySuggestSurpriseRemoval;
} else if ((DeviceNode->InstancePath.Length > 10) &&
(!_wcsnicmp(DeviceNode->InstancePath.Buffer, L"SBP2\\", 5))) {
deviceRemovalPolicy = RemovalPolicySuggestSurpriseRemoval;
} else if ((DeviceNode->InstancePath.Length > 14) &&
(!_wcsnicmp(DeviceNode->InstancePath.Buffer, L"PCMCIA\\", 7))) {
deviceRemovalPolicy = RemovalPolicySuggestSurpriseRemoval;
} else if ((DeviceNode->InstancePath.Length > 8) &&
(!_wcsnicmp(DeviceNode->InstancePath.Buffer, L"PCI\\", 4)) &&
(DeviceNode->Parent->ServiceName.Length == 12) &&
(!_wcsicmp(DeviceNode->Parent->ServiceName.Buffer, L"PCMCIA"))) {
deviceRemovalPolicy = RemovalPolicySuggestSurpriseRemoval;
} else {
deviceRemovalPolicy = RemovalPolicySuggestOrderlyRemoval;
}
*RemovalPolicy = deviceRemovalPolicy;
}
VOID
PiHotSwapGetDetachableNode(
IN PDEVICE_NODE DeviceNode,
OUT PDEVICE_NODE *DetachableNode
)
/*++
Routine Description:
This function starts at the DeviceNode and walks up the tree to find the
first node that is removable.
Arguments:
DeviceNode - DevNode to start walk from.
DetachableNode - Receives detachable node, NULL if none.
Return Value:
Nothing.
--*/
{
PDEVICE_NODE currentNode;
PAGED_CODE();
PPDEVNODE_ASSERT_LOCK_HELD(PPL_SIMPLE_READ);
//
// We haven't started yet or asked the bus. Our policy is based on
// whether the device is removable or ejectable.
//
for(currentNode = DeviceNode;
currentNode != NULL;
currentNode = currentNode->Parent) {
if ((IopDeviceNodeFlagsToCapabilities(currentNode)->Removable) ||
(IopDeviceNodeFlagsToCapabilities(currentNode)->EjectSupported)) {
break;
}
}
*DetachableNode = currentNode;
}