Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4952 lines
143 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
ppcontrol.c
Abstract:
User-mode -> Kernel-mode PnP Manager control routines.
Author:
Lonny McMichael (lonnym) 02/14/95
Revision History:
--*/
#include "pnpmgrp.h"
#include "picontrol.h"
#define _APPHELP_CACHE_INIT_
#include "ahcache.h"
#pragma hdrstop
//
// Global driver object that is used by calls to NtPlugPlayControl
// with control type of PlugPlayControlDetectResourceConflict.
//
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif
//
// Define mask of devnode flags that are settable from user-mode via the
// NtPlugPlayControl, PlugPlayControlGetDeviceStatus (which is a misnomer,
// since it can perform both gets and sets).
//
#define DEVICE_NODE_SETTABLE_FLAG_BITS (DNF_HAS_PROBLEM | \
DNF_HAS_PRIVATE_PROBLEM \
)
NTSTATUS
PiControlCopyUserModeCallersBuffer(
IN PVOID Destination,
IN PVOID Src,
IN ULONG Length,
IN ULONG Alignment,
IN KPROCESSOR_MODE CallerMode,
IN BOOLEAN ReadUserModeBuffer
);
NTSTATUS
PiGetInterfaceDeviceAlias(
IN PUNICODE_STRING SymbolicLinkName,
IN LPGUID AliasClassGuid,
OUT PWSTR AliasSymbolicLinkName,
IN OUT PULONG AliasSymbolicLinkNameLength
);
NTSTATUS
PiGenerateLegacyDeviceInstance(
IN PUNICODE_STRING ServiceKeyName,
OUT PWSTR DeviceInstance,
IN OUT PULONG DeviceInstanceLength
);
NTSTATUS
PiQueueQueryAndRemoveEvent(
IN PUNICODE_STRING DeviceInstance,
IN PPNP_VETO_TYPE VetoType,
IN LPWSTR VetoName,
IN PULONG VetoNameLength,
IN ULONG Flags
);
NTSTATUS
PiQueueDeviceRequest(
IN PUNICODE_STRING DeviceInstance,
IN DEVICE_REQUEST_TYPE RequestType,
IN ULONG Flags,
IN BOOLEAN Synchronous
);
NTSTATUS
PiInitializeDevice(
IN PUNICODE_STRING DeviceInstance
);
NTSTATUS
PiDetectResourceConflict(
IN PCM_RESOURCE_LIST ResourceList,
IN ULONG ResourceListSize
);
NTSTATUS
PiGetInterfaceDeviceList(
IN GUID *InterfaceGuid,
IN PUNICODE_STRING DeviceInstance,
IN ULONG Flags,
OUT PWSTR InterfaceList,
IN OUT PULONG InterfaceListSize
);
NTSTATUS
PiDeviceClassAssociation(
IN PUNICODE_STRING DeviceInstance,
IN GUID * ClassGuid,
IN PUNICODE_STRING Reference, OPTIONAL
IN OUT PWSTR SymbolicLink,
IN OUT PULONG SymbolicLinkLength,
IN BOOLEAN Register
);
NTSTATUS
PiGetRelatedDevice(
IN PUNICODE_STRING TargetDeviceInstance,
OUT LPWSTR RelatedDeviceInstance,
IN OUT PULONG RelatedDeviceInstanceLength,
IN ULONG Relation
);
NTSTATUS
PiQueryDeviceRelations(
IN PUNICODE_STRING DeviceInstance,
IN PNP_QUERY_RELATION Operation,
OUT PULONG BufferLength,
OUT LPWSTR Buffer
);
DEVICE_RELATION_TYPE
PiDeviceRelationType(
PNP_QUERY_RELATION Operation
);
NTSTATUS
PiControlGetBlockedDriverData(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_BLOCKED_DRIVER_DATA BlockedDriverData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGELK, PpShutdownSystem) // this gets called after paging shutdown
#pragma alloc_text(PAGE, NtPlugPlayControl)
#pragma alloc_text(PAGE, PiControlMakeUserModeCallersCopy)
#pragma alloc_text(PAGE, PiControlCopyUserModeCallersBuffer)
#pragma alloc_text(PAGE, PiGetInterfaceDeviceAlias)
#pragma alloc_text(PAGE, PiGenerateLegacyDeviceInstance)
#pragma alloc_text(PAGE, PiQueueQueryAndRemoveEvent)
#pragma alloc_text(PAGE, PiInitializeDevice)
#pragma alloc_text(PAGE, PiDetectResourceConflict)
#pragma alloc_text(PAGE, PiGetInterfaceDeviceList)
#pragma alloc_text(PAGE, PiDeviceClassAssociation)
#pragma alloc_text(PAGE, PiGetRelatedDevice)
#pragma alloc_text(PAGE, PiQueryDeviceRelations)
#pragma alloc_text(PAGE, PiDeviceRelationType)
#pragma alloc_text(PAGE, PiControlGetUserFlagsFromDeviceNode)
#pragma alloc_text(PAGE, PiQueueDeviceRequest)
#pragma alloc_text(PAGE, PiControlEnumerateDevice)
#pragma alloc_text(PAGE, PiControlRegisterNewDevice)
#pragma alloc_text(PAGE, PiControlDeregisterDevice)
#pragma alloc_text(PAGE, PiControlInitializeDevice)
#pragma alloc_text(PAGE, PiControlStartDevice)
#pragma alloc_text(PAGE, PiControlResetDevice)
#pragma alloc_text(PAGE, PiControlQueryAndRemoveDevice)
#pragma alloc_text(PAGE, PiControlUserResponse)
#pragma alloc_text(PAGE, PiControlGenerateLegacyDevice)
#pragma alloc_text(PAGE, PiControlGetInterfaceDeviceList)
#pragma alloc_text(PAGE, PiControlGetPropertyData)
#pragma alloc_text(PAGE, PiControlDeviceClassAssociation)
#pragma alloc_text(PAGE, PiControlGetRelatedDevice)
#pragma alloc_text(PAGE, PiControlGetInterfaceDeviceAlias)
#pragma alloc_text(PAGE, PiControlGetSetDeviceStatus)
#pragma alloc_text(PAGE, PiControlGetDeviceDepth)
#pragma alloc_text(PAGE, PiControlQueryDeviceRelations)
#pragma alloc_text(PAGE, PiControlQueryTargetDeviceRelation)
#pragma alloc_text(PAGE, PiControlQueryConflictList)
#pragma alloc_text(PAGE, PiControlGetDevicePowerData)
#pragma alloc_text(PAGE, PiControlRetrieveDockData)
#pragma alloc_text(PAGE, PiControlHaltDevice)
#pragma alloc_text(PAGE, PiControlGetBlockedDriverData)
#if DBG
#pragma alloc_text(PAGE, PiControlExceptionFilter)
#endif
#endif // ALLOC_PRAGMA
//
// This table contains handlers for all the messages coming from the
// umpnpmgr.dll.
//
PLUGPLAY_CONTROL_HANDLER_DATA PlugPlayHandlerTable[] = {
{ PlugPlayControlEnumerateDevice,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
PiControlEnumerateDevice },
{ PlugPlayControlRegisterNewDevice,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
PiControlRegisterNewDevice },
{ PlugPlayControlDeregisterDevice,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
PiControlDeregisterDevice },
{ PlugPlayControlInitializeDevice,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
PiControlInitializeDevice },
{ PlugPlayControlStartDevice,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
PiControlStartDevice },
{ PlugPlayControlUnlockDevice,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
NULL },
{ PlugPlayControlQueryAndRemoveDevice,
sizeof(PLUGPLAY_CONTROL_QUERY_AND_REMOVE_DATA),
PiControlQueryAndRemoveDevice },
{ PlugPlayControlUserResponse,
sizeof(PLUGPLAY_CONTROL_USER_RESPONSE_DATA),
PiControlUserResponse },
{ PlugPlayControlGenerateLegacyDevice,
sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA),
PiControlGenerateLegacyDevice },
{ PlugPlayControlGetInterfaceDeviceList,
sizeof(PLUGPLAY_CONTROL_INTERFACE_LIST_DATA),
PiControlGetInterfaceDeviceList },
{ PlugPlayControlProperty,
sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA),
PiControlGetPropertyData },
{ PlugPlayControlDeviceClassAssociation,
sizeof(PLUGPLAY_CONTROL_CLASS_ASSOCIATION_DATA),
PiControlDeviceClassAssociation },
{ PlugPlayControlGetRelatedDevice,
sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA),
PiControlGetRelatedDevice },
{ PlugPlayControlGetInterfaceDeviceAlias,
sizeof(PLUGPLAY_CONTROL_INTERFACE_ALIAS_DATA),
PiControlGetInterfaceDeviceAlias },
{ PlugPlayControlDeviceStatus,
sizeof(PLUGPLAY_CONTROL_STATUS_DATA),
PiControlGetSetDeviceStatus },
{ PlugPlayControlGetDeviceDepth,
sizeof(PLUGPLAY_CONTROL_DEPTH_DATA),
PiControlGetDeviceDepth },
{ PlugPlayControlQueryDeviceRelations,
sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA),
PiControlQueryDeviceRelations },
{ PlugPlayControlTargetDeviceRelation,
sizeof(PLUGPLAY_CONTROL_TARGET_RELATION_DATA),
PiControlQueryTargetDeviceRelation },
{ PlugPlayControlQueryConflictList,
sizeof(PLUGPLAY_CONTROL_CONFLICT_DATA),
PiControlQueryConflictList },
{ PlugPlayControlRetrieveDock,
sizeof(PLUGPLAY_CONTROL_RETRIEVE_DOCK_DATA),
PiControlRetrieveDockData },
{ PlugPlayControlResetDevice,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
PiControlResetDevice },
{ PlugPlayControlHaltDevice,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
PiControlHaltDevice },
{ PlugPlayControlGetBlockedDriverList,
sizeof(PLUGPLAY_CONTROL_BLOCKED_DRIVER_DATA),
PiControlGetBlockedDriverData },
{ MaxPlugPlayControl,
0,
NULL }
};
NTSTATUS
NtPlugPlayControl(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PVOID PnPControlData,
IN ULONG PnPControlDataLength
)
/*++
Routine Description:
This Plug and Play Manager API provides a mechanism for the user-mode
PnP Manager to control the activity of its kernel-mode counterpart.
Arguments:
PnPControlClass - Specifies what action to perform.
PnPControlData - Supplies a pointer to data specific to this action.
PnPControlDataLength - Specifies the size, in bytes, of the buffer pointed
to by PnPControlData
Return Value:
NT status code indicating success or failure. Set of possible return
values includes the following:
STATUS_SUCCESS - normal, successful completion.
STATUS_INVALID_PARAMETER_1 - The PnPControlClass parameter did not
specify a valid control class.
STATUS_INVALID_PARAMETER_MIX - The value of the PnPControlDataLength
parameter did not match the length required for the control
class requested by the PnPControlClass parameter.
STATUS_BUFFER_TOO_SMALL - The size of the supplied output buffer is not
large enough to hold the output generated by this control class.
STATUS_ACCESS_VIOLATION - One of the following pointers specified
an invalid address: (1) the PnPControlData buffer pointer,
(2) some pointer contained in the PnPControlData buffer.
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist
for this request to complete.
--*/
{
NTSTATUS status, tempStatus;
KPROCESSOR_MODE previousMode;
ULONG index;
PPLUGPLAY_CONTROL_HANDLER_DATA handlerData;
PVOID controlDataSnapshot;
PAGED_CODE();
//
// Get previous processor mode and probe arguments if necessary.
//
previousMode = KeGetPreviousMode();
if (previousMode != KernelMode) {
//
// Does the caller have "trusted computer base" privilge?
//
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) {
IopDbgPrint((IOP_IOAPI_WARNING_LEVEL,
"NtPlugPlayControl: SecurityCheck failed\n"));
return STATUS_PRIVILEGE_NOT_HELD;
}
}
//
// Look through the table to find the appropriate handler. Note that
// the control class *should* be an index into the table itself.
//
index = (ULONG)PnPControlClass;
handlerData = NULL;
if (index < MaxPlugPlayControl) {
if (PlugPlayHandlerTable[index].ControlCode == PnPControlClass) {
handlerData = &PlugPlayHandlerTable[index];
} else {
//
// Someone broke the table.
//
IopDbgPrint((IOP_IOAPI_ERROR_LEVEL,
"NtPlugPlayControl: Lookup table isn't ordered correctly (entry %d)!\n",
PnPControlClass));
ASSERT(PlugPlayHandlerTable[index].ControlCode == PnPControlClass);
return STATUS_INTERNAL_ERROR;
}
}
//
// Do we have handler data?
//
if (handlerData == NULL) {
//
// Invalid control class.
//
IopDbgPrint((IOP_IOAPI_ERROR_LEVEL,
"NtPlugPlayControl: Unknown control class, Class = %d, Size = %d\n",
PnPControlClass,
PnPControlDataLength));
return STATUS_INVALID_PARAMETER_1;
}
//
// No control function means not implemented.
//
if (handlerData->ControlFunction == NULL) {
return STATUS_NOT_IMPLEMENTED;
}
//
// Check the data size.
//
if (handlerData->ControlDataSize != PnPControlDataLength) {
IopDbgPrint((IOP_IOAPI_ERROR_LEVEL,
"NtPlugPlayControl: Invalid size for control, Class = %d, Size = %d\n",
PnPControlClass,
PnPControlDataLength));
return STATUS_INVALID_PARAMETER_MIX;
}
//
// Make copy of caller's buffer.
//
status = PiControlMakeUserModeCallersCopy(
&controlDataSnapshot,
PnPControlData,
PnPControlDataLength,
sizeof(ULONG),
previousMode,
TRUE
);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Invoke the handler.
//
status = handlerData->ControlFunction(
PnPControlClass,
controlDataSnapshot,
PnPControlDataLength,
previousMode
);
//
// Copy the buffer if the operation was successful or the value is
// a warning like STATUS_BUFFER_OVERFLOW.
//
// ISSUE - 2000/09/11 - Misused STATUS code
// Here we hack around the fact that we've been returning
// STATUS_BUFFER_TOO_SMALL instead of STATUS_BUFFER_OVERFLOW. This
// should be fixed here and in UMPNPMGR.
//
if ((!NT_ERROR(status)) || (status == STATUS_BUFFER_TOO_SMALL)) {
//
// Copy result back into caller's buffer.
//
tempStatus = PiControlMakeUserModeCallersCopy(
&PnPControlData,
controlDataSnapshot,
PnPControlDataLength,
sizeof(ULONG),
previousMode,
FALSE
);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
//
// Free buffer allocated for user mode caller.
//
PiControlFreeUserModeCallersBuffer(previousMode, controlDataSnapshot);
return status;
}
NTSTATUS
PiControlCopyUserModeCallersBuffer(
IN PVOID Destination,
IN PVOID Src,
IN ULONG Length,
IN ULONG Alignment,
IN KPROCESSOR_MODE CallerMode,
IN BOOLEAN ReadUserModeBuffer
)
{
NTSTATUS status;
PAGED_CODE();
status = STATUS_SUCCESS;
if (CallerMode == KernelMode) {
//
// Copy from Src to Destination.
//
RtlCopyMemory(
Destination,
Src,
Length);
return status;
}
try {
if (ReadUserModeBuffer) {
//
// Probe user-mode buffer before reading from it.
//
ProbeForRead(
Src,
Length,
Alignment);
} else {
//
// Probe user-mode buffer before writing to it.
//
ProbeForWrite(
Destination,
Length,
Alignment);
}
//
// Copy from Src to Destination.
//
RtlCopyMemory(
Destination,
Src,
Length);
} except(PiControlExceptionFilter(GetExceptionInformation())) {
status = GetExceptionCode();
IopDbgPrint((IOP_IOAPI_ERROR_LEVEL,
"PiControlMakeUserModeCallersCopy: Exception 0x%08x copying data to or from user's buffer\n", status));
}
return status;
}
NTSTATUS
PiControlMakeUserModeCallersCopy(
IN OUT PVOID *Destination,
IN PVOID Src,
IN ULONG Length,
IN ULONG Alignment,
IN KPROCESSOR_MODE CallerMode,
IN BOOLEAN AllocateDestination
)
/*++
Routine Description:
This routine copies data from and to a callers (potentially UserMode) buffer
after appropriate probing and under try-except.
If CallerMode is KernelMode, then it does not do any copy.
If CallerMode is not KernelMode and AllocateDestination is TRUE, Src is a user
mode buffer that will be probed. We will also allocate the Destination buffer and
make a copy of the user mode buffer.
If CallerMode is not KernelMode and AllocateDestination is FALSE, Src is the kernel
mode buffer whose data needs to be copied into Destination.
Arguments:
Destination - Receives pointer to buffer allocated if AllocateDestination is TRUE,
else contains the pointer to the buffer where data needs to be copied.
Src - Pointer to data to be copied.
Length - Length of data to be copied in bytes.
Alignment - Alignment for probing user mode buffer.
CallerMode - KernelMode\UserMode
AllocateDestination - If TRUE, allocate Length sized buffer and return the pointer
in Destination.
Return Value:
A NTSTATUS code indicating success or cause of failure.
--*/
{
NTSTATUS status;
PAGED_CODE();
if (CallerMode == KernelMode) {
//
// We really dont need to call this function when CallerMode == KernelMode but
// we do so that the callee does not have to special case.
//
*Destination = Src;
return STATUS_SUCCESS;
}
if (Length == 0) {
*Destination = NULL;
return STATUS_SUCCESS;
}
if (AllocateDestination) {
//
// Allocate kernel mode buffer to copy user data.
//
*Destination = ExAllocatePoolWithQuota(
PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
Length);
if (*Destination == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
status = STATUS_SUCCESS;
if (*Destination) {
status = PiControlCopyUserModeCallersBuffer(
*Destination,
Src,
Length,
Alignment,
CallerMode,
AllocateDestination);
if (!NT_SUCCESS(status)) {
if (AllocateDestination == TRUE) {
ExFreePool(*Destination);
*Destination = NULL;
}
}
}
//
// Return final status.
//
return status;
}
NTSTATUS
PiGetInterfaceDeviceAlias(
IN PUNICODE_STRING SymbolicLinkName,
IN LPGUID AliasClassGuid,
OUT PWSTR AliasSymbolicLinkName,
IN OUT PULONG AliasSymbolicLinkNameLength
)
/*++
Routine Description:
This routine retrieves the interface device of the specified class that aliases
a particular interface device. See IoGetAliasForDeviceClassAssociation for
more details.
Arguments:
SymbolicLinkName - Supplies the name of the interface device whose alias is to
be retrieved.
AliasClassGuid - Supplies a pointer to the GUID representing the interface class
in which an alias of SymbolicLinkName is to be found.
AliasSymbolicLinkName - Supplies a character buffer that, upon success, receives
the name of the alias interface device.
AliasSymbolicLinkNameLength - Supplies the length, in bytes, of the
AliasSymbolicLinkName character buffer.
RequiredLength - Supplies the address of a variable that will be filled in with
the number of bytes (including terminating NULL) required to store the
interface device name in the AliasSymbolicLinkName buffer. This will be
filled in upon successful return, or when the return is STATUS_BUFFER_TOO_SMALL.
Return Value:
A NTSTATUS code indicating success or cause of failure.
--*/
{
NTSTATUS status;
UNICODE_STRING aliasString;
PAGED_CODE();
status = IoGetDeviceInterfaceAlias( SymbolicLinkName,
AliasClassGuid,
&aliasString);
if (NT_SUCCESS(status)) {
if (aliasString.Length < *AliasSymbolicLinkNameLength) {
RtlCopyMemory(AliasSymbolicLinkName, aliasString.Buffer, aliasString.Length);
*(PWCHAR)((PUCHAR)AliasSymbolicLinkName + aliasString.Length) = L'\0';
*AliasSymbolicLinkNameLength = aliasString.Length;
} else {
*AliasSymbolicLinkNameLength = aliasString.Length + sizeof(UNICODE_NULL);
status = STATUS_BUFFER_TOO_SMALL;
}
ExFreePool(aliasString.Buffer);
}
return status;
}
NTSTATUS
PiGenerateLegacyDeviceInstance(
IN PUNICODE_STRING ServiceKeyName,
OUT PWSTR DeviceInstance,
IN OUT PULONG DeviceInstanceLength
)
/*++
Routine Description:
This routine creates a new instance node under System\Enum\Root\LEGACY_<Name>
key and all the required default value entries. Also a value entry under
Service\ServiceKeyName\Enum is created to point to the newly created madeup
entry. A handle and the keyname of the new key are returned to caller.
Caller must free the unicode string when he is done with it.
Arguments:
ServiceKeyName - Supplies a pointer to the name of the subkey in the
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
that caused the driver to load.
DeviceInstance - Supplies a pointer to the character buffer that receives the
newly-generated device instance name.
DeviceInstanceLength - Supplies the size, in bytes, of the DeviceInstance
buffer.
Return Value:
A NTSTATUS code.
If the legacy device instance exists already, this function returns success.
--*/
{
NTSTATUS status;
HANDLE handle;
ULONG junk;
UNICODE_STRING tempUnicodeString;
PAGED_CODE();
PiLockPnpRegistry(FALSE);
status = PipCreateMadeupNode(ServiceKeyName,
&handle,
&tempUnicodeString,
&junk,
TRUE
);
if (NT_SUCCESS(status)) {
//
// We have successfully retrieved the newly-generated device instance name.
// Now store it in the supplied buffer.
//
ZwClose(handle);
if (tempUnicodeString.Length < *DeviceInstanceLength) {
RtlCopyMemory(DeviceInstance,
tempUnicodeString.Buffer,
tempUnicodeString.Length);
*(PWCHAR)((PUCHAR)DeviceInstance + tempUnicodeString.Length) = L'\0';
*DeviceInstanceLength = tempUnicodeString.Length;
} else {
*DeviceInstanceLength = tempUnicodeString.Length + sizeof(UNICODE_NULL);
status = STATUS_BUFFER_TOO_SMALL;
}
RtlFreeUnicodeString(&tempUnicodeString);
}
PiUnlockPnpRegistry();
return status;
}
NTSTATUS
PiQueueQueryAndRemoveEvent(
IN PUNICODE_STRING DeviceInstance,
IN PPNP_VETO_TYPE VetoType,
IN LPWSTR VetoName,
IN PULONG VetoNameLength,
IN ULONG Flags
)
/*++
Routine Description:
This routine queues an event to handle the specified operation later in
the context of a system thread. There is one master event queue and all
events are handled in the order they were submitted.
This routine also handles user-mode requests to eject the device specified
in DeviceInstance. If the device's capabilities report the device
ejectable or lockable then it is handled by the same code that processes
IoRequestDeviceEject, otherwise the driver stack is removed and the device
node is marked with the problem CM_PROB_DEVICE_NOT_THERE which prevents it
from being reenumerated until the device is physically removed. This later
method is used primarily for things like PCCARD devices.
Arguments:
DeviceInstance - Supplies the device instance name of the device that is
the target of the event.
EventGuid - This is the GUID that uniquely identifies the type of event.
Synchronous - This is a boolean flag indicating whether the action should
be performed synchronously or asynchronously (synchronous if TRUE).
Return Value:
A NTSTATUS code.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT deviceObject = NULL;
PDEVICE_NODE deviceNode = NULL;
UNICODE_STRING vetoNameString;
PUNICODE_STRING vetoNameStringPtr;
BOOLEAN noRestart, doEject, onlyRestartRelations;
ULONG problem;
KEVENT userEvent;
ULONG eventResult;
PAGED_CODE();
deviceObject = IopDeviceObjectFromDeviceInstance(DeviceInstance);
if (!deviceObject) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean1;
}
//
// Retrieve the device node for this device object.
//
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
if (!deviceNode) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean1;
}
if (deviceNode == IopRootDeviceNode) {
status = STATUS_ACCESS_DENIED;
goto Clean1;
}
vetoNameString.Length = 0;
vetoNameString.MaximumLength = (USHORT)(*VetoNameLength);
if (vetoNameString.MaximumLength != 0) {
vetoNameString.Buffer = ExAllocatePool(PagedPool, vetoNameString.MaximumLength);
if (vetoNameString.Buffer == NULL) {
vetoNameString.MaximumLength = 0;
}
vetoNameStringPtr = &vetoNameString;
} else {
vetoNameString.Buffer = NULL;
vetoNameStringPtr = NULL;
}
//
// Do preprocessing of device node before queueing notification.
//
if (Flags & (PNP_QUERY_AND_REMOVE_DISABLE |
PNP_QUERY_AND_REMOVE_EJECT_DEVICE)) {
noRestart = TRUE;
} else {
noRestart = FALSE;
}
//
// Nobody has ever used this flag. We should not see it here, and we ignore
// it if we do see it.
//
ASSERT(!(Flags & PNP_QUERY_AND_REMOVE_UNINSTALL));
onlyRestartRelations = FALSE;
if (Flags & PNP_QUERY_AND_REMOVE_DISABLE) {
//
// this particular problem may cause a
// "NonDisableable" Veto
//
problem = CM_PROB_DISABLED;
doEject = FALSE;
} else if (Flags & PNP_QUERY_AND_REMOVE_EJECT_DEVICE) {
problem = CM_PROB_HELD_FOR_EJECT;
doEject = TRUE;
} else {
problem = CM_PROB_WILL_BE_REMOVED;
doEject = FALSE;
if (Flags & PNP_QUERY_AND_REMOVE_NO_RESTART) {
onlyRestartRelations = TRUE;
}
}
//
// Queue this device event
//
KeInitializeEvent(&userEvent, NotificationEvent, FALSE);
//
// Queue the event, this call will return immediately. Note that status
// is the status of the PpSetTargetDeviceChange while result is the
// outcome of the actual event.
//
status = PpSetTargetDeviceRemove(deviceObject,
FALSE,
noRestart,
onlyRestartRelations,
doEject,
problem,
&userEvent,
&eventResult,
VetoType,
vetoNameStringPtr);
//
// Drop the ref since there is now one in PpSetTargetDeviceRemove itself.
//
ObDereferenceObject(deviceObject);
deviceObject = NULL;
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Wait for the event we just queued to finish since synchronous operation
// was requested (non alertable wait).
//
// FUTURE ITEM - Use a timeout here?
//
status = KeWaitForSingleObject(&userEvent, Executive, KernelMode, FALSE, NULL);
if (NT_SUCCESS(status)) {
status = eventResult;
}
if (vetoNameString.Length != 0) {
if (vetoNameString.Length >= vetoNameString.MaximumLength) {
vetoNameString.Length--;
}
RtlCopyMemory(VetoName, vetoNameString.Buffer, vetoNameString.Length);
VetoName[ vetoNameString.Length / sizeof(WCHAR) ] = L'\0';
}
if (VetoNameLength != NULL) {
*VetoNameLength = vetoNameString.Length;
}
Clean0:
if (vetoNameString.Buffer != NULL) {
ExFreePool(vetoNameString.Buffer);
}
Clean1:
if (deviceObject) {
ObDereferenceObject(deviceObject);
}
return status;
} // PiQueueDeviceEvent
NTSTATUS
PiInitializeDevice(
IN PUNICODE_STRING DeviceInstance
)
/*++
Routine Description:
This routine creates a devnode for the device instance and performs
any other necessary initialization of the device instance.
Arguments:
DeviceInstance - Supplies the path in the registry (relative to
HKLM\System\Enum) to the device instance to initalize.
Return Value:
NT status code indicating success or failure of this routine.
--*/
{
NTSTATUS status;
UNICODE_STRING serviceName;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
ULONG deviceFlags;
HANDLE hEnum, hDevInst;
PDEVICE_OBJECT deviceObject;
PDEVICE_NODE deviceNode = NULL;
PAGED_CODE();
hEnum = NULL;
hDevInst = NULL;
keyValueInformation = NULL;
//
// Acquire lock on the registry before we do any initialization.
//
PiLockPnpRegistry(TRUE);
deviceObject = IopDeviceObjectFromDeviceInstance(DeviceInstance);
if (deviceObject) {
ObDereferenceObject(deviceObject);
status = STATUS_SUCCESS;
goto Clean0;
}
//
// Open a key to HKLM\SYSTEM\CCC\Enum
//
status = IopOpenRegistryKeyEx( &hEnum,
NULL,
&CmRegistryMachineSystemCurrentControlSetEnumName,
KEY_ALL_ACCESS);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Open a key to the specified device instance
//
status = IopCreateRegistryKeyEx( &hDevInst,
hEnum,
DeviceInstance,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
NULL);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// We need to propagate the ConfigFlag to problem and values (devnode flags)
//
deviceFlags = 0;
status = IopGetRegistryValue(hDevInst,
REGSTR_VALUE_CONFIG_FLAGS,
&keyValueInformation);
if (NT_SUCCESS(status)) {
if ((keyValueInformation->Type == REG_DWORD) &&
(keyValueInformation->DataLength >= sizeof(ULONG))) {
deviceFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
}
ExFreePool(keyValueInformation);
keyValueInformation = NULL;
}
//
// Get the "Service=" value entry from KeyHandle
//
PiWstrToUnicodeString(&serviceName, NULL);
status = IopGetRegistryValue(hDevInst,
REGSTR_VALUE_SERVICE,
&keyValueInformation);
if (NT_SUCCESS(status)) {
if ((keyValueInformation->Type == REG_SZ) &&
(keyValueInformation->DataLength != 0)) {
//
// Set up ServiceKeyName unicode string
//
IopRegistryDataToUnicodeString(&serviceName,
(PWSTR)KEY_VALUE_DATA(keyValueInformation),
keyValueInformation->DataLength);
}
//
// Do not free keyValueInformation right now (contains Service Name).
//
}
//
// Create madeup PDO and device node to represent the root device.
//
status = IoCreateDevice( IoPnpDriverObject,
0,
NULL,
FILE_DEVICE_CONTROLLER,
FILE_AUTOGENERATED_DEVICE_NAME,
FALSE,
&deviceObject);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
deviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
status = PipAllocateDeviceNode(deviceObject, &deviceNode);
if (!deviceNode) {
if (status == STATUS_SYSTEM_HIVE_TOO_LARGE) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
goto Clean0;
}
deviceNode->Flags = DNF_MADEUP | DNF_ENUMERATED;
PipSetDevNodeState(deviceNode, DeviceNodeInitialized, NULL);
if (deviceFlags & CONFIGFLAG_REINSTALL) {
PipSetDevNodeProblem(deviceNode, CM_PROB_REINSTALL);
} else if (deviceFlags & CONFIGFLAG_PARTIAL_LOG_CONF) {
PipSetDevNodeProblem(deviceNode, CM_PROB_PARTIAL_LOG_CONF);
}
//
// Make a copy of the device instance path and save it in
// device node.
//
status = PipConcatenateUnicodeStrings(&deviceNode->InstancePath,
DeviceInstance,
NULL);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
if (serviceName.Length != 0) {
//
// Make a copy of the service name and save it in device node.
//
status = PipConcatenateUnicodeStrings(&deviceNode->ServiceName,
&serviceName,
NULL);
} else {
PiWstrToUnicodeString(&deviceNode->ServiceName, NULL);
}
//
// Add an entry into the table to set up a mapping between the DO
// and the instance path.
//
status = IopMapDeviceObjectToDeviceInstance(
deviceNode->PhysicalDeviceObject,
&deviceNode->InstancePath);
if (NT_SUCCESS(status)) {
PpDevNodeInsertIntoTree(IopRootDeviceNode, deviceNode);
//
// Add an event so user-mode will attempt to install this device later.
//
PpSetPlugPlayEvent(&GUID_DEVICE_ENUMERATED,
deviceNode->PhysicalDeviceObject);
}
Clean0:
//
// Clean up.
//
if (!NT_SUCCESS(status)) {
if (deviceObject) {
IoDeleteDevice(deviceObject);
}
}
//
// Release the registry lock.
//
PiUnlockPnpRegistry();
if (keyValueInformation != NULL) {
ExFreePool(keyValueInformation);
}
if (hDevInst) {
ZwClose(hDevInst);
}
if (hEnum) {
ZwClose(hEnum);
}
return status;
} // PiInitializeDevice
NTSTATUS
PiDetectResourceConflict(
IN PCM_RESOURCE_LIST ResourceList,
IN ULONG ResourceListSize
)
/*++
Routine Description:
This routine is invoked to test whether the specified resource
list conflicts with any already assigned resources.
Arguments:
ResourceList - Specifies a resource list buffer.
ResourceListSize - Specifies the size of the resource list buffer.
Return Value:
The function value is an NTSTATUS value; STATUS_SUCCESS indicates
that the resources do not conflict, STATUS_INSUFFICIENT_RESOURCES
indicates that the resource conflict with already assigned
resources (or some other NTSTATUS value may indicate a different
internal error).
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
HANDLE handle;
PWSTR buffer;
NTSTATUS status;
UNICODE_STRING DriverName;
ULONG i;
BOOLEAN bTemp;
CM_RESOURCE_LIST EmptyResourceList;
static PDRIVER_OBJECT driverObject = NULL;
PAGED_CODE();
if (driverObject == NULL) {
//
// Driver object has not been created yet, do that now.
//
PiWstrToUnicodeString(&DriverName, L"\\Device\\PlugPlay");
//
// Begin by creating the permanent driver object.
//
InitializeObjectAttributes(&objectAttributes,
&DriverName,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
(HANDLE)NULL,
(PSECURITY_DESCRIPTOR)NULL);
//
// Specify "KernelMode" here since it refers to the source of
// the objectAttributes buffer, not the previous operating system
// mode.
//
status = ObCreateObject(KernelMode,
IoDriverObjectType,
&objectAttributes,
KernelMode,
(PVOID)NULL,
(ULONG)(sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION)),
0,
0,
(PVOID)&driverObject);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Initialize the driver object.
//
RtlZeroMemory(driverObject,
sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION));
driverObject->DriverExtension = (PDRIVER_EXTENSION)(driverObject + 1);
driverObject->DriverExtension->DriverObject = driverObject;
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
driverObject->MajorFunction[i] = NULL; // OK???
}
driverObject->Type = IO_TYPE_DRIVER;
driverObject->Size = sizeof(DRIVER_OBJECT);
driverObject->DriverInit = NULL;
//
// Insert the driver object into the object table.
//
status = ObInsertObject(driverObject,
NULL,
FILE_READ_DATA,
0,
(PVOID *)NULL,
&handle);
if (!NT_SUCCESS(status)) {
//
// Object is dereferenced by the object manager if insert fails.
//
return status;
}
//
// Save the name of the driver so that it can be easily located by functions
// such as error logging.
//
buffer = ExAllocatePool(PagedPool, DriverName.MaximumLength + 2);
if (buffer) {
driverObject->DriverName.Buffer = buffer;
driverObject->DriverName.MaximumLength = DriverName.MaximumLength;
driverObject->DriverName.Length = DriverName.Length;
RtlCopyMemory(driverObject->DriverName.Buffer,
DriverName.Buffer,
DriverName.MaximumLength);
buffer[DriverName.Length / sizeof(UNICODE_NULL)] = L'\0';
}
}
//
// Attempt to acquire the resource, if successful, we know the
// resource is avaiable, overwise assume it conflicts with another
// devices resource's.
//
status = IoReportResourceUsage(NULL,
driverObject,
ResourceList,
ResourceListSize,
NULL,
NULL,
0,
FALSE,
&bTemp);
if (NT_SUCCESS(status)) {
//
// Clear any resources that might have been assigned to my fake device.
//
RtlZeroMemory(&EmptyResourceList, sizeof(CM_RESOURCE_LIST));
IoReportResourceUsage(NULL,
driverObject,
&EmptyResourceList,
sizeof(CM_RESOURCE_LIST),
NULL,
NULL,
0,
FALSE,
&bTemp);
}
if (status == STATUS_CONFLICTING_ADDRESSES) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
} // PiDetectResourceConflict
NTSTATUS
PiGetInterfaceDeviceList(
IN GUID *InterfaceGuid,
IN PUNICODE_STRING DeviceInstance,
IN ULONG Flags,
OUT PWSTR InterfaceList,
IN OUT PULONG InterfaceListSize
)
/*++
Routine Description:
This routine is invoked to return an interface device list based on
the specified interface device guid class and optional device instance.
Arguments:
Return Value:
The function value is an NTSTATUS.
--*/
{
NTSTATUS status;
PWSTR tempBuffer = NULL;
ULONG tempSize = 0;
PAGED_CODE();
//
// Note: This Iop routine allocates a memory buffer and store the
// interface device list in that buffer. I need to copy it to the
// users buffer (if any) and then free it before returning.
//
if (DeviceInstance->Length == 0) {
status = IopGetDeviceInterfaces(InterfaceGuid,
NULL,
Flags,
TRUE, // user-mode format
&tempBuffer,
&tempSize);
} else {
status = IopGetDeviceInterfaces(InterfaceGuid,
DeviceInstance,
Flags,
TRUE, // user-mode format
&tempBuffer,
&tempSize);
}
if (NT_SUCCESS(status)) {
if (InterfaceList) {
//
// Not just asking for the size, copy the buffer too.
//
if (tempSize > *InterfaceListSize) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
RtlCopyMemory(InterfaceList, tempBuffer, tempSize);
}
}
*InterfaceListSize = tempSize;
ExFreePool(tempBuffer);
}
return status;
} // PiGetInterfaceDeviceList
NTSTATUS
PiDeviceClassAssociation(
IN PUNICODE_STRING DeviceInstance,
IN GUID * InterfaceGuid,
IN PUNICODE_STRING Reference, OPTIONAL
IN OUT LPWSTR SymbolicLink,
IN OUT PULONG SymbolicLinkLength,
IN BOOLEAN Register
)
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING tempString;
PAGED_CODE();
if (Register) {
//
// An interface GUID and device instance are required to register a
// symbolic link.
//
if (!ARGUMENT_PRESENT(InterfaceGuid)) {
return STATUS_INVALID_PARAMETER;
}
if ((!ARGUMENT_PRESENT(DeviceInstance)) ||
(DeviceInstance->Buffer == NULL) ||
(DeviceInstance->Length == 0)) {
return STATUS_INVALID_PARAMETER;
}
status = IopRegisterDeviceInterface(DeviceInstance,
InterfaceGuid,
Reference,
TRUE, // user-mode format
&tempString);
if (NT_SUCCESS(status)) {
ASSERT(tempString.Buffer);
if ((tempString.Length + sizeof(UNICODE_NULL)) <= *SymbolicLinkLength) {
//
// copy the returned symbolic link to user buffer
//
RtlCopyMemory(SymbolicLink, tempString.Buffer, tempString.Length);
SymbolicLink[tempString.Length / sizeof(WCHAR)] = L'\0';
*SymbolicLinkLength = tempString.Length + sizeof(UNICODE_NULL);
} else {
//
// return only the length of the registered symbolic link.
//
*SymbolicLinkLength = tempString.Length + sizeof(UNICODE_NULL);
status = STATUS_BUFFER_TOO_SMALL;
}
ExFreePool(tempString.Buffer);
}
} else {
//
// A symbolic link name is required to unregister a device interface.
//
if ((!ARGUMENT_PRESENT(SymbolicLink)) ||
(!ARGUMENT_PRESENT(SymbolicLinkLength)) ||
(*SymbolicLinkLength == 0)) {
return STATUS_INVALID_PARAMETER;
}
RtlInitUnicodeString(&tempString, SymbolicLink);
//
// Unregister any interfaces using this symbolic link
//
status = IopUnregisterDeviceInterface(&tempString);
}
return status;
} // PiDeviceClassAssociation
NTSTATUS
PiGetRelatedDevice(
IN PUNICODE_STRING TargetDeviceInstance,
OUT LPWSTR RelatedDeviceInstance,
IN OUT PULONG RelatedDeviceInstanceLength,
IN ULONG Relation
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT deviceObject, relatedDeviceObject;
PDEVICE_NODE deviceNode, originalDeviceNode, relatedDeviceNode;
PAGED_CODE();
PpDevNodeLockTree(PPL_SIMPLE_READ);
//
// Retrieve the PDO from the device instance string.
//
deviceObject = IopDeviceObjectFromDeviceInstance(TargetDeviceInstance);
if (!deviceObject) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
//
// Retrieve the devnode from the PDO
//
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
if (!deviceNode) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
originalDeviceNode = deviceNode;
if ((deviceNode->State == DeviceNodeDeleted) ||
(deviceNode->State == DeviceNodeDeletePendingCloses)) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
switch (Relation) {
case PNP_RELATION_PARENT:
relatedDeviceNode = deviceNode->Parent;
break;
case PNP_RELATION_CHILD:
relatedDeviceNode = deviceNode->Child;
if (relatedDeviceNode &&
PipIsDevNodeProblem(relatedDeviceNode, CM_PROB_DEVICE_NOT_THERE) &&
(relatedDeviceNode->Flags & DNF_LEGACY_DRIVER)) {
deviceNode = relatedDeviceNode;
//
// Fall through...
//
} else {
break;
}
case PNP_RELATION_SIBLING:
relatedDeviceNode = deviceNode->Sibling;
while (relatedDeviceNode &&
PipIsDevNodeProblem(relatedDeviceNode, CM_PROB_DEVICE_NOT_THERE) &&
(relatedDeviceNode->Flags & DNF_LEGACY_DRIVER)) {
relatedDeviceNode = relatedDeviceNode->Sibling;
}
break;
default:
status = STATUS_INVALID_PARAMETER;
goto Clean0;
}
//
// We now have what we think is the relatedDeviceNode but we need to make
// sure that it hasn't been uninstalled or had its registry info
// removed in some other way. Otherwise we won't be able to find its
// siblings. If we can't map from its InstancePath to a PDO skip it and go
// on to the next sibling.
//
if (Relation != PNP_RELATION_PARENT) {
PiLockPnpRegistry(FALSE);
while (relatedDeviceNode) {
if (relatedDeviceNode->InstancePath.Length != 0) {
//
// Retrieve the PDO from the device instance string.
//
relatedDeviceObject = IopDeviceObjectFromDeviceInstance(&relatedDeviceNode->InstancePath);
if (relatedDeviceObject != NULL) {
ObDereferenceObject(relatedDeviceObject);
break;
}
}
relatedDeviceNode = relatedDeviceNode->Sibling;
}
PiUnlockPnpRegistry();
}
if (relatedDeviceNode != NULL) {
if (*RelatedDeviceInstanceLength > relatedDeviceNode->InstancePath.Length) {
RtlCopyMemory(RelatedDeviceInstance,
relatedDeviceNode->InstancePath.Buffer,
relatedDeviceNode->InstancePath.Length);
*(PWCHAR)((PUCHAR)RelatedDeviceInstance + relatedDeviceNode->InstancePath.Length) = L'\0';
*RelatedDeviceInstanceLength = relatedDeviceNode->InstancePath.Length;
} else {
*RelatedDeviceInstanceLength = relatedDeviceNode->InstancePath.Length + sizeof(UNICODE_NULL);
status = STATUS_BUFFER_TOO_SMALL;
}
} else {
status = STATUS_NO_SUCH_DEVICE;
}
Clean0:
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
if (deviceObject) {
ObDereferenceObject(deviceObject);
}
return status;
} // PiGetRelatedDevice
NTSTATUS
PiQueryDeviceRelations(
IN PUNICODE_STRING DeviceInstance,
IN PNP_QUERY_RELATION Operation,
OUT PULONG BufferLength,
OUT LPWSTR Buffer
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT deviceObject = NULL;
PDEVICE_NODE deviceNode, relatedDeviceNode;
IO_STACK_LOCATION irpSp;
PDEVICE_RELATIONS deviceRelations = NULL;
DEVICE_RELATION_TYPE relationType;
ULONG length = 0, i;
ULONG maxCount, currentCount;
LPWSTR pBuffer;
PAGED_CODE();
//
// Map the private operation code into a DEVICE_RELATION_TYPE enum value.
//
relationType = PiDeviceRelationType(Operation);
if (relationType == (ULONG)-1) {
return STATUS_INVALID_PARAMETER;
}
PpDevNodeLockTree(PPL_SIMPLE_READ);
//
// Retrieve the device object from the device instance string.
//
deviceObject = IopDeviceObjectFromDeviceInstance(DeviceInstance);
if (!deviceObject) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
deviceNode = deviceObject->DeviceObjectExtension->DeviceNode;
ASSERT(deviceNode != NULL);
//
// We don't want to bother with things not in the tree...
//
if ((deviceNode->State == DeviceNodeDeletePendingCloses) ||
(deviceNode->State == DeviceNodeDeleted)) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
if (relationType == BusRelations) {
//
// Querying the bus relations from the FDO has side effects. Besides
// we are really interested in the current relations, not those that
// may be appearing or disappearing.
//
//
// Walk the bus relations list counting the number of children
//
maxCount = 0;
for (relatedDeviceNode = deviceNode->Child;
relatedDeviceNode != NULL;
relatedDeviceNode = relatedDeviceNode->Sibling) {
maxCount++;
}
deviceRelations = ExAllocatePool( PagedPool,
sizeof(DEVICE_RELATIONS) +
maxCount * sizeof(PDEVICE_OBJECT));
if (deviceRelations != NULL) {
deviceRelations->Count = maxCount;
currentCount = 0;
//
// Walk the bus relations list counting the number of relations.
// Note that we carefully take into account that legacy devnodes
// can be added to the root totally asynchronously!
//
for (relatedDeviceNode = deviceNode->Child;
((relatedDeviceNode != NULL) && (currentCount < maxCount));
relatedDeviceNode = relatedDeviceNode->Sibling) {
ObReferenceObject(relatedDeviceNode->PhysicalDeviceObject);
deviceRelations->Objects[currentCount++] =
relatedDeviceNode->PhysicalDeviceObject;
}
ASSERT(currentCount == deviceRelations->Count);
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
//
// Initialize the stack location to pass to IopSynchronousCall()
//
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
//
// Query the device's relations.
//
irpSp.MajorFunction = IRP_MJ_PNP_POWER;
irpSp.MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
irpSp.Parameters.QueryDeviceRelations.Type = relationType;
status = IopSynchronousCall(deviceObject, &irpSp, (PULONG_PTR)&deviceRelations);
if (!NT_SUCCESS(status)) {
deviceRelations = NULL;
}
}
//
// Convert these relation device objects into a multisz list of device instances
//
if (deviceRelations && (deviceRelations->Count > 0)) {
pBuffer = Buffer;
length = sizeof(UNICODE_NULL); // account for that last extra trailing null
for (i = 0; i < deviceRelations->Count; i++) {
relatedDeviceNode = deviceRelations->Objects[i]->DeviceObjectExtension->DeviceNode;
//
// The devnode might be NULL if:
// 1) A driver make a mistake
// 2) We got back a removal/ejection relation on a newly created
// PDO that hasn't made it's way back up to the OS (we don't
// raise the tree lock to BlockReads while an enumeration
// IRP is outstanding...)
//
if (relatedDeviceNode) {
if (pBuffer) {
//
// We're retrieving the device instance strings (not just determining
// required buffer size). Validate buffer size (including room for
// null terminator).
//
if (*BufferLength < length + relatedDeviceNode->InstancePath.Length + sizeof(UNICODE_NULL)) {
//
// ADRIAO ISSUE 02/06/2001 -
// We aren't returning the proper length here. We
// need to continue on, copying nothing more yet
// continuing to calculate the length. This should be
// fixed this in XP+1, once we have time to verify no
// one will get an app compat break.
//
status = STATUS_BUFFER_TOO_SMALL;
goto Clean0;
}
//
// Copy this device instance over to the buffer, null terminate it, and
// update the length used in the buffer so far.
//
RtlCopyMemory(pBuffer,
relatedDeviceNode->InstancePath.Buffer,
relatedDeviceNode->InstancePath.Length);
pBuffer += relatedDeviceNode->InstancePath.Length / sizeof(UNICODE_NULL);
*pBuffer++ = L'\0'; // always need the single-term
}
length += relatedDeviceNode->InstancePath.Length + sizeof(UNICODE_NULL);
}
ObDereferenceObject(deviceRelations->Objects[i]);
}
if (pBuffer) {
*pBuffer = L'\0'; // This is the last, double-term
}
}
Clean0:
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
if (NT_SUCCESS(status)) {
*BufferLength = length;
} else {
*BufferLength = 0;
}
if (deviceRelations) {
ExFreePool(deviceRelations);
}
if (deviceObject) {
ObDereferenceObject(deviceObject);
}
return status;
} // PiQueryDeviceRelations
DEVICE_RELATION_TYPE
PiDeviceRelationType(
PNP_QUERY_RELATION Operation
)
/*++
Routine Description:
This private routine converts the PNP_QUERY_RELATION enum value into a
DEVICE_RELATION_TYPE enum value. User-mode and kernel-mode both know about
PNP_QUERY_RELATION but only kernel-mode knows about DEVICE_RELATION_TYPE.
Arguments:
Operation - Specifies a PNP_QUERY_RELATION enum value
Return Value:
The function returns a DEVICE_RELATION_TYPE enum value.
--*/
{
PAGED_CODE();
switch (Operation) {
case PnpQueryEjectRelations:
return EjectionRelations;
case PnpQueryRemovalRelations:
return RemovalRelations;
case PnpQueryPowerRelations:
return PowerRelations;
case PnpQueryBusRelations:
return BusRelations;
default:
return (ULONG)-1;
}
} // PiDeviceRelationType
VOID
PiControlGetUserFlagsFromDeviceNode(
IN PDEVICE_NODE DeviceNode,
OUT ULONG *StatusFlags
)
/*++
Routine Description:
This private routine converts the DeviceNode's state into the
corresponding user-mode StatusFlags.
Arguments:
DeviceNode - Specifies the DeviceNode get retrieve user flags for.
StatusFlags - Receives the corresponding user-mode status flags.
Return Value:
The function returns an NTSTATUS value.
--*/
{
ULONG returnedFlags;
PAGED_CODE();
//
// Convert DNF_Xxx flags to the appropriate status and problem values.
// With problems, order is important since we only keep track of a single
// problem (use the most recent problem that occured if possible).
//
returnedFlags = (DN_NT_DRIVER | DN_NT_ENUMERATOR);
if (PipAreDriversLoaded(DeviceNode)) {
returnedFlags |= DN_DRIVER_LOADED;
}
if (PipIsDevNodeDNStarted(DeviceNode)) {
returnedFlags |= DN_STARTED;
}
if (DeviceNode->UserFlags & DNUF_WILL_BE_REMOVED) {
returnedFlags |= DN_WILL_BE_REMOVED;
}
if (DeviceNode->UserFlags & DNUF_DONT_SHOW_IN_UI) {
returnedFlags |= DN_NO_SHOW_IN_DM;
}
if (DeviceNode->UserFlags & DNUF_NEED_RESTART) {
returnedFlags |= DN_NEED_RESTART;
}
if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM) {
returnedFlags |= DN_PRIVATE_PROBLEM;
}
if (DeviceNode->Flags & DNF_HAS_PROBLEM) {
returnedFlags |= DN_HAS_PROBLEM;
}
if ((DeviceNode->Flags & DNF_DRIVER_BLOCKED)) {
returnedFlags |= DN_DRIVER_BLOCKED;
}
if ((DeviceNode->Flags & DNF_LEGACY_DRIVER)) {
returnedFlags |= DN_LEGACY_DRIVER;
}
if ((DeviceNode->Flags & DNF_CHILD_WITH_INVALID_ID)) {
returnedFlags |= DN_CHILD_WITH_INVALID_ID;
}
if (DeviceNode->DisableableDepends == 0) {
//
// if there's no reason for us not to be disableable, flag we are disableable
//
returnedFlags |= DN_DISABLEABLE;
}
//
// DN_ROOT_ENUMERATED is currently set on umpnpmgr side based on device
// instance name. We should be able to simply set this flag
// based on the devnode's LevelNumber except that we don't want BIOS
// enumerated devices to have the DN_ROOT_ENUMERATED flag even though they
// are being enumerated by the root enumerator.
//
// DN_REMOVABLE - set on umpnpmgr side based on capabilities bits
// DN_MANUAL - set on umpnpmgr side based on CONFIGFLAG_MANUAL_INSTALL bit.
// DN_NO_WAIT_INSTALL ???
*StatusFlags = returnedFlags;
}
VOID
PpShutdownSystem (
IN BOOLEAN Reboot,
IN ULONG Phase,
IN OUT PVOID *Context
)
/*++
Routine Description:
This routine invokes real code to performs Pnp shutdown preparation.
This is nonpage code and that's why it is so small.
Arguments:
Reboot - specifies if the system is going to reboot.
Phase - specifies the shutdown phase.
Context - at phase 0, it supplies a variable to receive the returned context info.
at phase 1, it supplies a variable to specify the context info.
Return Value:
None.
--*/
{
#if defined(_X86_)
if (Reboot) {
if (!PpDisableFirmwareMapper) {
PnPBiosShutdownSystem(Phase, Context);
}
}
#else
UNREFERENCED_PARAMETER( Reboot );
UNREFERENCED_PARAMETER( Phase );
UNREFERENCED_PARAMETER( Context );
#endif
if (Phase == 0) {
ApphelpCacheShutdown(Phase);
}
}
NTSTATUS
PiQueueDeviceRequest(
IN PUNICODE_STRING DeviceInstance,
IN DEVICE_REQUEST_TYPE RequestType,
IN ULONG Flags,
IN BOOLEAN Synchronous
)
{
PDEVICE_OBJECT deviceObject;
PDEVICE_NODE deviceNode;
KEVENT completionEvent;
NTSTATUS status;
PAGED_CODE();
deviceObject = IopDeviceObjectFromDeviceInstance(DeviceInstance);
if (!deviceObject) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
if (!deviceNode) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
if (Synchronous) {
KeInitializeEvent(&completionEvent, NotificationEvent, FALSE);
}
status = PipRequestDeviceAction( deviceObject,
RequestType,
FALSE,
Flags,
Synchronous ? &completionEvent : NULL,
NULL);
if (NT_SUCCESS(status) && Synchronous) {
status = KeWaitForSingleObject( &completionEvent,
Executive,
KernelMode,
FALSE,
NULL);
}
Clean0:
if (deviceObject != NULL) {
ObDereferenceObject( deviceObject );
}
return status;
}
NTSTATUS
PiControlStartDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine starts the specified device instance.
Arguments:
PnPControlClass - Should be PlugPlayControlStartDevice.
DeviceControlData - Points to buffer describing the operation.
DeviceInstance - Specifies the device instance to be started.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status;
UNICODE_STRING instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlStartDevice);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA));
//
// Validate supplied DeviceInstance.
//
instance.Length = instance.MaximumLength = DeviceControlData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
//
// Make a copy of caller supplied DeviceInstance.
//
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
DeviceControlData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (NT_SUCCESS(status)) {
//
// Queue an event to start the device
//
status = PiQueueDeviceRequest(
&instance,
StartDevice,
0,
TRUE);
//
// Free the copy of user mode supplied DeviceInstance.
//
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
}
return status;
}
NTSTATUS
PiControlResetDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine "resets" a devnode, which means bringing it out of the removed
state without actually starting it.
Arguments:
PnPControlClass - Should be PlugPlayControlResetDevice
ConflictData - Points to buffer that receives conflict data.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
--*/
{
NTSTATUS status;
UNICODE_STRING instance;
UNREFERENCED_PARAMETER (PnPControlDataLength);
UNREFERENCED_PARAMETER (PnPControlClass);
instance.Length = instance.MaximumLength = DeviceControlData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
DeviceControlData->DeviceInstance.Buffer,
DeviceControlData->DeviceInstance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (NT_SUCCESS(status)) {
//
// Queue an event to start the device
//
status = PiQueueDeviceRequest(
&instance,
ResetDevice,
0,
TRUE);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
}
return status;
}
NTSTATUS
PiControlInitializeDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine initializes the specified device instance.
Arguments:
PnPControlClass - Should be PlugPlayControlInitializeDevice.
DeviceControlData - Points to buffer describing the operation.
DeviceInstance - Specifies the device instance to be initialized.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status;
UNICODE_STRING instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlInitializeDevice);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA));
//
// Make a copy of caller supplied DeviceInstance.
//
instance.Length = instance.MaximumLength = DeviceControlData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
DeviceControlData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (NT_SUCCESS(status)) {
status = PiInitializeDevice(&instance);
//
// Free the copy of user mode supplied DeviceInstance.
//
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
}
return status;
}
NTSTATUS
PiControlDeregisterDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine deregisters the specified device instance.
Arguments:
PnPControlClass - Should be PlugPlayControlDeregisterDevice.
DeviceControlData - Points to buffer describing the operation.
DeviceInstance - Specifies the device instance to be deregistered.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status;
UNICODE_STRING instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlDeregisterDevice);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA));
//
// Make a copy of caller supplied DeviceInstance.
//
instance.Length = instance.MaximumLength = DeviceControlData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
DeviceControlData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (NT_SUCCESS(status)) {
//
// Acquire PnP device-specific registry resource for exclusive (read/write) access.
//
PiLockPnpRegistry(TRUE);
status = PiDeviceRegistration(&instance,
FALSE,
NULL);
if (NT_SUCCESS(status)) {
//
// Remove all interfaces to this device.
//
IopRemoveDeviceInterfaces(&instance);
}
PiUnlockPnpRegistry();
//
// Free the copy of user mode supplied DeviceInstance.
//
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
}
return status;
}
NTSTATUS
PiControlRegisterNewDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine registers the specified device instance.
Arguments:
PnPControlClass - Should be PlugPlayControlRegisterNewDevice.
DeviceControlData - Points to buffer describing the operation.
DeviceInstance - Specifies the device instance to be registered.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status;
UNICODE_STRING instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlRegisterNewDevice);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA));
//
// Make a copy of caller supplied DeviceInstance.
//
instance.Length = instance.MaximumLength = DeviceControlData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
DeviceControlData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (NT_SUCCESS(status)) {
status = PpDeviceRegistration(
&instance,
TRUE,
NULL);
//
// Free the copy of user mode supplied DeviceInstance.
//
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
}
return status;
}
NTSTATUS
PiControlEnumerateDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to queue reenumeration of the specified device.
Arguments:
PnPControlClass - Should be PlugPlayControlEnumerateDevice.
DeviceControlData - Points to buffer describing the operation.
DeviceInstance - Specifies the device instance to be reenumerated.
Flags - Specifies type of reenumeration. The following flags are
currently defined:
PNP_ENUMERATE_DEVICE_ONLY - Specifies shallow re-enumeration of
specified device. If not specified, perfoms reenumeration of
the entire device subtree rooted at the specified device.
PNP_ENUMERATE_ASYNCHRONOUS - Specifies that the re-enumeration should
be done asynchronously.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status;
UNICODE_STRING instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlEnumerateDevice);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA));
//
// Make a copy of caller supplied DeviceInstance.
//
instance.Length = instance.MaximumLength = DeviceControlData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
DeviceControlData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Queue a request to enumerate the device
//
status = PiQueueDeviceRequest(
&instance,
(DeviceControlData->Flags & PNP_ENUMERATE_DEVICE_ONLY) ? ReenumerateDeviceOnly : ReenumerateDeviceTree,
0,
(DeviceControlData->Flags & PNP_ENUMERATE_ASYNCHRONOUS) ? FALSE : TRUE);
//
// Free the copy of user mode supplied DeviceInstance.
//
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
return status;
}
NTSTATUS
PiControlQueryAndRemoveDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_QUERY_AND_REMOVE_DATA QueryAndRemoveData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to queue query removal of the specified device.
Arguments:
PnPControlClass - Should be PlugPlayControlQueryAndRemoveDevice.
DeviceControlData - Points to buffer describing the operation.
DeviceInstance - Specifies the device instance to be query removed.
VetoType - Vetotype for query remove failure.
VetoName - Veto information for query remove failure.
VetoNameLength - Length of VetoName buffer.
Flags - Remove specific flags.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_QUERY_AND_REMOVE_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status, tempStatus;
UNICODE_STRING instance;
PWCHAR vetoName;
ULONG vetoNameLength;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlQueryAndRemoveDevice);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_QUERY_AND_REMOVE_DATA));
//
// Validate input device instance length.
//
instance.Length = instance.MaximumLength = QueryAndRemoveData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
vetoName = NULL;
instance.Buffer = NULL;
//
// Check if the caller wants veto information or not.
//
if (QueryAndRemoveData->VetoNameLength && QueryAndRemoveData->VetoName) {
vetoNameLength = QueryAndRemoveData->VetoNameLength * sizeof(WCHAR);
} else {
QueryAndRemoveData->VetoNameLength = vetoNameLength = 0;
}
//
// Allocate our own buffer for veto information for user mode callers,
// otherwise use the supplied one.
//
status = PiControlAllocateBufferForUserModeCaller(
&vetoName,
vetoNameLength,
CallerMode,
QueryAndRemoveData->VetoName);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Make a copy of caller supplied DeviceInstance.
//
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
QueryAndRemoveData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Queue an event to query remove the device
//
status = PiQueueQueryAndRemoveEvent(
&instance,
&QueryAndRemoveData->VetoType,
vetoName,
&vetoNameLength,
QueryAndRemoveData->Flags);
if (vetoName) {
tempStatus = PiControlMakeUserModeCallersCopy(
&QueryAndRemoveData->VetoName,
vetoName,
QueryAndRemoveData->VetoNameLength * sizeof(WCHAR),
sizeof(WCHAR),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
QueryAndRemoveData->VetoNameLength = vetoNameLength / sizeof(WCHAR);
//
// Free vetoName buffer if we allocate one on behalf of user mode caller.
//
Clean0:
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
PiControlFreeUserModeCallersBuffer(CallerMode, vetoName);
return status;
}
NTSTATUS
PiControlUserResponse(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_USER_RESPONSE_DATA UserResponseData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to accept user mode response.
Arguments:
PnPControlClass - Should be PlugPlayControlUserResponse.
UserResponseData - Points to buffer describing the operation.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_USER_RESPONSE_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status;
PWCHAR vetoName;
ULONG vetoNameLength;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlUserResponse);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_USER_RESPONSE_DATA));
if (UserResponseData->VetoNameLength && UserResponseData->VetoName) {
vetoNameLength = UserResponseData->VetoNameLength * sizeof(WCHAR);
} else {
vetoNameLength = 0;
}
//
// Make a copy of callers buffer.
//
status = PiControlMakeUserModeCallersCopy(
&vetoName,
UserResponseData->VetoName,
vetoNameLength,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Copy the user response.
//
PiUserResponse(
UserResponseData->Response,
UserResponseData->VetoType,
vetoName,
vetoNameLength);
PiControlFreeUserModeCallersBuffer(CallerMode, vetoName);
return STATUS_SUCCESS;
}
NTSTATUS
PiControlGenerateLegacyDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA LegacyDevGenData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to generate legacy device instance.
Arguments:
PnPControlClass - Should be PlugPlayControlGenerateLegacyDevice.
UserResponseData - Points to buffer describing the operation.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status, tempStatus;
UNICODE_STRING service;
ULONG instanceLength;
PWCHAR instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlGenerateLegacyDevice);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA));
//
// Validate service name length.
//
service.Length = service.MaximumLength = LegacyDevGenData->ServiceName.Length;
if ( service.Length == 0 ||
service.Length > CWC_TO_CB(MAX_SERVICE_NAME_LEN) ||
(service.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
instance = NULL;
service.Buffer = NULL;
instanceLength = LegacyDevGenData->DeviceInstanceLength * sizeof(WCHAR);
status = PiControlAllocateBufferForUserModeCaller(
&instance,
instanceLength,
CallerMode,
LegacyDevGenData->DeviceInstance);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&service.Buffer,
LegacyDevGenData->ServiceName.Buffer,
service.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiGenerateLegacyDeviceInstance(
&service,
instance,
&instanceLength);
//
// Copy the instance and length to the callers buffer.
//
if (instance) {
tempStatus = PiControlMakeUserModeCallersCopy(
&LegacyDevGenData->DeviceInstance,
instance,
LegacyDevGenData->DeviceInstanceLength * sizeof(WCHAR),
sizeof(WCHAR),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
LegacyDevGenData->DeviceInstanceLength = instanceLength / sizeof(WCHAR);
//
// Release any allocated storage.
//
Clean0:
PiControlFreeUserModeCallersBuffer(CallerMode, service.Buffer);
PiControlFreeUserModeCallersBuffer(CallerMode, instance);
return status;
}
NTSTATUS
PiControlGetInterfaceDeviceList(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_INTERFACE_LIST_DATA InterfaceData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to get devices with specified interface.
Arguments:
PnPControlClass - Should be PlugPlayControlGetInterfaceDeviceList.
InterfaceData - Points to buffer describing the operation.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_INTERFACE_LIST_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status, tempStatus;
UNICODE_STRING instance;
PWCHAR list;
ULONG listSize;
GUID *guid;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlGetInterfaceDeviceList);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_INTERFACE_LIST_DATA));
//
// Validate device instance length if provided.
//
if (InterfaceData->DeviceInstance.Buffer) {
instance.Length = instance.MaximumLength = InterfaceData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
instance.Buffer = NULL;
} else {
PiWstrToUnicodeString(&instance, NULL);
}
list = NULL;
guid = NULL;
//
// For user mode callers, allocate storage to retrieve the interfacelist.
//
if (InterfaceData->InterfaceListSize && InterfaceData->InterfaceList) {
listSize = InterfaceData->InterfaceListSize * sizeof(WCHAR);
} else {
listSize = 0;
}
status = PiControlAllocateBufferForUserModeCaller(
&list,
listSize,
CallerMode,
InterfaceData->InterfaceList);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Copy the user supplied interface GUID.
//
status = PiControlMakeUserModeCallersCopy(
&guid,
InterfaceData->InterfaceGuid,
sizeof(GUID),
sizeof(UCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Copy the user supplied DeviceInstance.
//
if (InterfaceData->DeviceInstance.Buffer) {
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
InterfaceData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
}
//
// Get the interface list.
//
status = PiGetInterfaceDeviceList(
guid,
&instance,
InterfaceData->Flags,
list,
&listSize);
if (list) {
//
// Copy the results into the caller's buffer.
//
tempStatus = PiControlMakeUserModeCallersCopy(
&InterfaceData->InterfaceList,
list,
InterfaceData->InterfaceListSize * sizeof(WCHAR),
sizeof(WCHAR),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
InterfaceData->InterfaceListSize = listSize / sizeof(WCHAR);
Clean0:
//
// Clean up.
//
PiControlFreeUserModeCallersBuffer(CallerMode, guid);
PiControlFreeUserModeCallersBuffer(CallerMode, list);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
return status;
}
NTSTATUS
PiControlGetPropertyData(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to get specified property data.
Arguments:
PnPControlClass - Should be PlugPlayControlProperty.
PropertyData - Points to buffer describing the operation.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status, tempStatus;
UNICODE_STRING instance;
PDEVICE_OBJECT deviceObject;
PDEVICE_NODE deviceNode;
PVOID buffer;
ULONG bufferSize;
DEVICE_REGISTRY_PROPERTY property;
PWCHAR deviceLocationStrings, p;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlProperty);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA));
buffer = NULL;
instance.Length = instance.MaximumLength = PropertyData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
PropertyData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Retrieve the physical device object that corresponds to this devinst
//
PpDevNodeLockTree(PPL_SIMPLE_READ);
deviceObject = IopDeviceObjectFromDeviceInstance(&instance);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
if (!deviceObject) {
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
return STATUS_NO_SUCH_DEVICE;
}
//
// Retrieve the device node for this device object.
//
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
if (!deviceNode) {
status = STATUS_NO_SUCH_DEVICE;
bufferSize = 0;
goto Clean0;
}
bufferSize = PropertyData->BufferSize;
status = PiControlAllocateBufferForUserModeCaller(
&buffer,
bufferSize,
CallerMode,
PropertyData->Buffer
);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
switch(PropertyData->PropertyType) {
case PNP_PROPERTY_PDONAME:
property = DevicePropertyPhysicalDeviceObjectName;
break;
case PNP_PROPERTY_BUSTYPEGUID:
property = DevicePropertyBusTypeGuid;
break;
case PNP_PROPERTY_LEGACYBUSTYPE:
property = DevicePropertyLegacyBusType;
break;
case PNP_PROPERTY_BUSNUMBER:
property = DevicePropertyBusNumber;
break;
case PNP_PROPERTY_ADDRESS:
property = DevicePropertyAddress;
break;
case PNP_PROPERTY_POWER_DATA:
status = PiControlGetDevicePowerData(
deviceNode,
CallerMode,
bufferSize,
buffer,
&PropertyData->BufferSize
);
if (status == STATUS_BUFFER_OVERFLOW) {
//
// See comment in NtPlugPlayControl.
//
status = STATUS_BUFFER_TOO_SMALL;
}
goto Clean0;
case PNP_PROPERTY_REMOVAL_POLICY:
property = DevicePropertyRemovalPolicy;
break;
case PNP_PROPERTY_REMOVAL_POLICY_OVERRIDE:
status = PiGetDeviceRegistryProperty(
deviceObject,
REG_DWORD,
REGSTR_VALUE_REMOVAL_POLICY,
NULL,
buffer,
&PropertyData->BufferSize
);
goto Clean0;
case PNP_PROPERTY_REMOVAL_POLICY_HARDWARE_DEFAULT:
if (bufferSize >= sizeof(ULONG)) {
PpHotSwapGetDevnodeRemovalPolicy(
deviceNode,
FALSE, // Include Registry Override
(PDEVICE_REMOVAL_POLICY) buffer
);
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
PropertyData->BufferSize = sizeof(ULONG);
goto Clean0;
case PNP_PROPERTY_INSTALL_STATE:
property = DevicePropertyInstallState;
break;
case PNP_PROPERTY_LOCATION_PATHS:
deviceLocationStrings = NULL;
status =
PpCriticalGetDeviceLocationStrings(
deviceNode,
&deviceLocationStrings);
if (NT_SUCCESS(status)) {
ASSERT(deviceLocationStrings != NULL);
//
// Compute the length of the returned multi-sz list.
//
p = deviceLocationStrings;
while (*p != UNICODE_NULL) {
p += wcslen(p) + 1;
}
//
// Compute the required buffer length, in bytes.
//
PropertyData->BufferSize =
(ULONG)(((PUCHAR)p - (PUCHAR)deviceLocationStrings) + sizeof(UNICODE_NULL));
if (PropertyData->BufferSize <= bufferSize) {
RtlCopyMemory(
buffer,
deviceLocationStrings,
PropertyData->BufferSize);
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
ExFreePool(deviceLocationStrings);
}
goto Clean0;
default:
status = STATUS_INVALID_PARAMETER;
property = DevicePropertyInstallState; // satisfy W4 compiler
break;
}
if (NT_SUCCESS(status)) {
status = IoGetDeviceProperty( deviceObject,
property,
bufferSize,
buffer,
&PropertyData->BufferSize
);
}
Clean0:
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
ObDereferenceObject(deviceObject);
tempStatus = PiControlMakeUserModeCallersCopy(
&PropertyData->Buffer,
buffer,
bufferSize,
sizeof(UCHAR),
CallerMode,
FALSE
);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
PiControlFreeUserModeCallersBuffer(CallerMode, buffer);
return status;
}
NTSTATUS
PiControlDeviceClassAssociation(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_CLASS_ASSOCIATION_DATA AssociationData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to get device class association.
Arguments:
PnPControlClass - Should be PlugPlayControlDeviceClassAssociation.
AssociationData - Points to buffer describing the operation.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_CLASS_ASSOCIATION_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status, tempStatus;
GUID *guid;
ULONG symLinkLength;
PWCHAR symLink;
UNICODE_STRING instance, reference;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlDeviceClassAssociation);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_CLASS_ASSOCIATION_DATA));
//
// Validate lengths of unicode strings.
//
if (AssociationData->Register) {
instance.Length = instance.MaximumLength = AssociationData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
reference.Length = reference.MaximumLength = AssociationData->Reference.Length;
if (reference.Length & 1) {
return STATUS_INVALID_PARAMETER;
}
}
symLink = NULL;
guid = NULL;
instance.Buffer = NULL;
reference.Buffer = NULL;
if (AssociationData->SymLinkLength && AssociationData->SymLink) {
symLinkLength = AssociationData->SymLinkLength * sizeof(WCHAR);
} else {
symLinkLength = 0;
}
if (AssociationData->Register) {
//
// If registering a device interface, allocate a buffer that is the same
// size as the one supplied by the caller.
//
status = PiControlAllocateBufferForUserModeCaller(
&symLink,
symLinkLength,
CallerMode,
AssociationData->SymLink);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Copy the user supplied interface GUID, DeviceInstance and Reference.
//
status = PiControlMakeUserModeCallersCopy(
&guid,
AssociationData->InterfaceGuid,
AssociationData->InterfaceGuid ? sizeof(GUID) : 0,
sizeof(UCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
AssociationData->DeviceInstance.Buffer,
AssociationData->DeviceInstance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&reference.Buffer,
AssociationData->Reference.Buffer,
AssociationData->Reference.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
} else {
//
// If unregistering a device interface, allocate and copy only the
// symbolic link path supplied by the caller. Interface GUID,
// DeviceInstance, and Reference are not required for unregistration.
//
if (symLinkLength < sizeof(UNICODE_NULL)) {
status = STATUS_INVALID_PARAMETER;
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&symLink,
AssociationData->SymLink,
symLinkLength,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Make sure the user-supplied buffer is NULL terminated, (the length
// supplied must reflect that).
//
symLink[(symLinkLength - sizeof(UNICODE_NULL)) / sizeof(WCHAR)] = L'\0';
}
//
// Register or unregister the device class association.
//
status = PiDeviceClassAssociation(
&instance,
guid,
&reference,
symLink,
&symLinkLength,
AssociationData->Register);
//
// If a symbolic link was registered, copy the symbolic link name to the
// caller's buffer.
//
if (AssociationData->Register && symLink && NT_SUCCESS(status)) {
tempStatus = PiControlMakeUserModeCallersCopy(
&AssociationData->SymLink,
symLink,
AssociationData->SymLinkLength * sizeof(WCHAR),
sizeof(WCHAR),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
//
// Return the size of the symbolic link name, in characters.
//
AssociationData->SymLinkLength = symLinkLength / sizeof(WCHAR);
Clean0:
//
// Clean up.
//
PiControlFreeUserModeCallersBuffer(CallerMode, guid);
PiControlFreeUserModeCallersBuffer(CallerMode, symLink);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
PiControlFreeUserModeCallersBuffer(CallerMode, reference.Buffer);
return status;
}
NTSTATUS
PiControlGetRelatedDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to get a related device.
Arguments:
PnPControlClass - Should be PlugPlayControlGetRelatedDevice.
RelatedData - Points to buffer describing the operation.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status, tempStatus;
UNICODE_STRING instance;
PWCHAR buffer;
ULONG length;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlGetRelatedDevice);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA));
//
// Validate length of device instance.
//
instance.Length = instance.MaximumLength = RelatedData->TargetDeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
buffer = NULL;
instance.Buffer = NULL;
if (RelatedData->RelatedDeviceInstance && RelatedData->RelatedDeviceInstanceLength) {
length = RelatedData->RelatedDeviceInstanceLength * sizeof(WCHAR);
} else {
length = 0;
}
status = PiControlAllocateBufferForUserModeCaller(
&buffer,
length,
CallerMode,
RelatedData->RelatedDeviceInstance);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
RelatedData->TargetDeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiGetRelatedDevice(
&instance,
buffer,
&length,
RelatedData->Relation);
if (buffer) {
tempStatus = PiControlMakeUserModeCallersCopy(
&RelatedData->RelatedDeviceInstance,
buffer,
RelatedData->RelatedDeviceInstanceLength * sizeof(WCHAR),
sizeof(WCHAR),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
RelatedData->RelatedDeviceInstanceLength = length / sizeof(WCHAR);
Clean0:
//
// Release any allocated storage.
//
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
PiControlFreeUserModeCallersBuffer(CallerMode, buffer);
return status;
}
NTSTATUS
PiControlGetInterfaceDeviceAlias(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_INTERFACE_ALIAS_DATA InterfaceAliasData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to get alias for device interface.
Arguments:
PnPControlClass - Should be PlugPlayControlGetInterfaceDeviceAlias.
InterfaceAliasData - Points to buffer describing the operation.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_INTERFACE_ALIAS_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
NTSTATUS status, tempStatus;
PWCHAR alias;
UNICODE_STRING linkName;
GUID *guid;
ULONG aliasLength;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlGetInterfaceDeviceAlias);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_INTERFACE_ALIAS_DATA));
//
// Validate length of unicode string.
//
linkName.Length = linkName.MaximumLength = InterfaceAliasData->SymbolicLinkName.Length;
if (linkName.Length & 1) {
return STATUS_INVALID_PARAMETER;
}
alias = NULL;
guid = NULL;
linkName.Buffer = NULL;
if (InterfaceAliasData->AliasSymbolicLinkName && InterfaceAliasData->AliasSymbolicLinkNameLength) {
aliasLength = InterfaceAliasData->AliasSymbolicLinkNameLength * sizeof(WCHAR);
} else {
aliasLength = 0;
}
status = PiControlAllocateBufferForUserModeCaller(
&alias,
aliasLength,
CallerMode,
InterfaceAliasData->AliasSymbolicLinkName);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&guid,
InterfaceAliasData->AliasClassGuid,
sizeof(GUID),
sizeof(UCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&linkName.Buffer,
InterfaceAliasData->SymbolicLinkName.Buffer,
linkName.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiGetInterfaceDeviceAlias(
&linkName,
guid,
alias,
&aliasLength);
if (alias) {
tempStatus = PiControlMakeUserModeCallersCopy(
&InterfaceAliasData->AliasSymbolicLinkName,
alias,
InterfaceAliasData->AliasSymbolicLinkNameLength * sizeof(WCHAR),
sizeof(WCHAR),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
InterfaceAliasData->AliasSymbolicLinkNameLength = aliasLength / sizeof(WCHAR);
Clean0:
//
// Release any allocated storage.
//
PiControlFreeUserModeCallersBuffer(CallerMode, alias);
PiControlFreeUserModeCallersBuffer(CallerMode, guid);
PiControlFreeUserModeCallersBuffer(CallerMode, linkName.Buffer);
return status;
}
NTSTATUS
PiControlGetSetDeviceStatus(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_STATUS_DATA StatusData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is used to get the cfgmgr32 status and problem values from
the specified device instance, or to set the appropriate flags on the devnode
so that they reflect the status and problem values (used by CM_Set_DevNode_Status).
Arguments:
PnPControlClass - Should be PlugPlayControlDeviceStatus.
StatusData - Points to buffer describing the operation.
PNP_GET_STATUS:
DeviceInstance - specifies the device instance name of the devnode
to return status information for.
Status - returns the current devnode status.
Problem - returns the current devnode problem (most recent).
PNP_SET_STATUS or PNP_CLEAR_STATUS:
DeviceInstance - specifies the device instance name of the devnode
whose internal flags are to be modified.
Status - supplies the address of a variable containing cfgmgr32
status flags to be translated into their DNF counterparts
to be set/cleared.
Problem - supplies the address of a variable containing a cfgmgr32
problem value to be translated into their DNF
counterparts to be set/cleared.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_STATUS_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
The function returns an NTSTATUS value.
--*/
{
UNICODE_STRING instance;
PDEVICE_OBJECT deviceObject;
PDEVICE_NODE deviceNode = NULL;
NTSTATUS status, result;
KEVENT event;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlDeviceStatus);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
instance.Length = instance.MaximumLength = StatusData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
StatusData->DeviceInstance.Buffer,
StatusData->DeviceInstance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
PpDevNodeLockTree(PPL_SIMPLE_READ);
//
// Retrieve the PDO from the device instance string.
//
deviceObject = IopDeviceObjectFromDeviceInstance(&instance);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
if (deviceObject != NULL) {
//
// Retrieve the devnode from the PDO
//
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
}
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
if (deviceNode == NULL ||
deviceNode == IopRootDeviceNode) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
switch(StatusData->Operation) {
case PNP_GET_STATUS:
//
// Retrieve the status from the devnode and convert it to a
// user-mode Win95 style Problem and Status flag values.
//
PiControlGetUserFlagsFromDeviceNode(
deviceNode,
&StatusData->DeviceStatus);
StatusData->DeviceProblem = deviceNode->Problem;
status = STATUS_SUCCESS;
break;
case PNP_SET_STATUS:
KeInitializeEvent(&event, NotificationEvent, FALSE);
status = PipRequestDeviceAction( deviceObject,
SetDeviceProblem,
FALSE,
(ULONG_PTR) StatusData,
&event,
&result);
if (NT_SUCCESS(status)) {
status = KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
NULL);
if (status == STATUS_WAIT_0) {
status = result;
}
}
break;
case PNP_CLEAR_STATUS:
KeInitializeEvent(&event, NotificationEvent, FALSE);
status = PipRequestDeviceAction( deviceObject,
ClearDeviceProblem,
FALSE,
0,
&event,
&result);
if (NT_SUCCESS(status)) {
status = KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
NULL);
if (status == STATUS_WAIT_0) {
status = result;
}
}
break;
default:
//
// ISSUE - 2000/08/16 - ADRIAO: Maintain behavior?
// We always used to succeed anything not understood!
//
status = STATUS_SUCCESS;
//status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
Clean0:
//
// Release any reference to the device object before returning.
//
if (deviceObject != NULL) {
ObDereferenceObject(deviceObject);
}
return status;
}
NTSTATUS
PiControlGetDeviceDepth(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEPTH_DATA DepthData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is invoked to return the depth of a particular devnode (i.e,
it's depth in the hierarchical devnode tree of parent-child relations).
Arguments:
PnPControlClass - Should be PlugPlayControlGetDeviceDepth.
DepthData - Points to buffer that receives the depth.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEPTH_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
--*/
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
PDEVICE_NODE deviceNode;
UNICODE_STRING instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlGetDeviceDepth);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_DEPTH_DATA));
instance.Length = instance.MaximumLength = DepthData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
DepthData->DeviceInstance.Buffer,
DepthData->DeviceInstance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
status = STATUS_NO_SUCH_DEVICE;
//
// Initiliaze output parameter.
//
DepthData->DeviceDepth = 0;
PpDevNodeLockTree(PPL_SIMPLE_READ);
//
// Retrieve the PDO from the device instance string.
//
deviceObject = IopDeviceObjectFromDeviceInstance(&instance);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
if (deviceObject) {
//
// Retrieve the devnode from the PDO
//
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
if (deviceNode) {
DepthData->DeviceDepth = deviceNode->Level;
status = STATUS_SUCCESS;
}
ObDereferenceObject(deviceObject);
}
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
return status;
}
NTSTATUS
PiControlQueryDeviceRelations(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA RelationsData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is invoked to query and return the device relations of a
particular devnode.
Arguments:
PnPControlClass - Should be PlugPlayControlQueryDeviceRelations.
RelationsData - Points to buffer that receives the depth.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
--*/
{
NTSTATUS status, tempStatus;
UNICODE_STRING instance;
ULONG length;
PVOID buffer;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlQueryDeviceRelations);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA));
//
// Validate device instance.
//
instance.Length = instance.MaximumLength = RelationsData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
buffer = NULL;
instance.Buffer = NULL;
if (RelationsData->BufferLength && RelationsData->Buffer) {
length = RelationsData->BufferLength * sizeof(WCHAR);
} else {
length = 0;
}
status = PiControlAllocateBufferForUserModeCaller(
&buffer,
length,
CallerMode,
RelationsData->Buffer);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
RelationsData->DeviceInstance.Buffer,
instance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiQueryDeviceRelations(&instance,
RelationsData->Operation,
&length,
buffer);
if (buffer) {
tempStatus = PiControlMakeUserModeCallersCopy(
&RelationsData->Buffer,
buffer,
RelationsData->BufferLength * sizeof(WCHAR),
sizeof(WCHAR),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
RelationsData->BufferLength = length / sizeof(WCHAR);
Clean0:
PiControlFreeUserModeCallersBuffer(CallerMode, buffer);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
return status;
}
NTSTATUS
PiControlQueryTargetDeviceRelation(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_TARGET_RELATION_DATA TargetData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine is invoked to query and return the target device relations of a
particular devnode.
Arguments:
PnPControlClass - Should be PlugPlayControlTargetDeviceRelation.
TargetData - Points to buffer that receives the depth.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_TARGET_RELATION_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
--*/
{
NTSTATUS status;
PFILE_OBJECT fileObject;
PDEVICE_NODE deviceNode;
ULONG requiredLength;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlTargetDeviceRelation);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_TARGET_RELATION_DATA));
//
// Retrieve the file object for the specified file handle.
//
status = ObReferenceObjectByHandle(
TargetData->UserFileHandle,
FILE_ANY_ACCESS,
IoFileObjectType,
CallerMode,
(PVOID *)&fileObject,
NULL);
if (!NT_SUCCESS(status)) {
return status;
}
deviceNode = NULL;
TargetData->DeviceInstanceLen *= sizeof(WCHAR);
//
// Now retrieve the actual target device object associate with this
// file object.
//
status = IopGetRelatedTargetDevice(fileObject, &deviceNode);
if (!NT_SUCCESS(status)) {
goto Clean;
}
ASSERT(deviceNode);
requiredLength = deviceNode->InstancePath.Length + sizeof(UNICODE_NULL);
if (TargetData->DeviceInstanceLen < requiredLength) {
status = STATUS_BUFFER_TOO_SMALL;
TargetData->DeviceInstanceLen = requiredLength;
goto Clean;
}
TargetData->DeviceInstanceLen = requiredLength;
status = PiControlCopyUserModeCallersBuffer(
TargetData->DeviceInstance,
deviceNode->InstancePath.Buffer,
requiredLength,
sizeof(WCHAR),
CallerMode,
FALSE);
Clean:
TargetData->DeviceInstanceLen /= sizeof(WCHAR);
if (deviceNode) {
//
// Drop the reference placed by IopGetRelatedTargetDevice.
//
ObDereferenceObject(deviceNode->PhysicalDeviceObject);
}
ObDereferenceObject(fileObject);
return status;
}
NTSTATUS
PiControlQueryConflictList(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_CONFLICT_DATA ConflictData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine retrieves device conflict data.
NOTE: This routine surpasses PiDetectResourceConflict in functionality
Arguments:
PnPControlClass - Should be PlugPlayControlQueryConflictList
ConflictData - Points to buffer that receives conflict data.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_CONFLICT_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
--*/
{
NTSTATUS status, tempStatus;
PDEVICE_OBJECT deviceObject;
PDEVICE_NODE deviceNode;
PVOID list, buffer;
UNICODE_STRING instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlQueryConflictList);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_CONFLICT_DATA));
//
// Sanity check the conflict list size.
//
if ( ConflictData->ConflictBuffer == NULL ||
ConflictData->ConflictBufferSize < MIN_CONFLICT_LIST_SIZE) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Make sure the resource list has one and only one descriptor.
//
if ( ConflictData->ResourceList == NULL ||
ConflictData->ResourceListSize < sizeof(CM_RESOURCE_LIST) ||
ConflictData->ResourceList->Count != 1 ||
ConflictData->ResourceList->List[0].PartialResourceList.Count != 1) {
return STATUS_INVALID_PARAMETER;
}
//
// Validate instance path.
//
instance.Length = instance.MaximumLength = ConflictData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
list = NULL;
buffer = NULL;
deviceObject = NULL;
instance.Buffer = NULL;
status = PiControlMakeUserModeCallersCopy(
&list,
ConflictData->ResourceList,
ConflictData->ResourceListSize,
sizeof(UCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlAllocateBufferForUserModeCaller(
&buffer,
ConflictData->ConflictBufferSize,
CallerMode,
ConflictData->ConflictBuffer);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
ConflictData->DeviceInstance.Buffer,
ConflictData->DeviceInstance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
//
// Preinit for failure
//
status = STATUS_NO_SUCH_DEVICE;
//
// We don't do simple reads because we want to ensure we don't send this
// while a remove is in progress...
//
PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
//
// Retrieve the PDO from the device instance string.
//
deviceObject = IopDeviceObjectFromDeviceInstance(&instance);
if (deviceObject) {
//
// Retrieve the devnode from the PDO
//
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
//
// We don't want to bother with things not in the tree, and we want to
// skip the root.
//
if ( deviceNode && deviceNode != IopRootDeviceNode &&
deviceNode->State != DeviceNodeDeletePendingCloses &&
deviceNode->State != DeviceNodeDeleted) {
//
// parameters validated
//
status = IopQueryConflictList(
deviceObject,
list,
ConflictData->ResourceListSize,
buffer,
ConflictData->ConflictBufferSize,
ConflictData->Flags);
tempStatus = PiControlMakeUserModeCallersCopy(
&ConflictData->ConflictBuffer,
buffer,
ConflictData->ConflictBufferSize,
sizeof(UCHAR),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
}
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
Clean0:
PiControlFreeUserModeCallersBuffer(CallerMode, list);
PiControlFreeUserModeCallersBuffer(CallerMode, buffer);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
if (deviceObject) {
ObDereferenceObject(deviceObject);
}
ConflictData->Status = status;
return status;
}
NTSTATUS
PiControlRetrieveDockData(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_RETRIEVE_DOCK_DATA DockData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine retrieves dock data.
Arguments:
PnPControlClass - Should be PlugPlayControlRetrieveDock
ConflictData - Points to buffer that receives conflict data.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_RETRIEVE_DOCK_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
--*/
{
NTSTATUS status;
PDEVICE_OBJECT dockDevice;
PDEVICE_NODE deviceNode;
ULONG requiredSize;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
ASSERT(PnPControlClass == PlugPlayControlRetrieveDock);
ASSERT(PnPControlDataLength == sizeof(PLUGPLAY_CONTROL_RETRIEVE_DOCK_DATA));
requiredSize = DockData->DeviceInstanceLength * sizeof(WCHAR);
dockDevice = PpProfileRetrievePreferredDockToEject();
if (dockDevice == NULL) {
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
deviceNode = (PDEVICE_NODE)dockDevice->DeviceObjectExtension->DeviceNode;
if (deviceNode == NULL) {
ASSERT(deviceNode);
status = STATUS_NO_SUCH_DEVICE;
goto Clean0;
}
requiredSize = deviceNode->InstancePath.Length + sizeof(UNICODE_NULL);
if (DockData->DeviceInstanceLength < requiredSize) {
status = STATUS_BUFFER_TOO_SMALL;
goto Clean0;
}
status = PiControlCopyUserModeCallersBuffer(
DockData->DeviceInstance,
deviceNode->InstancePath.Buffer,
requiredSize,
sizeof(WCHAR),
CallerMode,
FALSE);
Clean0:
DockData->DeviceInstanceLength = requiredSize / sizeof(WCHAR);
if (dockDevice) {
ObDereferenceObject(dockDevice);
}
return status;
}
NTSTATUS
PiControlGetDevicePowerData(
IN PDEVICE_NODE DeviceNode,
IN KPROCESSOR_MODE CallerMode,
IN ULONG OutputBufferLength,
IN PVOID PowerDataBuffer OPTIONAL,
OUT ULONG *BytesWritten
)
/*++
Routine Description:
This routine retrieves power information for a given devnode.
Arguments:
DeviceNode - The device node to retrieve CM_POWER_DATA for.
CallerMode - Processor mode of caller (UserMode/KernelMode)
OutputBufferLength - Size of the output buffer.
PowerDataBuffer - Points to buffer that receives the power data.
BytesWritten - Receives the number of bytes written into the buffer.
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
If the status is STATUS_BUFFER_OVERFLOW, BytesWritten isn't
filled with OutputBufferLength, but rather the full size of
the requested structure.
--*/
{
NTSTATUS status;
DEVICE_CAPABILITIES deviceCapabilities;
DEVICE_POWER_STATE dState, deepestDeviceWakeState;
SYSTEM_POWER_STATE sState;
ULONG i;
CM_POWER_DATA cmPowerData;
UNREFERENCED_PARAMETER (CallerMode);
//
// The structure size serves as a versioning mechanism. Since we only have
// one version of the data today, we don't have to test OutputBufferLength.
//
cmPowerData.PD_Size = sizeof(CM_POWER_DATA);
*BytesWritten = 0;
if (OutputBufferLength < sizeof(ULONG)) {
//
// Assume the *minimum* structure size.
//
*BytesWritten = cmPowerData.PD_Size;
return STATUS_BUFFER_OVERFLOW;
}
status = PpIrpQueryCapabilities(DeviceNode->PhysicalDeviceObject, &deviceCapabilities);
if (!NT_SUCCESS(status)) {
return STATUS_NO_SUCH_DEVICE;
}
//
// Fill out the "current" power state. Nonstarted devices are said to be
// in D3.
//
if (PipIsDevNodeDNStarted(DeviceNode)) {
PoGetDevicePowerState(
DeviceNode->PhysicalDeviceObject,
&cmPowerData.PD_MostRecentPowerState
);
} else {
cmPowerData.PD_MostRecentPowerState = PowerDeviceD3;
}
//
// Fill out the power data.
//
cmPowerData.PD_Capabilities = PDCAP_D0_SUPPORTED | PDCAP_D3_SUPPORTED;
if (deviceCapabilities.DeviceD1) {
cmPowerData.PD_Capabilities |= PDCAP_D1_SUPPORTED;
}
if (deviceCapabilities.DeviceD2) {
cmPowerData.PD_Capabilities |= PDCAP_D2_SUPPORTED;
}
if (deviceCapabilities.WakeFromD0) {
cmPowerData.PD_Capabilities |= PDCAP_WAKE_FROM_D0_SUPPORTED;
}
if (deviceCapabilities.WakeFromD1) {
cmPowerData.PD_Capabilities |= PDCAP_WAKE_FROM_D1_SUPPORTED;
}
if (deviceCapabilities.WakeFromD2) {
cmPowerData.PD_Capabilities |= PDCAP_WAKE_FROM_D2_SUPPORTED;
}
if (deviceCapabilities.WakeFromD3) {
cmPowerData.PD_Capabilities |= PDCAP_WAKE_FROM_D3_SUPPORTED;
}
if (deviceCapabilities.WarmEjectSupported) {
cmPowerData.PD_Capabilities |= PDCAP_WARM_EJECT_SUPPORTED;
}
RtlCopyMemory(
cmPowerData.PD_PowerStateMapping,
deviceCapabilities.DeviceState,
sizeof(cmPowerData.PD_PowerStateMapping)
);
cmPowerData.PD_D1Latency = deviceCapabilities.D1Latency;
cmPowerData.PD_D2Latency = deviceCapabilities.D2Latency;
cmPowerData.PD_D3Latency = deviceCapabilities.D3Latency;
//
// First examine DeviceWake, then SystemWake, and update the Wake/D-state
// bits appropriately. This is for those older WDM 1.0 bus drivers that
// don't bother to set the DeviceDx and WakeFromDx fields.
//
dState = deviceCapabilities.DeviceWake;
for(i=0; i<2; i++) {
switch(dState) {
case PowerDeviceD0:
cmPowerData.PD_Capabilities |= PDCAP_WAKE_FROM_D0_SUPPORTED;
break;
case PowerDeviceD1:
cmPowerData.PD_Capabilities |= ( PDCAP_D1_SUPPORTED |
PDCAP_WAKE_FROM_D1_SUPPORTED );
break;
case PowerDeviceD2:
cmPowerData.PD_Capabilities |= ( PDCAP_D2_SUPPORTED |
PDCAP_WAKE_FROM_D2_SUPPORTED );
break;
case PowerDeviceD3:
cmPowerData.PD_Capabilities |= PDCAP_WAKE_FROM_D3_SUPPORTED;
break;
default:
ASSERT(0);
case PowerDeviceUnspecified:
break;
}
if (deviceCapabilities.SystemWake != PowerSystemUnspecified) {
dState = deviceCapabilities.DeviceState[deviceCapabilities.SystemWake];
} else {
dState = PowerDeviceUnspecified;
}
}
//
// Calculate the deepest D state for wake
//
if (cmPowerData.PD_Capabilities & PDCAP_WAKE_FROM_D3_SUPPORTED) {
deepestDeviceWakeState = PowerDeviceD3;
} else if (cmPowerData.PD_Capabilities & PDCAP_WAKE_FROM_D2_SUPPORTED) {
deepestDeviceWakeState = PowerDeviceD2;
} else if (cmPowerData.PD_Capabilities & PDCAP_WAKE_FROM_D1_SUPPORTED) {
deepestDeviceWakeState = PowerDeviceD1;
} else if (cmPowerData.PD_Capabilities & PDCAP_WAKE_FROM_D0_SUPPORTED) {
deepestDeviceWakeState = PowerDeviceD0;
} else {
deepestDeviceWakeState = PowerDeviceUnspecified;
}
//
// Now fill in the SystemWake field. If this field is unspecified, then we
// should infer it from the D-state information.
//
sState = deviceCapabilities.SystemWake;
if (sState != PowerSystemUnspecified) {
//
// The D-state for SystemWake should provide enough power to cover
// the deepest device wake state we've found. The only reason this field
// exists is:
// 1) Some systems can handle WakeFromS4/S5, while most can't.
// 2) Some systems use the S state as a proxy for describing
// D3Hot/D3Cold dependancies.
//
ASSERT(deviceCapabilities.DeviceState[sState] <= deepestDeviceWakeState);
} else if (deepestDeviceWakeState != PowerDeviceUnspecified) {
//
// A system wake state wasn't specified, examine each S state and pick
// the first one that supplies enough power to wake the system. Note
// that we start with S3. If a driver doesn't set the SystemWake field
// but can wake the system from D3, we do *not* assume the driver can
// wake the system from S4 or S5.
//
for(sState=PowerSystemSleeping3; sState>=PowerSystemWorking; sState--) {
if ((deviceCapabilities.DeviceState[i] != PowerDeviceUnspecified) &&
(deviceCapabilities.DeviceState[i] <= deepestDeviceWakeState)) {
break;
}
}
//
// If we didn't find a state, sState is PowerSystemUnspecified.
//
}
cmPowerData.PD_DeepestSystemWake = sState;
if (OutputBufferLength < cmPowerData.PD_Size) {
if (ARGUMENT_PRESENT(PowerDataBuffer)) {
RtlCopyMemory(PowerDataBuffer, &cmPowerData, OutputBufferLength);
}
*BytesWritten = cmPowerData.PD_Size;
status = STATUS_BUFFER_OVERFLOW;
} else {
if (ARGUMENT_PRESENT(PowerDataBuffer)) {
RtlCopyMemory(PowerDataBuffer, &cmPowerData, cmPowerData.PD_Size);
}
*BytesWritten = cmPowerData.PD_Size;
status = STATUS_SUCCESS;
}
return status;
}
NTSTATUS
PiControlHaltDevice(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine simulates a surprise remove for a given device.
Arguments:
PnPControlClass - Should be PlugPlayControlHaltDevice
ConflictData - Points to buffer that receives conflict data.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
--*/
{
NTSTATUS status;
UNICODE_STRING instance;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlClass);
UNREFERENCED_PARAMETER (PnPControlDataLength);
instance.Length = instance.MaximumLength = DeviceControlData->DeviceInstance.Length;
if ( instance.Length == 0 ||
instance.Length > CWC_TO_CB(MAX_DEVICE_ID_LEN) ||
(instance.Length & 1)) {
return STATUS_INVALID_PARAMETER;
}
status = PiControlMakeUserModeCallersCopy(
&instance.Buffer,
DeviceControlData->DeviceInstance.Buffer,
DeviceControlData->DeviceInstance.Length,
sizeof(WCHAR),
CallerMode,
TRUE);
if (NT_SUCCESS(status)) {
//
// Queue an event to start the device
//
status = PiQueueDeviceRequest(
&instance,
HaltDevice,
DeviceControlData->Flags,
TRUE);
PiControlFreeUserModeCallersBuffer(CallerMode, instance.Buffer);
}
return status;
}
NTSTATUS
PiControlGetBlockedDriverData(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PPLUGPLAY_CONTROL_BLOCKED_DRIVER_DATA BlockedDriverData,
IN ULONG PnPControlDataLength,
IN KPROCESSOR_MODE CallerMode
)
/*++
Routine Description:
This routine retrieves the information about drivers blocked from loading
on this boot.
Arguments:
PnPControlClass - Should be PlugPlayControlHaltDevice
BlockedDriverData - Points to buffer that receives blocked driver data.
PnPControlDataLength - Should be sizeof(PLUGPLAY_CONTROL_BLOCKED_DRIVER_DATA)
CallerMode - Processor mode of caller (UserMode/KernelMode)
Return Value:
NTSTATUS code (note: must be convertible to user-mode Win32 error)
--*/
{
NTSTATUS status, tempStatus;
ULONG length;
PWCHAR buffer;
PAGED_CODE();
UNREFERENCED_PARAMETER (PnPControlDataLength);
UNREFERENCED_PARAMETER (PnPControlClass);
buffer = NULL;
if (BlockedDriverData->BufferLength && BlockedDriverData->Buffer) {
length = BlockedDriverData->BufferLength;
} else {
length = 0;
}
status = PiControlAllocateBufferForUserModeCaller(
&buffer,
length,
CallerMode,
BlockedDriverData->Buffer);
if (!NT_SUCCESS(status)) {
goto Clean0;
}
status = PpGetBlockedDriverList(
(GUID *)buffer,
&length,
BlockedDriverData->Flags);
if (buffer && NT_SUCCESS(status)) {
tempStatus = PiControlMakeUserModeCallersCopy(
&BlockedDriverData->Buffer,
buffer,
BlockedDriverData->BufferLength,
sizeof(ULONG),
CallerMode,
FALSE);
if (!NT_SUCCESS(tempStatus)) {
status = tempStatus;
}
}
BlockedDriverData->BufferLength = length;
Clean0:
//
// Release any allocated storage.
//
PiControlFreeUserModeCallersBuffer(CallerMode, buffer);
return status;
}
#if DBG
LONG
PiControlExceptionFilter(
IN PEXCEPTION_POINTERS ExceptionPointers
)
{
PAGED_CODE();
IopDbgPrint((IOP_IOAPI_ERROR_LEVEL,
"PiExceptionFilter: Exception = 0x%08X, Exception Record = 0x%p, Context Record = 0x%p\n",
ExceptionPointers->ExceptionRecord->ExceptionCode,
ExceptionPointers->ExceptionRecord,
ExceptionPointers->ContextRecord));
DbgBreakPoint();
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif