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.
5386 lines
166 KiB
5386 lines
166 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
pnpevent.c
|
|
|
|
Abstract:
|
|
|
|
Routines dealing with Plug and Play event management/notification.
|
|
|
|
Author:
|
|
|
|
Lonny McMichael (lonnym) 02/14/95
|
|
Paula Tomlinson (paulat) 07/01/96
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pnpmgrp.h"
|
|
#pragma hdrstop
|
|
#include <wdmguid.h>
|
|
#include <pnpmgr.h>
|
|
#include <pnpsetup.h>
|
|
|
|
/*
|
|
* Design notes:
|
|
*
|
|
* When UmPnpMgr needs to initiate an action (which it might do to complete
|
|
* a CmXxx API), it calls NtPlugPlayControl. NtPlugPlayControl then usually
|
|
* invokes one of the PpSetXxx functions. Similarly, if Io routines need to
|
|
* initiate such an action (say due a hardware initiated eject), they call one
|
|
* of the following PpSetXxx functions (or an intermediate):
|
|
*
|
|
* Operations synchronized via the queue
|
|
* PpSetDeviceClassChange (async)
|
|
* PpSetTargetDeviceRemove (optional event)
|
|
* PpSetCustomTargetEvent (optional event)
|
|
* PpSetHwProfileChangeEvent (optional event)
|
|
* PpSetPowerEvent (optional event)
|
|
* PpSetPlugPlayEvent (async)
|
|
* PpSetDeviceRemovalSafe (optional event)
|
|
* PpSetBlockedDriverEvent (async)
|
|
* PpSynchronizeDeviceEventQueue (sync, enqueues a noop to flush queue)
|
|
*
|
|
* The PpSetXxx functions enqueue items to be processed into the Pnp event
|
|
* queue (via PiInsertEventInQueue). Whenever one of these events are inserted
|
|
* into the queue a worker routine is ensured to be available to process it
|
|
* (PiWalkDeviceList).
|
|
*
|
|
* In general, events processed in PiWalkDeviceList fall into two categories -
|
|
* those that are notifications for user mode (queued by kernel mode), and those
|
|
* that are queued operations.
|
|
*
|
|
* User mode notifications are sent by invoking PiNotifyUserMode. That routine
|
|
* gets UmPnpMgr's attention and copies up a buffer for it to digest. This
|
|
* operation is synchronous, PiNotifyUserMode waits until UmPnpMgr.Dll signals
|
|
* it is done (NtPlugPlayControl calls PiUserResponse) before returning.
|
|
*
|
|
* Queued operations (such as PiProcessQueryRemoveAndEject) may be very involved
|
|
* and could generate other events solely for user mode (via calls to
|
|
* PiNotifyUserMode, PiNotifyUserModeRemoveVetoed). These operations may also
|
|
* need to synchronously call kernel and user mode code that registered for the
|
|
* appropriate events (via the IopNotifyXxx functions).
|
|
*
|
|
*/
|
|
|
|
//
|
|
// Pool Tags
|
|
//
|
|
#define PNP_DEVICE_EVENT_LIST_TAG 'LEpP'
|
|
#define PNP_DEVICE_EVENT_ENTRY_TAG 'EEpP'
|
|
#define PNP_USER_BLOCK_TAG 'BUpP'
|
|
#define PNP_DEVICE_WORK_ITEM_TAG 'IWpP'
|
|
#define PNP_POOL_EVENT_BUFFER 'BEpP'
|
|
|
|
//
|
|
// PNP_USER_BLOCK
|
|
//
|
|
// The caller block contains info describing the caller of
|
|
// NtGetPlugPlayEvent. There's only one caller block.
|
|
//
|
|
|
|
typedef struct _PNP_USER_BLOCK {
|
|
NTSTATUS Status;
|
|
ULONG Result;
|
|
PPNP_VETO_TYPE VetoType;
|
|
PUNICODE_STRING VetoName;
|
|
ERESOURCE Lock;
|
|
KEVENT Registered;
|
|
KEVENT NotifyUserEvent;
|
|
KEVENT UserResultEvent;
|
|
PVOID PoolBuffer;
|
|
ULONG PoolUsed;
|
|
ULONG PoolSize;
|
|
BOOLEAN Deferred;
|
|
|
|
} PNP_USER_BLOCK, *PPNP_USER_BLOCK;
|
|
|
|
//
|
|
// Local (private) function prototypes
|
|
//
|
|
|
|
NTSTATUS
|
|
PiInsertEventInQueue(
|
|
IN PPNP_DEVICE_EVENT_ENTRY DeviceEvent
|
|
);
|
|
|
|
VOID
|
|
PiWalkDeviceList(
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
PiNotifyUserMode(
|
|
PPNP_DEVICE_EVENT_ENTRY DeviceEvent
|
|
);
|
|
|
|
NTSTATUS
|
|
PiNotifyUserModeDeviceRemoval(
|
|
IN PPNP_DEVICE_EVENT_ENTRY TemplateDeviceEvent,
|
|
IN CONST GUID *EventGuid,
|
|
OUT PPNP_VETO_TYPE VetoType OPTIONAL,
|
|
OUT PUNICODE_STRING VetoName OPTIONAL
|
|
);
|
|
|
|
NTSTATUS
|
|
PiNotifyUserModeRemoveVetoed(
|
|
IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PNP_VETO_TYPE VetoType,
|
|
IN PUNICODE_STRING VetoName OPTIONAL
|
|
);
|
|
|
|
NTSTATUS
|
|
PiNotifyUserModeRemoveVetoedByList(
|
|
IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PNP_VETO_TYPE VetoType,
|
|
IN PWSTR MultiSzVetoList
|
|
);
|
|
|
|
NTSTATUS
|
|
PiNotifyUserModeKernelInitiatedEject(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PNP_VETO_TYPE *VetoType,
|
|
OUT PUNICODE_STRING VetoName
|
|
);
|
|
|
|
NTSTATUS
|
|
PiProcessQueryRemoveAndEject(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
|
|
);
|
|
|
|
NTSTATUS
|
|
PiProcessTargetDeviceEvent(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
|
|
);
|
|
|
|
NTSTATUS
|
|
PiProcessCustomDeviceEvent(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
|
|
);
|
|
|
|
NTSTATUS
|
|
PiResizeTargetDeviceBlock(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent,
|
|
IN PLUGPLAY_DEVICE_DELETE_TYPE DeleteType,
|
|
IN PRELATION_LIST RelationsList,
|
|
IN BOOLEAN ExcludeIndirectRelations
|
|
);
|
|
|
|
VOID
|
|
PiBuildUnsafeRemovalDeviceBlock(
|
|
IN PPNP_DEVICE_EVENT_ENTRY OriginalDeviceEvent,
|
|
IN PRELATION_LIST RelationsList,
|
|
OUT PPNP_DEVICE_EVENT_ENTRY *AllocatedDeviceEvent
|
|
);
|
|
|
|
VOID
|
|
PiFinalizeVetoedRemove(
|
|
IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
|
|
IN PNP_VETO_TYPE VetoType,
|
|
IN PUNICODE_STRING VetoName OPTIONAL
|
|
);
|
|
|
|
VOID
|
|
LookupGuid(
|
|
IN CONST GUID *Guid,
|
|
IN OUT PCHAR String,
|
|
IN ULONG StringLength
|
|
);
|
|
|
|
VOID
|
|
DumpMultiSz(
|
|
IN PWCHAR MultiSz
|
|
);
|
|
|
|
VOID
|
|
DumpPnpEvent(
|
|
IN PPLUGPLAY_EVENT_BLOCK EventBlock
|
|
);
|
|
|
|
typedef struct {
|
|
|
|
ULONG HandleCount;
|
|
LOGICAL DumpHandles;
|
|
LOGICAL CollectHandles;
|
|
PUNICODE_STRING VetoString;
|
|
|
|
} ENUM_HANDLES_CONTEXT, *PENUM_HANDLES_CONTEXT;
|
|
|
|
LOGICAL
|
|
PiCollectOpenHandles(
|
|
IN PDEVICE_OBJECT *DeviceObjectArray,
|
|
IN ULONG ArrayCount,
|
|
IN LOGICAL KnownHandleFailure,
|
|
IN OUT PUNICODE_STRING VetoString
|
|
);
|
|
|
|
LOGICAL
|
|
PiCollectOpenHandlesCallBack(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PEPROCESS Process,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN HANDLE HandleId,
|
|
IN PVOID Context
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, DumpMultiSz)
|
|
#pragma alloc_text(PAGE, DumpPnpEvent)
|
|
#pragma alloc_text(PAGE, LookupGuid)
|
|
#pragma alloc_text(PAGE, PiCollectOpenHandles)
|
|
#pragma alloc_text(PAGE, PiCollectOpenHandlesCallBack)
|
|
|
|
#pragma alloc_text(PAGE, NtGetPlugPlayEvent)
|
|
|
|
#pragma alloc_text(PAGE, PiCompareGuid)
|
|
#pragma alloc_text(PAGE, PiInsertEventInQueue)
|
|
#pragma alloc_text(PAGE, PiNotifyUserMode)
|
|
#pragma alloc_text(PAGE, PiNotifyUserModeDeviceRemoval)
|
|
#pragma alloc_text(PAGE, PiNotifyUserModeKernelInitiatedEject)
|
|
#pragma alloc_text(PAGE, PiNotifyUserModeRemoveVetoed)
|
|
#pragma alloc_text(PAGE, PiNotifyUserModeRemoveVetoedByList)
|
|
#pragma alloc_text(PAGE, PiProcessCustomDeviceEvent)
|
|
#pragma alloc_text(PAGE, PiProcessQueryRemoveAndEject)
|
|
#pragma alloc_text(PAGE, PiProcessTargetDeviceEvent)
|
|
#pragma alloc_text(PAGE, PiResizeTargetDeviceBlock)
|
|
#pragma alloc_text(PAGE, PiBuildUnsafeRemovalDeviceBlock)
|
|
#pragma alloc_text(PAGE, PiUserResponse)
|
|
#pragma alloc_text(PAGE, PiWalkDeviceList)
|
|
#pragma alloc_text(PAGE, PiFinalizeVetoedRemove)
|
|
|
|
#pragma alloc_text(PAGE, PpCompleteDeviceEvent)
|
|
#pragma alloc_text(PAGE, PpInitializeNotification)
|
|
#pragma alloc_text(PAGE, PpNotifyUserModeRemovalSafe)
|
|
#pragma alloc_text(PAGE, PpSetCustomTargetEvent)
|
|
#pragma alloc_text(PAGE, PpSetDeviceClassChange)
|
|
#pragma alloc_text(PAGE, PpSetDeviceRemovalSafe)
|
|
#pragma alloc_text(PAGE, PpSetHwProfileChangeEvent)
|
|
#pragma alloc_text(PAGE, PpSetBlockedDriverEvent)
|
|
#pragma alloc_text(PAGE, PpSetPlugPlayEvent)
|
|
#pragma alloc_text(PAGE, PpSetPowerEvent)
|
|
#pragma alloc_text(PAGE, PpSetPowerVetoEvent)
|
|
#pragma alloc_text(PAGE, PpSetTargetDeviceRemove)
|
|
#pragma alloc_text(PAGE, PpSynchronizeDeviceEventQueue)
|
|
#pragma alloc_text(PAGE, PiAllocateCriticalMemory)
|
|
#pragma alloc_text(PAGE, PpSetInvalidIDEvent)
|
|
#endif
|
|
|
|
//
|
|
// Global Data
|
|
//
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGEDATA")
|
|
#endif
|
|
|
|
PPNP_DEVICE_EVENT_LIST PpDeviceEventList = NULL;
|
|
PPNP_USER_BLOCK PpUserBlock = NULL;
|
|
BOOLEAN PiUserModeRunning = FALSE;
|
|
BOOLEAN PiNotificationInProgress = FALSE;
|
|
PETHREAD PpDeviceEventThread = NULL;
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
KGUARDED_MUTEX PiNotificationInProgressLock;
|
|
|
|
LOGICAL PiDumpVetoedHandles = FALSE;
|
|
LOGICAL PiCollectVetoedHandles = FALSE;
|
|
|
|
|
|
NTSTATUS
|
|
NtGetPlugPlayEvent(
|
|
IN HANDLE EventHandle,
|
|
IN PVOID Context OPTIONAL,
|
|
OUT PPLUGPLAY_EVENT_BLOCK EventBlock,
|
|
IN ULONG EventBufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FRONT-END
|
|
|
|
This Plug and Play Manager API allows the user-mode PnP manager to
|
|
receive notification of a (kernel-mode) PnP hardware event.
|
|
|
|
THIS API IS ONLY CALLABLE BY THE USER-MODE PNP MANAGER. IF ANY OTHER
|
|
COMPONENT CALLS THIS API, THE DELIVERED EVENT WILL BE LOST TO THE REST
|
|
OF THE OPERATING SYSTEM. FURTHERMORE, THERE IS COMPLEX SYNCHRONIZATION
|
|
BETWEEN THE USER-MODE AND KERNEL-MODE PNP MANAGERS, ANYONE ELSE CALLING
|
|
THIS API WILL EVENTUALLY DEADLOCK THE SYSTEM.
|
|
|
|
Arguments:
|
|
|
|
EventHandle - Supplies an event handle that is signalled when an event
|
|
is ready to be delivered to user-mode.
|
|
|
|
EventBlock - Pointer to a PLUGPLAY_EVENT_BLOCK structure that will receive
|
|
information on the hardware event that has occurred.
|
|
|
|
EventBufferLength - Specifies the size, in bytes, of the EventBuffer field
|
|
in the PLUGPLAY_EVENT_BLOCK pointed to by EventBlock.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code indicating whether or not the function was successful
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
#if DBG
|
|
CHAR guidString[256];
|
|
#endif
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(EventHandle);
|
|
|
|
PAGED_CODE();
|
|
|
|
if (KeGetPreviousMode() != UserMode) {
|
|
//
|
|
// This routine only supports user-mode callers.
|
|
//
|
|
IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
|
|
"NtGetPlugPlayEvent: Only allows user-mode callers\n"));
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) {
|
|
//
|
|
// Caller does not have "trusted computer base" privilge.
|
|
//
|
|
IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
|
|
"NtGetPlugPlayEvent: Caller does not have \"trusted computer base\" privilge\n"));
|
|
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
//
|
|
// UMPNPMGR is now around.
|
|
//
|
|
PiUserModeRunning = TRUE;
|
|
//
|
|
// If we have not deferred on last call, we need to wait for kernel
|
|
// to make data available.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
if (!PpUserBlock->Deferred) {
|
|
|
|
PpUserBlock->PoolUsed = 0;
|
|
//
|
|
// Tell kernel we have a waiter.
|
|
//
|
|
KeSetEvent(&PpUserBlock->Registered, 0, FALSE);
|
|
//
|
|
// Make it a UserMode wait so the terminate APC will unblock us,
|
|
// and we can leave, which cleans up the thread
|
|
//
|
|
status = KeWaitForSingleObject(&PpUserBlock->NotifyUserEvent,
|
|
Executive,
|
|
UserMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
if (!NT_SUCCESS(status) || (status == STATUS_USER_APC) ) {
|
|
|
|
return status;
|
|
}
|
|
//
|
|
// Data is now available, validate user buffer size.
|
|
//
|
|
if (EventBufferSize < PpUserBlock->PoolUsed) {
|
|
//
|
|
// If user buffer is too small, then return appropriate status and
|
|
// set the Deferred to TRUE so on the next call, we wont wait on kernel
|
|
// mode (since data was already available).
|
|
//
|
|
PpUserBlock->Deferred = TRUE;
|
|
|
|
IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
|
|
"NtGetPlugPlayEvent: User-mode buffer too small for event\n"));
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
}
|
|
//
|
|
// User buffer is big enough so copy any data on success.
|
|
//
|
|
PpUserBlock->Deferred = FALSE;
|
|
status = PpUserBlock->Status;
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (PpUserBlock->PoolBuffer) {
|
|
|
|
#if DBG
|
|
LookupGuid(&((PPLUGPLAY_EVENT_BLOCK)(PpUserBlock->PoolBuffer))->EventGuid, guidString, sizeof(guidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"NtGetPlugPlayEvent: Returning event - EventGuid = %s\n",
|
|
guidString));
|
|
#endif
|
|
status = PiControlMakeUserModeCallersCopy(
|
|
&EventBlock,
|
|
PpUserBlock->PoolBuffer,
|
|
PpUserBlock->PoolUsed,
|
|
sizeof(ULONG),
|
|
UserMode,
|
|
FALSE);
|
|
} else {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
|
|
"NtGetPlugPlayEvent: Invalid event buffer\n"));
|
|
ASSERT(PpUserBlock->PoolBuffer);
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
} // NtGetPlugPlayEvent
|
|
|
|
NTSTATUS
|
|
PpInitializeNotification(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs initialization required before any of the notification
|
|
events can be processed. This routine performs init for the master device
|
|
event queue processing.
|
|
|
|
Parameters:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Returns a STATUS_Xxx value that indicates whether the function succeeded
|
|
or not.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allocate and initialize the master device event list.
|
|
//
|
|
|
|
PpDeviceEventList = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(PNP_DEVICE_EVENT_LIST),
|
|
PNP_DEVICE_EVENT_LIST_TAG);
|
|
if (PpDeviceEventList == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Clean0;
|
|
}
|
|
|
|
KeInitializeMutex(&PpDeviceEventList->EventQueueMutex, 0);
|
|
KeInitializeGuardedMutex(&PpDeviceEventList->Lock);
|
|
InitializeListHead(&(PpDeviceEventList->List));
|
|
PpDeviceEventList->Status = STATUS_PENDING;
|
|
|
|
//
|
|
// Intialize the PpUserBlock buffer - this buffer contains info about
|
|
// the user-mode caller for NtGetPlugPlayEvent and describes who in user
|
|
// mode we pass the events to.
|
|
//
|
|
|
|
PpUserBlock = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(PNP_USER_BLOCK),
|
|
PNP_USER_BLOCK_TAG);
|
|
if (PpUserBlock == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Clean0;
|
|
}
|
|
|
|
RtlZeroMemory(PpUserBlock, sizeof(PNP_USER_BLOCK));
|
|
|
|
PpUserBlock->PoolSize = sizeof (PLUGPLAY_EVENT_BLOCK)+
|
|
sizeof (PNP_DEVICE_EVENT_ENTRY);
|
|
PpUserBlock->PoolBuffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
PpUserBlock->PoolSize,
|
|
PNP_USER_BLOCK_TAG);
|
|
if (PpUserBlock->PoolBuffer == NULL ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
PpUserBlock->PoolSize = 0;
|
|
goto Clean0;
|
|
}
|
|
|
|
KeInitializeEvent(&PpUserBlock->Registered, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&PpUserBlock->NotifyUserEvent, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&PpUserBlock->UserResultEvent, SynchronizationEvent, FALSE);
|
|
ExInitializeResourceLite(&PpUserBlock->Lock);
|
|
// PpUserBlock->Status = STATUS_SUCCESS;
|
|
// PpUserBlock->Result = 0;
|
|
// PpUserBlock->EventBuffer = NULL;
|
|
// PpUserBlock->EventBufferSize = 0;
|
|
// PpUserBlock->PoolUsed = 0;
|
|
|
|
KeInitializeGuardedMutex(&PiNotificationInProgressLock);
|
|
|
|
Clean0:
|
|
|
|
return status;
|
|
|
|
} // PpInitializeNotification
|
|
|
|
|
|
NTSTATUS
|
|
PiInsertEventInQueue(
|
|
IN PPNP_DEVICE_EVENT_ENTRY DeviceEvent
|
|
)
|
|
{
|
|
PWORK_QUEUE_ITEM workItem;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
workItem = NULL;
|
|
status = STATUS_SUCCESS;
|
|
|
|
#if DBG
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiInsertEventInQueue: Event queued\n"));
|
|
DumpPnpEvent(&DeviceEvent->Data);
|
|
#endif
|
|
|
|
//
|
|
// Check if a new work item needs to be kicked off. A new work item gets
|
|
// kicked off iff this is the first event in the list.
|
|
//
|
|
KeAcquireGuardedMutex(&PpDeviceEventList->Lock);
|
|
KeAcquireGuardedMutex(&PiNotificationInProgressLock);
|
|
|
|
if (!PiNotificationInProgress) {
|
|
|
|
workItem = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(WORK_QUEUE_ITEM),
|
|
PNP_DEVICE_WORK_ITEM_TAG);
|
|
if (workItem) {
|
|
|
|
PiNotificationInProgress = TRUE;
|
|
KeClearEvent(&PiEventQueueEmpty);
|
|
} else {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
|
|
"PiInsertEventInQueue: Could not allocate memory to kick off a worker thread\n"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiInsertEventInQueue: Worker thread already running\n"));
|
|
}
|
|
//
|
|
// Insert the event iff successfull so far.
|
|
//
|
|
InsertTailList(&PpDeviceEventList->List, &DeviceEvent->ListEntry);
|
|
|
|
KeReleaseGuardedMutex(&PiNotificationInProgressLock);
|
|
KeReleaseGuardedMutex(&PpDeviceEventList->Lock);
|
|
//
|
|
// Queue the work item if any.
|
|
//
|
|
if (workItem) {
|
|
|
|
ExInitializeWorkItem(workItem, PiWalkDeviceList, workItem);
|
|
ExQueueWorkItem(workItem, DelayedWorkQueue);
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiInsertEventInQueue: Kicked off worker thread\n"));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PpSetDeviceClassChange(
|
|
IN CONST GUID *EventGuid,
|
|
IN CONST GUID *ClassGuid,
|
|
IN PUNICODE_STRING SymbolicLinkName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by user-mode pnp manager and drivers (indirectly) to
|
|
submit device interface change events into a serialized asynchronous queue.
|
|
This queue is processed by a work item.
|
|
|
|
Arguments:
|
|
|
|
EventGuid - Indicates what event is triggered has occured.
|
|
|
|
ClassGuid - Indicates the class of the device interface that changed.
|
|
|
|
SymbolicLinkName - Specifies the symbolic link name associated with the
|
|
interface device.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG dataSize, totalSize;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
CHAR eventGuidString[80];
|
|
CHAR classGuidString[80];
|
|
|
|
LookupGuid(EventGuid, eventGuidString, sizeof(eventGuidString));
|
|
LookupGuid(ClassGuid, classGuidString, sizeof(classGuidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PpSetDeviceClassChange: Entered\n EventGuid = %s\n ClassGuid = %s\n SymbolicLinkName = %wZ\n",
|
|
eventGuidString,
|
|
classGuidString,
|
|
SymbolicLinkName));
|
|
|
|
}
|
|
#endif
|
|
|
|
try {
|
|
|
|
ASSERT(EventGuid != NULL);
|
|
ASSERT(ClassGuid != NULL);
|
|
ASSERT(SymbolicLinkName != NULL);
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (the length doesn't count the
|
|
// terminating null but we're already counting the first index into the
|
|
// SymbolicLinkName field so it works out.
|
|
//
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) + SymbolicLinkName->Length;
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag( PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
status = STATUS_NO_MEMORY;
|
|
goto Clean0;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
RtlCopyMemory(&deviceEvent->Data.EventGuid, EventGuid, sizeof(GUID));
|
|
|
|
deviceEvent->Data.EventCategory = DeviceClassChangeEvent;
|
|
//deviceEvent->Data.Result = NULL;
|
|
//deviceEvent->Data.Flags = 0;
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
|
|
RtlCopyMemory(&deviceEvent->Data.u.DeviceClass.ClassGuid, ClassGuid, sizeof(GUID));
|
|
RtlCopyMemory(&deviceEvent->Data.u.DeviceClass.SymbolicLinkName,
|
|
SymbolicLinkName->Buffer,
|
|
SymbolicLinkName->Length);
|
|
deviceEvent->Data.u.DeviceClass.SymbolicLinkName[SymbolicLinkName->Length/sizeof(WCHAR)] = 0x0;
|
|
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(PiControlExceptionFilter(GetExceptionInformation())) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
|
|
"PpSetDeviceClassChange: Exception 0x%08X\n", GetExceptionCode()));
|
|
}
|
|
|
|
return status;
|
|
|
|
} // PpSetDeviceClassChange
|
|
|
|
|
|
NTSTATUS
|
|
PpSetCustomTargetEvent(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PKEVENT SyncEvent OPTIONAL,
|
|
OUT PULONG Result OPTIONAL,
|
|
IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
|
|
IN PVOID Context OPTIONAL,
|
|
IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by user-mode pnp manager and drivers (indirectly) to
|
|
submit target device change events into a serialized asynchronous queue.
|
|
This queue is processed by a work item.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Indicates the device object for the device that changed.
|
|
|
|
SyncEvent - Optionally, specifies a kernel-mode event that will be set when the
|
|
event is finished processing.
|
|
|
|
Result - Supplies a pointer to a ULONG that will be filled in with the status
|
|
after the event has actual completed (notification finished and the
|
|
event processed). This value is not used when SyncEvent is NULL and
|
|
is REQUIRED when SyncEvent is supplied.
|
|
|
|
NotificationStructure - Specifies the custom Notification to be processed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_NODE deviceNode;
|
|
ULONG dataSize, totalSize;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(NotificationStructure != NULL);
|
|
ASSERT(DeviceObject != NULL);
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PpSetCustomTargetEvent: DeviceObject = 0x%p, SyncEvent = 0x%p, Result = 0x%p\n",
|
|
DeviceObject,
|
|
SyncEvent,
|
|
Result));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" Callback = 0x%p, Context = 0x%p, NotificationStructure = 0x%p\n",
|
|
Callback,
|
|
Context,
|
|
NotificationStructure));
|
|
|
|
if (SyncEvent) {
|
|
ASSERT(Result);
|
|
*Result = STATUS_PENDING;
|
|
}
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
|
|
//
|
|
// Reference the device object so it can't go away until after we're
|
|
// done with notification.
|
|
//
|
|
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
ASSERT(deviceNode);
|
|
|
|
//
|
|
// This is a custom event block, so build up the PLUGPLAY_EVENT_BLOCK
|
|
// but copy the Notification Structure and put that in the EventBlock
|
|
// so we can dig it out in the handler later
|
|
//
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) + deviceNode->InstancePath.Length + sizeof(UNICODE_NULL);
|
|
|
|
//
|
|
// We need to ensure that the Notification structure remains aligned
|
|
// so round up dataSize to a multiple of sizeof(PVOID).
|
|
//
|
|
|
|
dataSize += sizeof(PVOID) - 1;
|
|
dataSize &= ~(sizeof(PVOID) - 1);
|
|
dataSize += NotificationStructure->Size;
|
|
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
deviceEvent->CallerEvent = SyncEvent;
|
|
deviceEvent->Callback = Callback;
|
|
deviceEvent->Context = Context;
|
|
deviceEvent->Data.EventGuid = GUID_PNP_CUSTOM_NOTIFICATION;
|
|
deviceEvent->Data.EventCategory = CustomDeviceEvent;
|
|
deviceEvent->Data.Result = Result;
|
|
deviceEvent->Data.Flags = 0;
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
|
|
if (deviceNode->InstancePath.Length != 0) {
|
|
|
|
RtlCopyMemory((PVOID)deviceEvent->Data.u.CustomNotification.DeviceIds,
|
|
(PVOID)deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length);
|
|
|
|
//
|
|
// No need to NUL terminate this string since we initially zeroed the
|
|
// buffer after allocation.
|
|
//
|
|
}
|
|
|
|
//
|
|
// Point the custom notification block to the extra space at the
|
|
// end of the allocation
|
|
//
|
|
|
|
deviceEvent->Data.u.CustomNotification.NotificationStructure =
|
|
(PVOID)((PUCHAR)deviceEvent + totalSize - NotificationStructure->Size);
|
|
|
|
RtlCopyMemory(deviceEvent->Data.u.CustomNotification.NotificationStructure,
|
|
NotificationStructure,
|
|
NotificationStructure->Size);
|
|
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
return status;
|
|
|
|
} // PpSetCustomTargetEvent
|
|
|
|
NTSTATUS
|
|
PpSetTargetDeviceRemove(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN KernelInitiated,
|
|
IN BOOLEAN NoRestart,
|
|
IN BOOLEAN OnlyRestartRelations,
|
|
IN BOOLEAN DoEject,
|
|
IN ULONG Problem,
|
|
IN PKEVENT SyncEvent OPTIONAL,
|
|
OUT PULONG Result OPTIONAL,
|
|
OUT PPNP_VETO_TYPE VetoType OPTIONAL,
|
|
OUT PUNICODE_STRING VetoName OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by user-mode pnp manager and drivers (indirectly) to
|
|
submit target device change events into a serialized asynchronous queue.
|
|
This queue is processed by a work item.
|
|
|
|
Arguments:
|
|
|
|
EventGuid - Indicates what event is triggered has occured.
|
|
|
|
DeviceObject - Indicates the device object for the device that changed.
|
|
|
|
SyncEvent - Optionally, specifies a kernel-mode event that will be set when the
|
|
event is finished processing.
|
|
|
|
Result - Supplies a pointer to a ULONG that will be filled in with the status
|
|
after the event has actual completed (notification finished and the
|
|
event processed). This value is not used when SyncEvent is NULL and
|
|
is REQUIRED when SyncEvent is supplied.
|
|
|
|
Flags - Current can be set to the following flags (bitfields)
|
|
TDF_PERFORMACTION
|
|
TDF_DEVICEEJECTABLE.
|
|
|
|
NotificationStructure - If present, implies that EventGuid is NULL, and specifies
|
|
a custom Notification to be processed. By definition it cannot be an event of
|
|
type GUID_TARGET_DEVICE_QUERY_REMOVE, GUID_TARGET_DEVICE_REMOVE_CANCELLED, or
|
|
GUID_TARGET_DEVICE_REMOVE_COMPLETE.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_NODE deviceNode;
|
|
ULONG dataSize, totalSize, i;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceObject != NULL);
|
|
|
|
if (SyncEvent) {
|
|
ASSERT(Result);
|
|
*Result = STATUS_PENDING;
|
|
}
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PpSetTargetDeviceRemove: Entered\n"));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceObject = 0x%p, NoRestart = %d, Problem = %d\n",
|
|
DeviceObject,
|
|
NoRestart,
|
|
Problem));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" SyncEvent = 0x%p, Result = 0x%p\n",
|
|
SyncEvent,
|
|
Result));
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
|
|
//
|
|
// Reference the device object so it can't go away until after we're
|
|
// done with notification.
|
|
//
|
|
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
ASSERT(deviceNode);
|
|
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (the length doesn't count the
|
|
// terminating null but we're already counting the first index into the
|
|
// DeviceId field so it works out. Add one more for double-null term.
|
|
//
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
|
|
|
|
dataSize += deviceNode->InstancePath.Length + sizeof(WCHAR);
|
|
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
|
|
deviceEvent->CallerEvent = SyncEvent;
|
|
deviceEvent->Argument = Problem;
|
|
deviceEvent->VetoType = VetoType;
|
|
deviceEvent->VetoName = VetoName;
|
|
deviceEvent->Data.EventGuid = DoEject ? GUID_DEVICE_EJECT : GUID_DEVICE_QUERY_AND_REMOVE;
|
|
deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
|
|
deviceEvent->Data.Result = Result;
|
|
|
|
if (NoRestart) {
|
|
deviceEvent->Data.Flags |= TDF_NO_RESTART;
|
|
}
|
|
|
|
if (KernelInitiated) {
|
|
deviceEvent->Data.Flags |= TDF_KERNEL_INITIATED;
|
|
}
|
|
|
|
if (OnlyRestartRelations) {
|
|
ASSERT(!NoRestart);
|
|
deviceEvent->Data.Flags |= TDF_ONLY_RESTART_RELATIONS;
|
|
}
|
|
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
|
|
if (deviceNode->InstancePath.Length != 0) {
|
|
|
|
RtlCopyMemory((PVOID)deviceEvent->Data.u.TargetDevice.DeviceIds,
|
|
(PVOID)deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length);
|
|
}
|
|
|
|
i = deviceNode->InstancePath.Length/sizeof(WCHAR);
|
|
deviceEvent->Data.u.TargetDevice.DeviceIds[i] = L'\0';
|
|
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
return status;
|
|
|
|
} // PpSetTargetDeviceRemove
|
|
|
|
|
|
NTSTATUS
|
|
PpSetDeviceRemovalSafe(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PKEVENT SyncEvent OPTIONAL,
|
|
OUT PULONG Result OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to notify user mode a device can be removed. The IO
|
|
system may queue this event when a hardware initiated eject has completed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Indicates the device object for the device that changed.
|
|
|
|
SyncEvent - Optionally, specifies a kernel-mode event that will be set when the
|
|
event is finished processing.
|
|
|
|
Result - Supplies a pointer to a ULONG that will be filled in with the status
|
|
after the event has actual completed (notification finished and the
|
|
event processed). This value is not used when SyncEvent is NULL and
|
|
is REQUIRED when SyncEvent is supplied.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_NODE deviceNode;
|
|
ULONG dataSize, totalSize, i;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceObject != NULL);
|
|
|
|
if (SyncEvent) {
|
|
ASSERT(Result);
|
|
*Result = STATUS_PENDING;
|
|
}
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PpSetDeviceRemovalSafe: Entered\n"));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceObject = 0x%p, SyncEvent = 0x%p, Result = 0x%p\n",
|
|
DeviceObject,
|
|
SyncEvent,
|
|
Result));
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
|
|
//
|
|
// Reference the device object so it can't go away until after we're
|
|
// done with notification.
|
|
//
|
|
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
ASSERT(deviceNode);
|
|
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (the length doesn't count the
|
|
// terminating null but we're already counting the first index into the
|
|
// DeviceId field so it works out. Add one more for double-null term.
|
|
//
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
|
|
|
|
dataSize += deviceNode->InstancePath.Length + sizeof(WCHAR);
|
|
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
|
|
deviceEvent->CallerEvent = SyncEvent;
|
|
deviceEvent->Argument = 0;
|
|
deviceEvent->VetoType = NULL;
|
|
deviceEvent->VetoName = NULL;
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_SAFE_REMOVAL;
|
|
deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
|
|
deviceEvent->Data.Result = Result;
|
|
deviceEvent->Data.Flags = 0;
|
|
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
|
|
if (deviceNode->InstancePath.Length != 0) {
|
|
|
|
RtlCopyMemory((PVOID)deviceEvent->Data.u.TargetDevice.DeviceIds,
|
|
(PVOID)deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length);
|
|
}
|
|
|
|
i = deviceNode->InstancePath.Length/sizeof(WCHAR);
|
|
deviceEvent->Data.u.TargetDevice.DeviceIds[i] = L'\0';
|
|
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
return status;
|
|
|
|
} // PpSetDeviceRemovalSafe
|
|
|
|
|
|
NTSTATUS
|
|
PpSetHwProfileChangeEvent(
|
|
IN GUID CONST *EventTypeGuid,
|
|
IN PKEVENT CompletionEvent OPTIONAL,
|
|
OUT PNTSTATUS CompletionStatus OPTIONAL,
|
|
OUT PPNP_VETO_TYPE VetoType OPTIONAL,
|
|
OUT PUNICODE_STRING VetoName OPTIONAL
|
|
)
|
|
{
|
|
ULONG dataSize,totalSize;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
{
|
|
CHAR eventGuidString[80];
|
|
|
|
LookupGuid(EventTypeGuid, eventGuidString, sizeof(eventGuidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PpSetHwProfileChangeEvent: Entered\n EventGuid = %s\n\n",
|
|
eventGuidString));
|
|
}
|
|
#endif
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
|
|
|
|
totalSize = dataSize + FIELD_OFFSET (PNP_DEVICE_EVENT_ENTRY,Data);
|
|
|
|
|
|
|
|
deviceEvent = ExAllocatePoolWithTag (PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
|
|
if (NULL == deviceEvent) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
//
|
|
// Setup the PLUGPLAY_EVENT_BLOCK
|
|
//
|
|
RtlZeroMemory ((PVOID)deviceEvent,totalSize);
|
|
deviceEvent->CallerEvent = CompletionEvent;
|
|
deviceEvent->VetoType = VetoType;
|
|
deviceEvent->VetoName = VetoName;
|
|
|
|
deviceEvent->Data.EventCategory = HardwareProfileChangeEvent;
|
|
RtlCopyMemory(&deviceEvent->Data.EventGuid, EventTypeGuid, sizeof(GUID));
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.Result = (PULONG)CompletionStatus;
|
|
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
return status;
|
|
|
|
} // PpSetHwProfileChangeEvent
|
|
|
|
|
|
NTSTATUS
|
|
PpSetBlockedDriverEvent(
|
|
IN GUID CONST *BlockedDriverGuid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to notify user mode of blocked driver events.
|
|
|
|
Arguments:
|
|
|
|
BlockedDriverGuid - Specifies the GUID which identifies the blocked driver.
|
|
|
|
Return Value:
|
|
|
|
Returns the status of inserting the event into the synchronized pnp event
|
|
queue.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG dataSize, totalSize;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PpPnpShuttingDown) {
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
|
|
//
|
|
// Allocate a device event entry.
|
|
//
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Setup the PLUGPLAY_EVENT_BLOCK
|
|
//
|
|
RtlZeroMemory ((PVOID)deviceEvent, totalSize);
|
|
deviceEvent->Data.EventGuid = GUID_DRIVER_BLOCKED;
|
|
deviceEvent->Data.EventCategory = BlockedDriverEvent;
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
RtlCopyMemory(&deviceEvent->Data.u.BlockedDriverNotification.BlockedDriverGuid,
|
|
BlockedDriverGuid,
|
|
sizeof(GUID));
|
|
|
|
//
|
|
// Insert the event into the queue.
|
|
//
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
return status;
|
|
|
|
} // PpSetBlockedDriverEvent
|
|
|
|
|
|
NTSTATUS
|
|
PpSetInvalidIDEvent(
|
|
IN PUNICODE_STRING ParentInstance
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to notify user mode when an invalid ID is encountered.
|
|
|
|
Arguments:
|
|
|
|
ParentInstance - Specifies the instance path of the parent of the device with
|
|
invalid ID.
|
|
|
|
Return Value:
|
|
|
|
Returns the status of inserting the event into the synchronized pnp event
|
|
queue.
|
|
|
|
--*/
|
|
{
|
|
ULONG dataSize, totalSize;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
//
|
|
// Allocate a device event entry (note that we have one WCHAR reserved
|
|
// as part of the block structure itself).
|
|
//
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) + ParentInstance->Length + sizeof(UNICODE_NULL);
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
//
|
|
// Setup the PLUGPLAY_EVENT_BLOCK
|
|
//
|
|
RtlZeroMemory ((PVOID)deviceEvent, totalSize);
|
|
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_INVALID_ID;
|
|
deviceEvent->Data.EventCategory = InvalidIDEvent;
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
RtlCopyMemory(&deviceEvent->Data.u.InvalidIDNotification.ParentId[0],
|
|
ParentInstance->Buffer,
|
|
ParentInstance->Length);
|
|
deviceEvent->Data.u.InvalidIDNotification.ParentId[ParentInstance->Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
deviceEvent->Data.u.InvalidIDNotification.ParentId[(ParentInstance->Length / sizeof(WCHAR)) + 1] = UNICODE_NULL;
|
|
|
|
//
|
|
// Insert the event into the queue.
|
|
//
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
return status;
|
|
|
|
} // PpSetInvalidIDEvent
|
|
|
|
|
|
NTSTATUS
|
|
PpSetPowerEvent(
|
|
IN ULONG EventCode,
|
|
IN ULONG EventData,
|
|
IN PKEVENT CompletionEvent OPTIONAL,
|
|
OUT PNTSTATUS CompletionStatus OPTIONAL,
|
|
OUT PPNP_VETO_TYPE VetoType OPTIONAL,
|
|
OUT PUNICODE_STRING VetoName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to notify user mode of system-wide power events.
|
|
|
|
Arguments:
|
|
|
|
EventCode - Supplies the power event code that is to be communicated
|
|
to user-mode components.
|
|
|
|
(Specifically, this event code is actually one of the PBT_APM*
|
|
user-mode power event ids, as defined in sdk\inc\winuser.h. It is
|
|
typically used as the WPARAM data associated with WM_POWERBROADCAST
|
|
user-mode window messages. It is supplied to kernel-mode PnP,
|
|
directly from win32k, for the explicit purpose of user-mode power
|
|
event notification.)
|
|
|
|
EventData - Specifies additional event-specific data for the specified
|
|
power event id.
|
|
|
|
(Specifically, this event data is the LPARAM data for the
|
|
corresponding PBT_APM* user-mode power event id, specified above.)
|
|
|
|
CompletionEvent - Optionally, specifies a kernel-mode event that will be set when the
|
|
event is finished processing.
|
|
|
|
CompletionStatus - Supplies a pointer to a ULONG that will be filled in with the status
|
|
after the event has actual completed (notification finished and the
|
|
event processed). This value is not used when SyncEvent is NULL and
|
|
is REQUIRED when SyncEvent is supplied.
|
|
|
|
|
|
VetoType - Optionally, if the specified EventCode is a query-type operation,
|
|
this argument supplies a pointer to an address that will receive the
|
|
type of the vetoing user-mode component, in the event that the
|
|
request is denied.
|
|
|
|
VetoName - Optionally, if the specified EventCode is a query-type operation,
|
|
this argument supplies a pointer to a UNICODE_STRING that will
|
|
receive the name of the vetoing user-mode component, in the event
|
|
that the request is denied.
|
|
|
|
|
|
Return Value:
|
|
|
|
Returns the status of inserting the event into the synchronized pnp event
|
|
queue.
|
|
|
|
For the final status of a synchronous power event, check the value at the
|
|
location specified by CompletionStatus, once the supplied CompletionEvent
|
|
has been set.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG dataSize,totalSize;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PpSetPowerEvent: Entered\n\n") );
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
|
|
|
|
totalSize = dataSize + FIELD_OFFSET (PNP_DEVICE_EVENT_ENTRY,Data);
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
|
|
if (NULL == deviceEvent) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
//Setup the PLUGPLAY_EVENT_BLOCK
|
|
//
|
|
RtlZeroMemory ((PVOID)deviceEvent,totalSize);
|
|
deviceEvent->CallerEvent = CompletionEvent;
|
|
deviceEvent->VetoType = VetoType;
|
|
deviceEvent->VetoName = VetoName;
|
|
|
|
deviceEvent->Data.EventCategory = PowerEvent;
|
|
deviceEvent->Data.EventGuid = GUID_PNP_POWER_NOTIFICATION;
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.Result = (PULONG)CompletionStatus;
|
|
deviceEvent->Data.u.PowerNotification.NotificationCode = EventCode;
|
|
deviceEvent->Data.u.PowerNotification.NotificationData = EventData;
|
|
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
return status;
|
|
} // PpSetPowerEvent
|
|
|
|
NTSTATUS
|
|
PpSetPowerVetoEvent(
|
|
IN POWER_ACTION VetoedPowerOperation,
|
|
IN PKEVENT CompletionEvent OPTIONAL,
|
|
OUT PNTSTATUS CompletionStatus OPTIONAL,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PNP_VETO_TYPE VetoType,
|
|
IN PUNICODE_STRING VetoName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
--*/
|
|
{
|
|
ULONG dataSize, totalSize, i;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
PDEVICE_NODE deviceNode;
|
|
PWCHAR vetoData;
|
|
NTSTATUS status;
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
|
|
//
|
|
// Reference the device object so it can't go away until after we're
|
|
// done with notification.
|
|
//
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
//
|
|
// Given the pdo, retrieve the devnode (the device instance string is
|
|
// attached to the devnode in the InstancePath field).
|
|
//
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (!deviceNode) {
|
|
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (because of the first index into
|
|
// the DeviceIdVetoNameBuffer, this is double null terminated).
|
|
//
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) +
|
|
deviceNode->InstancePath.Length +
|
|
(VetoName ? VetoName->Length : 0) +
|
|
sizeof(WCHAR)*2;
|
|
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
|
|
if (deviceEvent == NULL) {
|
|
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
deviceEvent->CallerEvent = CompletionEvent;
|
|
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
deviceEvent->Data.Result = (PULONG)CompletionStatus;
|
|
deviceEvent->Data.u.VetoNotification.VetoType = VetoType;
|
|
|
|
//
|
|
// You can think of this as a MultiSz string where the first entry is the
|
|
// DeviceId for the device being removed, and the next Id's all corrospond
|
|
// to the vetoers.
|
|
//
|
|
RtlCopyMemory(
|
|
deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer,
|
|
deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length
|
|
);
|
|
|
|
i = deviceNode->InstancePath.Length;
|
|
deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
if (VetoName) {
|
|
|
|
vetoData = (&deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)])+1;
|
|
|
|
RtlCopyMemory(vetoData, VetoName->Buffer, VetoName->Length);
|
|
vetoData[VetoName->Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
|
|
//
|
|
// No need to NULL terminate the entry after the last one as we prezero'd
|
|
// the buffer. Now set the appropriate GUID so the UI looks right.
|
|
//
|
|
if (VetoedPowerOperation == PowerActionWarmEject) {
|
|
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_WARM_EJECT_VETOED;
|
|
|
|
} else if (VetoedPowerOperation == PowerActionHibernate) {
|
|
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_HIBERNATE_VETOED;
|
|
|
|
} else {
|
|
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_STANDBY_VETOED;
|
|
}
|
|
|
|
deviceEvent->Data.EventCategory = VetoEvent;
|
|
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
PpSetPlugPlayEvent(
|
|
IN CONST GUID *EventGuid,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allows kernel mode enumerator to inform Plug and Play Manager
|
|
on the events triggered by enumeration, i.e., DeviceArrived and DeviceRemoved.
|
|
The PnP manager can then inform user-mode about the event.
|
|
|
|
Arguments:
|
|
|
|
EventId - Indicates what event is triggered by enumeration.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG dataSize, totalSize;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(EventGuid != NULL);
|
|
ASSERT(DeviceObject != NULL);
|
|
|
|
#if DBG
|
|
{
|
|
CHAR eventGuidString[80];
|
|
|
|
LookupGuid(EventGuid, eventGuidString, sizeof(eventGuidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PpSetPlugPlayEvent: Entered\n EventGuid = %s\n",
|
|
eventGuidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceObject = 0x%p\n",
|
|
DeviceObject));
|
|
}
|
|
#endif
|
|
|
|
if (PpPnpShuttingDown) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Given the pdo, retrieve the devnode (the device instance string is
|
|
// attached to the devnode in the InstancePath field).
|
|
//
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (the length doesn't count the
|
|
// terminating null but we're already counting the first index into the
|
|
// DeviceId field. also include a final terminating null, in case this is
|
|
// a TargetDevice event, where DeviceIds is a multi-sz list).
|
|
//
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) + deviceNode->InstancePath.Length + sizeof(WCHAR);
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
RtlCopyMemory(&deviceEvent->Data.EventGuid, EventGuid, sizeof(GUID));
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
|
|
if (PiCompareGuid(EventGuid, &GUID_DEVICE_ENUMERATED)) {
|
|
//
|
|
// GUID_DEVICE_ENUMERATED events are device installation requests for
|
|
// user-mode, and are sent using the DeviceInstallEvent event type.
|
|
//
|
|
deviceEvent->Data.EventCategory = DeviceInstallEvent;
|
|
RtlCopyMemory(&deviceEvent->Data.u.InstallDevice.DeviceId,
|
|
deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length);
|
|
deviceEvent->Data.u.InstallDevice.DeviceId[deviceNode->InstancePath.Length/sizeof(WCHAR)] = 0x0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// All other target events are sent using the TargetDeviceChangeEvent
|
|
// event type, and are distinguished by the EventGuid. Note that
|
|
// DeviceIds is a multi-sz list.
|
|
//
|
|
deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
|
|
RtlCopyMemory(&deviceEvent->Data.u.TargetDevice.DeviceIds,
|
|
deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length);
|
|
deviceEvent->Data.u.TargetDevice.DeviceIds[deviceNode->InstancePath.Length/sizeof(WCHAR)] = 0x0;
|
|
deviceEvent->Data.u.TargetDevice.DeviceIds[deviceNode->InstancePath.Length/sizeof(WCHAR)+1] = 0x0;
|
|
}
|
|
|
|
//
|
|
// Don't hold a reference count against the DO for these events. This can
|
|
// lead to really annoying Critical Device Database vs. Installation vs.
|
|
// I/O driver refcount races.
|
|
//
|
|
if (PiCompareGuid(EventGuid, &GUID_DEVICE_ENUMERATED) ||
|
|
PiCompareGuid(EventGuid, &GUID_DEVICE_ARRIVAL)) {
|
|
|
|
DeviceObject = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Reference the device object so it can't go away until after we're
|
|
// done with notification.
|
|
//
|
|
ASSERT(DeviceObject);
|
|
ObReferenceObject(DeviceObject);
|
|
}
|
|
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
|
|
PiInsertEventInQueue(deviceEvent);
|
|
|
|
return;
|
|
|
|
} // PpSetPlugPlayEvent
|
|
|
|
NTSTATUS
|
|
PpSynchronizeDeviceEventQueue(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
KEVENT event;
|
|
ULONG result;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Note this is the only queuing function which is valid when PpShuttingDown
|
|
// is TRUE.
|
|
//
|
|
|
|
deviceEvent = ExAllocatePoolWithTag( PagedPool,
|
|
sizeof(PNP_DEVICE_EVENT_ENTRY),
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
RtlZeroMemory((PVOID)deviceEvent, sizeof(PNP_DEVICE_EVENT_ENTRY));
|
|
|
|
deviceEvent->CallerEvent = &event;
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_NOOP;
|
|
deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
|
|
deviceEvent->Data.Result = &result;
|
|
deviceEvent->Data.TotalSize = sizeof(PLUGPLAY_EVENT_BLOCK);
|
|
|
|
status = PiInsertEventInQueue(deviceEvent);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
0); // infinite
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
PiWalkDeviceList(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the master device list contains any device events, empty the list now.
|
|
This is a worker item thread routine (queued by PiPostNotify). We walk the
|
|
list - this will cause the oldest device event on the list to be sent to
|
|
all registered recipients and then the device event will be removed (if at
|
|
least one recipient received it).
|
|
|
|
Order Rules:
|
|
Interface Devices - kernel mode first, user-mode second
|
|
Hardware profile changes - user-mode first, kernel-mode second
|
|
Target device changes (query remove, remove) : user-mode first, send
|
|
(cancel remove) : kernel-mode first, post
|
|
(custom) : kernel-mode first, post
|
|
|
|
Arguments:
|
|
|
|
NONE.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
PLIST_ENTRY current;
|
|
UNICODE_STRING tempString;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
PAGED_CODE();
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiWalkDeviceList: Worker thread entered\n"));
|
|
PpDeviceEventThread = PsGetCurrentThread();
|
|
|
|
//
|
|
// Empty the device event list, remove entries from the head of the list
|
|
// (deliver oldest entries first).
|
|
//
|
|
// As this function always executes in a system process workitem, we
|
|
// don't have to grab a critical region lock.
|
|
//
|
|
|
|
status = KeWaitForSingleObject(&PpDeviceEventList->EventQueueMutex,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
0); // infinite
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KeAcquireGuardedMutex(&PiNotificationInProgressLock);
|
|
KeSetEvent(&PiEventQueueEmpty, 0, FALSE);
|
|
PiNotificationInProgress = FALSE;
|
|
KeReleaseGuardedMutex(&PiNotificationInProgressLock);
|
|
PpDeviceEventThread = NULL;
|
|
return;
|
|
}
|
|
|
|
for ( ; ; ) {
|
|
|
|
KeAcquireGuardedMutex(&PpDeviceEventList->Lock);
|
|
|
|
if (!IsListEmpty(&PpDeviceEventList->List)) {
|
|
|
|
current = RemoveHeadList(&PpDeviceEventList->List);
|
|
KeReleaseGuardedMutex(&PpDeviceEventList->Lock);
|
|
|
|
deviceEvent = CONTAINING_RECORD(current, // address
|
|
PNP_DEVICE_EVENT_ENTRY, // type
|
|
ListEntry); // field
|
|
|
|
#if DBG
|
|
{
|
|
CHAR guidString[256];
|
|
|
|
LookupGuid(&deviceEvent->Data.EventGuid, guidString, sizeof(guidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiWalkDeviceList: Processing queued event - EventGuid = %s\n",
|
|
guidString));
|
|
}
|
|
#endif
|
|
|
|
status = STATUS_SUCCESS;
|
|
if (deviceEvent->Data.DeviceObject != NULL) {
|
|
|
|
deviceNode = PP_DO_TO_DN((PDEVICE_OBJECT)deviceEvent->Data.DeviceObject);
|
|
if (deviceNode == NULL || PipIsDevNodeDeleted(deviceNode)) {
|
|
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
switch (deviceEvent->Data.EventCategory) {
|
|
|
|
case DeviceClassChangeEvent: {
|
|
|
|
//
|
|
// Notify kernel-mode (synchronous).
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiWalkDeviceList: DeviceClassChangeEvent - notifying kernel-mode\n"));
|
|
|
|
RtlInitUnicodeString(&tempString, deviceEvent->Data.u.DeviceClass.SymbolicLinkName);
|
|
IopNotifyDeviceClassChange(&deviceEvent->Data.EventGuid,
|
|
&deviceEvent->Data.u.DeviceClass.ClassGuid,
|
|
&tempString);
|
|
|
|
//
|
|
// Notify user-mode (synchronous).
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiWalkDeviceList: DeviceClassChangeEvent - user kernel-mode\n"));
|
|
|
|
PiNotifyUserMode(deviceEvent);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
|
|
case CustomDeviceEvent: {
|
|
|
|
status = PiProcessCustomDeviceEvent(&deviceEvent);
|
|
break;
|
|
}
|
|
|
|
case TargetDeviceChangeEvent: {
|
|
|
|
status = PiProcessTargetDeviceEvent(&deviceEvent);
|
|
break;
|
|
}
|
|
|
|
case DeviceInstallEvent: {
|
|
|
|
//
|
|
// Notify user-mode (synchronous).
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiWalkDeviceList: DeviceInstallEvent - notifying user-mode\n"));
|
|
|
|
PiNotifyUserMode(deviceEvent);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case HardwareProfileChangeEvent: {
|
|
|
|
//
|
|
// Notify user-mode (synchronous).
|
|
//
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Notify K-Mode
|
|
//
|
|
IopNotifyHwProfileChange(&deviceEvent->Data.EventGuid,
|
|
deviceEvent->VetoType,
|
|
deviceEvent->VetoName);
|
|
}
|
|
break;
|
|
}
|
|
case PowerEvent: {
|
|
|
|
//
|
|
// Notify user-mode (synchronous).
|
|
//
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
break;
|
|
}
|
|
|
|
case VetoEvent: {
|
|
|
|
//
|
|
// Forward onto user-mode.
|
|
//
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
break;
|
|
}
|
|
|
|
case BlockedDriverEvent: {
|
|
|
|
//
|
|
// Forward onto user-mode.
|
|
//
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
break;
|
|
}
|
|
|
|
case InvalidIDEvent: {
|
|
|
|
//
|
|
// Forward onto user-mode.
|
|
//
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
|
|
//
|
|
// These should never be queued to kernel mode. They are
|
|
// notifications for user mode, and should only be seen
|
|
// through the PiNotifyUserModeXxx functions.
|
|
//
|
|
ASSERT(0);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status != STATUS_PENDING) {
|
|
|
|
PpCompleteDeviceEvent(deviceEvent, status);
|
|
}
|
|
|
|
//
|
|
// Commit pending registrations after processing each event.
|
|
//
|
|
IopProcessDeferredRegistrations();
|
|
|
|
} else {
|
|
KeAcquireGuardedMutex(&PiNotificationInProgressLock);
|
|
KeSetEvent(&PiEventQueueEmpty, 0, FALSE);
|
|
PiNotificationInProgress = FALSE;
|
|
|
|
//
|
|
// Commit pending registrations after processing all queued events.
|
|
//
|
|
IopProcessDeferredRegistrations();
|
|
|
|
KeReleaseGuardedMutex(&PiNotificationInProgressLock);
|
|
KeReleaseGuardedMutex(&PpDeviceEventList->Lock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Context != NULL) {
|
|
|
|
ExFreePool(Context);
|
|
}
|
|
|
|
PpDeviceEventThread = NULL;
|
|
KeReleaseMutex(&PpDeviceEventList->EventQueueMutex, FALSE);
|
|
|
|
return;
|
|
} // PiWalkDeviceList
|
|
|
|
|
|
VOID
|
|
PpCompleteDeviceEvent(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY DeviceEvent,
|
|
IN NTSTATUS FinalStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceEvent - Event to complete.
|
|
FinalStatus - Final status for this event.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
|
|
{
|
|
#if DBG
|
|
CHAR guidString[256];
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
LookupGuid(&DeviceEvent->Data.EventGuid, guidString, sizeof(guidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PpCompleteDeviceEvent: Completing queued event - EventGuid = %s with %08lx\n",
|
|
guidString,
|
|
FinalStatus));
|
|
#endif
|
|
|
|
//
|
|
// If synchronous, signal the user-supplied event.
|
|
//
|
|
|
|
if (DeviceEvent->CallerEvent) {
|
|
*DeviceEvent->Data.Result = FinalStatus;
|
|
KeSetEvent(DeviceEvent->CallerEvent, 0, FALSE);
|
|
}
|
|
|
|
if (DeviceEvent->Callback) {
|
|
DeviceEvent->Callback(DeviceEvent->Context);
|
|
}
|
|
|
|
//
|
|
// Release the reference we took for this device object during
|
|
// the PpSetCustomTargetEvent call.
|
|
//
|
|
if (DeviceEvent->Data.DeviceObject != NULL) {
|
|
ObDereferenceObject(DeviceEvent->Data.DeviceObject);
|
|
}
|
|
|
|
//
|
|
// Assume device event was delivered successfully, get rid of it.
|
|
//
|
|
|
|
ExFreePool(DeviceEvent);
|
|
return;
|
|
} // PpCompleteDeviceEvent
|
|
|
|
|
|
NTSTATUS
|
|
PiNotifyUserMode(
|
|
PPNP_DEVICE_EVENT_ENTRY DeviceEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dispatches the device event to user-mode for processing.
|
|
|
|
Arguments:
|
|
|
|
DeviceEvent - Data describing what change and how.
|
|
|
|
Return Value:
|
|
|
|
Retuns an NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS, status1 = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiNotifyUserMode: Entered\n"));
|
|
|
|
//
|
|
// First make sure user-mode is up and running before attempting to deliver
|
|
// an event. If not running yet, skip user-mode for this event.
|
|
//
|
|
|
|
if (PiUserModeRunning) {
|
|
|
|
|
|
//
|
|
// User-mode notification is a single-shot model, once user-mode is
|
|
// running, I need to wait until user-mode is ready to take the next
|
|
// event (i.e, wait until we're sitting in another NtGetPlugPlayEvent
|
|
// call).
|
|
//
|
|
|
|
status1 = KeWaitForSingleObject(&PpUserBlock->Registered,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
ASSERT (PpUserBlock->Deferred == FALSE);
|
|
|
|
|
|
//
|
|
// Change the status after the wait.
|
|
//
|
|
PpUserBlock->Status = STATUS_SUCCESS;
|
|
|
|
if (NT_SUCCESS(status1)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiNotifyUserMode: User-mode ready\n"));
|
|
|
|
//
|
|
// Make sure we can handle it in the pool buffer and copy it out
|
|
//
|
|
if (PpUserBlock->PoolSize < DeviceEvent->Data.TotalSize) {
|
|
//
|
|
//Allocate a new block (well, conceptually grow the block)
|
|
// only when it's not big enough, so that we know we've always got
|
|
// room for normal events, and in the very low memory case, we can
|
|
// fail custom events, but keep the system running
|
|
//
|
|
PVOID pHold;
|
|
|
|
|
|
pHold = ExAllocatePoolWithTag(NonPagedPool,
|
|
DeviceEvent->Data.TotalSize,
|
|
PNP_POOL_EVENT_BUFFER);
|
|
|
|
if (!pHold) {
|
|
IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
|
|
"PiNotifyUserMode: Out of NonPagedPool!!\n"));
|
|
|
|
PpUserBlock->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
PpUserBlock->PoolSize = DeviceEvent->Data.TotalSize;
|
|
|
|
ExFreePool (PpUserBlock->PoolBuffer );
|
|
PpUserBlock->PoolBuffer = pHold;
|
|
|
|
}
|
|
|
|
PpUserBlock->PoolUsed = DeviceEvent->Data.TotalSize;
|
|
RtlCopyMemory(PpUserBlock->PoolBuffer,
|
|
&DeviceEvent->Data,
|
|
PpUserBlock->PoolUsed);
|
|
|
|
}
|
|
|
|
//
|
|
// Veto information is only propogated where needed, ie
|
|
// QUERY_REMOVE's, Profile change requests and PowerEvents.
|
|
//
|
|
if (PiCompareGuid(&DeviceEvent->Data.EventGuid,
|
|
&GUID_TARGET_DEVICE_QUERY_REMOVE) ||
|
|
PiCompareGuid(&DeviceEvent->Data.EventGuid,
|
|
&GUID_HWPROFILE_QUERY_CHANGE) ||
|
|
PiCompareGuid(&DeviceEvent->Data.EventGuid,
|
|
&GUID_DEVICE_KERNEL_INITIATED_EJECT) ||
|
|
(DeviceEvent->Data.EventCategory == PowerEvent)) {
|
|
|
|
PpUserBlock->VetoType = DeviceEvent->VetoType;
|
|
PpUserBlock->VetoName = DeviceEvent->VetoName;
|
|
} else {
|
|
PpUserBlock->VetoType = NULL;
|
|
PpUserBlock->VetoName = NULL;
|
|
}
|
|
|
|
//
|
|
// Set the system event that causes NtGetPlugPlayEvent to return to caller.
|
|
//
|
|
|
|
KeSetEvent(&PpUserBlock->NotifyUserEvent, 0, FALSE);
|
|
|
|
//
|
|
// Wait until we get an answer back from user-mode.
|
|
//
|
|
// ADRIAO N.B. 2002/03/24 - This checks for Kernel mode alerts???
|
|
// Bizarre!
|
|
//
|
|
|
|
status1 = KeWaitForSingleObject(&PpUserBlock->UserResultEvent,
|
|
Executive,
|
|
KernelMode,
|
|
TRUE,
|
|
NULL);
|
|
|
|
//
|
|
// Check the result from this user-mode notification.
|
|
//
|
|
|
|
if (status1 == STATUS_ALERTED || status1 == STATUS_SUCCESS) {
|
|
if (!PpUserBlock->Result) {
|
|
|
|
//
|
|
// For query-remove case, any errors are treated as a
|
|
// failure during notification (since it may result in our
|
|
// inability to let a registered caller vote in the query-remove)
|
|
// and the PpUserBlock->Result is set accordingly.
|
|
//
|
|
|
|
//
|
|
// Note! User mode ONLY returns a 0 or !0 response.
|
|
// if 1 then it succeeded.
|
|
//
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
PpUserBlock->VetoType = NULL;
|
|
PpUserBlock->VetoName = NULL;
|
|
}
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiNotifyUserMode: User-mode returned, status = 0x%08X, status1 = 0x%08X, Result = 0x%08X\n",
|
|
status,
|
|
status1,
|
|
PpUserBlock->Result));
|
|
|
|
return status;
|
|
|
|
} // PiNotifyUserMode
|
|
|
|
|
|
|
|
VOID
|
|
PiUserResponse(
|
|
IN ULONG Response,
|
|
IN PNP_VETO_TYPE VetoType,
|
|
IN LPWSTR VetoName,
|
|
IN ULONG VetoNameLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when user-mode pnp manager is signalling that it's
|
|
done processing an event; the result of the event processing is passed in
|
|
the Response parameter.
|
|
|
|
Arguments:
|
|
|
|
Response - Result of event processing in user-mode.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING vetoString;
|
|
|
|
PAGED_CODE();
|
|
|
|
PpUserBlock->Result = Response;
|
|
|
|
if (PpUserBlock->VetoType != NULL) {
|
|
*PpUserBlock->VetoType = VetoType;
|
|
}
|
|
|
|
if (PpUserBlock->VetoName != NULL) {
|
|
ASSERT(VetoNameLength == (USHORT)VetoNameLength);
|
|
|
|
vetoString.MaximumLength = (USHORT)VetoNameLength;
|
|
vetoString.Length = (USHORT)VetoNameLength;
|
|
vetoString.Buffer = VetoName;
|
|
RtlCopyUnicodeString(PpUserBlock->VetoName, &vetoString);
|
|
}
|
|
|
|
KeSetEvent(&PpUserBlock->UserResultEvent, 0, FALSE);
|
|
|
|
} // PiUserResponse
|
|
|
|
|
|
NTSTATUS
|
|
PiNotifyUserModeDeviceRemoval(
|
|
IN PPNP_DEVICE_EVENT_ENTRY TemplateDeviceEvent,
|
|
IN CONST GUID *EventGuid,
|
|
OUT PPNP_VETO_TYPE VetoType OPTIONAL,
|
|
OUT PUNICODE_STRING VetoName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tells user mode to perform a specific device removal
|
|
operation.
|
|
|
|
Arguments:
|
|
|
|
TemplateDeviceEvent - Device event containing information about the
|
|
intended event (includes a list of devices.) The
|
|
event is temporarily used by this function, and is
|
|
restored before this function returns.
|
|
|
|
EventGuid - Points to the event user mode should process:
|
|
GUID_TARGET_DEVICE_QUERY_REMOVE
|
|
GUID_TARGET_DEVICE_REMOVE_CANCELLED
|
|
GUID_DEVICE_REMOVE_PENDING
|
|
GUID_TARGET_DEVICE_REMOVE_COMPLETE
|
|
GUID_DEVICE_SURPRISE_REMOVAL
|
|
|
|
VetoType - Pointer to address that receives the veto type if the operation
|
|
failed.
|
|
|
|
VetoName - Pointer to a unicode string that will receive data appropriate
|
|
to the veto type.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
GUID oldGuid;
|
|
PPNP_VETO_TYPE oldVetoType;
|
|
PUNICODE_STRING oldVetoName;
|
|
#if DBG
|
|
CHAR guidString[256];
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
LookupGuid(EventGuid, guidString, sizeof(guidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiNotifyUserModeDeviceRemoval: %s - notifying user-mode\n",
|
|
guidString));
|
|
#endif
|
|
|
|
//
|
|
// Save the old guid so we can use the template without making a copy. We
|
|
// preserve it so that the removal veto UI can use the original event GUID
|
|
// to let the UI differentiate disables from ejects, etc.
|
|
//
|
|
RtlCopyMemory(&oldGuid, &TemplateDeviceEvent->Data.EventGuid, sizeof(GUID));
|
|
|
|
//
|
|
// Do the same with the vetoname and vetobuffer.
|
|
//
|
|
oldVetoType = TemplateDeviceEvent->VetoType;
|
|
oldVetoName = TemplateDeviceEvent->VetoName;
|
|
|
|
//
|
|
// Copy in the new data.
|
|
//
|
|
RtlCopyMemory(&TemplateDeviceEvent->Data.EventGuid, EventGuid, sizeof(GUID));
|
|
TemplateDeviceEvent->VetoType = VetoType;
|
|
TemplateDeviceEvent->VetoName = VetoName;
|
|
|
|
//
|
|
// Send it.
|
|
//
|
|
status = PiNotifyUserMode(TemplateDeviceEvent);
|
|
|
|
//
|
|
// Restore the old info.
|
|
//
|
|
RtlCopyMemory(&TemplateDeviceEvent->Data.EventGuid, &oldGuid, sizeof(GUID));
|
|
TemplateDeviceEvent->VetoType = oldVetoType;
|
|
TemplateDeviceEvent->VetoName = oldVetoName;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PiNotifyUserModeKernelInitiatedEject(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PNP_VETO_TYPE *VetoType,
|
|
OUT PUNICODE_STRING VetoName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to notify user mode a device has a kenel-mode
|
|
initated eject outstanding. UmPnpMgr might decide to veto the event if
|
|
a user with the appropriate permissions hasn't logged on locally.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Indicates the device object is to be ejected.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_NODE deviceNode;
|
|
ULONG dataSize, totalSize, i;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceObject != NULL);
|
|
|
|
//
|
|
// Reference the device object so it can't go away until after we're
|
|
// done with notification.
|
|
//
|
|
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
ASSERT(deviceNode);
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (the length doesn't count the
|
|
// terminating null but we're already counting the first index into the
|
|
// DeviceId field so it works out. Add one more for double-null term.
|
|
//
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
|
|
|
|
dataSize += deviceNode->InstancePath.Length + sizeof(WCHAR);
|
|
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
|
|
deviceEvent->CallerEvent = NULL;
|
|
deviceEvent->Argument = 0;
|
|
deviceEvent->VetoType = VetoType;
|
|
deviceEvent->VetoName = VetoName;
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_KERNEL_INITIATED_EJECT;
|
|
deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
|
|
deviceEvent->Data.Result = 0;
|
|
deviceEvent->Data.Flags = 0;
|
|
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
|
|
if (deviceNode->InstancePath.Length != 0) {
|
|
|
|
RtlCopyMemory((PVOID)deviceEvent->Data.u.TargetDevice.DeviceIds,
|
|
(PVOID)deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length);
|
|
}
|
|
|
|
i = deviceNode->InstancePath.Length/sizeof(WCHAR);
|
|
|
|
deviceEvent->Data.u.TargetDevice.DeviceIds[i] = L'\0';
|
|
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
|
|
ExFreePool(deviceEvent);
|
|
|
|
ObDereferenceObject(DeviceObject);
|
|
|
|
return status;
|
|
|
|
} // PiNotifyUserModeKernelInitiatedEject
|
|
|
|
NTSTATUS
|
|
PiNotifyUserModeRemoveVetoed(
|
|
IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PNP_VETO_TYPE VetoType,
|
|
IN PUNICODE_STRING VetoName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
--*/
|
|
{
|
|
ULONG dataSize, totalSize, i;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
PDEVICE_NODE deviceNode;
|
|
PWCHAR vetoData;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// This device should be locked during this operation, but all good designs
|
|
// includes healthy doses of paranoia.
|
|
//
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
//
|
|
// Given the pdo, retrieve the devnode (the device instance string is
|
|
// attached to the devnode in the InstancePath field).
|
|
//
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (!deviceNode) {
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (because of the first index into
|
|
// the DeviceIdVetoNameBuffer, this is double null terminated).
|
|
//
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) +
|
|
deviceNode->InstancePath.Length +
|
|
(VetoName ? VetoName->Length : 0) +
|
|
sizeof(WCHAR)*2;
|
|
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
|
|
if (deviceEvent == NULL) {
|
|
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
deviceEvent->Data.u.VetoNotification.VetoType = VetoType;
|
|
|
|
//
|
|
// You can think of this as a MultiSz string where the first entry is the
|
|
// DeviceId for the device being removed, and the next Id's all corrospond
|
|
// to the vetoers.
|
|
//
|
|
RtlCopyMemory(
|
|
deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer,
|
|
deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length
|
|
);
|
|
|
|
i = deviceNode->InstancePath.Length;
|
|
deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
if (VetoName) {
|
|
|
|
vetoData = (&deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)])+1;
|
|
|
|
RtlCopyMemory(vetoData, VetoName->Buffer, VetoName->Length);
|
|
vetoData[VetoName->Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
|
|
//
|
|
// No need to NULL terminate the entry after the last one as we prezero'd
|
|
// the buffer. Now set the appropriate GUID so the UI looks right.
|
|
//
|
|
if (PiCompareGuid(&VetoedDeviceEvent->Data.EventGuid, &GUID_DEVICE_EJECT)) {
|
|
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_EJECT_VETOED;
|
|
|
|
} else {
|
|
|
|
ASSERT(PiCompareGuid(&VetoedDeviceEvent->Data.EventGuid, &GUID_DEVICE_QUERY_AND_REMOVE));
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_REMOVAL_VETOED;
|
|
}
|
|
|
|
deviceEvent->Data.EventCategory = VetoEvent;
|
|
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
|
|
ExFreePool(deviceEvent);
|
|
|
|
ObDereferenceObject(DeviceObject);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PiNotifyUserModeRemoveVetoedByList(
|
|
IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PNP_VETO_TYPE VetoType,
|
|
IN PWSTR MultiSzVetoList
|
|
)
|
|
/*++
|
|
|
|
--*/
|
|
{
|
|
ULONG dataSize, totalSize, i, vetoListLength;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
PDEVICE_NODE deviceNode;
|
|
PWCHAR vetoData;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// This device should be locked during this operation, but all good designs
|
|
// includes healthy doses of paranoia.
|
|
//
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
//
|
|
// Given the pdo, retrieve the devnode (the device instance string is
|
|
// attached to the devnode in the InstancePath field).
|
|
//
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (!deviceNode) {
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (because of the first index into
|
|
// the DeviceIdVetoNameBuffer, this is double null terminated).
|
|
//
|
|
|
|
for(vetoData = MultiSzVetoList; *vetoData; vetoData += vetoListLength) {
|
|
|
|
vetoListLength = (ULONG)(wcslen(vetoData) + 1);
|
|
}
|
|
|
|
vetoListLength = ((ULONG)(vetoData - MultiSzVetoList))*sizeof(WCHAR);
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) +
|
|
deviceNode->InstancePath.Length +
|
|
vetoListLength +
|
|
sizeof(WCHAR);
|
|
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool,
|
|
totalSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
|
|
if (deviceEvent == NULL) {
|
|
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
deviceEvent->Data.u.VetoNotification.VetoType = VetoType;
|
|
|
|
//
|
|
// You can think of this as a MultiSz string where the first entry is the
|
|
// DeviceId for the device being removed, and the next Id's all corrospond
|
|
// to the vetoers.
|
|
//
|
|
RtlCopyMemory(
|
|
deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer,
|
|
deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length
|
|
);
|
|
|
|
i = deviceNode->InstancePath.Length;
|
|
deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
vetoData = (&deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)])+1;
|
|
|
|
RtlCopyMemory(vetoData, MultiSzVetoList, vetoListLength);
|
|
vetoData[vetoListLength/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
//
|
|
// No need to NULL terminate the entry after the last one as we prezero'd
|
|
// the buffer. Now set the appropriate GUID so the UI looks right.
|
|
//
|
|
if (PiCompareGuid(&VetoedDeviceEvent->Data.EventGuid, &GUID_DEVICE_EJECT)) {
|
|
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_EJECT_VETOED;
|
|
|
|
} else {
|
|
|
|
ASSERT(PiCompareGuid(&VetoedDeviceEvent->Data.EventGuid, &GUID_DEVICE_QUERY_AND_REMOVE));
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_REMOVAL_VETOED;
|
|
}
|
|
|
|
deviceEvent->Data.EventCategory = VetoEvent;
|
|
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
|
|
ExFreePool(deviceEvent);
|
|
|
|
ObDereferenceObject(DeviceObject);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpNotifyUserModeRemovalSafe(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to notify user mode a device can be removed. This
|
|
is similar to PpSetDeviceRemovalSafe except that we are already in a
|
|
kernel mode PnP device event we must complete, from which this function
|
|
will piggyback a notification up to just user mode.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Indicates the device object is ready for removal.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_NODE deviceNode;
|
|
ULONG dataSize, totalSize, i;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceObject != NULL);
|
|
|
|
//
|
|
// Reference the device object so it can't go away until after we're
|
|
// done with notification.
|
|
//
|
|
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
ASSERT(deviceNode);
|
|
|
|
|
|
//
|
|
// Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
|
|
// we record in the TotalSize field later (the length doesn't count the
|
|
// terminating null but we're already counting the first index into the
|
|
// DeviceId field so it works out. Add one more for double-null term.
|
|
//
|
|
|
|
dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
|
|
|
|
dataSize += deviceNode->InstancePath.Length + sizeof(WCHAR);
|
|
|
|
totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
|
|
|
|
deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
|
|
if (deviceEvent == NULL) {
|
|
ObDereferenceObject(DeviceObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlZeroMemory((PVOID)deviceEvent, totalSize);
|
|
|
|
deviceEvent->CallerEvent = NULL;
|
|
deviceEvent->Argument = 0;
|
|
deviceEvent->VetoType = NULL;
|
|
deviceEvent->VetoName = NULL;
|
|
deviceEvent->Data.EventGuid = GUID_DEVICE_SAFE_REMOVAL;
|
|
deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
|
|
deviceEvent->Data.Result = 0;
|
|
deviceEvent->Data.Flags = 0;
|
|
|
|
deviceEvent->Data.TotalSize = dataSize;
|
|
deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
|
|
|
|
if (deviceNode->InstancePath.Length != 0) {
|
|
|
|
RtlCopyMemory((PVOID)deviceEvent->Data.u.TargetDevice.DeviceIds,
|
|
(PVOID)deviceNode->InstancePath.Buffer,
|
|
deviceNode->InstancePath.Length);
|
|
}
|
|
|
|
i = deviceNode->InstancePath.Length/sizeof(WCHAR);
|
|
|
|
deviceEvent->Data.u.TargetDevice.DeviceIds[i] = L'\0';
|
|
|
|
status = PiNotifyUserMode(deviceEvent);
|
|
|
|
ExFreePool(deviceEvent);
|
|
|
|
ObDereferenceObject(DeviceObject);
|
|
|
|
return status;
|
|
|
|
} // PpNotifyUserModeRemovalSafe
|
|
|
|
|
|
NTSTATUS
|
|
PiProcessQueryRemoveAndEject(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the various flavours of remove: Eject, SurpriseRemove,
|
|
Remove, and QueryRemove.
|
|
|
|
Eject
|
|
Retrieve bus, removal, and eject relations.
|
|
Do queries on all relations
|
|
Send IRP_MN_REMOVE_DEVICE to all the relations.
|
|
Queue the pending eject
|
|
|
|
Once the eject happens
|
|
Reenumerate all the indirect relation's parents
|
|
|
|
SurpriseRemove
|
|
Retrieve bus, removal, and eject relations.
|
|
Send IRP_MN_SURPRISE_REMOVAL to all the direct relations.
|
|
Notify everyone that the device is gone.
|
|
Reenumerate the parents of all the indirect relations.
|
|
Remove the indirect relations from the relations list.
|
|
Queue the pending surprise removal.
|
|
|
|
Once the last handle is closed.
|
|
|
|
Send IRP_MN_REMOVE_DEVICE to all the direct relations.
|
|
|
|
RemoveFailedDevice
|
|
Retrieve bus and removal relations.
|
|
Notify everyone that the device is going.
|
|
Reenumerate the parents of all the indirect relations.
|
|
Remove the indirect relations from the relations list.
|
|
Queue as a pending surprise removal.
|
|
|
|
Once the last handle is closed.
|
|
|
|
Send IRP_MN_REMOVE_DEVICE to all the direct relations.
|
|
|
|
RemoveUnstartedFailedDevice
|
|
Retrieve bus relations.
|
|
Notify everyone that the device is going.
|
|
Send IRP_MN_REMOVE_DEVICE to all the direct relations.
|
|
|
|
Remove
|
|
Use the relations from the last QueryRemove or retrieve new bus, removal,
|
|
and eject relations if the device wasn't already QueryRemoved.
|
|
Send IRP_MN_REMOVE_DEVICE to all the relations.
|
|
|
|
Arguments:
|
|
|
|
Response - Result of event processing in user-mode.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
PPNP_DEVICE_EVENT_ENTRY surpriseRemovalEvent;
|
|
PLUGPLAY_DEVICE_DELETE_TYPE deleteType;
|
|
PPENDING_RELATIONS_LIST_ENTRY pendingRelations;
|
|
PNP_VETO_TYPE vetoType;
|
|
PDEVICE_OBJECT deviceObject, relatedDeviceObject;
|
|
PDEVICE_OBJECT *pdoList;
|
|
PDEVICE_NODE deviceNode, relatedDeviceNode;
|
|
PRELATION_LIST relationsList;
|
|
ULONG relationCount;
|
|
NTSTATUS status;
|
|
ULONG marker;
|
|
BOOLEAN directDescendant;
|
|
PDEVICE_OBJECT vetoingDevice = NULL;
|
|
PDRIVER_OBJECT vetoingDriver = NULL;
|
|
LONG index;
|
|
BOOLEAN possibleProfileChangeInProgress = FALSE;
|
|
BOOLEAN subsumingProfileChange = FALSE;
|
|
BOOLEAN hotEjectSupported;
|
|
BOOLEAN warmEjectSupported;
|
|
BOOLEAN excludeIndirectRelations;
|
|
UNICODE_STRING singleVetoListItem;
|
|
PWSTR vetoList;
|
|
UNICODE_STRING internalVetoString;
|
|
PWSTR internalVetoBuffer;
|
|
PDOCK_INTERFACE dockInterface = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceEvent = *DeviceEvent;
|
|
deviceObject = (PDEVICE_OBJECT)deviceEvent->Data.DeviceObject;
|
|
deviceNode = deviceObject->DeviceObjectExtension->DeviceNode;
|
|
surpriseRemovalEvent = NULL;
|
|
|
|
PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
|
|
|
|
if (PiCompareGuid(&deviceEvent->Data.EventGuid, &GUID_DEVICE_EJECT)) {
|
|
|
|
deleteType = EjectDevice;
|
|
|
|
} else if (deviceEvent->Data.Flags & TDF_KERNEL_INITIATED) {
|
|
|
|
if (!(deviceNode->Flags & DNF_ENUMERATED)) {
|
|
|
|
ASSERT(deviceNode->State == DeviceNodeAwaitingQueuedDeletion);
|
|
|
|
if ((deviceNode->PreviousState == DeviceNodeStarted) ||
|
|
(deviceNode->PreviousState == DeviceNodeStopped) ||
|
|
(deviceNode->PreviousState == DeviceNodeStartPostWork) ||
|
|
(deviceNode->PreviousState == DeviceNodeRestartCompletion)) {
|
|
|
|
deleteType = SurpriseRemoveDevice;
|
|
|
|
} else {
|
|
deleteType = RemoveDevice;
|
|
}
|
|
} else {
|
|
|
|
ASSERT(deviceNode->State == DeviceNodeAwaitingQueuedRemoval);
|
|
|
|
if ((deviceNode->PreviousState == DeviceNodeStarted) ||
|
|
(deviceNode->PreviousState == DeviceNodeStopped) ||
|
|
(deviceNode->PreviousState == DeviceNodeStartPostWork) ||
|
|
(deviceNode->PreviousState == DeviceNodeRestartCompletion)) {
|
|
|
|
deleteType = RemoveFailedDevice;
|
|
} else {
|
|
deleteType = RemoveUnstartedFailedDevice;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
deleteType = QueryRemoveDevice;
|
|
}
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Remove type %x\n", deleteType));
|
|
|
|
if (deleteType == QueryRemoveDevice || deleteType == EjectDevice) {
|
|
|
|
if (deviceNode->Flags & DNF_LEGACY_DRIVER) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed by legacy driver\n"));
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoLegacyDevice,
|
|
&deviceNode->InstancePath
|
|
);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
}
|
|
|
|
if (deleteType == QueryRemoveDevice && deviceEvent->Argument == CM_PROB_DISABLED) {
|
|
|
|
//
|
|
// if we're trying to remove the device to disable the device
|
|
//
|
|
if (deviceNode->DisableableDepends > 0) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Device is non-disableable\n"));
|
|
//
|
|
// we should have caught this before (in usermode PnP)
|
|
// but a rare scenario can exist where the device becomes non-disableable
|
|
// There is still a potential gap, if the device hasn't got around
|
|
// to marking itself as non-disableable yet
|
|
//
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoNonDisableable,
|
|
&deviceNode->InstancePath
|
|
);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate room for a possible veto buffer.
|
|
//
|
|
internalVetoBuffer = (PWSTR) PiAllocateCriticalMemory(
|
|
deleteType,
|
|
PagedPool,
|
|
MAX_VETO_NAME_LENGTH * sizeof(WCHAR),
|
|
'rcpP'
|
|
);
|
|
|
|
if (internalVetoBuffer == NULL) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed due to failure to allocate vetobuffer\n"));
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoTypeUnknown,
|
|
NULL
|
|
);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
|
|
//
|
|
// Preinit the veto information
|
|
//
|
|
vetoType = PNP_VetoTypeUnknown;
|
|
internalVetoString.MaximumLength = MAX_VETO_NAME_LENGTH;
|
|
internalVetoString.Length = 0;
|
|
internalVetoString.Buffer = internalVetoBuffer;
|
|
|
|
if (deleteType == EjectDevice) {
|
|
|
|
if (deviceNode->Flags & DNF_LOCKED_FOR_EJECT) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Device already being ejected\n"));
|
|
//
|
|
// Either this node or one of its ancestors is already being ejected.
|
|
//
|
|
ExFreePool(internalVetoBuffer);
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (deviceEvent->Data.Flags & TDF_KERNEL_INITIATED) {
|
|
|
|
//
|
|
// Check permissions.
|
|
//
|
|
status = PiNotifyUserModeKernelInitiatedEject(
|
|
deviceObject,
|
|
&vetoType,
|
|
&internalVetoString
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Kernel initiated eject vetoed by user mode\n"));
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
vetoType,
|
|
&internalVetoString
|
|
);
|
|
|
|
ExFreePool(internalVetoBuffer);
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
}
|
|
|
|
if ((deviceNode->DockInfo.DockStatus == DOCK_DEPARTING) ||
|
|
(deviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Dock already being ejected\n"));
|
|
//
|
|
// We already have an eject queued against this device. Don't allow
|
|
// another eject to break into the middle of a queue/cancel warm
|
|
// eject sequence.
|
|
//
|
|
ExFreePool(internalVetoBuffer);
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// What types of ejection can we do? (warm/hot)
|
|
//
|
|
if (!IopDeviceNodeFlagsToCapabilities(deviceNode)->Removable) {
|
|
|
|
//
|
|
// This device is neither ejectable, nor even removable.
|
|
//
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Device not removable\n"));
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoIllegalDeviceRequest,
|
|
&deviceNode->InstancePath
|
|
);
|
|
|
|
ExFreePool(internalVetoBuffer);
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
}
|
|
|
|
if ((deleteType == QueryRemoveDevice) && (!PipAreDriversLoaded(deviceNode))) {
|
|
|
|
//
|
|
// The device doesn't have an FDO.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
if ((deviceNode->State == DeviceNodeInitialized) ||
|
|
(deviceNode->State == DeviceNodeRemoved)) {
|
|
|
|
//
|
|
// The rules are:
|
|
// 1) !TDF_NO_RESTART means clear the devnode and get it ready
|
|
// as long as the problem is user resettable. Ignore the passed
|
|
// in problem code (probably either CM_PROB_WILL_BE_REMOVED or
|
|
// CM_PROB_DEVICE_NOT_THERE), as it means nothing.
|
|
// 2) TDF_NO_RESTART means change the problem code over if you can.
|
|
// If the problem code is not user resettable, the problem code
|
|
// won't change.
|
|
//
|
|
|
|
//
|
|
// In all cases we try to clear the problem.
|
|
//
|
|
if (PipDoesDevNodeHaveProblem(deviceNode)) {
|
|
|
|
if (!PipIsProblemReadonly(deviceNode->Problem)) {
|
|
|
|
PipClearDevNodeProblem(deviceNode);
|
|
}
|
|
}
|
|
|
|
if (!PipDoesDevNodeHaveProblem(deviceNode)) {
|
|
|
|
if (!(deviceEvent->Data.Flags & TDF_NO_RESTART)) {
|
|
|
|
//
|
|
// This is a reset attempt. Mark the devnode so that it
|
|
// comes online next enumeration.
|
|
//
|
|
IopRestartDeviceNode(deviceNode);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We're changing or setting problem codes. Note that the
|
|
// device is still in DeviceNodeInitialized or
|
|
// DeviceNodeRemoved.
|
|
//
|
|
PipSetDevNodeProblem(deviceNode, deviceEvent->Argument);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The problem is fixed, so the devnode state is immutable
|
|
// as far as user mode is concerned. Here we fail the call
|
|
// if we can't bring the devnode back online. We always succeed
|
|
// the call if it was an attempt to change the code, as the
|
|
// user either wants to prepare the device for ejection (done),
|
|
// or wants to disable it (as good as done.)
|
|
//
|
|
if (!(deviceEvent->Data.Flags & TDF_NO_RESTART)) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
ExFreePool(internalVetoBuffer);
|
|
return status;
|
|
}
|
|
|
|
status = IopBuildRemovalRelationList( deviceObject,
|
|
deleteType,
|
|
&vetoType,
|
|
&internalVetoString,
|
|
&relationsList);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Failed to build removal relations\n"));
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
vetoType,
|
|
&internalVetoString
|
|
);
|
|
|
|
ExFreePool(internalVetoBuffer);
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
|
|
ASSERT(relationsList != NULL);
|
|
|
|
//
|
|
// Resize the event buffer and add these device instance strings to the
|
|
// list to notify.
|
|
//
|
|
relationCount = IopGetRelationsCount( relationsList );
|
|
ASSERT(!IopGetRelationsTaggedCount( relationsList ));
|
|
|
|
//
|
|
// PdoList will become a list of devices that must be queried. This is
|
|
// a subset of all the devices that might disappear, all of which appear
|
|
// in the relations list.
|
|
//
|
|
pdoList = (PDEVICE_OBJECT *) PiAllocateCriticalMemory(
|
|
deleteType,
|
|
NonPagedPool,
|
|
relationCount * sizeof(PDEVICE_OBJECT),
|
|
'rcpP'
|
|
);
|
|
|
|
if (pdoList != NULL) {
|
|
|
|
relationCount = 0;
|
|
marker = 0;
|
|
while (IopEnumerateRelations( relationsList,
|
|
&marker,
|
|
&relatedDeviceObject,
|
|
&directDescendant,
|
|
NULL,
|
|
TRUE)) {
|
|
|
|
//
|
|
// Here is a list of what operations retrieve what relations,
|
|
// who they query, and who/how they notify.
|
|
//
|
|
// Operation Relations Queries Notifies
|
|
// --------- --------- ------- --------
|
|
// EjectDevice Ejection Everyone Everyone (Remove)
|
|
// SurpriseRemoveDevice Ejection NA Descendants (SurpriseRemove)
|
|
// RemoveDevice Ejection NA Descendants (Remove)
|
|
// RemoveFailedDevice Removal NA Descendants (SurpriseRemove)
|
|
// RemoveUnstartedFailedDevice Removal NA Descendants (Remove)
|
|
// QueryRemoveDevice Removal Everyone Everyone (Remove)
|
|
//
|
|
//
|
|
// N.B.
|
|
// We do not send SurpriseRemove's to removal relations.
|
|
// While doing so might seem to be the correct behavior, many
|
|
// drivers do not handle this well. Simply reenumerating the
|
|
// parents of the removal relations works much better. Similarly
|
|
// ejection relations have their parents reenumerated (which
|
|
// makes sense, as they are speculative in nature anyway).
|
|
//
|
|
// If we get in a case where a *parent* of a dock gets
|
|
// into the RemoveFailedDevice case (ie, failed restart,
|
|
// responded to QueryDeviceState with Removed, etc), then we
|
|
// will be shortly losing the children when we stop the parent.
|
|
// However, we want to eject the dock child, not just remove it
|
|
// as starting and ejecting are symmetric here. Note that
|
|
// currently the only such parent would be the root ACPI devnode.
|
|
//
|
|
// Ejection relations of a device (eg dock) that has been
|
|
// surprise removed are not notified that they *may* have been
|
|
// pulled (remember, ejection relations are speculative). We
|
|
// will notify only DirectDescendants and queue an enumeration
|
|
// on every parent of the ejection relations. If they really
|
|
// disappeared, they will get their notification, albeit a bit
|
|
// later than some of the other devices in the tree.
|
|
//
|
|
if (directDescendant || deleteType == EjectDevice || deleteType == QueryRemoveDevice) {
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
//
|
|
// PiProcessQueryRemoveAndEject will be called twice for
|
|
// the dock during an eject. Once with EjectDevice, and
|
|
// after the dock is listed as missing once more with
|
|
// RemoveDevice. We don't want to start a profile change
|
|
// for RemoveDevice as we are already in one, and we would
|
|
// deadlock if we tried. We don't start one for QueryRemove
|
|
// either as the dock isn't *physically* going away.
|
|
//
|
|
ASSERT(relatedDeviceNode->DockInfo.DockStatus != DOCK_ARRIVING);
|
|
if (deleteType != RemoveDevice &&
|
|
deleteType != QueryRemoveDevice) {
|
|
|
|
if (relatedDeviceNode->DockInfo.DockStatus == DOCK_QUIESCENT) {
|
|
|
|
possibleProfileChangeInProgress = TRUE;
|
|
|
|
} else if (relatedDeviceNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) {
|
|
|
|
subsumingProfileChange = TRUE;
|
|
}
|
|
}
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (deleteType == QueryRemoveDevice || deleteType == EjectDevice) {
|
|
|
|
if (relatedDeviceNode->Flags & DNF_LEGACY_DRIVER) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed by legacy driver relation\n"));
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoLegacyDevice,
|
|
&relatedDeviceNode->InstancePath
|
|
);
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
if (relatedDeviceNode->State == DeviceNodeRemovePendingCloses) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed due to device in DeviceNodeRemovePendingCloses\n"));
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoOutstandingOpen,
|
|
&relatedDeviceNode->InstancePath
|
|
);
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
pdoList[ relationCount++ ] = relatedDeviceObject;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ((deleteType == SurpriseRemoveDevice ||
|
|
deleteType == RemoveFailedDevice ||
|
|
deleteType == RemoveUnstartedFailedDevice ||
|
|
deleteType == RemoveDevice)) {
|
|
|
|
excludeIndirectRelations = TRUE;
|
|
|
|
} else {
|
|
excludeIndirectRelations = FALSE;
|
|
}
|
|
|
|
status = PiResizeTargetDeviceBlock( DeviceEvent,
|
|
deleteType,
|
|
relationsList,
|
|
excludeIndirectRelations );
|
|
|
|
deviceEvent = *DeviceEvent;
|
|
|
|
|
|
if (deleteType == SurpriseRemoveDevice) {
|
|
|
|
PiBuildUnsafeRemovalDeviceBlock(
|
|
deviceEvent,
|
|
relationsList,
|
|
&surpriseRemovalEvent
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopFreeRelationList(relationsList);
|
|
|
|
if (pdoList) {
|
|
|
|
ExFreePool(pdoList);
|
|
}
|
|
|
|
ExFreePool(internalVetoBuffer);
|
|
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoTypeUnknown,
|
|
NULL
|
|
);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// We may need to take the hardware profile change semaphore, and also
|
|
// broadcast a hardware profile change request...
|
|
//
|
|
if (possibleProfileChangeInProgress) {
|
|
|
|
PpProfileBeginHardwareProfileTransition(subsumingProfileChange);
|
|
|
|
//
|
|
// Walk the list of docks who are going to disappear and mark them as
|
|
// in profile transition.
|
|
//
|
|
for (index = relationCount - 1; index >= 0; index--) {
|
|
|
|
relatedDeviceObject = pdoList[ index ];
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
ASSERT(relatedDeviceNode->DockInfo.DockStatus != DOCK_ARRIVING);
|
|
if (relatedDeviceNode->DockInfo.DockStatus == DOCK_QUIESCENT) {
|
|
|
|
PpProfileIncludeInHardwareProfileTransition(
|
|
relatedDeviceNode,
|
|
DOCK_DEPARTING
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We can only be in one of the following deleteType situations
|
|
//
|
|
// 1) EjectDevice - Good, normal ejection request by our user
|
|
// (we are using EjectionRelations)
|
|
//
|
|
// 2) SurpriseRemoveDevice - Someone yanked the dock out.
|
|
// (we are using EjectionRelations)
|
|
//
|
|
// 3) RemoveFailedDevice - A start failed after a stop on a parent or
|
|
// even our device. This case is not handled
|
|
// correctly. We assert for now, and we
|
|
// maroon the dock, ie lose it's devnode but
|
|
// the dock stays physically present and is
|
|
// in the users eye's unejectable.
|
|
//
|
|
// 4) RemoveDevice - This occurs in three cases:
|
|
// a) A removed device is disappearing.
|
|
// b) A device is being removed but has not
|
|
// been started.
|
|
// c) A device has failed start.
|
|
//
|
|
// We pass through case a) during a normal
|
|
// ejection and as part of a profile
|
|
// transition begun earlier. c) is similar
|
|
// to a) but the transition was begun by the
|
|
// start code. For case b) we don't want to
|
|
// turn it into an eject, as the OS might be
|
|
// removing our parent as a normal part of
|
|
// setup, and we wouldn't want to undock then
|
|
// (and we probably aren't changing profiles
|
|
// anyway).
|
|
//
|
|
// 5) QueryRemoveDevice This should never be the case here per the
|
|
// explicit veto in the IopEnumerateRelations
|
|
// code above.
|
|
//
|
|
|
|
//
|
|
// RemoveFailedDevice is a PathTrap - the only parent of a dock is
|
|
// the ACPI root devnode right now. We shouldn't get into that case.
|
|
//
|
|
ASSERT(deleteType != QueryRemoveDevice &&
|
|
deleteType != RemoveFailedDevice);
|
|
|
|
if (deleteType == EjectDevice) {
|
|
|
|
//
|
|
// Are there any legacy drivers in the system?
|
|
//
|
|
status = IoGetLegacyVetoList(&vetoList, &vetoType);
|
|
|
|
if (NT_SUCCESS(status) &&
|
|
(vetoType != PNP_VetoTypeUnknown)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed due to presence of a legacy driver\n"));
|
|
//
|
|
// Release any docks in profile transition
|
|
//
|
|
PpProfileCancelHardwareProfileTransition();
|
|
|
|
IopFreeRelationList(relationsList);
|
|
|
|
//
|
|
// Failure occured, notify user mode as appropriate, or fill in
|
|
// the veto buffer.
|
|
//
|
|
if (deviceEvent->VetoType != NULL) {
|
|
|
|
*deviceEvent->VetoType = vetoType;
|
|
}
|
|
|
|
if (deviceEvent->VetoName == NULL) {
|
|
|
|
//
|
|
// If there is not a VetoName passed in then call user mode
|
|
// to display the eject veto notification to the user.
|
|
//
|
|
PiNotifyUserModeRemoveVetoedByList(
|
|
deviceEvent,
|
|
deviceObject,
|
|
vetoType,
|
|
vetoList
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The veto data in the PNP_DEVICE_EVENT_ENTRY block is
|
|
// a UNICODE_STRING field. Since that type of data structure
|
|
// cannot handle Multi-Sz data, we cull the information down
|
|
// to one entry here.
|
|
//
|
|
RtlCopyUnicodeString(deviceEvent->VetoName, &singleVetoListItem);
|
|
RtlInitUnicodeString(&singleVetoListItem, vetoList);
|
|
}
|
|
|
|
ExFreePool(vetoList);
|
|
ExFreePool(pdoList);
|
|
ExFreePool(internalVetoBuffer);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
|
|
//
|
|
// Broadcast the query for a profile change against our current
|
|
// list of docks in transition...
|
|
//
|
|
status = PpProfileQueryHardwareProfileChange(
|
|
subsumingProfileChange,
|
|
PROFILE_IN_PNPEVENT,
|
|
&vetoType,
|
|
&internalVetoString
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed due to failed HW profile change\n"));
|
|
//
|
|
// Release any docks in profile transition
|
|
//
|
|
PpProfileCancelHardwareProfileTransition();
|
|
|
|
IopFreeRelationList(relationsList);
|
|
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
vetoType,
|
|
&internalVetoString
|
|
);
|
|
|
|
ExFreePool(pdoList);
|
|
ExFreePool(internalVetoBuffer);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (deleteType == QueryRemoveDevice || deleteType == EjectDevice) {
|
|
|
|
//
|
|
// Send query notification to user-mode.
|
|
//
|
|
|
|
status = PiNotifyUserModeDeviceRemoval(
|
|
deviceEvent,
|
|
&GUID_TARGET_DEVICE_QUERY_REMOVE,
|
|
&vetoType,
|
|
&internalVetoString
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: QUERY_REMOVE - notifying kernel-mode\n"));
|
|
|
|
//
|
|
// Send query notification to kernel-mode drivers.
|
|
//
|
|
|
|
for (index = 0; index < (LONG)relationCount; index++) {
|
|
|
|
relatedDeviceObject = pdoList[ index ];
|
|
|
|
status = IopNotifyTargetDeviceChange( &GUID_TARGET_DEVICE_QUERY_REMOVE,
|
|
relatedDeviceObject,
|
|
NULL,
|
|
&vetoingDriver);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed due to driver failing QR notification\n"));
|
|
vetoType = PNP_VetoDriver;
|
|
|
|
if (vetoingDriver != NULL) {
|
|
|
|
RtlCopyUnicodeString(&internalVetoString, &vetoingDriver->DriverName);
|
|
|
|
} else {
|
|
|
|
RtlInitUnicodeString(&internalVetoString, NULL);
|
|
}
|
|
|
|
for (index--; index >= 0; index--) {
|
|
relatedDeviceObject = pdoList[ index ];
|
|
|
|
IopNotifyTargetDeviceChange( &GUID_TARGET_DEVICE_REMOVE_CANCELLED,
|
|
relatedDeviceObject,
|
|
NULL,
|
|
NULL);
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// If we haven't already performed the action yet (a query remove
|
|
// to the target device, in this case), then do it now.
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: QueryRemove DevNodes\n"));
|
|
|
|
status = IopDeleteLockedDeviceNodes(deviceObject,
|
|
relationsList,
|
|
QueryRemoveDevice,
|
|
TRUE,
|
|
0,
|
|
&vetoType,
|
|
&internalVetoString);
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Everyone has been notified and had a chance to close their handles.
|
|
// Since no one has vetoed it yet, let's see if there are any open
|
|
// references.
|
|
//
|
|
|
|
if (IopNotifyPnpWhenChainDereferenced( pdoList, relationCount, TRUE, &vetoingDevice )) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed due to open handles against the device\n"));
|
|
|
|
vetoType = PNP_VetoOutstandingOpen;
|
|
|
|
if (!PiCollectOpenHandles(pdoList, relationCount, FALSE, &internalVetoString)) {
|
|
|
|
if (vetoingDevice != NULL) {
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)vetoingDevice->DeviceObjectExtension->DeviceNode;
|
|
|
|
ASSERT(relatedDeviceNode != NULL);
|
|
|
|
RtlCopyUnicodeString(&internalVetoString, &relatedDeviceNode->InstancePath);
|
|
|
|
} else {
|
|
|
|
RtlInitUnicodeString(&internalVetoString, NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send cancel remove to the target devices.
|
|
//
|
|
|
|
IopDeleteLockedDeviceNodes(deviceObject,
|
|
relationsList,
|
|
CancelRemoveDevice,
|
|
TRUE,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
} else if (vetoType == PNP_VetoDevice) {
|
|
|
|
PiCollectOpenHandles(pdoList, relationCount, FALSE, &internalVetoString);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed due someone in the stack failed QR\n"));
|
|
//
|
|
// Send cancel notification to kernel-mode drivers.
|
|
//
|
|
|
|
for (index = relationCount - 1; index >= 0; index--) {
|
|
|
|
relatedDeviceObject = pdoList[ index ];
|
|
|
|
IopNotifyTargetDeviceChange( &GUID_TARGET_DEVICE_REMOVE_CANCELLED,
|
|
relatedDeviceObject,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_WARNING_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed by \"%wZ\" (type 0x%x)\n",
|
|
&internalVetoString,
|
|
vetoType));
|
|
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
vetoType,
|
|
&internalVetoString
|
|
);
|
|
|
|
//
|
|
// A driver vetoed the query remove, go back and send
|
|
// cancels to user-mode (cancels already sent to drivers
|
|
// that received the query).
|
|
//
|
|
PiNotifyUserModeDeviceRemoval(
|
|
deviceEvent,
|
|
&GUID_TARGET_DEVICE_REMOVE_CANCELLED,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed due to UM failing QR\n"));
|
|
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
vetoType,
|
|
&internalVetoString
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Broadcast a cancel HW profile change event if appropriate.
|
|
//
|
|
if (possibleProfileChangeInProgress) {
|
|
|
|
//
|
|
// Release any docks in profile transition. We also broadcast
|
|
// the cancel.
|
|
//
|
|
PpProfileCancelHardwareProfileTransition();
|
|
}
|
|
|
|
//
|
|
// User-mode vetoed the request (cancels already sent
|
|
// to user-mode callers that received the query).
|
|
//
|
|
IopFreeRelationList(relationsList);
|
|
|
|
ExFreePool(pdoList);
|
|
ExFreePool(internalVetoBuffer);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
|
|
} else if (deleteType == SurpriseRemoveDevice || deleteType == RemoveFailedDevice) {
|
|
|
|
//
|
|
// Send IRP_MN_SURPRISE_REMOVAL, IopDeleteLockDeviceNodes ignores
|
|
// indirect descendants for SurpriseRemoveDevice.
|
|
//
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: QueryRemove DevNodes\n"));
|
|
|
|
IopDeleteLockedDeviceNodes( deviceObject,
|
|
relationsList,
|
|
SurpriseRemoveDevice,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
//
|
|
// Notify user-mode and drivers that a remove is happening. User-mode
|
|
// sees this as a remove pending if it's user initiated, we don't give
|
|
// them the "remove" until it's actually gone.
|
|
//
|
|
if (deleteType != SurpriseRemoveDevice) {
|
|
|
|
//
|
|
// ISSUE - 2000/08/20 - ADRIAO: Busted message path
|
|
// We send GUID_DEVICE_REMOVE_PENDING to devices that are already
|
|
// dead in the case of RemoveFailedDevice.
|
|
//
|
|
PiNotifyUserModeDeviceRemoval(
|
|
deviceEvent,
|
|
&GUID_DEVICE_REMOVE_PENDING,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
} else {
|
|
|
|
if (surpriseRemovalEvent) {
|
|
|
|
PiNotifyUserModeDeviceRemoval(
|
|
surpriseRemovalEvent,
|
|
&GUID_DEVICE_SURPRISE_REMOVAL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
ExFreePool(surpriseRemovalEvent);
|
|
}
|
|
|
|
PiNotifyUserModeDeviceRemoval(
|
|
deviceEvent,
|
|
&GUID_TARGET_DEVICE_REMOVE_COMPLETE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: REMOVE_COMPLETE - notifying kernel-mode\n"));
|
|
|
|
for (index = 0; index < (LONG)relationCount; index++) {
|
|
|
|
relatedDeviceObject = pdoList[ index ];
|
|
|
|
status = IopNotifyTargetDeviceChange( &GUID_TARGET_DEVICE_REMOVE_COMPLETE,
|
|
relatedDeviceObject,
|
|
NULL,
|
|
NULL);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
}
|
|
|
|
if (deleteType == RemoveDevice ||
|
|
deleteType == RemoveFailedDevice ||
|
|
deleteType == SurpriseRemoveDevice) {
|
|
|
|
//
|
|
// For these operations indirect relations are speculative.
|
|
//
|
|
// So for each of the indirect relations, invalidate their parents and
|
|
// remove them from the relations list.
|
|
//
|
|
|
|
IopInvalidateRelationsInList( relationsList, deleteType, TRUE, FALSE );
|
|
|
|
IopRemoveIndirectRelationsFromList( relationsList );
|
|
}
|
|
|
|
if (deleteType == RemoveFailedDevice ||
|
|
deleteType == SurpriseRemoveDevice) {
|
|
|
|
//
|
|
// We've sent the surprise remove IRP to the original device and all its
|
|
// direct descendants. We've also notified user-mode.
|
|
//
|
|
|
|
//
|
|
// Unlock the device relations list.
|
|
//
|
|
// Note there could be a potential race condition here between
|
|
// unlocking the devnodes in the relation list and completing the
|
|
// execution of IopNotifyPnpWhenChainDereferenced. If an enumeration
|
|
// takes places (we've unlocked the devnode) before the eventual remove
|
|
// is sent then problems could occur.
|
|
//
|
|
// This is prevented by the setting of DNF_REMOVE_PENDING_CLOSES when
|
|
// we sent the IRP_MN_SURPRISE_REMOVAL.
|
|
//
|
|
// We do need to do it prior to calling IopQueuePendingSurpriseRemoval
|
|
// since we lose ownership of the relation list in that call. Also
|
|
// IopNotifyPnpWhenChainDereferenced may cause the relation list to be
|
|
// freed before it returns.
|
|
//
|
|
// If this is a RemoveFailedDevice then we don't want to remove the
|
|
// device node from the tree but we do want to remove children without
|
|
// resources.
|
|
//
|
|
|
|
IopUnlinkDeviceRemovalRelations( deviceObject,
|
|
relationsList,
|
|
deleteType == SurpriseRemoveDevice ?
|
|
UnlinkAllDeviceNodesPendingClose :
|
|
UnlinkOnlyChildDeviceNodesPendingClose);
|
|
|
|
//
|
|
// Add the relation list to a list of pending surprise removals.
|
|
//
|
|
IopQueuePendingSurpriseRemoval( deviceObject, relationsList, deviceEvent->Argument );
|
|
|
|
//
|
|
// Release the engine lock *before* IopNotifyPnpWhenChainDereferenced,
|
|
// as it may call back into us...
|
|
//
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
IopNotifyPnpWhenChainDereferenced( pdoList, relationCount, FALSE, NULL );
|
|
|
|
ExFreePool(pdoList);
|
|
ExFreePool(internalVetoBuffer);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (deviceNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) {
|
|
|
|
status = IopQueryDockRemovalInterface(
|
|
deviceObject,
|
|
&dockInterface
|
|
);
|
|
|
|
if (dockInterface) {
|
|
|
|
//
|
|
// Make sure updates don't occur on removes during an ejection.
|
|
// We may change this to PDS_UPDATE_ON_EJECT *after* the remove
|
|
// IRPs go through (as only then do we know our power
|
|
// constraints)
|
|
//
|
|
dockInterface->ProfileDepartureSetMode(
|
|
dockInterface->Context,
|
|
PDS_UPDATE_ON_INTERFACE
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send the remove to the devnode tree.
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: RemoveDevice DevNodes\n"));
|
|
|
|
status = IopDeleteLockedDeviceNodes(deviceObject,
|
|
relationsList,
|
|
RemoveDevice,
|
|
(BOOLEAN)(deleteType == QueryRemoveDevice || deleteType == EjectDevice),
|
|
deviceEvent->Argument,
|
|
NULL,
|
|
NULL);
|
|
|
|
hotEjectSupported =
|
|
(BOOLEAN) IopDeviceNodeFlagsToCapabilities(deviceNode)->EjectSupported;
|
|
|
|
warmEjectSupported =
|
|
(BOOLEAN) IopDeviceNodeFlagsToCapabilities(deviceNode)->WarmEjectSupported;
|
|
|
|
if (deleteType != EjectDevice) {
|
|
|
|
if (!(deviceEvent->Data.Flags & TDF_NO_RESTART)) {
|
|
|
|
//
|
|
// Set a flag to let kernel-mode know we'll be wanting to
|
|
// restart these devnodes, eventually.
|
|
//
|
|
|
|
marker = 0;
|
|
while (IopEnumerateRelations( relationsList,
|
|
&marker,
|
|
&relatedDeviceObject,
|
|
NULL,
|
|
NULL,
|
|
TRUE)) {
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (relatedDeviceNode &&
|
|
relatedDeviceNode->State == DeviceNodeRemoved &&
|
|
PipIsDevNodeProblem(relatedDeviceNode, CM_PROB_WILL_BE_REMOVED)) {
|
|
|
|
if (!(deviceEvent->Data.Flags & TDF_ONLY_RESTART_RELATIONS) ||
|
|
deviceObject != relatedDeviceObject) {
|
|
|
|
PipClearDevNodeProblem(relatedDeviceNode);
|
|
|
|
IopRestartDeviceNode(relatedDeviceNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Unlock the device relations list.
|
|
//
|
|
IopUnlinkDeviceRemovalRelations( deviceObject,
|
|
relationsList,
|
|
UnlinkRemovedDeviceNodes );
|
|
|
|
IopFreeRelationList(relationsList);
|
|
|
|
} else if (hotEjectSupported || warmEjectSupported) {
|
|
|
|
//
|
|
// From this point on we cannot return any sort of failure without
|
|
// going through IopEjectDevice or cancelling any outstanding profile
|
|
// change.
|
|
//
|
|
|
|
//
|
|
// Set a flag to let kernel-mode know we'll be wanting to
|
|
// restart these devnodes, eventually.
|
|
//
|
|
|
|
marker = 0;
|
|
while (IopEnumerateRelations( relationsList,
|
|
&marker,
|
|
&relatedDeviceObject,
|
|
NULL,
|
|
NULL,
|
|
TRUE)) {
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (relatedDeviceNode) {
|
|
|
|
relatedDeviceNode->Flags |= DNF_LOCKED_FOR_EJECT;
|
|
}
|
|
}
|
|
|
|
IopUnlinkDeviceRemovalRelations( deviceObject,
|
|
relationsList,
|
|
UnlinkRemovedDeviceNodes );
|
|
|
|
//
|
|
// Send the eject
|
|
//
|
|
pendingRelations = ExAllocatePool( NonPagedPool, sizeof(PENDING_RELATIONS_LIST_ENTRY) );
|
|
|
|
if (pendingRelations == NULL) {
|
|
|
|
//
|
|
// It's cleanup time. Free up everything that matters
|
|
//
|
|
if (dockInterface) {
|
|
|
|
dockInterface->ProfileDepartureSetMode(
|
|
dockInterface->Context,
|
|
PDS_UPDATE_DEFAULT
|
|
);
|
|
|
|
dockInterface->InterfaceDereference(dockInterface->Context);
|
|
}
|
|
|
|
ExFreePool(pdoList);
|
|
ExFreePool(internalVetoBuffer);
|
|
|
|
if (possibleProfileChangeInProgress) {
|
|
|
|
//
|
|
// Release any docks in profile transition. We also broadcast
|
|
// the cancel.
|
|
//
|
|
PpProfileCancelHardwareProfileTransition();
|
|
}
|
|
|
|
//
|
|
// This will bring back online the devices that were held offline
|
|
// for the duration of the undock.
|
|
//
|
|
IopInvalidateRelationsInList(relationsList, deleteType, FALSE, TRUE);
|
|
|
|
//
|
|
// Free the relations list
|
|
//
|
|
IopFreeRelationList(relationsList);
|
|
|
|
//
|
|
// Let the user know we were unable to process the request.
|
|
//
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoTypeUnknown,
|
|
NULL
|
|
);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
|
|
//
|
|
// Fill out the pending eject information.
|
|
//
|
|
ObReferenceObject(deviceObject);
|
|
pendingRelations->DeviceEvent = deviceEvent;
|
|
pendingRelations->DeviceObject = deviceObject;
|
|
pendingRelations->RelationsList = relationsList;
|
|
pendingRelations->ProfileChangingEject = possibleProfileChangeInProgress;
|
|
pendingRelations->DisplaySafeRemovalDialog =
|
|
(BOOLEAN)(deviceEvent->VetoName == NULL);
|
|
pendingRelations->DockInterface = dockInterface;
|
|
|
|
//
|
|
// Now that we've removed all the devices that won't be present
|
|
// in the new hardware profile state (eg batteries, etc),
|
|
//
|
|
status = PoGetLightestSystemStateForEject(
|
|
possibleProfileChangeInProgress,
|
|
hotEjectSupported,
|
|
warmEjectSupported,
|
|
&pendingRelations->LightestSleepState
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
if (status == STATUS_INSUFFICIENT_POWER) {
|
|
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoInsufficientPower,
|
|
NULL
|
|
);
|
|
|
|
} else {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_WARNING_LEVEL,
|
|
"PiProcessQueryRemoveAndEject: Vetoed by power system (%x)\n",
|
|
status));
|
|
|
|
PiFinalizeVetoedRemove(
|
|
deviceEvent,
|
|
PNP_VetoTypeUnknown,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
//
|
|
// We'll complete this one ourselves thank you.
|
|
//
|
|
pendingRelations->DeviceEvent = NULL;
|
|
pendingRelations->DisplaySafeRemovalDialog = FALSE;
|
|
|
|
//
|
|
// Release any profile transitions.
|
|
//
|
|
InitializeListHead( &pendingRelations->Link );
|
|
IopProcessCompletedEject((PVOID) pendingRelations);
|
|
|
|
ExFreePool(pdoList);
|
|
ExFreePool(internalVetoBuffer);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_PLUGPLAY_QUERY_VETOED;
|
|
}
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
|
|
//
|
|
// Completion routine for the eject IRP handles display of the
|
|
// safe removal dialog and completion of the event. Returning
|
|
// STATUS_PENDING does let other events get processed though.
|
|
//
|
|
IopEjectDevice( deviceObject, pendingRelations );
|
|
|
|
ExFreePool(pdoList);
|
|
ExFreePool(internalVetoBuffer);
|
|
|
|
return STATUS_PENDING;
|
|
|
|
} else {
|
|
|
|
//
|
|
// All docks must be hot or warm ejectable.
|
|
//
|
|
ASSERT(!dockInterface);
|
|
|
|
//
|
|
// Unlock the device relations list.
|
|
//
|
|
IopUnlinkDeviceRemovalRelations( deviceObject,
|
|
relationsList,
|
|
UnlinkRemovedDeviceNodes );
|
|
|
|
IopFreeRelationList(relationsList);
|
|
|
|
//
|
|
// This hardware supports neither hot nor warm eject, but it is
|
|
// removable. It can therefore be thought of as a "user assisted" hot
|
|
// eject. In this case we do *not* want to wait around for the user to
|
|
// "complete the eject" and then put up the message. So we piggyback a
|
|
// safe removal notification while UmPnPMgr is alert and waiting in
|
|
// user mode, and the user gets the dialog now.
|
|
//
|
|
if (deviceEvent->VetoName == NULL) {
|
|
|
|
PpNotifyUserModeRemovalSafe(deviceObject);
|
|
}
|
|
}
|
|
|
|
if (deleteType == RemoveDevice) {
|
|
|
|
//
|
|
// Notify user-mode one last time that everything is actually done.
|
|
//
|
|
PiNotifyUserModeDeviceRemoval(
|
|
deviceEvent,
|
|
&GUID_TARGET_DEVICE_REMOVE_COMPLETE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
ExFreePool(pdoList);
|
|
|
|
if (dockInterface) {
|
|
|
|
dockInterface->ProfileDepartureSetMode(
|
|
dockInterface->Context,
|
|
PDS_UPDATE_DEFAULT
|
|
);
|
|
|
|
dockInterface->InterfaceDereference(dockInterface->Context);
|
|
}
|
|
|
|
ExFreePool(internalVetoBuffer);
|
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PiProcessTargetDeviceEvent(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes each type of event in the target device category.
|
|
These events may have been initiated by either user-mode or kernel mode.
|
|
|
|
Arguments:
|
|
|
|
deviceEvent - Data describing the type of target device event and the
|
|
target device itself.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceEvent = *DeviceEvent;
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessTargetDeviceEvent: Entered\n"));
|
|
|
|
//-----------------------------------------------------------------
|
|
// QUERY and REMOVE
|
|
//-----------------------------------------------------------------
|
|
|
|
if (PiCompareGuid(&deviceEvent->Data.EventGuid,
|
|
&GUID_DEVICE_QUERY_AND_REMOVE)) {
|
|
|
|
status = PiProcessQueryRemoveAndEject(DeviceEvent);
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// EJECT
|
|
//-----------------------------------------------------------------
|
|
|
|
else if (PiCompareGuid(&deviceEvent->Data.EventGuid,
|
|
&GUID_DEVICE_EJECT)) {
|
|
|
|
status = PiProcessQueryRemoveAndEject(DeviceEvent);
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// ARRIVAL
|
|
//-----------------------------------------------------------------
|
|
|
|
else if (PiCompareGuid(&deviceEvent->Data.EventGuid,
|
|
&GUID_DEVICE_ARRIVAL)) {
|
|
|
|
//
|
|
// Notify user-mode (not drivers) that an arrival just happened.
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessTargetDeviceEvent: ARRIVAL - notifying user-mode\n"));
|
|
|
|
PiNotifyUserMode(deviceEvent);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// NO-OP REQUEST (to flush device event queue)
|
|
//-----------------------------------------------------------------
|
|
|
|
else if (PiCompareGuid(&deviceEvent->Data.EventGuid,
|
|
&GUID_DEVICE_NOOP)) {
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// SAFE REMOVAL NOTIFICATION
|
|
//-----------------------------------------------------------------
|
|
|
|
else if (PiCompareGuid(&deviceEvent->Data.EventGuid, &GUID_DEVICE_SAFE_REMOVAL)) {
|
|
|
|
//
|
|
// Notify user-mode (and nobody else) that it is now safe to remove
|
|
// someone.
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessTargetDeviceEvent: SAFE_REMOVAL - notifying user-mode\n"));
|
|
|
|
PiNotifyUserMode(deviceEvent);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // PiProcessTargetDeviceEvent
|
|
|
|
|
|
NTSTATUS
|
|
PiProcessCustomDeviceEvent(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes each type of event in the custom device category.
|
|
These events may have been initiated by either user-mode or kernel mode.
|
|
|
|
Arguments:
|
|
|
|
deviceEvent - Data describing the type of custom device event and the
|
|
target device itself.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|
PTARGET_DEVICE_CUSTOM_NOTIFICATION customNotification;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceEvent = *DeviceEvent;
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessCustomDeviceEvent: Entered\n"));
|
|
|
|
ASSERT(PiCompareGuid(&deviceEvent->Data.EventGuid,
|
|
&GUID_PNP_CUSTOM_NOTIFICATION));
|
|
|
|
deviceObject = (PDEVICE_OBJECT)deviceEvent->Data.DeviceObject;
|
|
customNotification = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)deviceEvent->Data.u.CustomNotification.NotificationStructure;
|
|
|
|
//
|
|
// Notify user-mode that something just happened.
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessCustomDeviceEvent: CUSTOM_NOTIFICATION - notifying user-mode\n"));
|
|
|
|
PiNotifyUserMode(deviceEvent);
|
|
|
|
//
|
|
// Notify K-mode
|
|
//
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PiProcessCustomDeviceEvent: CUSTOM_NOTIFICATION - notifying kernel-mode\n"));
|
|
|
|
IopNotifyTargetDeviceChange( &customNotification->Event,
|
|
deviceObject,
|
|
customNotification,
|
|
NULL);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // PiProcessCustomDeviceEvent
|
|
|
|
|
|
NTSTATUS
|
|
PiResizeTargetDeviceBlock(
|
|
IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent,
|
|
IN PLUGPLAY_DEVICE_DELETE_TYPE DeleteType,
|
|
IN PRELATION_LIST RelationsList,
|
|
IN BOOLEAN ExcludeIndirectRelations
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the passed in device event block and resizes it to
|
|
hold a multisz list of device instance strings in the DeviceIds field.
|
|
This list includes the original target device id plus the device id
|
|
for all the device objects in the specified DeviceRelations struct.
|
|
|
|
Arguments:
|
|
|
|
DeviceEvent - On entry, contains the original device event block, on
|
|
return it contains the newly allocated device event block and
|
|
a complete list of related device id strings.
|
|
|
|
DeviceRelations - structure that contains a list of related device objects.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS value.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_NODE relatedDeviceNode;
|
|
PDEVICE_OBJECT relatedDeviceObject;
|
|
ULONG newSize, currentSize;
|
|
PPNP_DEVICE_EVENT_ENTRY newDeviceEvent;
|
|
LPWSTR targetDevice, p;
|
|
ULONG marker;
|
|
BOOLEAN directDescendant;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (RelationsList == NULL) {
|
|
return STATUS_SUCCESS; // nothing to do
|
|
}
|
|
|
|
targetDevice = (*DeviceEvent)->Data.u.TargetDevice.DeviceIds;
|
|
|
|
//
|
|
// Calculate the size of the PNP_DEVICE_EVENT_ENTRY block
|
|
//
|
|
|
|
currentSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) +
|
|
(*DeviceEvent)->Data.TotalSize;
|
|
|
|
newSize = currentSize;
|
|
newSize -= (ULONG)((wcslen(targetDevice)+1) * sizeof(WCHAR));
|
|
|
|
marker = 0;
|
|
while (IopEnumerateRelations( RelationsList,
|
|
&marker,
|
|
&relatedDeviceObject,
|
|
&directDescendant,
|
|
NULL,
|
|
FALSE)) {
|
|
|
|
if (!ExcludeIndirectRelations || directDescendant) {
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (relatedDeviceNode != NULL) {
|
|
if (relatedDeviceNode->InstancePath.Length != 0) {
|
|
newSize += relatedDeviceNode->InstancePath.Length + sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(newSize >= currentSize);
|
|
|
|
if (newSize == currentSize) {
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else if (newSize < currentSize) {
|
|
|
|
newSize = currentSize;
|
|
}
|
|
|
|
newDeviceEvent = (PPNP_DEVICE_EVENT_ENTRY) PiAllocateCriticalMemory(
|
|
DeleteType,
|
|
PagedPool,
|
|
newSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG
|
|
);
|
|
|
|
if (newDeviceEvent == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID)newDeviceEvent, newSize);
|
|
|
|
//
|
|
// Copy the old buffer into the new buffer, it's only the new stuff at the
|
|
// end that changes.
|
|
//
|
|
|
|
RtlCopyMemory(newDeviceEvent, *DeviceEvent, currentSize);
|
|
|
|
//
|
|
// Update the size of the PLUGPLAY_EVENT_BLOCK
|
|
//
|
|
newDeviceEvent->Data.TotalSize = newSize - FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data);
|
|
|
|
//
|
|
// Add device instance string for each device relation to the list.
|
|
// Leave the target device first in the list, and skip it during the
|
|
// enumeration below.
|
|
//
|
|
|
|
marker = 0;
|
|
p = newDeviceEvent->Data.u.TargetDevice.DeviceIds + wcslen(targetDevice) + 1;
|
|
while (IopEnumerateRelations( RelationsList,
|
|
&marker,
|
|
&relatedDeviceObject,
|
|
&directDescendant,
|
|
NULL,
|
|
FALSE)) {
|
|
|
|
if ((relatedDeviceObject != newDeviceEvent->Data.DeviceObject) &&
|
|
(!ExcludeIndirectRelations || directDescendant)) {
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (relatedDeviceNode != NULL) {
|
|
if (relatedDeviceNode->InstancePath.Length != 0) {
|
|
RtlCopyMemory(p,
|
|
relatedDeviceNode->InstancePath.Buffer,
|
|
relatedDeviceNode->InstancePath.Length);
|
|
p += relatedDeviceNode->InstancePath.Length / sizeof(WCHAR) + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*p = UNICODE_NULL;
|
|
|
|
ExFreePool(*DeviceEvent);
|
|
*DeviceEvent = newDeviceEvent;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // PiResizeTargetDeviceBlock
|
|
|
|
|
|
VOID
|
|
PiBuildUnsafeRemovalDeviceBlock(
|
|
IN PPNP_DEVICE_EVENT_ENTRY OriginalDeviceEvent,
|
|
IN PRELATION_LIST RelationsList,
|
|
OUT PPNP_DEVICE_EVENT_ENTRY *AllocatedDeviceEvent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a device event block to send to user mode in case of
|
|
unsafe removal.
|
|
|
|
Arguments:
|
|
|
|
OriginalDeviceEvent - Contains the original device event block.
|
|
|
|
RelationList - structure that contains a list of related device objects.
|
|
|
|
AllocatedDeviceEvent - Receives the new device event, NULL on error or
|
|
no entries.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_NODE relatedDeviceNode;
|
|
PDEVICE_OBJECT relatedDeviceObject;
|
|
ULONG dataSize, eventSize, headerSize;
|
|
PPNP_DEVICE_EVENT_ENTRY newDeviceEvent;
|
|
LPWSTR targetDevice, p;
|
|
ULONG marker;
|
|
BOOLEAN directDescendant;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Preinit
|
|
//
|
|
*AllocatedDeviceEvent = NULL;
|
|
|
|
if (RelationsList == NULL) {
|
|
|
|
return; // nothing to do
|
|
}
|
|
|
|
targetDevice = OriginalDeviceEvent->Data.u.TargetDevice.DeviceIds;
|
|
|
|
//
|
|
// Calculate the size of the PNP_DEVICE_EVENT_ENTRY block
|
|
//
|
|
dataSize = 0;
|
|
|
|
marker = 0;
|
|
while (IopEnumerateRelations( RelationsList,
|
|
&marker,
|
|
&relatedDeviceObject,
|
|
&directDescendant,
|
|
NULL,
|
|
FALSE)) {
|
|
|
|
if (!directDescendant) {
|
|
|
|
continue;
|
|
}
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if ((relatedDeviceNode == NULL) ||
|
|
PipIsBeingRemovedSafely(relatedDeviceNode)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if (relatedDeviceNode->InstancePath.Length != 0) {
|
|
dataSize += relatedDeviceNode->InstancePath.Length + sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
if (dataSize == 0) {
|
|
|
|
//
|
|
// No entries, bail.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Add the terminating MultiSz NULL.
|
|
//
|
|
dataSize += sizeof(WCHAR);
|
|
|
|
headerSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) +
|
|
FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, u);
|
|
|
|
eventSize = dataSize + headerSize;
|
|
|
|
//
|
|
// If we can't get memory, there simply won't be a message sent.
|
|
//
|
|
newDeviceEvent = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
eventSize,
|
|
PNP_DEVICE_EVENT_ENTRY_TAG
|
|
);
|
|
|
|
if (newDeviceEvent == NULL) {
|
|
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID)newDeviceEvent, eventSize);
|
|
|
|
//
|
|
// Copy the header into the new buffer.
|
|
//
|
|
RtlCopyMemory(newDeviceEvent, OriginalDeviceEvent, headerSize);
|
|
|
|
//
|
|
// Update the size of the PLUGPLAY_EVENT_BLOCK
|
|
//
|
|
newDeviceEvent->Data.TotalSize = dataSize + FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, u);
|
|
|
|
//
|
|
// Add device instance string for each device relation to the list.
|
|
//
|
|
|
|
marker = 0;
|
|
p = newDeviceEvent->Data.u.TargetDevice.DeviceIds;
|
|
while (IopEnumerateRelations( RelationsList,
|
|
&marker,
|
|
&relatedDeviceObject,
|
|
&directDescendant,
|
|
NULL,
|
|
FALSE)) {
|
|
|
|
if (!directDescendant) {
|
|
|
|
continue;
|
|
}
|
|
|
|
relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if ((relatedDeviceNode == NULL) ||
|
|
PipIsBeingRemovedSafely(relatedDeviceNode)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if (relatedDeviceNode->InstancePath.Length != 0) {
|
|
|
|
RtlCopyMemory(p,
|
|
relatedDeviceNode->InstancePath.Buffer,
|
|
relatedDeviceNode->InstancePath.Length);
|
|
p += relatedDeviceNode->InstancePath.Length / sizeof(WCHAR) + 1;
|
|
}
|
|
}
|
|
|
|
*p = UNICODE_NULL;
|
|
|
|
*AllocatedDeviceEvent = newDeviceEvent;
|
|
|
|
return;
|
|
|
|
} // PiBuildUnsafeRemovalDeviceBlock
|
|
|
|
|
|
VOID
|
|
PiFinalizeVetoedRemove(
|
|
IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
|
|
IN PNP_VETO_TYPE VetoType,
|
|
IN PUNICODE_STRING VetoName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes care of updating the event results with the veto
|
|
information, puts up UI if neccessary, and dumps failure information to
|
|
the debugger for debugging purposes.
|
|
|
|
Arguments:
|
|
|
|
VetoedDeviceEvent - Data describing the device event failed.
|
|
|
|
VetoType - The veto code best describing why the operation failed.
|
|
|
|
VetoName - A unicode string appropriate to the veto code that describes
|
|
the vetoer.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
#if DBG
|
|
PUNICODE_STRING devNodeName;
|
|
const char *failureReason;
|
|
#endif
|
|
|
|
deviceObject = (PDEVICE_OBJECT) VetoedDeviceEvent->Data.DeviceObject;
|
|
|
|
#if DBG
|
|
devNodeName = &((PDEVICE_NODE) deviceObject->DeviceObjectExtension->DeviceNode)->InstancePath;
|
|
|
|
switch(VetoType) {
|
|
|
|
case PNP_VetoTypeUnknown:
|
|
failureReason = "for unspecified reason";
|
|
break;
|
|
|
|
case PNP_VetoLegacyDevice:
|
|
failureReason = "due to legacy device";
|
|
break;
|
|
|
|
case PNP_VetoPendingClose:
|
|
|
|
//
|
|
// ADRIAO N.B. 07/10/2000 - I believe this case is vestigal...
|
|
//
|
|
ASSERT(0);
|
|
failureReason = "due to pending close";
|
|
break;
|
|
|
|
case PNP_VetoWindowsApp:
|
|
failureReason = "due to windows application";
|
|
break;
|
|
|
|
case PNP_VetoWindowsService:
|
|
failureReason = "due to service";
|
|
break;
|
|
|
|
case PNP_VetoOutstandingOpen:
|
|
failureReason = "due to outstanding handles on device";
|
|
break;
|
|
|
|
case PNP_VetoDevice:
|
|
failureReason = "by device";
|
|
break;
|
|
|
|
case PNP_VetoDriver:
|
|
failureReason = "by driver";
|
|
break;
|
|
|
|
case PNP_VetoIllegalDeviceRequest:
|
|
failureReason = "as the request was invalid for the device";
|
|
break;
|
|
|
|
case PNP_VetoInsufficientPower:
|
|
failureReason = "because there would be insufficient system power to continue";
|
|
break;
|
|
|
|
case PNP_VetoNonDisableable:
|
|
failureReason = "due to non-disableable device";
|
|
break;
|
|
|
|
case PNP_VetoLegacyDriver:
|
|
failureReason = "due to legacy driver";
|
|
break;
|
|
|
|
case PNP_VetoInsufficientRights:
|
|
failureReason = "insufficient permissions";
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
failureReason = "due to uncoded reason";
|
|
break;
|
|
}
|
|
|
|
if (VetoName != NULL) {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_WARNING_LEVEL,
|
|
"PiFinalizeVetoedRemove: Removal of %wZ vetoed %s %wZ.\n",
|
|
devNodeName,
|
|
failureReason,
|
|
VetoName
|
|
));
|
|
|
|
} else {
|
|
|
|
IopDbgPrint((IOP_IOEVENT_WARNING_LEVEL,
|
|
"PiFinalizeVetoedRemove: Removal of %wZ vetoed %s.\n",
|
|
devNodeName,
|
|
failureReason
|
|
));
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Update the vetoType field if the caller is interested.
|
|
//
|
|
if (VetoedDeviceEvent->VetoType != NULL) {
|
|
|
|
*VetoedDeviceEvent->VetoType = VetoType;
|
|
}
|
|
|
|
//
|
|
// The VetoName field tells us whether UI should be displayed (if NULL,
|
|
// kernel mode UI is implicitely requested.)
|
|
//
|
|
if (VetoedDeviceEvent->VetoName != NULL) {
|
|
|
|
if (VetoName != NULL) {
|
|
|
|
RtlCopyUnicodeString(VetoedDeviceEvent->VetoName, VetoName);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If there is not a VetoName passed in then call user mode to display the
|
|
// eject veto notification to the user
|
|
//
|
|
PiNotifyUserModeRemoveVetoed(
|
|
VetoedDeviceEvent,
|
|
deviceObject,
|
|
VetoType,
|
|
VetoName
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
PiCompareGuid(
|
|
CONST GUID *Guid1,
|
|
CONST GUID *Guid2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine compares two guids.
|
|
|
|
Arguments:
|
|
|
|
Guid1 - First guid to compare
|
|
|
|
Guid2 - Second guid to compare
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the guids are equal and FALSE if they're different.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (RtlCompareMemory((PVOID)Guid1, (PVOID)Guid2, sizeof(GUID)) == sizeof(GUID)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
|
|
} // PiCompareGuid
|
|
|
|
|
|
PVOID
|
|
PiAllocateCriticalMemory(
|
|
IN PLUGPLAY_DEVICE_DELETE_TYPE DeleteType,
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T Size,
|
|
IN ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates memory and never fails if the DeleteType isn't
|
|
QueryRemoveDevice or EjectDevice. This function will disappear in the next
|
|
version of the PnP engine as we will instead requeue failed operations
|
|
(which will also result in a second attempt to allocate the memory) or
|
|
preallocate the required memory when bringing new devnode's into the world.
|
|
|
|
Arguments:
|
|
|
|
DeleteType - Operation (EjectDevice, SurpriseRemoveDevice, ...)
|
|
|
|
PoolType - PagedPool, NonPagedPool
|
|
|
|
Size - Size
|
|
|
|
Tag - Allocation tag
|
|
|
|
Return Value:
|
|
|
|
Allocation, NULL due to insufficient resources.
|
|
|
|
--*/
|
|
{
|
|
PVOID memory;
|
|
LARGE_INTEGER timeOut;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Retries only have a hope of succeeding if we are at PASSIVE_LEVEL
|
|
//
|
|
ASSERT(KeGetCurrentIrql() != DISPATCH_LEVEL);
|
|
|
|
while(1) {
|
|
|
|
memory = ExAllocatePoolWithTag(PoolType, Size, Tag);
|
|
|
|
if (memory ||
|
|
(DeleteType == QueryRemoveDevice) ||
|
|
(DeleteType == EjectDevice)) {
|
|
|
|
//
|
|
// Either we got memory or the op was failable. Get out of here.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We're stuck until more memory comes along. Let some other
|
|
// threads run before we get another shot...
|
|
//
|
|
timeOut.QuadPart = Int32x32To64( 1, -10000 );
|
|
KeDelayExecutionThread(KernelMode, FALSE, &timeOut);
|
|
}
|
|
|
|
return memory;
|
|
}
|
|
|
|
|
|
struct {
|
|
CONST GUID *Guid;
|
|
PCHAR Name;
|
|
} EventGuidTable[] = {
|
|
{ &GUID_HWPROFILE_QUERY_CHANGE, "GUID_HWPROFILE_QUERY_CHANGE" },
|
|
{ &GUID_HWPROFILE_CHANGE_CANCELLED, "GUID_HWPROFILE_CHANGE_CANCELLED" },
|
|
{ &GUID_HWPROFILE_CHANGE_COMPLETE, "GUID_HWPROFILE_CHANGE_COMPLETE" },
|
|
{ &GUID_DEVICE_INTERFACE_ARRIVAL, "GUID_DEVICE_INTERFACE_ARRIVAL" },
|
|
{ &GUID_DEVICE_INTERFACE_REMOVAL, "GUID_DEVICE_INTERFACE_REMOVAL" },
|
|
{ &GUID_TARGET_DEVICE_QUERY_REMOVE, "GUID_TARGET_DEVICE_QUERY_REMOVE" },
|
|
{ &GUID_TARGET_DEVICE_REMOVE_CANCELLED, "GUID_TARGET_DEVICE_REMOVE_CANCELLED" },
|
|
{ &GUID_TARGET_DEVICE_REMOVE_COMPLETE, "GUID_TARGET_DEVICE_REMOVE_COMPLETE" },
|
|
{ &GUID_PNP_CUSTOM_NOTIFICATION, "GUID_PNP_CUSTOM_NOTIFICATION" },
|
|
{ &GUID_DEVICE_ARRIVAL, "GUID_DEVICE_ARRIVAL" },
|
|
{ &GUID_DEVICE_ENUMERATED, "GUID_DEVICE_ENUMERATED" },
|
|
{ &GUID_DEVICE_ENUMERATE_REQUEST, "GUID_DEVICE_ENUMERATE_REQUEST" },
|
|
{ &GUID_DEVICE_START_REQUEST, "GUID_DEVICE_START_REQUEST" },
|
|
{ &GUID_DEVICE_REMOVE_PENDING, "GUID_DEVICE_REMOVE_PENDING" },
|
|
{ &GUID_DEVICE_QUERY_AND_REMOVE, "GUID_DEVICE_QUERY_AND_REMOVE" },
|
|
{ &GUID_DEVICE_EJECT, "GUID_DEVICE_EJECT" },
|
|
{ &GUID_DEVICE_NOOP, "GUID_DEVICE_NOOP" },
|
|
{ &GUID_DEVICE_SURPRISE_REMOVAL, "GUID_DEVICE_SURPRISE_REMOVAL" },
|
|
{ &GUID_DEVICE_SAFE_REMOVAL, "GUID_DEVICE_SAFE_REMOVAL" },
|
|
{ &GUID_DEVICE_EJECT_VETOED, "GUID_DEVICE_EJECT_VETOED" },
|
|
{ &GUID_DEVICE_REMOVAL_VETOED, "GUID_DEVICE_REMOVAL_VETOED" },
|
|
};
|
|
#define EVENT_GUID_TABLE_SIZE (sizeof(EventGuidTable) / sizeof(EventGuidTable[0]))
|
|
|
|
VOID
|
|
LookupGuid(
|
|
IN CONST GUID *Guid,
|
|
IN OUT PCHAR String,
|
|
IN ULONG StringLength
|
|
)
|
|
{
|
|
int i;
|
|
|
|
PAGED_CODE();
|
|
|
|
for (i = 0; i < EVENT_GUID_TABLE_SIZE; i++) {
|
|
if (PiCompareGuid(Guid, EventGuidTable[i].Guid)) {
|
|
strncpy(String, EventGuidTable[i].Name, StringLength - 1);
|
|
String[StringLength - 1] = '\0';
|
|
return;
|
|
}
|
|
}
|
|
|
|
StringCchPrintfA( String, StringLength, "%08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X",
|
|
Guid->Data1,
|
|
Guid->Data2,
|
|
Guid->Data3,
|
|
Guid->Data4[0],
|
|
Guid->Data4[1],
|
|
Guid->Data4[2],
|
|
Guid->Data4[3],
|
|
Guid->Data4[4],
|
|
Guid->Data4[5],
|
|
Guid->Data4[6],
|
|
Guid->Data4[7] );
|
|
}
|
|
|
|
VOID
|
|
DumpMultiSz(
|
|
IN PWCHAR MultiSz
|
|
)
|
|
{
|
|
PWCHAR p = MultiSz;
|
|
ULONG length;
|
|
|
|
PAGED_CODE();
|
|
|
|
while (*p) {
|
|
length = (ULONG)wcslen(p);
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" %S\n", p));
|
|
|
|
p += length + 1;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DumpPnpEvent(
|
|
IN PPLUGPLAY_EVENT_BLOCK EventBlock
|
|
)
|
|
{
|
|
CHAR guidString[256];
|
|
|
|
PAGED_CODE();
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
"PlugPlay Event Block @ 0x%p\n", EventBlock));
|
|
|
|
LookupGuid(&EventBlock->EventGuid, guidString, sizeof(guidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" EventGuid = %s\n", guidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceObject = 0x%p\n", EventBlock->DeviceObject));
|
|
|
|
switch (EventBlock->EventCategory) {
|
|
case HardwareProfileChangeEvent:
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" HardwareProfileChangeEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
|
|
EventBlock->Result,
|
|
EventBlock->Flags,
|
|
EventBlock->TotalSize));
|
|
|
|
break;
|
|
|
|
case TargetDeviceChangeEvent:
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" TargetDeviceChangeEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
|
|
EventBlock->Result,
|
|
EventBlock->Flags,
|
|
EventBlock->TotalSize));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceIds:\n"));
|
|
|
|
DumpMultiSz( EventBlock->u.TargetDevice.DeviceIds );
|
|
break;
|
|
|
|
case DeviceClassChangeEvent:
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceClassChangeEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
|
|
EventBlock->Result,
|
|
EventBlock->Flags,
|
|
EventBlock->TotalSize));
|
|
|
|
LookupGuid(&EventBlock->u.DeviceClass.ClassGuid, guidString, sizeof(guidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" ClassGuid = %s\n",
|
|
guidString));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" SymbolicLinkName = %S\n",
|
|
EventBlock->u.DeviceClass.SymbolicLinkName));
|
|
break;
|
|
|
|
case CustomDeviceEvent:
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" CustomDeviceEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
|
|
EventBlock->Result,
|
|
EventBlock->Flags,
|
|
EventBlock->TotalSize));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" NotificationStructure = 0x%p\n DeviceIds:\n",
|
|
EventBlock->u.CustomNotification.NotificationStructure));
|
|
|
|
DumpMultiSz( EventBlock->u.CustomNotification.DeviceIds );
|
|
break;
|
|
|
|
case DeviceInstallEvent:
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceInstallEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
|
|
EventBlock->Result,
|
|
EventBlock->Flags,
|
|
EventBlock->TotalSize));
|
|
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceId = %S\n", EventBlock->u.InstallDevice.DeviceId));
|
|
|
|
break;
|
|
|
|
case DeviceArrivalEvent:
|
|
IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
|
|
" DeviceArrivalEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
|
|
EventBlock->Result,
|
|
EventBlock->Flags,
|
|
EventBlock->TotalSize));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
LOGICAL
|
|
PiCollectOpenHandles(
|
|
IN PDEVICE_OBJECT *DeviceObjectArray,
|
|
IN ULONG ArrayCount,
|
|
IN LOGICAL KnownHandleFailure,
|
|
IN OUT PUNICODE_STRING VetoString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This helper routine finds any handles opened against the passed in array of
|
|
device objects to either the veto string, the debugger console, or neither.
|
|
|
|
Arguments:
|
|
|
|
DeviceObjectArray - Array of Physical Device Objects.
|
|
|
|
ArrayCount - Number of device objects in the passed in array
|
|
|
|
KnownHandleFailure - TRUE if the removal was vetoed due to open handles,
|
|
FALSE if not.
|
|
|
|
VetoString - String to populate with veto information if told. This data is
|
|
not currently "sanitized" enough to be user-readable.
|
|
|
|
Return Value:
|
|
|
|
TRUE if veto information populated, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
LOGICAL collectHandles, dumpHandles;
|
|
ENUM_HANDLES_CONTEXT enumContext;
|
|
|
|
//
|
|
// If we have enabled the dumping flag, or the user ran oh.exe, spit all
|
|
// handles on a veto to the debugger.
|
|
//
|
|
dumpHandles =
|
|
(PiDumpVetoedHandles ||
|
|
((NtGlobalFlag & FLG_MAINTAIN_OBJECT_TYPELIST) != 0));
|
|
|
|
if (dumpHandles) {
|
|
|
|
DbgPrint("Beginning handle dump:\n");
|
|
|
|
if (!KnownHandleFailure) {
|
|
|
|
DbgPrint(" (Failed Query-Remove - *Might* by due to leaked handles)\n");
|
|
}
|
|
}
|
|
|
|
collectHandles = PiCollectVetoedHandles;
|
|
|
|
if (!(collectHandles || dumpHandles)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VetoString->Length = 0;
|
|
ASSERT(VetoString->MaximumLength >= sizeof(WCHAR));
|
|
*VetoString->Buffer = UNICODE_NULL;
|
|
|
|
enumContext.DumpHandles = dumpHandles;
|
|
enumContext.CollectHandles = collectHandles;
|
|
enumContext.VetoString = VetoString;
|
|
enumContext.HandleCount = 0;
|
|
|
|
for(i=0; i<ArrayCount; i++) {
|
|
|
|
PpHandleEnumerateHandlesAgainstPdoStack(
|
|
DeviceObjectArray[i],
|
|
PiCollectOpenHandlesCallBack,
|
|
(PVOID) &enumContext
|
|
);
|
|
}
|
|
|
|
if (dumpHandles) {
|
|
|
|
DbgPrint("Dump complete - %d total handles found.\n", enumContext.HandleCount);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
LOGICAL
|
|
PiCollectOpenHandlesCallBack(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PEPROCESS Process,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN HANDLE HandleId,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This helper routine for PiCollectOpenHandlesCallBack. It gets called
|
|
back for each handle opened against a given device object.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device Object handle was against. Will be valid (referenced)
|
|
|
|
Process - Process handle was against. Will be valid (referenced)
|
|
|
|
FileObject - File object pertaining to handle - might not be valid
|
|
|
|
HandleId - Handle relating to open device - might not be valid
|
|
|
|
Context - Context passed in to PpHandleEnumerateHandlesAgainstPdoStack.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the enumeration should be stopped, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PENUM_HANDLES_CONTEXT enumContext;
|
|
WCHAR localBuf[23]; // "PPPPPPPPPP.0xHHHHHHHH_\0"
|
|
HRESULT result;
|
|
PWSTR endString;
|
|
|
|
enumContext = (PENUM_HANDLES_CONTEXT) Context;
|
|
|
|
if (enumContext->DumpHandles) {
|
|
|
|
//
|
|
// Display the handle.
|
|
//
|
|
DbgPrint(
|
|
" DeviceObject:%p ProcessID:%dT FileObject:%p Handle:%dT\n",
|
|
DeviceObject,
|
|
Process->UniqueProcessId,
|
|
FileObject,
|
|
HandleId
|
|
);
|
|
}
|
|
|
|
if (enumContext->CollectHandles) {
|
|
|
|
result = StringCbPrintfW(
|
|
localBuf,
|
|
sizeof(localBuf),
|
|
L"%dT.0x%08x ",
|
|
(ULONG)(ULONG_PTR)Process->UniqueProcessId,
|
|
(ULONG)(ULONG_PTR)HandleId
|
|
);
|
|
|
|
if (SUCCEEDED(result)) {
|
|
|
|
result = StringCbCatExW(
|
|
enumContext->VetoString->Buffer,
|
|
enumContext->VetoString->MaximumLength,
|
|
localBuf,
|
|
&endString,
|
|
NULL,
|
|
STRSAFE_NO_TRUNCATION
|
|
);
|
|
|
|
if (SUCCEEDED(result)) {
|
|
|
|
enumContext->VetoString->Length = (USHORT)
|
|
(endString - enumContext->VetoString->Buffer)*sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
|
|
enumContext->HandleCount++;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|