NT4/private/ntos/pnp/control.c
2020-09-30 17:12:29 +02:00

1308 lines
41 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
control.c
Abstract:
User-mode -> Kernel-mode PnP Manager control routines.
Author:
Lonny McMichael (lonnym) 02/14/95
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// Global driver object that is used by calls to NtPlugPlayControl
// with control type of PlugPlayControlDetectResourceConflict.
//
PDRIVER_OBJECT driverObject = NULL;
#ifdef _PNP_POWER_
//
// Define the context structure for the PiDevicePathToServiceInstance
// callback routine.
//
typedef struct _PI_DEVPATH_TO_SVCINST_CONTEXT {
NTSTATUS ReturnStatus;
PUNICODE_STRING DevicePath;
UNICODE_STRING DeviceInstanceMatch;
} PI_DEVPATH_TO_SVCINST_CONTEXT, *PPI_DEVPATH_TO_SVCINST_CONTEXT;
//
// Prototype utility functions internal to this file.
//
BOOLEAN
PiDevicePathToServiceInstance(
IN HANDLE DeviceInstanceHandle,
IN PUNICODE_STRING DeviceInstancePath,
IN OUT PVOID Context
);
#endif // _PNP_POWER_
NTSTATUS
PiGenerateLegacyDeviceInstance(
IN PUNICODE_STRING ServiceKeyName,
OUT PWSTR DeviceInstance,
IN ULONG DeviceInstanceLength,
OUT PULONG RequiredLength
);
NTSTATUS
PiDetectResourceConflict(
IN PCM_RESOURCE_LIST ResourceList,
IN ULONG ResourceListSize
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtPlugPlayControl)
#pragma alloc_text(PAGE, PiGenerateLegacyDeviceInstance)
#pragma alloc_text(PAGE, PiDetectResourceConflict)
#ifdef _PNP_POWER_
#pragma alloc_text(PAGE, PiQueryRemoveDevice)
#pragma alloc_text(PAGE, PiRemoveDevice)
#pragma alloc_text(PAGE, PiCancelRemoveDevice)
#pragma alloc_text(PAGE, PiAddDevice)
#pragma alloc_text(PAGE, PiEjectDevice)
#pragma alloc_text(PAGE, PiUnlockDevice)
#pragma alloc_text(PAGE, PiQueryDeviceCapabilities)
#pragma alloc_text(PAGE, PiGetDevicePathInformation)
#endif // _PNP_POWER_
#endif // ALLOC_PRAGMA
NTSTATUS
NtPlugPlayControl(
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
IN OUT PVOID PnPControlData,
IN ULONG PnPControlDataLength,
OUT PULONG RequiredLength OPTIONAL
)
/*++
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
RequiredLength - Optional pointer to a variable the receives the actual
size required to store the output data in
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, or
(3) the RequiredLength pointer
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist
for this request to complete.
--*/
{
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode;
PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData;
PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA LegacyDevGenData;
PPLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA DeviceResourceData;
#ifdef _PNP_POWER_
PPLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA DeviceCapabilitiesData;
PPLUGPLAY_CONTROL_DEVICE_PATH_DATA DevicePathData;
ULONG ReturnBufferSize;
#endif
try {
//
// Get previous processor mode and probe arguments if necessary.
//
PreviousMode = KeGetPreviousMode();
if(PreviousMode != KernelMode) {
ProbeForWrite(PnPControlData, PnPControlDataLength, sizeof(ULONG));
if(ARGUMENT_PRESENT(RequiredLength)) {
ProbeForWriteUlong(RequiredLength);
}
}
switch(PnPControlClass) {
case PlugPlayControlQueryRemoveDevice:
case PlugPlayControlRemoveDevice:
case PlugPlayControlCancelRemoveDevice:
case PlugPlayControlAddDevice:
case PlugPlayControlEjectDevice:
case PlugPlayControlUnlockDevice:
case PlugPlayControlEnumerateDevice:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#endif
case PlugPlayControlRegisterNewDevice:
case PlugPlayControlDeregisterDevice:
//
// Validate buffer for all control classes using a
// PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA structure.
//
if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)) {
return STATUS_INVALID_PARAMETER_MIX;
}
DeviceControlData = (PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)PnPControlData;
if(PreviousMode != KernelMode) {
ProbeForRead(DeviceControlData->DeviceInstance.Buffer,
DeviceControlData->DeviceInstance.Length,
sizeof(WCHAR)
);
}
//
// Since this structure is a fixed size, store RequiredLength now, if necessary,
// so we don't have to do it for each class separately.
//
if(ARGUMENT_PRESENT(RequiredLength)) {
*RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA);
}
break;
case PlugPlayControlQueryDeviceCapabilities:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
//
// Validate buffer for all control classes using a
// PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA structure.
//
if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA)) {
return STATUS_INVALID_PARAMETER_MIX;
}
DeviceCapabilitiesData =
(PPLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA)PnPControlData;
if(PreviousMode != KernelMode) {
ProbeForRead(DeviceCapabilitiesData->DeviceInstance.Buffer,
DeviceCapabilitiesData->DeviceInstance.Length,
sizeof(WCHAR)
);
}
#endif
break;
case PlugPlayControlGetDevicePathInformation:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
//
// Validate buffer for all control classes using a
// PLUGPLAY_CONTROL_DEVICE_PATH_DATA structure.
//
if(PnPControlDataLength < sizeof(PLUGPLAY_CONTROL_DEVICE_PATH_DATA)) {
return STATUS_INVALID_PARAMETER_MIX;
}
DevicePathData = (PPLUGPLAY_CONTROL_DEVICE_PATH_DATA)PnPControlData;
if(PreviousMode != KernelMode) {
ProbeForRead(DevicePathData->DevicePath.Buffer,
DevicePathData->DevicePath.Length,
sizeof(WCHAR)
);
}
#endif
break;
case PlugPlayControlGenerateLegacyDevice:
//
// Validate buffer for all control classes using a
// PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA structure.
//
if(PnPControlDataLength < sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)) {
return STATUS_INVALID_PARAMETER_MIX;
}
LegacyDevGenData = (PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)PnPControlData;
if(PreviousMode != KernelMode) {
ProbeForRead(LegacyDevGenData->ServiceName.Buffer,
LegacyDevGenData->ServiceName.Length,
sizeof(WCHAR)
);
}
break;
case PlugPlayControlDetectResourceConflict:
//
// Determine whether resource list specified in buffer
// is avaiable (not conflicting with other devices).
//
if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA)) {
return STATUS_INVALID_PARAMETER_MIX;
}
DeviceResourceData = (PPLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA)PnPControlData;
if(PreviousMode != KernelMode) {
ProbeForRead(DeviceResourceData->DeviceInstance.Buffer,
DeviceResourceData->DeviceInstance.Length,
sizeof(WCHAR)
);
ProbeForRead(DeviceResourceData->ResourceList,
DeviceResourceData->ResourceListSize,
sizeof(UCHAR)
);
}
//
// Since this structure is a fixed size, store RequiredLength now, if necessary,
// so we don't have to do it for each class separately.
//
if(ARGUMENT_PRESENT(RequiredLength)) {
*RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA);
}
break;
default:
//
// Invalid control class.
//
return STATUS_INVALID_PARAMETER_1;
}
//
// Now invoke the proper routine for this control class.
//
switch(PnPControlClass) {
case PlugPlayControlQueryRemoveDevice:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
Status = PiQueryRemoveDevice(&(DeviceControlData->DeviceInstance),
&(DeviceControlData->Status)
);
#endif
break;
case PlugPlayControlRemoveDevice:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
Status = PiRemoveDevice(&(DeviceControlData->DeviceInstance),
&(DeviceControlData->Status)
);
#endif
break;
case PlugPlayControlCancelRemoveDevice:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
Status = PiCancelRemoveDevice(&(DeviceControlData->DeviceInstance),
&(DeviceControlData->Status)
);
#endif
break;
case PlugPlayControlAddDevice:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
Status = PiAddDevice(&(DeviceControlData->DeviceInstance),
&(DeviceControlData->Status)
);
#endif
break;
case PlugPlayControlEjectDevice:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
Status = PiEjectDevice(&(DeviceControlData->DeviceInstance),
&(DeviceControlData->Status)
);
#endif
break;
case PlugPlayControlUnlockDevice:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
Status = PiUnlockDevice(&(DeviceControlData->DeviceInstance),
&(DeviceControlData->Status)
);
#endif
break;
case PlugPlayControlQueryDeviceCapabilities:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
//
// BUGBUG (lonnym): need to add argument for slot capabilities once it
// is defined by KenR.
//
Status = PiQueryDeviceCapabilities(&(DeviceCapabilitiesData->DeviceInstance)
);
if(ARGUMENT_PRESENT(RequiredLength)) {
*RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA);
}
#endif
break;
case PlugPlayControlGetDevicePathInformation:
#ifndef _PNP_POWER_
return STATUS_NOT_IMPLEMENTED;
#else
Status = PiGetDevicePathInformation(
&(DevicePathData->DevicePath),
DevicePathData->ServiceName,
PnPControlDataLength -
FIELD_OFFSET(PLUGPLAY_CONTROL_DEVICE_PATH_DATA, ServiceName),
&ReturnBufferSize,
&(DevicePathData->ServiceNameLength),
&(DevicePathData->DeviceInstanceOffset),
&(DevicePathData->DeviceInstanceLength),
&(DevicePathData->ServiceInstanceOrdinal)
);
if(ARGUMENT_PRESENT(RequiredLength)) {
if(NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) {
*RequiredLength = ReturnBufferSize +
FIELD_OFFSET(PLUGPLAY_CONTROL_DEVICE_PATH_DATA, ServiceName);
}
}
#endif
break;
case PlugPlayControlRegisterNewDevice:
Status = PpDeviceRegistration(&DeviceControlData->DeviceInstance, TRUE);
DeviceControlData->Status = Status;
break;
case PlugPlayControlDeregisterDevice:
Status = PpDeviceRegistration(&DeviceControlData->DeviceInstance, FALSE);
DeviceControlData->Status = Status;
break;
case PlugPlayControlEnumerateDevice:
//
// BUGBUG (lonnym): This is where a call to PiEnumerateDevice() should go.
// This API was previously the NT API, NtEnumerateBus.
//
Status = STATUS_NOT_IMPLEMENTED;
break;
case PlugPlayControlGenerateLegacyDevice:
Status = PiGenerateLegacyDeviceInstance(
&LegacyDevGenData->ServiceName,
LegacyDevGenData->DeviceInstance,
PnPControlDataLength -
FIELD_OFFSET(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA, DeviceInstance),
&LegacyDevGenData->DeviceInstanceLength
);
if(ARGUMENT_PRESENT(RequiredLength)) {
if(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_TOO_SMALL)) {
*RequiredLength = LegacyDevGenData->DeviceInstanceLength +
sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA);
}
}
break;
case PlugPlayControlDetectResourceConflict:
Status = PiDetectResourceConflict(DeviceResourceData->ResourceList,
DeviceResourceData->ResourceListSize);
DeviceResourceData->Status = Status;
break;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
return Status;
}
NTSTATUS
PiGenerateLegacyDeviceInstance(
IN PUNICODE_STRING ServiceKeyName,
OUT PWSTR DeviceInstance,
IN ULONG DeviceInstanceLength,
OUT PULONG RequiredLength
)
/*++
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.
RequiredLength - Supplies a pointer to a variable that receives the size,
in bytes (excluding terminating NULL) of the device instance name stored
in the buffer.
Return Value:
A NTSTATUS code.
If the Lengacy Device Instance exists already, this function returns sucessful.
--*/
{
NTSTATUS status;
HANDLE handle;
ULONG junk;
BOOLEAN isPlugPlayDriver = FALSE;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
UNICODE_STRING tempUnicodeString;
KeEnterCriticalRegion();
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
status = IopOpenServiceEnumKeys(ServiceKeyName,
KEY_READ,
&handle,
NULL,
TRUE
);
if (NT_SUCCESS(status)) {
//
// Check whether this madeup key should be created. It must be a legacy driver
// to create the madeup key.
//
status = IopGetRegistryValue(handle, REGSTR_VALUE_PLUGPLAY_SERVICE_TYPE, &keyValueInformation);
ZwClose(handle);
if (NT_SUCCESS(status)) {
if ((keyValueInformation->Type == REG_DWORD) &&
(keyValueInformation->DataLength >= sizeof(ULONG))) {
isPlugPlayDriver = TRUE;
status = STATUS_INVALID_PARAMETER_2;
}
ExFreePool(keyValueInformation);
}
if (!isPlugPlayDriver) {
status = IopCreateMadeupNode(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);
*RequiredLength = tempUnicodeString.Length;
if(DeviceInstanceLength >= tempUnicodeString.Length + sizeof(UNICODE_NULL)) {
try {
RtlMoveMemory(DeviceInstance,
tempUnicodeString.Buffer,
tempUnicodeString.Length + sizeof(UNICODE_NULL)
);
} except(EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
RtlFreeUnicodeString(&tempUnicodeString);
}
}
}
ExReleaseResource(&PpRegistryDeviceResource);
KeLeaveCriticalRegion();
return status;
}
#ifdef _PNP_POWER_
NTSTATUS
PiQueryRemoveDevice(
IN PUNICODE_STRING DeviceInstance,
OUT PNTSTATUS ReturnedStatus
)
/*++
Routine Description:
This routine queries a device driver for whether a particular device
instance can be removed.
Arguments:
DeviceInstance - Supplies the path in the registry (relative to
HKLM\System\Enum) to the device instance being query-removed.
ReturnedStatus - If the function is successful, this receives the
NT status code returned by the driver in response to the
query-remove.
Return Value:
NT status code indicating success or failure of this routine. If
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
the driver's response to the request.
--*/
{
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
PiRemoveDevice(
IN PUNICODE_STRING DeviceInstance,
OUT PNTSTATUS ReturnedStatus
)
/*++
Routine Description:
This routine causes a device driver to remove a particular device
instance from the system.
Arguments:
DeviceInstance - Supplies the path in the registry (relative to
HKLM\System\Enum) to the device instance to be removed.
ReturnedStatus - If the function is successful, this receives the
NT status code returned by the driver in response to the
remove request.
Return Value:
NT status code indicating success or failure of this routine. If
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
the driver's response to the request.
--*/
{
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
PiCancelRemoveDevice(
IN PUNICODE_STRING DeviceInstance,
OUT PNTSTATUS ReturnedStatus
)
/*++
Routine Description:
This routine cancels a previously-submitted request to query-remove
a device. It should only be called if the device instance was
previously successfully query-removed.
Arguments:
DeviceInstance - Supplies the path in the registry (relative to
HKLM\System\Enum) to the device instance for which a remove
request is to be cancelled.
ReturnedStatus - If the function is successful, this receives the
NT status code returned by the driver in response to the
remove cancel request.
Return Value:
NT status code indicating success or failure of this routine. If
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
the driver's response to the request.
--*/
{
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
PiAddDevice(
IN PUNICODE_STRING DeviceInstance,
OUT PNTSTATUS ReturnedStatus
)
/*++
Routine Description:
This routine causes a device driver to add a new device instance
(i.e., create a new device object).
Arguments:
DeviceInstance - Supplies the path in the registry (relative to
HKLM\System\Enum) to the new device instance to be added.
ReturnedStatus - If the function is successful, this receives the
NT status code returned by the driver in response to the
add device request.
Return Value:
NT status code indicating success or failure of this routine. If
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
the driver's response to the request.
--*/
{
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
PiEjectDevice(
IN PUNICODE_STRING DeviceInstance,
OUT PNTSTATUS ReturnedStatus
)
/*++
Routine Description:
This routine causes the specified device instance to be ejected (if
the device is in a slot that supports soft-eject).
Arguments:
DeviceInstance - Supplies the path in the registry (relative to
HKLM\System\Enum) to the device instance to be ejected.
ReturnedStatus - If the function is successful, this receives the
NT status code returned by the HAL bus extender that controls
the bus where this device is located.
Return Value:
NT status code indicating success or failure of this routine. If
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
the HAL bus extender's response to the request.
--*/
{
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
PiUnlockDevice(
IN PUNICODE_STRING DeviceInstance,
OUT PNTSTATUS ReturnedStatus
)
/*++
Routine Description:
This routine causes the specified device instance to be unlocked
for removal (if the device is in a slot that supports slot locking).
Arguments:
DeviceInstance - Supplies the path in the registry (relative to
HKLM\System\Enum) to the device instance to be unlocked.
ReturnedStatus - If the function is successful, this receives the
NT status code returned by the HAL bus extender that controls
the bus where this device is located.
Return Value:
NT status code indicating success or failure of this routine. If
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
the HAL bus extender's response to the request.
--*/
{
return STATUS_NOT_IMPLEMENTED;
}
//
// BUGBUG (lonnym): un-comment the 2nd parameter to the following
// routine once KenR defines the SLOT_CAPABILITIES structure.
//
NTSTATUS
PiQueryDeviceCapabilities(
IN PUNICODE_STRING DeviceInstance
// ,OUT PSLOT_CAPABILITIES Capabilities
)
/*++
Routine Description:
This routine returns the capabilities of a particular device (e.g.,
is the device soft-ejectable?).
(Note that while these capabilities are really an attribute of the
particular bus slot on which the device resides, the user-mode PnP
manager does not track bus slots--only devices. Therefore, from
user-mode, these attributes are associated with a device instance,
and the kernel-mode PnP manager makes the association between device
instances and actual bus slots.)
Arguments:
DeviceInstance - Supplies the path in the registry (relative to
HKLM\System\Enum) to the device instance whose capabilities
are to be determined.
Capabilities - Pointer to a buffer that receives the capabilities of
the slot in which this device instance is located.
Return Value:
NT status code indicating success or failure of this routine.
--*/
{
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
PiGetDevicePathInformation(
IN PUNICODE_STRING DevicePath,
OUT PWCHAR ServiceName,
IN ULONG BufferLength,
OUT PULONG ReturnLength,
OUT PULONG ServiceNameLength,
OUT PULONG DeviceInstanceOffset,
OUT PULONG DeviceInstanceLength,
OUT PULONG ServiceInstanceOrdinal OPTIONAL
)
/*++
Routine Description:
This routine takes as input an NT device path, and returns the
corresponding service name and (if possible) device instance it
is associated with. It also optionally returns the device instance's
service ordinal as listed under the service entry's volatile Enum subkey.
Arguments:
DevicePath - Supplies the NT device path
ServiceName - Pointer to a character buffer that receives both
the ServiceName _and_ DeviceInstance strings. The
ServiceName will be stored at the beginning of the buffer,
and the DeviceInstance string (if applicable) will be stored
at offset DeviceInstanceOffset in the buffer. Both strings will
be NULL-terminated.
BufferLength - Supplies the length, in bytes, of the buffer pointed
to by ServiceName.
ReturnLength - Receives the size, in bytes, required to store
both strings in the ServiceName buffer. If the buffer isn't
large enough, then no data will be stored in it, and this value
will indicate the size necessary to store the data.
ServiceNameLength - Receives the length, in bytes, of the ServiceName
string stored in the ServiceName buffer (not including terminating
NULL).
DeviceInstanceOffset - If applicable, this value will receive the offset
from the beginning of the ServiceName buffer (in characters) where the
DeviceInstance string is located. If no device instance is associated
with this device path (e.g., the device path was created by a legacy
driver), then this value will be set to zero.
DeviceInstanceLength - Receives the length, in bytes, of the DeviceInstance
string stored in the ServiceName buffer (not including terminating
NULL). If there is no associated device instance, then this value will
be set to zero.
ServiceInstanceOrdinal - If specified, receives the ordinal of the
device instance within the service's volatile Enum list. If there is no
associated device instance (i.e., DeviceInstanceOffset and
DeviceInstanceLength are zero), then this value is set to PLUGPLAY_NO_INSTANCE.
Return Value:
NT status code indicating success or failure of this routine.
--*/
{
NTSTATUS Status;
PFILE_OBJECT FileObject;
PUNICODE_STRING DriverServiceName;
HANDLE ServiceEnumHandle;
PI_DEVPATH_TO_SVCINST_CONTEXT DevPathToSvcInstContext;
ULONG ReturnedServiceOrdinal, DevInstStringLength;
//
// Get a pointer to a file object for the specified device so that we can
// retrieve the controlling service name from its driver object.
//
Status = PiGetDeviceObjectFilePointer(DevicePath,
&FileObject
);
if(!NT_SUCCESS(Status)) {
return Status;
}
DriverServiceName = &(FileObject->DeviceObject->DriverObject->DriverExtension->ServiceKeyName);
//
// The driver object may not have an associated service name, so only search
// for a service instance if it does.
//
if(DriverServiceName->Length) {
//
// Now search through each device instance listed under the service
// entry's volatile Enum subkey, looking for a match in one of the
// NtDevicePaths REG_MULTI_SZ value entries.
//
DevPathToSvcInstContext.ReturnStatus = STATUS_SUCCESS;
DevPathToSvcInstContext.DevicePath = DevicePath;
RtlInitUnicodeString(&(DevPathToSvcInstContext.DeviceInstanceMatch), NULL);
//
// We need to acquire the PnP device registry resource for shared (read) access.
//
KeEnterCriticalRegion();
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
Status = IopApplyFunctionToServiceInstances(NULL,
DriverServiceName,
KEY_READ,
TRUE,
PiDevicePathToServiceInstance,
&DevPathToSvcInstContext,
&ReturnedServiceOrdinal
);
ExReleaseResource(&PpRegistryDeviceResource);
KeLeaveCriticalRegion();
if(NT_SUCCESS(Status) &&
NT_SUCCESS(Status = DevPathToSvcInstContext.ReturnStatus)) {
DevInstStringLength = DevPathToSvcInstContext.DeviceInstanceMatch.Length;
} else {
goto PrepareForReturn1;
}
} else {
DevInstStringLength = 0;
}
//
// Now we know the size of the string buffer required, so check to make sure
// the buffer we were given is large enough, and if so, fill it with the
// string(s).
//
*ReturnLength = DriverServiceName->Length + DevInstStringLength
+ ((DevInstStringLength) ? 2 : 1) * sizeof(WCHAR);
if(BufferLength < *ReturnLength) {
Status = STATUS_BUFFER_TOO_SMALL;
goto PrepareForReturn2;
}
*ServiceNameLength = DriverServiceName->Length;
if(*ServiceNameLength) {
RtlMoveMemory(ServiceName,
DriverServiceName->Buffer,
*ServiceNameLength
);
}
ServiceName[CB_TO_CWC(*ServiceNameLength)] = UNICODE_NULL;
if(*DeviceInstanceLength = DevInstStringLength) {
RtlMoveMemory(&(ServiceName[*DeviceInstanceOffset = CB_TO_CWC(*ServiceNameLength) + 1]),
DevPathToSvcInstContext.DeviceInstanceMatch.Buffer,
*DeviceInstanceLength
);
ServiceName[*DeviceInstanceOffset + CB_TO_CWC(*DeviceInstanceLength)] = UNICODE_NULL;
} else {
*DeviceInstanceOffset = 0;
}
if(ARGUMENT_PRESENT(ServiceInstanceOrdinal)) {
*ServiceInstanceOrdinal = DevInstStringLength ? ReturnedServiceOrdinal
: PLUGPLAY_NO_INSTANCE;
}
PrepareForReturn2:
if(DevInstStringLength) {
ExFreePool(DevPathToSvcInstContext.DeviceInstanceMatch.Buffer);
}
PrepareForReturn1:
ObDereferenceObject(FileObject);
return Status;
}
BOOLEAN
PiDevicePathToServiceInstance(
IN HANDLE DeviceInstanceHandle,
IN PUNICODE_STRING DeviceInstancePath,
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is a callback function for IopApplyFunctionToServiceInstances.
It is called for each device instance key referenced by a service instance
value under the specified service's volatile Enum subkey. Its purpose is to
determine whether this device instance corresponds to a specified NT device
path (as registered in the device instance's NtDevicePaths REG_MULTI_SZ list).
NOTE: The PnP device-specific registry resource must be acquired for shared
(read) access before invoking this routine.
Arguments:
DeviceInstanceHandle - Supplies a handle to the current device instance key.
The access to this key is that specified in the call to
IopApplyFunctionToServiceInstances.
DeviceInstancePath - Supplies the registry path (relative to HKLM\System\Enum)
to this device instance.
Context - Supplies a pointer to a PI_DEVPATH_TO_SVCINST_CONTEXT structure with
the following fields:
NTSTATUS ReturnStatus - Fill this in with the NT error status code if an
error occurs. This is assumed to be initialized to STATUS_SUCCESS
when this routine is called.
PUNICODE_STRING DevicePath - Supplies the NT device path that we're looking
for.
UNICODE_STRING DeviceInstanceMatch - If the current device instance corresponds
to the specified NT device path, then fill this unicode string in with
the device instance path. The caller is responsible for freeing the
(PagedPool) memory allocated for the unicode string buffer.
Return Value:
TRUE to continue the enumeration.
FALSE to abort it. If the current device instance corresponds to the NT device
path being searched for, this function should return FALSE to terminate the
search.
--*/
{
PPI_DEVPATH_TO_SVCINST_CONTEXT DevPathToSvcInstContext;
NTSTATUS Status;
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
PUNICODE_STRING DevicePathList;
ULONG DevicePathCount, i;
DevPathToSvcInstContext = (PPI_DEVPATH_TO_SVCINST_CONTEXT)Context;
//
// Retrieve the NtDevicePath REG_MULTI_SZ list from the device instance key.
//
Status = IopGetRegistryValue(DeviceInstanceHandle,
REGSTR_VALUE_NTDEVICEPATHS,
&KeyValueInformation
);
if(!NT_SUCCESS(Status)) {
//
// Ignore this device instance and continue search
//
return TRUE;
}
Status = IopRegMultiSzToUnicodeStrings(KeyValueInformation,
&DevicePathList,
&DevicePathCount
);
ExFreePool(KeyValueInformation);
if(!NT_SUCCESS(Status)) {
//
// An error here is most likely STATUS_INSUFFICIENT_RESOURCES, which is
// severe enough to abort the search.
//
DevPathToSvcInstContext->ReturnStatus = Status;
return FALSE;
}
//
// Now, search the device path list, looking for a match.
//
for(i = 0; i < DevicePathCount; i++) {
if(RtlEqualUnicodeString(&(DevicePathList[i]),
DevPathToSvcInstContext->DevicePath,
TRUE)) {
//
// We found a match, so store a copy of the device instance path string
// in the DeviceInstanceMatch field of the context structure.
//
if(!IopConcatenateUnicodeStrings(&(DevPathToSvcInstContext->DeviceInstanceMatch),
DeviceInstancePath,
NULL
)) {
DevPathToSvcInstContext->ReturnStatus = STATUS_INSUFFICIENT_RESOURCES;
}
break;
}
}
IopFreeUnicodeStringList(DevicePathList, DevicePathCount);
return (i == DevicePathCount) ? TRUE : FALSE;
}
#endif // _PNP_POWER_
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 bConflictDetected = FALSE, bTemp;
CM_RESOURCE_LIST EmptyResourceList;
if (driverObject == NULL) {
//
// Driver object has not been created yet, do that now.
//
RtlInitUnicodeString(&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)) {
ObMakeTemporaryObject(driverObject); //?
ObDereferenceObject(driverObject); //?
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 >> 1] = (WCHAR) '\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,
TRUE,
&bConflictDetected);
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,
TRUE,
&bTemp);
}
if (NT_SUCCESS(status) && bConflictDetected) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}