/*++ 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; }