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