2935 lines
88 KiB
C
2935 lines
88 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1998-2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
notify.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains APIs and routines for handling device event
|
|||
|
notifications.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pnpmgrp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#include <pnpmgr.h>
|
|||
|
#include <pnpsetup.h>
|
|||
|
|
|||
|
#define PNP_DEVICE_EVENT_ENTRY_TAG 'EEpP'
|
|||
|
|
|||
|
typedef struct _ASYNC_TDC_WORK_ITEM {
|
|||
|
WORK_QUEUE_ITEM WorkItem;
|
|||
|
PDEVICE_OBJECT DeviceObject;
|
|||
|
PDEVICE_CHANGE_COMPLETE_CALLBACK Callback;
|
|||
|
PVOID Context;
|
|||
|
PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure;
|
|||
|
} ASYNC_TDC_WORK_ITEM, *PASYNC_TDC_WORK_ITEM;
|
|||
|
|
|||
|
typedef struct _DEFERRED_REGISTRATION_ENTRY {
|
|||
|
LIST_ENTRY ListEntry;
|
|||
|
PNOTIFY_ENTRY_HEADER NotifyEntry;
|
|||
|
} DEFERRED_REGISTRATION_ENTRY, *PDEFERRED_REGISTRATION_ENTRY;
|
|||
|
//
|
|||
|
// Kernel mode notification data
|
|||
|
//
|
|||
|
|
|||
|
#ifdef ALLOC_DATA_PRAGMA
|
|||
|
#pragma data_seg("PAGEDATA")
|
|||
|
#pragma const_seg("PAGECONST")
|
|||
|
#endif
|
|||
|
LIST_ENTRY IopDeviceClassNotifyList[NOTIFY_DEVICE_CLASS_HASH_BUCKETS] = {NULL};
|
|||
|
PSETUP_NOTIFY_DATA IopSetupNotifyData = NULL;
|
|||
|
LIST_ENTRY IopProfileNotifyList = {NULL};
|
|||
|
LIST_ENTRY IopDeferredRegistrationList = {NULL};
|
|||
|
#ifdef ALLOC_DATA_PRAGMA
|
|||
|
#pragma data_seg()
|
|||
|
#endif
|
|||
|
|
|||
|
FAST_MUTEX IopDeviceClassNotifyLock;
|
|||
|
FAST_MUTEX IopTargetDeviceNotifyLock;
|
|||
|
FAST_MUTEX IopHwProfileNotifyLock;
|
|||
|
FAST_MUTEX IopDeferredRegistrationLock;
|
|||
|
|
|||
|
BOOLEAN PiNotificationInProgress;
|
|||
|
FAST_MUTEX PiNotificationInProgressLock;
|
|||
|
|
|||
|
//
|
|||
|
// Prototypes
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
IopDereferenceNotify(
|
|||
|
PNOTIFY_ENTRY_HEADER Notify
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
IopInitializePlugPlayNotification(
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PiNotifyUserMode(
|
|||
|
PPNP_DEVICE_EVENT_ENTRY DeviceEvent
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PiNotifyDriverCallback(
|
|||
|
IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
|
|||
|
IN PVOID NotificationStructure,
|
|||
|
IN PVOID Context,
|
|||
|
IN ULONG SessionId,
|
|||
|
IN PVOID OpaqueSession,
|
|||
|
OUT PNTSTATUS CallbackStatus OPTIONAL
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
IopReferenceNotify(
|
|||
|
PNOTIFY_ENTRY_HEADER notify
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
IopReportTargetDeviceChangeAsyncWorker(
|
|||
|
PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, IoGetRelatedTargetDevice)
|
|||
|
#pragma alloc_text(PAGE, IoNotifyPowerOperationVetoed)
|
|||
|
#pragma alloc_text(PAGE, IoPnPDeliverServicePowerNotification)
|
|||
|
#pragma alloc_text(PAGE, IoRegisterPlugPlayNotification)
|
|||
|
#pragma alloc_text(PAGE, IoReportTargetDeviceChange)
|
|||
|
#pragma alloc_text(PAGE, IoUnregisterPlugPlayNotification)
|
|||
|
#pragma alloc_text(PAGE, IopDereferenceNotify)
|
|||
|
#pragma alloc_text(PAGE, IopGetRelatedTargetDevice)
|
|||
|
#pragma alloc_text(PAGE, IopInitializePlugPlayNotification)
|
|||
|
#pragma alloc_text(PAGE, IopNotifyDeviceClassChange)
|
|||
|
#pragma alloc_text(PAGE, IopNotifyHwProfileChange)
|
|||
|
#pragma alloc_text(PAGE, IopNotifySetupDeviceArrival)
|
|||
|
#pragma alloc_text(PAGE, IopNotifyTargetDeviceChange)
|
|||
|
#pragma alloc_text(PAGE, IopOrphanNotification)
|
|||
|
#pragma alloc_text(PAGE, IopProcessDeferredRegistrations)
|
|||
|
#pragma alloc_text(PAGE, IopReferenceNotify)
|
|||
|
#pragma alloc_text(PAGE, IopReportTargetDeviceChangeAsyncWorker)
|
|||
|
#pragma alloc_text(PAGE, IopRequestHwProfileChangeNotification)
|
|||
|
#pragma alloc_text(PAGE, PiNotifyDriverCallback)
|
|||
|
#endif // ALLOC_PRAGMA
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IoUnregisterPlugPlayNotification(
|
|||
|
IN PVOID NotificationEntry
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine unregisters a notification previously registered via
|
|||
|
IoRegisterPlugPlayNotification. A driver cannot be unloaded until it has
|
|||
|
unregistered all of its notification handles.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
NotificationEntry - This provices the cookie returned by IoRegisterPlugPlayNotification
|
|||
|
which identifies the registration in question.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PNOTIFY_ENTRY_HEADER entry;
|
|||
|
PFAST_MUTEX lock;
|
|||
|
BOOLEAN wasDeferred = FALSE;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT(NotificationEntry);
|
|||
|
|
|||
|
entry = (PNOTIFY_ENTRY_HEADER)NotificationEntry;
|
|||
|
|
|||
|
lock = entry->Lock;
|
|||
|
|
|||
|
ExAcquireFastMutex(&PiNotificationInProgressLock);
|
|||
|
if (PiNotificationInProgress) {
|
|||
|
//
|
|||
|
// Before unregistering the entry, we need to make sure that it's not sitting
|
|||
|
// around in the deferred registration list.
|
|||
|
//
|
|||
|
IopAcquireNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
|
|||
|
if (!IsListEmpty(&IopDeferredRegistrationList)) {
|
|||
|
|
|||
|
PLIST_ENTRY link;
|
|||
|
PDEFERRED_REGISTRATION_ENTRY deferredNode;
|
|||
|
|
|||
|
link = IopDeferredRegistrationList.Flink;
|
|||
|
deferredNode = (PDEFERRED_REGISTRATION_ENTRY)link;
|
|||
|
|
|||
|
while (link != (PLIST_ENTRY)&IopDeferredRegistrationList) {
|
|||
|
ASSERT(deferredNode->NotifyEntry->Unregistered);
|
|||
|
if (deferredNode->NotifyEntry == entry) {
|
|||
|
wasDeferred = TRUE;
|
|||
|
if (lock) {
|
|||
|
IopAcquireNotifyLock(lock);
|
|||
|
}
|
|||
|
link = link->Flink;
|
|||
|
RemoveEntryList((PLIST_ENTRY)deferredNode);
|
|||
|
IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)deferredNode->NotifyEntry);
|
|||
|
if (lock) {
|
|||
|
IopReleaseNotifyLock(lock);
|
|||
|
}
|
|||
|
ExFreePool(deferredNode);
|
|||
|
deferredNode = (PDEFERRED_REGISTRATION_ENTRY)link;
|
|||
|
} else {
|
|||
|
link = link->Flink;
|
|||
|
deferredNode = (PDEFERRED_REGISTRATION_ENTRY)link;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IopReleaseNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
} else {
|
|||
|
//
|
|||
|
// If there is currently no notification in progress, the deferred
|
|||
|
// registration list must be empty.
|
|||
|
//
|
|||
|
ASSERT(IsListEmpty(&IopDeferredRegistrationList));
|
|||
|
}
|
|||
|
ExReleaseFastMutex(&PiNotificationInProgressLock);
|
|||
|
|
|||
|
//
|
|||
|
// Acquire lock
|
|||
|
//
|
|||
|
if (lock) {
|
|||
|
IopAcquireNotifyLock(lock);
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(wasDeferred == entry->Unregistered);
|
|||
|
|
|||
|
if (!entry->Unregistered || wasDeferred) {
|
|||
|
//
|
|||
|
// Dereference the entry if it is currently registered, or had its
|
|||
|
// registration pending completion of the notification in progress.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Mark the entry as unregistered so we don't notify on it
|
|||
|
//
|
|||
|
|
|||
|
entry->Unregistered = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Dereference it thus deleting if no longer required
|
|||
|
//
|
|||
|
|
|||
|
IopDereferenceNotify(entry);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Release the lock
|
|||
|
//
|
|||
|
|
|||
|
if (lock) {
|
|||
|
IopReleaseNotifyLock(lock);
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
IopProcessDeferredRegistrations(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine removes notification entries from the deferred registration
|
|||
|
list, marking them as "registered" so that they can receive notifications.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEFERRED_REGISTRATION_ENTRY deferredNode;
|
|||
|
PFAST_MUTEX lock;
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
|
|||
|
while (!IsListEmpty(&IopDeferredRegistrationList)) {
|
|||
|
|
|||
|
deferredNode = (PDEFERRED_REGISTRATION_ENTRY)RemoveHeadList(&IopDeferredRegistrationList);
|
|||
|
|
|||
|
//
|
|||
|
// Acquire this entry's list lock.
|
|||
|
//
|
|||
|
lock = deferredNode->NotifyEntry->Lock;
|
|||
|
if (lock) {
|
|||
|
IopAcquireNotifyLock(lock);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Mark this entry as registered.
|
|||
|
//
|
|||
|
deferredNode->NotifyEntry->Unregistered = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the notification entry when removing it from the deferred
|
|||
|
// list, and free the node.
|
|||
|
//
|
|||
|
IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)deferredNode->NotifyEntry);
|
|||
|
ExFreePool(deferredNode);
|
|||
|
|
|||
|
//
|
|||
|
// Release this entry's list lock.
|
|||
|
//
|
|||
|
if (lock) {
|
|||
|
IopReleaseNotifyLock(lock);
|
|||
|
lock = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IopReleaseNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IoReportTargetDeviceChange(
|
|||
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|||
|
IN PVOID NotificationStructure // always begins with a PLUGPLAY_NOTIFICATION_HEADER
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine may be used to give notification of 3rd-party target device
|
|||
|
change events. This API will notify every driver that has registered for
|
|||
|
notification on a file object associated with PhysicalDeviceObject about
|
|||
|
the event indicated in the NotificationStructure.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
PhysicalDeviceObject - Provides a pointer to the PDO that the change begin
|
|||
|
reported is associated with.
|
|||
|
|
|||
|
NotificationStructure - Provides a pointer to the notification structure to be
|
|||
|
sent to all parties registered for notifications about changes to
|
|||
|
PhysicalDeviceObject.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
This API may only be used to report non-PnP target device changes. In particular,
|
|||
|
it will fail if it's called with the NotificationStructure->Event field set to
|
|||
|
GUID_TARGET_DEVICE_QUERY_REMOVE, GUID_TARGET_DEVICE_REMOVE_CANCELLED, or
|
|||
|
GUID_TARGET_DEVICE_REMOVE_COMPLETE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
KEVENT completionEvent;
|
|||
|
NTSTATUS completionStatus;
|
|||
|
PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct;
|
|||
|
LONG dataSize;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
|
|||
|
|
|||
|
ASSERT(notifyStruct);
|
|||
|
|
|||
|
ASSERT_PDO(PhysicalDeviceObject);
|
|||
|
|
|||
|
ASSERT(NULL == notifyStruct->FileObject);
|
|||
|
|
|||
|
|
|||
|
if (IopCompareGuid(¬ifyStruct->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE) ||
|
|||
|
IopCompareGuid(¬ifyStruct->Event, &GUID_TARGET_DEVICE_REMOVE_CANCELLED) ||
|
|||
|
IopCompareGuid(¬ifyStruct->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Passed in an illegal value
|
|||
|
//
|
|||
|
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_ERROR_LEVEL,
|
|||
|
"IoReportTargetDeviceChange: "
|
|||
|
"Illegal Event type passed as custom notification\n"));
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
if (notifyStruct->Size < FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer)) {
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
dataSize = notifyStruct->Size - FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|||
|
|
|||
|
if (notifyStruct->NameBufferOffset != -1 && notifyStruct->NameBufferOffset > dataSize) {
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
KeInitializeEvent(&completionEvent, NotificationEvent, FALSE);
|
|||
|
|
|||
|
status = PpSetCustomTargetEvent( PhysicalDeviceObject,
|
|||
|
&completionEvent,
|
|||
|
(PULONG)&completionStatus,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
notifyStruct);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
KeWaitForSingleObject( &completionEvent, Executive, KernelMode, FALSE, NULL );
|
|||
|
|
|||
|
status = completionStatus;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IoReportTargetDeviceChangeAsynchronous(
|
|||
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|||
|
IN PVOID NotificationStructure, // always begins with a PLUGPLAY_NOTIFICATION_HEADER
|
|||
|
IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
|
|||
|
IN PVOID Context OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine may be used to give notification of 3rd-party target device
|
|||
|
change events. This API will notify every driver that has registered for
|
|||
|
notification on a file object associated with PhysicalDeviceObject about
|
|||
|
the event indicated in the NotificationStructure.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
PhysicalDeviceObject - Provides a pointer to the PDO that the change begin
|
|||
|
reported is associated with.
|
|||
|
|
|||
|
NotificationStructure - Provides a pointer to the notification structure to be
|
|||
|
sent to all parties registered for notifications about changes to
|
|||
|
PhysicalDeviceObject.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
This API may only be used to report non-PnP target device changes. In particular,
|
|||
|
it will fail if it's called with the NotificationStructure->Event field set to
|
|||
|
GUID_TARGET_DEVICE_QUERY_REMOVE, GUID_TARGET_DEVICE_REMOVE_CANCELLED, or
|
|||
|
GUID_TARGET_DEVICE_REMOVE_COMPLETE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PASYNC_TDC_WORK_ITEM asyncWorkItem;
|
|||
|
PWORK_QUEUE_ITEM workItem;
|
|||
|
NTSTATUS status;
|
|||
|
LONG dataSize;
|
|||
|
|
|||
|
PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct;
|
|||
|
|
|||
|
notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
|
|||
|
|
|||
|
ASSERT(notifyStruct);
|
|||
|
|
|||
|
ASSERT_PDO(PhysicalDeviceObject);
|
|||
|
|
|||
|
ASSERT(NULL == notifyStruct->FileObject);
|
|||
|
|
|||
|
if (IopCompareGuid(¬ifyStruct->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE) ||
|
|||
|
IopCompareGuid(¬ifyStruct->Event, &GUID_TARGET_DEVICE_REMOVE_CANCELLED) ||
|
|||
|
IopCompareGuid(¬ifyStruct->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Passed in an illegal value
|
|||
|
//
|
|||
|
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_ERROR_LEVEL,
|
|||
|
"IoReportTargetDeviceChangeAsynchronous: "
|
|||
|
"Illegal Event type passed as custom notification\n"));
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
if (notifyStruct->Size < FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer)) {
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
dataSize = notifyStruct->Size - FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|||
|
|
|||
|
if (notifyStruct->NameBufferOffset != -1 && notifyStruct->NameBufferOffset > dataSize) {
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Since this routine can be called at DPC level we need to queue
|
|||
|
// a work item and process it when the irql drops.
|
|||
|
//
|
|||
|
|
|||
|
asyncWorkItem = ExAllocatePool( NonPagedPool,
|
|||
|
sizeof(ASYNC_TDC_WORK_ITEM) + notifyStruct->Size);
|
|||
|
|
|||
|
if (asyncWorkItem != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// ISSUE-ADRIAO-2000/08/24 - We should use an IO work item here.
|
|||
|
//
|
|||
|
ObReferenceObject(PhysicalDeviceObject);
|
|||
|
|
|||
|
asyncWorkItem->DeviceObject = PhysicalDeviceObject;
|
|||
|
asyncWorkItem->NotificationStructure =
|
|||
|
(PTARGET_DEVICE_CUSTOM_NOTIFICATION)((PUCHAR)asyncWorkItem + sizeof(ASYNC_TDC_WORK_ITEM));
|
|||
|
|
|||
|
RtlCopyMemory( asyncWorkItem->NotificationStructure,
|
|||
|
notifyStruct,
|
|||
|
notifyStruct->Size);
|
|||
|
|
|||
|
asyncWorkItem->Callback = Callback;
|
|||
|
asyncWorkItem->Context = Context;
|
|||
|
workItem = &asyncWorkItem->WorkItem;
|
|||
|
|
|||
|
ExInitializeWorkItem(workItem, IopReportTargetDeviceChangeAsyncWorker, asyncWorkItem);
|
|||
|
|
|||
|
//
|
|||
|
// Queue a work item to do the enumeration
|
|||
|
//
|
|||
|
|
|||
|
ExQueueWorkItem(workItem, DelayedWorkQueue);
|
|||
|
status = STATUS_PENDING;
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Failed to allocate memory for work item. Nothing we can do ...
|
|||
|
//
|
|||
|
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
IopReportTargetDeviceChangeAsyncWorker(
|
|||
|
PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the worker routine of IoInvalidateDeviceState.
|
|||
|
Its main purpose is to invoke IopSynchronousQueryDeviceState and release
|
|||
|
work item space.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
Context - Supplies a pointer to the ASYNC_TDC_WORK_ITEM.
|
|||
|
|
|||
|
ReturnValue:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PASYNC_TDC_WORK_ITEM asyncWorkItem = (PASYNC_TDC_WORK_ITEM)Context;
|
|||
|
|
|||
|
PpSetCustomTargetEvent( asyncWorkItem->DeviceObject,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
asyncWorkItem->Callback,
|
|||
|
asyncWorkItem->Context,
|
|||
|
asyncWorkItem->NotificationStructure);
|
|||
|
|
|||
|
ObDereferenceObject(asyncWorkItem->DeviceObject);
|
|||
|
ExFreePool(asyncWorkItem);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
IopInitializePlugPlayNotification(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine performs initialization required before any of the notification
|
|||
|
APIs can be called.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG count;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the notification structures
|
|||
|
//
|
|||
|
|
|||
|
for (count = 0; count < NOTIFY_DEVICE_CLASS_HASH_BUCKETS; count++) {
|
|||
|
|
|||
|
InitializeListHead(&IopDeviceClassNotifyList[count]);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the profile notification list
|
|||
|
//
|
|||
|
InitializeListHead(&IopProfileNotifyList);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the deferred registration list
|
|||
|
//
|
|||
|
InitializeListHead(&IopDeferredRegistrationList);
|
|||
|
|
|||
|
ExInitializeFastMutex(&IopDeviceClassNotifyLock);
|
|||
|
ExInitializeFastMutex(&IopTargetDeviceNotifyLock);
|
|||
|
ExInitializeFastMutex(&IopHwProfileNotifyLock);
|
|||
|
ExInitializeFastMutex(&IopDeferredRegistrationLock);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
IopReferenceNotify(
|
|||
|
PNOTIFY_ENTRY_HEADER Notify
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine increments the reference count for a notification entry.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
Notify - Supplies a pointer to the notification entry to be referenced
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
The appropriate synchronization lock must be held on the notification
|
|||
|
list before this routine can be called
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT(Notify);
|
|||
|
ASSERT(Notify->RefCount > 0);
|
|||
|
|
|||
|
Notify->RefCount++;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
IopDereferenceNotify(
|
|||
|
PNOTIFY_ENTRY_HEADER Notify
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine decrements the reference count for a notification entry, removing
|
|||
|
the entry from the list and freeing the associated memory if there are no
|
|||
|
outstanding reference counts.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
Notify - Supplies a pointer to the notification entry to be referenced
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
The appropriate synchronization lock must be held on the notification
|
|||
|
list before this routine can be called
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT(Notify);
|
|||
|
ASSERT(Notify->RefCount > 0);
|
|||
|
|
|||
|
Notify->RefCount--;
|
|||
|
|
|||
|
if (Notify->RefCount == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// If the refcount is zero then the node should have been deregisterd
|
|||
|
// and is no longer needs to be in the list so remove and free it
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(Notify->Unregistered);
|
|||
|
|
|||
|
//
|
|||
|
// Remove the notification entry from its list.
|
|||
|
//
|
|||
|
// Note that this MUST be done first, since the notification list head
|
|||
|
// for a target device notification entry resides in the target device
|
|||
|
// node, which may be freed immediately after the device object is
|
|||
|
// dereferenced. For notification entry types other than target device
|
|||
|
// change this is not critical, but still a good idea.
|
|||
|
//
|
|||
|
|
|||
|
RemoveEntryList((PLIST_ENTRY)Notify);
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the driver object that registered for notifications
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject(Notify->DriverObject);
|
|||
|
|
|||
|
//
|
|||
|
// If this notification entry is for target device change, dereference
|
|||
|
// the PDO upon which this notification entry was hooked.
|
|||
|
//
|
|||
|
|
|||
|
if (Notify->EventCategory == EventCategoryTargetDeviceChange) {
|
|||
|
PTARGET_DEVICE_NOTIFY_ENTRY entry = (PTARGET_DEVICE_NOTIFY_ENTRY)Notify;
|
|||
|
|
|||
|
if (entry->PhysicalDeviceObject) {
|
|||
|
ObDereferenceObject(entry->PhysicalDeviceObject);
|
|||
|
entry->PhysicalDeviceObject = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the opaque session object
|
|||
|
//
|
|||
|
|
|||
|
if (Notify->OpaqueSession) {
|
|||
|
MmQuitNextSession(Notify->OpaqueSession);
|
|||
|
Notify->OpaqueSession = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the notification entry
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool(Notify);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopRequestHwProfileChangeNotification(
|
|||
|
IN LPGUID EventGuid,
|
|||
|
IN PROFILE_NOTIFICATION_TIME NotificationTime,
|
|||
|
OUT PPNP_VETO_TYPE VetoType OPTIONAL,
|
|||
|
OUT PUNICODE_STRING VetoName OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to notify all registered drivers of a hardware profile
|
|||
|
change. If the operation is a HW provile change query then the operation
|
|||
|
is synchronous and the veto information is propagated. All other operations
|
|||
|
are asynchronous and veto information is not returned.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
EventTypeGuid - The event that has occured
|
|||
|
|
|||
|
NotificationTime - This is used to tell if we are already in an event
|
|||
|
when delivering a synchronous notification (ie,
|
|||
|
querying profile change to eject). It is one of
|
|||
|
three values:
|
|||
|
PROFILE_IN_PNPEVENT
|
|||
|
PROFILE_NOT_IN_PNPEVENT
|
|||
|
PROFILE_PERHAPS_IN_PNPEVENT
|
|||
|
|
|||
|
VetoType - Type of vetoer.
|
|||
|
|
|||
|
VetoName - Name of vetoer.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
The contents of the notification structure *including* all pointers is only
|
|||
|
valid during the callback routine to which it was passed. If the data is
|
|||
|
required after the duration of the callback then it must be physically copied
|
|||
|
by the callback routine.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status=STATUS_SUCCESS,completionStatus;
|
|||
|
KEVENT completionEvent;
|
|||
|
ULONG dataSize,totalSize;
|
|||
|
PPNP_DEVICE_EVENT_ENTRY deviceEvent;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if ((!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE)) &&
|
|||
|
(!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_CHANGE_CANCELLED)) &&
|
|||
|
(!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_CHANGE_COMPLETE))) {
|
|||
|
|
|||
|
//
|
|||
|
// Passed in an illegal value
|
|||
|
//
|
|||
|
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_ERROR_LEVEL,
|
|||
|
"IopRequestHwProfileChangeNotification: "
|
|||
|
"Illegal Event type passed as profile notification\n"));
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Only the query changes are synchronous, and in that case we must
|
|||
|
// know definitely whether we are nested within a Pnp event or not.
|
|||
|
//
|
|||
|
ASSERT((!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE))||
|
|||
|
(NotificationTime != PROFILE_PERHAPS_IN_PNPEVENT)) ;
|
|||
|
|
|||
|
if (!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Asynchronous case. Very easy.
|
|||
|
//
|
|||
|
ASSERT(!ARGUMENT_PRESENT(VetoName));
|
|||
|
ASSERT(!ARGUMENT_PRESENT(VetoType));
|
|||
|
|
|||
|
return PpSetHwProfileChangeEvent( EventGuid,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Query notifications are synchronous. Determine if we are currently
|
|||
|
// within an event, in which case we must do the notify here instead
|
|||
|
// of queueing it up.
|
|||
|
//
|
|||
|
if (NotificationTime == PROFILE_NOT_IN_PNPEVENT) {
|
|||
|
|
|||
|
//
|
|||
|
// Queue up and block on the notification.
|
|||
|
//
|
|||
|
KeInitializeEvent(&completionEvent, NotificationEvent, FALSE);
|
|||
|
|
|||
|
status = PpSetHwProfileChangeEvent( EventGuid,
|
|||
|
&completionEvent,
|
|||
|
&completionStatus,
|
|||
|
VetoType,
|
|||
|
VetoName
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
KeWaitForSingleObject( &completionEvent, Executive, KernelMode, FALSE, NULL );
|
|||
|
|
|||
|
status = completionStatus;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Synchronous notify inside our Pnp event.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// ISSUE-ADRIAO-1998/11/12 - We are MANUALLY sending the profile
|
|||
|
// query change notification because we are blocking inside a PnPEvent and
|
|||
|
// thus can't queue/wait on another!
|
|||
|
//
|
|||
|
ASSERT(PiNotificationInProgress == TRUE);
|
|||
|
|
|||
|
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->Data.EventCategory = HardwareProfileChangeEvent;
|
|||
|
RtlCopyMemory(&deviceEvent->Data.EventGuid, EventGuid, sizeof(GUID));
|
|||
|
deviceEvent->Data.TotalSize = dataSize;
|
|||
|
deviceEvent->CallerEvent = &completionEvent;
|
|||
|
deviceEvent->Data.Result = (PULONG)&completionStatus;
|
|||
|
deviceEvent->VetoType = VetoType;
|
|||
|
deviceEvent->VetoName = VetoName;
|
|||
|
|
|||
|
//
|
|||
|
// Notify K-Mode
|
|||
|
//
|
|||
|
status = IopNotifyHwProfileChange(&deviceEvent->Data.EventGuid,
|
|||
|
VetoType,
|
|||
|
VetoName);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Notify user-mode (synchronously).
|
|||
|
//
|
|||
|
status = PiNotifyUserMode(deviceEvent);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// Notify K-mode that the query has been cancelled.
|
|||
|
//
|
|||
|
IopNotifyHwProfileChange((LPGUID)&GUID_HWPROFILE_CHANGE_CANCELLED,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopNotifyHwProfileChange(
|
|||
|
IN LPGUID EventGuid,
|
|||
|
OUT PPNP_VETO_TYPE VetoType OPTIONAL,
|
|||
|
OUT PUNICODE_STRING VetoName OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to deliver the HWProfileNotifications. It is
|
|||
|
called from the worker thread only
|
|||
|
It does not return until all interested parties have been notified.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
EventTypeGuid - The event that has occured
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
The contents of the notification structure *including* all pointers is only
|
|||
|
valid during the callback routine to which it was passed. If the data is
|
|||
|
required after the duration of the callback then it must be physically copied
|
|||
|
by the callback routine.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status = STATUS_SUCCESS, dispatchStatus;
|
|||
|
PHWPROFILE_NOTIFY_ENTRY pNotifyList, vetoEntry;
|
|||
|
PLIST_ENTRY link;
|
|||
|
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//Lock the Profile Notification List
|
|||
|
IopAcquireNotifyLock (&IopHwProfileNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Grab the list head (inside the lock)
|
|||
|
//
|
|||
|
link = IopProfileNotifyList.Flink;
|
|||
|
pNotifyList=(PHWPROFILE_NOTIFY_ENTRY)link;
|
|||
|
|
|||
|
//
|
|||
|
//circular list
|
|||
|
//
|
|||
|
while (link != (PLIST_ENTRY)&IopProfileNotifyList) {
|
|||
|
if (!pNotifyList->Unregistered) {
|
|||
|
|
|||
|
HWPROFILE_CHANGE_NOTIFICATION notification;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the entry so that no one deletes during the callback
|
|||
|
// and then release the lock
|
|||
|
//
|
|||
|
IopReferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
|
|||
|
IopReleaseNotifyLock(&IopHwProfileNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the notification structure
|
|||
|
//
|
|||
|
notification.Version = PNP_NOTIFICATION_VERSION;
|
|||
|
notification.Size = sizeof(HWPROFILE_CHANGE_NOTIFICATION);
|
|||
|
notification.Event = *EventGuid;
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch the notification to the callback routine for the
|
|||
|
// appropriate session.
|
|||
|
//
|
|||
|
dispatchStatus = PiNotifyDriverCallback(pNotifyList->CallbackRoutine,
|
|||
|
¬ification,
|
|||
|
pNotifyList->Context,
|
|||
|
pNotifyList->SessionId,
|
|||
|
pNotifyList->OpaqueSession,
|
|||
|
&status);
|
|||
|
ASSERT(NT_SUCCESS(dispatchStatus));
|
|||
|
|
|||
|
//
|
|||
|
// Failure to dispatch the notification to the specified callback
|
|||
|
// should not be considered a veto.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(dispatchStatus)) {
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the caller returned anything other than success and it was a
|
|||
|
// query hardware profile change, we veto the query and send cancels
|
|||
|
// to all callers that already got the query.
|
|||
|
//
|
|||
|
|
|||
|
if ((!NT_SUCCESS(status)) &&
|
|||
|
(IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE))) {
|
|||
|
|
|||
|
if (VetoType) {
|
|||
|
*VetoType = PNP_VetoDriver;
|
|||
|
}
|
|||
|
if (VetoName) {
|
|||
|
VetoName->Length = 0;
|
|||
|
RtlCopyUnicodeString(VetoName, &pNotifyList->DriverObject->DriverName);
|
|||
|
}
|
|||
|
|
|||
|
notification.Event = GUID_HWPROFILE_CHANGE_CANCELLED;
|
|||
|
notification.Size = sizeof(GUID_HWPROFILE_CHANGE_CANCELLED);
|
|||
|
|
|||
|
//
|
|||
|
// Keep track of the entry that vetoed the query. We can't
|
|||
|
// dereference it just yet, because we may need to send it a
|
|||
|
// cancel-remove first. Since it's possible that the entry
|
|||
|
// may have been unregistered when the list was unlocked
|
|||
|
// during the query callback (removing all but the reference
|
|||
|
// we are currently holding), we need to make sure we don't
|
|||
|
// dereference it until we're absolutely done with it.
|
|||
|
//
|
|||
|
vetoEntry = pNotifyList;
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopHwProfileNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we are starting where we left off above, at the
|
|||
|
// vetoing entry.
|
|||
|
//
|
|||
|
ASSERT((PHWPROFILE_NOTIFY_ENTRY)link == vetoEntry);
|
|||
|
|
|||
|
do {
|
|||
|
pNotifyList = (PHWPROFILE_NOTIFY_ENTRY)link;
|
|||
|
if (!pNotifyList->Unregistered) {
|
|||
|
IopReferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
|
|||
|
IopReleaseNotifyLock(&IopHwProfileNotifyLock);
|
|||
|
|
|||
|
dispatchStatus = PiNotifyDriverCallback(pNotifyList->CallbackRoutine,
|
|||
|
¬ification,
|
|||
|
pNotifyList->Context,
|
|||
|
pNotifyList->SessionId,
|
|||
|
pNotifyList->OpaqueSession,
|
|||
|
NULL);
|
|||
|
ASSERT(NT_SUCCESS(dispatchStatus));
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopHwProfileNotifyLock);
|
|||
|
link = link->Blink;
|
|||
|
IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
|
|||
|
|
|||
|
} else {
|
|||
|
link = link->Blink;
|
|||
|
}
|
|||
|
|
|||
|
if (pNotifyList == vetoEntry) {
|
|||
|
//
|
|||
|
// Dereference the entry which vetoed the query change.
|
|||
|
//
|
|||
|
IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
|
|||
|
}
|
|||
|
|
|||
|
} while (link != (PLIST_ENTRY)&IopProfileNotifyList);
|
|||
|
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Reacquire the lock, walk forward, and dereference
|
|||
|
//
|
|||
|
IopAcquireNotifyLock (&IopHwProfileNotifyLock);
|
|||
|
link = link->Flink;
|
|||
|
IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
|
|||
|
pNotifyList=(PHWPROFILE_NOTIFY_ENTRY)link;
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
//Walk forward if we hit an unregistered node
|
|||
|
//
|
|||
|
if (pNotifyList) {
|
|||
|
//
|
|||
|
//walk forward
|
|||
|
//
|
|||
|
link = link->Flink;
|
|||
|
pNotifyList=(PHWPROFILE_NOTIFY_ENTRY)link;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Clean0:
|
|||
|
|
|||
|
//UnLock the Profile Notification List
|
|||
|
IopReleaseNotifyLock (&IopHwProfileNotifyLock);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopNotifyTargetDeviceChange(
|
|||
|
IN LPCGUID EventGuid,
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure OPTIONAL,
|
|||
|
OUT PDRIVER_OBJECT *VetoingDriver
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to notify all registered drivers of a change to a
|
|||
|
particular device. It does not return until all interested parties have
|
|||
|
been notified.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
EventGuid - The event guid to send to the drivers.
|
|||
|
|
|||
|
DeviceObject - The device object for the affected device. The devnode for
|
|||
|
this device object contains a list of callback routines that have
|
|||
|
registered for notification of any changes on this device object.
|
|||
|
|
|||
|
NotificationStructure - Custom notification structure to send to the
|
|||
|
registrants.
|
|||
|
|
|||
|
VetoingDriver - Driver that vetoed the event if
|
|||
|
(EventGuid == GUID_TARGET_DEVICE_QUERY_REMOVE).
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
The contents of the notification structure *including* all pointers is only
|
|||
|
valid during the callback routine to which it was passed. If the data is
|
|||
|
required after the duration of the callback then it must be physically copied
|
|||
|
by the callback routine.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status, dispatchStatus;
|
|||
|
PLIST_ENTRY link;
|
|||
|
PTARGET_DEVICE_NOTIFY_ENTRY entry, vetoEntry;
|
|||
|
TARGET_DEVICE_REMOVAL_NOTIFICATION targetNotification;
|
|||
|
PVOID notification;
|
|||
|
PDEVICE_NODE deviceNode;
|
|||
|
BOOLEAN reverse;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT(DeviceObject != NULL);
|
|||
|
ASSERT(EventGuid != NULL);
|
|||
|
|
|||
|
//
|
|||
|
// Reference the device object so it can't go away while we're doing notification
|
|||
|
//
|
|||
|
ObReferenceObject(DeviceObject);
|
|||
|
|
|||
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|||
|
|
|||
|
ASSERT(deviceNode != NULL);
|
|||
|
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(NotificationStructure)) {
|
|||
|
|
|||
|
//
|
|||
|
// We're handling a custom notification
|
|||
|
//
|
|||
|
NotificationStructure->Version = PNP_NOTIFICATION_VERSION;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the notification structure
|
|||
|
//
|
|||
|
targetNotification.Version = PNP_NOTIFICATION_VERSION;
|
|||
|
targetNotification.Size = sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION);
|
|||
|
targetNotification.Event = *EventGuid;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Lock the notify list
|
|||
|
//
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Get the first entry
|
|||
|
//
|
|||
|
|
|||
|
reverse = (BOOLEAN)IopCompareGuid(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_REMOVE_CANCELLED);
|
|||
|
|
|||
|
if (reverse) {
|
|||
|
link = deviceNode->TargetDeviceNotify.Blink;
|
|||
|
} else {
|
|||
|
link = deviceNode->TargetDeviceNotify.Flink;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Iterate through the list
|
|||
|
//
|
|||
|
|
|||
|
while (link != &deviceNode->TargetDeviceNotify) {
|
|||
|
|
|||
|
entry = (PTARGET_DEVICE_NOTIFY_ENTRY)link;
|
|||
|
|
|||
|
//
|
|||
|
// Only callback on registered nodes
|
|||
|
//
|
|||
|
|
|||
|
if (!entry->Unregistered) {
|
|||
|
|
|||
|
//
|
|||
|
// Reference the entry so that no one deletes during the callback
|
|||
|
// and then release the lock
|
|||
|
//
|
|||
|
IopReferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
|
|||
|
IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Select the notification structure to deliver and set the file
|
|||
|
// object in the notification structure to that for the current
|
|||
|
// entry
|
|||
|
//
|
|||
|
if (ARGUMENT_PRESENT(NotificationStructure)) {
|
|||
|
NotificationStructure->FileObject = entry->FileObject;
|
|||
|
notification = (PVOID)NotificationStructure;
|
|||
|
} else {
|
|||
|
targetNotification.FileObject = entry->FileObject;
|
|||
|
notification = (PVOID)&targetNotification;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch the notification to the callback routine for the
|
|||
|
// appropriate session.
|
|||
|
//
|
|||
|
dispatchStatus = PiNotifyDriverCallback(entry->CallbackRoutine,
|
|||
|
notification,
|
|||
|
entry->Context,
|
|||
|
entry->SessionId,
|
|||
|
entry->OpaqueSession,
|
|||
|
&status);
|
|||
|
ASSERT(NT_SUCCESS(dispatchStatus));
|
|||
|
|
|||
|
//
|
|||
|
// Failure to dispatch the notification to the specified callback
|
|||
|
// should not be considered a veto.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(dispatchStatus)) {
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the caller returned anything other than success and it was
|
|||
|
// a query remove, we veto the query remove and send cancels to
|
|||
|
// all callers that already got the query remove.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
if (IopCompareGuid(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_QUERY_REMOVE)) {
|
|||
|
|
|||
|
ASSERT(notification == (PVOID)&targetNotification);
|
|||
|
|
|||
|
if (VetoingDriver != NULL) {
|
|||
|
*VetoingDriver = entry->DriverObject;
|
|||
|
}
|
|||
|
|
|||
|
targetNotification.Event = GUID_TARGET_DEVICE_REMOVE_CANCELLED;
|
|||
|
|
|||
|
//
|
|||
|
// Keep track of the entry that vetoed the query. We can't
|
|||
|
// dereference it just yet, because we may need to send it a
|
|||
|
// cancel-remove first. Since it's possible that the entry
|
|||
|
// may have been unregistered when the list was unlocked
|
|||
|
// during the query callback (removing all but the reference
|
|||
|
// we are currently holding), we need to make sure we don't
|
|||
|
// dereference it until we're absolutely done with it.
|
|||
|
//
|
|||
|
vetoEntry = entry;
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we are starting where we left off above, at the
|
|||
|
// vetoing entry.
|
|||
|
//
|
|||
|
ASSERT((PTARGET_DEVICE_NOTIFY_ENTRY)link == vetoEntry);
|
|||
|
|
|||
|
do {
|
|||
|
entry = (PTARGET_DEVICE_NOTIFY_ENTRY)link;
|
|||
|
if (!entry->Unregistered) {
|
|||
|
|
|||
|
//
|
|||
|
// Reference the entry so that no one deletes during
|
|||
|
// the callback and then release the lock
|
|||
|
//
|
|||
|
IopReferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
|
|||
|
IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Set the file object in the notification structure
|
|||
|
// to that for the current entry
|
|||
|
//
|
|||
|
targetNotification.FileObject = entry->FileObject;
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch the notification to the callback routine
|
|||
|
// for the appropriate session.
|
|||
|
//
|
|||
|
dispatchStatus = PiNotifyDriverCallback(entry->CallbackRoutine,
|
|||
|
&targetNotification,
|
|||
|
entry->Context,
|
|||
|
entry->SessionId,
|
|||
|
entry->OpaqueSession,
|
|||
|
NULL);
|
|||
|
ASSERT(NT_SUCCESS(dispatchStatus));
|
|||
|
|
|||
|
//
|
|||
|
// Reacquire the lock and dereference
|
|||
|
//
|
|||
|
IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
link = link->Blink;
|
|||
|
IopDereferenceNotify( (PNOTIFY_ENTRY_HEADER) entry );
|
|||
|
|
|||
|
} else {
|
|||
|
link = link->Blink;
|
|||
|
}
|
|||
|
|
|||
|
if (entry == vetoEntry) {
|
|||
|
//
|
|||
|
// Dereference the entry which vetoed the query remove.
|
|||
|
//
|
|||
|
IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)vetoEntry);
|
|||
|
}
|
|||
|
|
|||
|
} while (link != &deviceNode->TargetDeviceNotify);
|
|||
|
|
|||
|
goto Clean0;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ASSERT(notification == (PVOID)NotificationStructure);
|
|||
|
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_ERROR_LEVEL,
|
|||
|
"IopNotifyTargetDeviceChange: "
|
|||
|
"Driver %Z, handler @ 0x%p failed non-failable notification 0x%p with return code %x\n",
|
|||
|
&entry->DriverObject->DriverName,
|
|||
|
entry->CallbackRoutine,
|
|||
|
notification,
|
|||
|
status));
|
|||
|
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Reacquire the lock and dereference
|
|||
|
//
|
|||
|
IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
if (reverse) {
|
|||
|
link = link->Blink;
|
|||
|
} else {
|
|||
|
link = link->Flink;
|
|||
|
}
|
|||
|
IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Advance down the list
|
|||
|
//
|
|||
|
if (reverse) {
|
|||
|
link = link->Blink;
|
|||
|
} else {
|
|||
|
link = link->Flink;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If it's not a query, it can't be failed.
|
|||
|
//
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
Clean0:
|
|||
|
|
|||
|
//
|
|||
|
// Release the lock and dereference the object
|
|||
|
//
|
|||
|
|
|||
|
IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
|
|||
|
ObDereferenceObject(DeviceObject);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopNotifyDeviceClassChange(
|
|||
|
LPGUID EventGuid,
|
|||
|
LPGUID ClassGuid,
|
|||
|
PUNICODE_STRING SymbolicLinkName
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to notify all registered drivers of a changes to a
|
|||
|
particular class of device. It does not return until all interested parties have
|
|||
|
been notified.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
EventTypeGuid - The event that has occured
|
|||
|
|
|||
|
ClassGuid - The device class this change has occured in
|
|||
|
|
|||
|
SymbolicLinkName - The kernel mode symbolic link name of the interface device
|
|||
|
that changed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
The contents of the notification structure *including* all pointers is only
|
|||
|
valid during the callback routine to which it was passed. If the data is
|
|||
|
required after the duration of the callback then it must be physically copied
|
|||
|
by the callback routine.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status, dispatchStatus;
|
|||
|
PLIST_ENTRY link;
|
|||
|
PDEVICE_CLASS_NOTIFY_ENTRY entry;
|
|||
|
DEVICE_INTERFACE_CHANGE_NOTIFICATION notification;
|
|||
|
ULONG hash;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the notification structure
|
|||
|
//
|
|||
|
|
|||
|
notification.Version = PNP_NOTIFICATION_VERSION;
|
|||
|
notification.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
|
|||
|
notification.Event = *EventGuid;
|
|||
|
notification.InterfaceClassGuid = *ClassGuid;
|
|||
|
notification.SymbolicLinkName = SymbolicLinkName;
|
|||
|
|
|||
|
//
|
|||
|
// Lock the notify list
|
|||
|
//
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopDeviceClassNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Get the first entry
|
|||
|
//
|
|||
|
|
|||
|
hash = IopHashGuid(ClassGuid);
|
|||
|
link = IopDeviceClassNotifyList[hash].Flink;
|
|||
|
|
|||
|
//
|
|||
|
// Iterate through the list
|
|||
|
//
|
|||
|
|
|||
|
while (link != &IopDeviceClassNotifyList[hash]) {
|
|||
|
|
|||
|
entry = (PDEVICE_CLASS_NOTIFY_ENTRY) link;
|
|||
|
|
|||
|
//
|
|||
|
// Only callback on registered nodes of the correct device class
|
|||
|
//
|
|||
|
|
|||
|
if ( !entry->Unregistered && IopCompareGuid(&(entry->ClassGuid), ClassGuid) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Reference the entry so that no one deletes during the callback
|
|||
|
// and then release the lock
|
|||
|
//
|
|||
|
IopReferenceNotify( (PNOTIFY_ENTRY_HEADER) entry );
|
|||
|
IopReleaseNotifyLock(&IopDeviceClassNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch the notification to the callback routine for the
|
|||
|
// appropriate session. Ignore the returned result for non-query
|
|||
|
// type events.
|
|||
|
//
|
|||
|
dispatchStatus = PiNotifyDriverCallback(entry->CallbackRoutine,
|
|||
|
¬ification,
|
|||
|
entry->Context,
|
|||
|
entry->SessionId,
|
|||
|
entry->OpaqueSession,
|
|||
|
&status);
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(dispatchStatus));
|
|||
|
|
|||
|
//
|
|||
|
// ISSUE -2000/11/27 - JAMESCA: Overactive assert
|
|||
|
// This assert is temporarily commented out until mountmgr is fixed.
|
|||
|
//
|
|||
|
// ASSERT(NT_SUCCESS(status));
|
|||
|
|
|||
|
//
|
|||
|
// Reacquire the lock and dereference
|
|||
|
//
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopDeviceClassNotifyLock);
|
|||
|
link = link->Flink;
|
|||
|
IopDereferenceNotify( (PNOTIFY_ENTRY_HEADER) entry );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Advance down the list
|
|||
|
//
|
|||
|
|
|||
|
link = link->Flink;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Release the lock
|
|||
|
//
|
|||
|
|
|||
|
IopReleaseNotifyLock(&IopDeviceClassNotifyLock);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IoRegisterPlugPlayNotification(
|
|||
|
IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
|
|||
|
IN ULONG EventCategoryFlags,
|
|||
|
IN PVOID EventCategoryData OPTIONAL,
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
|
|||
|
IN PVOID Context,
|
|||
|
OUT PVOID *NotificationEntry
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
IoRegisterPlugPlayNotification provides a mechanism by which WDM drivers may
|
|||
|
receive notification (via callback) for a variety of Plug&Play events.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
EventCategory - Specifies the event category being registered for. WDM drivers
|
|||
|
may currently register for hard-ware profile changes, device class changes
|
|||
|
(instance arrivals and removals), and target device changes (query-removal,
|
|||
|
cancel-removal, removal-complete, as well as 3rd-party extensible events).
|
|||
|
|
|||
|
EventCategoryFlags - Supplies flags that modify the behavior of event registration.
|
|||
|
There is a separate group of flags defined for each event category. Presently,
|
|||
|
only the interface device change event category has any flags defined:
|
|||
|
|
|||
|
DEVICE_CLASS_NOTIFY_FOR_EXISTING_DEVICES -- Drivers wishing to retrieve a
|
|||
|
complete list of all interface devices presently available, and keep
|
|||
|
the list up-to-date (i.e., receive notification of interface device
|
|||
|
arrivals and removals), may specify this flag. This will cause the
|
|||
|
PnP manager to immediately notify the driver about every currently-existing
|
|||
|
device of the specified interface class.
|
|||
|
|
|||
|
EventCategoryData - Used to 'filter' events of the desired category based on the
|
|||
|
supplied criteria. Not all event categories will use this parameter. The
|
|||
|
event categories presently defined use this information as fol-lows:
|
|||
|
|
|||
|
EventCategoryHardwareProfileChange -- this parameter is unused, and should be NULL.
|
|||
|
EventCategoryDeviceClassChange -- LPGUID representing the interface class of interest
|
|||
|
EventCategoryTargetDeviceChange -- PFILE_OBJECT of interest
|
|||
|
|
|||
|
DriverObject - The caller must supply a reference to its driver object (obtained via
|
|||
|
ObReferenceObject), to prevent the driver from being unloaded while registered for
|
|||
|
notification. The PnP Manager will dereference the driver object when the driver
|
|||
|
unregisters for notification via IoUnregisterPlugPlayNotification).
|
|||
|
|
|||
|
CallbackRoutine - Entry point within the driver that the PnP manager should call
|
|||
|
whenever an applicable PnP event occurs. The entry point must have the
|
|||
|
following prototype:
|
|||
|
|
|||
|
typedef
|
|||
|
NTSTATUS
|
|||
|
(*PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) (
|
|||
|
IN PVOID NotificationStructure,
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
where NotificationStructure contains information about the event. Each event
|
|||
|
GUID within an event category may potentially have its own notification structure
|
|||
|
format, but the buffer must al-ways begin with a PLUGPLAY_NOTIFICATION_HEADER,
|
|||
|
which indicates the size and ver-sion of the structure, as well as the GUID for
|
|||
|
the event.
|
|||
|
|
|||
|
The Context parameter provides the callback with the same context data that the
|
|||
|
caller passed in during registration.
|
|||
|
|
|||
|
Context - Points to the context data passed to the callback upon event notification.
|
|||
|
|
|||
|
NotificationEntry - Upon success, receives a handle representing the notification
|
|||
|
registration. This handle may be used to unregister for notification via
|
|||
|
IoUnregisterPlugPlayNotification.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT(NotificationEntry);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize out parameters
|
|||
|
//
|
|||
|
|
|||
|
*NotificationEntry = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the driver object so it doesn't go away while we still have
|
|||
|
// a pointer outstanding
|
|||
|
//
|
|||
|
status = ObReferenceObjectByPointer(DriverObject,
|
|||
|
0,
|
|||
|
IoDriverObjectType,
|
|||
|
KernelMode
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
switch (EventCategory) {
|
|||
|
|
|||
|
case EventCategoryReserved:
|
|||
|
{
|
|||
|
|
|||
|
PSETUP_NOTIFY_DATA setupData;
|
|||
|
|
|||
|
//
|
|||
|
// Note that the only setup notification callback currently supported
|
|||
|
// (setupdd.sys) is never in session space.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(MmIsSessionAddress((PVOID)CallbackRoutine) == FALSE);
|
|||
|
ASSERT(MmGetSessionId(PsGetCurrentProcess()) == 0);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate space for the setup data
|
|||
|
//
|
|||
|
|
|||
|
setupData = ExAllocatePool(PagedPool, sizeof(SETUP_NOTIFY_DATA));
|
|||
|
if (!setupData) {
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Store the required information
|
|||
|
//
|
|||
|
|
|||
|
InitializeListHead(&(setupData->ListEntry));
|
|||
|
setupData->EventCategory = EventCategory;
|
|||
|
setupData->SessionId = MmGetSessionId(PsGetCurrentProcess());
|
|||
|
setupData->CallbackRoutine = CallbackRoutine;
|
|||
|
setupData->Context = Context;
|
|||
|
setupData->RefCount = 1;
|
|||
|
setupData->Unregistered = FALSE;
|
|||
|
setupData->Lock = NULL;
|
|||
|
setupData->DriverObject = DriverObject;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the session object only if the callback is in session space
|
|||
|
//
|
|||
|
|
|||
|
if (MmIsSessionAddress((PVOID)CallbackRoutine)) {
|
|||
|
setupData->OpaqueSession = MmGetSessionById(setupData->SessionId);
|
|||
|
} else {
|
|||
|
setupData->OpaqueSession = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Activate the notifications
|
|||
|
//
|
|||
|
|
|||
|
IopSetupNotifyData = setupData;
|
|||
|
|
|||
|
//
|
|||
|
// Explicitly NULL out the returned entry as you can *NOT* unregister
|
|||
|
// for setup notifications
|
|||
|
//
|
|||
|
|
|||
|
*NotificationEntry = NULL;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
case EventCategoryHardwareProfileChange:
|
|||
|
{
|
|||
|
PHWPROFILE_NOTIFY_ENTRY entry;
|
|||
|
|
|||
|
//
|
|||
|
// new entry
|
|||
|
//
|
|||
|
entry =ExAllocatePool (PagedPool,sizeof (HWPROFILE_NOTIFY_ENTRY));
|
|||
|
if (!entry) {
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// grab the fields
|
|||
|
//
|
|||
|
|
|||
|
entry->EventCategory = EventCategory;
|
|||
|
entry->SessionId = MmGetSessionId(PsGetCurrentProcess());
|
|||
|
entry->CallbackRoutine = CallbackRoutine;
|
|||
|
entry->Context = Context;
|
|||
|
entry->RefCount = 1;
|
|||
|
entry->Unregistered = FALSE;
|
|||
|
entry->Lock = &IopHwProfileNotifyLock;
|
|||
|
entry->DriverObject = DriverObject;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the session object only if the callback is in session space
|
|||
|
//
|
|||
|
|
|||
|
if (MmIsSessionAddress((PVOID)CallbackRoutine)) {
|
|||
|
entry->OpaqueSession = MmGetSessionById(entry->SessionId);
|
|||
|
} else {
|
|||
|
entry->OpaqueSession = NULL;
|
|||
|
}
|
|||
|
|
|||
|
ExAcquireFastMutex(&PiNotificationInProgressLock);
|
|||
|
if (PiNotificationInProgress) {
|
|||
|
//
|
|||
|
// If a notification is in progress, mark the entry as
|
|||
|
// Unregistered until after the current notification is
|
|||
|
// complete.
|
|||
|
//
|
|||
|
|
|||
|
PDEFERRED_REGISTRATION_ENTRY deferredNode;
|
|||
|
|
|||
|
deferredNode = ExAllocatePool(PagedPool, sizeof(DEFERRED_REGISTRATION_ENTRY));
|
|||
|
if (!deferredNode) {
|
|||
|
ExReleaseFastMutex(&PiNotificationInProgressLock);
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
deferredNode->NotifyEntry = (PNOTIFY_ENTRY_HEADER)entry;
|
|||
|
|
|||
|
//
|
|||
|
// Consider this entry unregistered during the current
|
|||
|
// notification
|
|||
|
//
|
|||
|
entry->Unregistered = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the entry so that it doesn't go away until it has
|
|||
|
// been removed from the deferred registration list
|
|||
|
//
|
|||
|
IopReferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
|
|||
|
|
|||
|
//
|
|||
|
// Add this entry to the deferred registration list
|
|||
|
//
|
|||
|
IopAcquireNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
InsertTailList(&IopDeferredRegistrationList, (PLIST_ENTRY)deferredNode);
|
|||
|
IopReleaseNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
} else {
|
|||
|
//
|
|||
|
// If there is currently no notification in progress, the deferred
|
|||
|
// registration list must be empty.
|
|||
|
//
|
|||
|
ASSERT(IsListEmpty(&IopDeferredRegistrationList));
|
|||
|
}
|
|||
|
ExReleaseFastMutex(&PiNotificationInProgressLock);
|
|||
|
|
|||
|
//
|
|||
|
// Lock the list, insert the new entry, and unlock it.
|
|||
|
//
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopHwProfileNotifyLock);
|
|||
|
InsertTailList(&IopProfileNotifyList, (PLIST_ENTRY)entry);
|
|||
|
IopReleaseNotifyLock(&IopHwProfileNotifyLock);
|
|||
|
|
|||
|
*NotificationEntry = entry;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
case EventCategoryTargetDeviceChange:
|
|||
|
{
|
|||
|
PTARGET_DEVICE_NOTIFY_ENTRY entry;
|
|||
|
PDEVICE_NODE deviceNode;
|
|||
|
|
|||
|
ASSERT(EventCategoryData);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a new list entry
|
|||
|
//
|
|||
|
|
|||
|
entry = ExAllocatePool(PagedPool, sizeof(TARGET_DEVICE_NOTIFY_ENTRY));
|
|||
|
if (!entry) {
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Retrieve the device object associated with this file handle.
|
|||
|
//
|
|||
|
status = IopGetRelatedTargetDevice((PFILE_OBJECT)EventCategoryData,
|
|||
|
&deviceNode);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
ExFreePool(entry);
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill out the entry
|
|||
|
//
|
|||
|
|
|||
|
entry->EventCategory = EventCategory;
|
|||
|
entry->SessionId = MmGetSessionId(PsGetCurrentProcess());
|
|||
|
entry->CallbackRoutine = CallbackRoutine;
|
|||
|
entry->Context = Context;
|
|||
|
entry->DriverObject = DriverObject;
|
|||
|
entry->RefCount = 1;
|
|||
|
entry->Unregistered = FALSE;
|
|||
|
entry->Lock = &IopTargetDeviceNotifyLock;
|
|||
|
entry->FileObject = (PFILE_OBJECT)EventCategoryData;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the session object only if the callback is in session space
|
|||
|
//
|
|||
|
|
|||
|
if (MmIsSessionAddress((PVOID)CallbackRoutine)) {
|
|||
|
entry->OpaqueSession = MmGetSessionById(entry->SessionId);
|
|||
|
} else {
|
|||
|
entry->OpaqueSession = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The PDO associated with the devnode we got back from
|
|||
|
// IopGetRelatedTargetDevice has already been referenced by that
|
|||
|
// routine. Store this reference away in the notification entry,
|
|||
|
// so we can deref it later when the notification entry is unregistered.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(deviceNode->PhysicalDeviceObject);
|
|||
|
entry->PhysicalDeviceObject = deviceNode->PhysicalDeviceObject;
|
|||
|
|
|||
|
ExAcquireFastMutex(&PiNotificationInProgressLock);
|
|||
|
if (PiNotificationInProgress) {
|
|||
|
//
|
|||
|
// If a notification is in progress, mark the entry as
|
|||
|
// Unregistered until after the current notification is
|
|||
|
// complete.
|
|||
|
//
|
|||
|
|
|||
|
PDEFERRED_REGISTRATION_ENTRY deferredNode;
|
|||
|
|
|||
|
deferredNode = ExAllocatePool(PagedPool, sizeof(DEFERRED_REGISTRATION_ENTRY));
|
|||
|
if (!deferredNode) {
|
|||
|
ExReleaseFastMutex(&PiNotificationInProgressLock);
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
deferredNode->NotifyEntry = (PNOTIFY_ENTRY_HEADER)entry;
|
|||
|
|
|||
|
//
|
|||
|
// Consider this entry unregistered during the current
|
|||
|
// notification
|
|||
|
//
|
|||
|
entry->Unregistered = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the entry so that it doesn't go away until it has
|
|||
|
// been removed from the deferred registration list
|
|||
|
//
|
|||
|
IopReferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
|
|||
|
|
|||
|
//
|
|||
|
// Add this entry to the deferred registration list
|
|||
|
//
|
|||
|
IopAcquireNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
InsertTailList(&IopDeferredRegistrationList, (PLIST_ENTRY)deferredNode);
|
|||
|
IopReleaseNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
} else {
|
|||
|
//
|
|||
|
// If there is currently no notification in progress, the deferred
|
|||
|
// registration list must be empty.
|
|||
|
//
|
|||
|
ASSERT(IsListEmpty(&IopDeferredRegistrationList));
|
|||
|
}
|
|||
|
ExReleaseFastMutex(&PiNotificationInProgressLock);
|
|||
|
|
|||
|
//
|
|||
|
// Lock the list, insert the new entry, and unlock it.
|
|||
|
//
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
InsertTailList(&deviceNode->TargetDeviceNotify, (PLIST_ENTRY)entry);
|
|||
|
IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
|
|||
|
*NotificationEntry = entry;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case EventCategoryDeviceInterfaceChange:
|
|||
|
{
|
|||
|
PDEVICE_CLASS_NOTIFY_ENTRY entry;
|
|||
|
|
|||
|
ASSERT(EventCategoryData);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a new list entry
|
|||
|
//
|
|||
|
|
|||
|
entry = ExAllocatePool(PagedPool, sizeof(DEVICE_CLASS_NOTIFY_ENTRY));
|
|||
|
if (!entry) {
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill out the entry
|
|||
|
//
|
|||
|
|
|||
|
entry->EventCategory = EventCategory;
|
|||
|
entry->SessionId = MmGetSessionId(PsGetCurrentProcess());
|
|||
|
entry->CallbackRoutine = CallbackRoutine;
|
|||
|
entry->Context = Context;
|
|||
|
entry->ClassGuid = *((LPGUID) EventCategoryData);
|
|||
|
entry->RefCount = 1;
|
|||
|
entry->Unregistered = FALSE;
|
|||
|
entry->Lock = &IopDeviceClassNotifyLock;
|
|||
|
entry->DriverObject = DriverObject;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the session object only if the callback is in session space
|
|||
|
//
|
|||
|
|
|||
|
if (MmIsSessionAddress((PVOID)CallbackRoutine)) {
|
|||
|
entry->OpaqueSession = MmGetSessionById(entry->SessionId);
|
|||
|
} else {
|
|||
|
entry->OpaqueSession = NULL;
|
|||
|
}
|
|||
|
|
|||
|
ExAcquireFastMutex(&PiNotificationInProgressLock);
|
|||
|
if (PiNotificationInProgress) {
|
|||
|
//
|
|||
|
// If a notification is in progress, mark the entry as
|
|||
|
// Unregistered until after the current notification is
|
|||
|
// complete.
|
|||
|
//
|
|||
|
|
|||
|
PDEFERRED_REGISTRATION_ENTRY deferredNode;
|
|||
|
|
|||
|
deferredNode = ExAllocatePool(PagedPool, sizeof(DEFERRED_REGISTRATION_ENTRY));
|
|||
|
if (!deferredNode) {
|
|||
|
ExReleaseFastMutex(&PiNotificationInProgressLock);
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
deferredNode->NotifyEntry = (PNOTIFY_ENTRY_HEADER)entry;
|
|||
|
|
|||
|
//
|
|||
|
// Consider this entry unregistered during the current
|
|||
|
// notification
|
|||
|
//
|
|||
|
entry->Unregistered = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the entry so that it doesn't go away until it has
|
|||
|
// been removed from the deferred registration list
|
|||
|
//
|
|||
|
IopReferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
|
|||
|
|
|||
|
//
|
|||
|
// Add this entry to the deferred registration list
|
|||
|
//
|
|||
|
IopAcquireNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
InsertTailList(&IopDeferredRegistrationList, (PLIST_ENTRY)deferredNode);
|
|||
|
IopReleaseNotifyLock(&IopDeferredRegistrationLock);
|
|||
|
} else {
|
|||
|
//
|
|||
|
// If there is currently no notification in progress, the deferred
|
|||
|
// registration list must be empty.
|
|||
|
//
|
|||
|
ASSERT(IsListEmpty(&IopDeferredRegistrationList));
|
|||
|
}
|
|||
|
ExReleaseFastMutex(&PiNotificationInProgressLock);
|
|||
|
|
|||
|
//
|
|||
|
// Lock the list
|
|||
|
//
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopDeviceClassNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Insert it at the tail
|
|||
|
//
|
|||
|
|
|||
|
InsertTailList( (PLIST_ENTRY) &(IopDeviceClassNotifyList[ IopHashGuid(&(entry->ClassGuid)) ]),
|
|||
|
(PLIST_ENTRY) entry
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the list
|
|||
|
//
|
|||
|
|
|||
|
IopReleaseNotifyLock(&IopDeviceClassNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// See if we need to notify for all the device classes already present
|
|||
|
//
|
|||
|
|
|||
|
if (EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES) {
|
|||
|
|
|||
|
PWCHAR pSymbolicLinks, pCurrent;
|
|||
|
DEVICE_INTERFACE_CHANGE_NOTIFICATION notification;
|
|||
|
UNICODE_STRING unicodeString;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the notification structure
|
|||
|
//
|
|||
|
|
|||
|
notification.Version = PNP_NOTIFICATION_VERSION;
|
|||
|
notification.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
|
|||
|
notification.Event = GUID_DEVICE_INTERFACE_ARRIVAL;
|
|||
|
notification.InterfaceClassGuid = entry->ClassGuid;
|
|||
|
|
|||
|
//
|
|||
|
// Get the list of all the devices of this function class that are
|
|||
|
// already in the system
|
|||
|
//
|
|||
|
|
|||
|
status = IoGetDeviceInterfaces(&(entry->ClassGuid),
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&pSymbolicLinks
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// No buffer will have been returned so just return status
|
|||
|
//
|
|||
|
goto clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Callback for each device currently in the system
|
|||
|
//
|
|||
|
|
|||
|
pCurrent = pSymbolicLinks;
|
|||
|
while(*pCurrent != UNICODE_NULL) {
|
|||
|
|
|||
|
NTSTATUS dispatchStatus, tempStatus;
|
|||
|
|
|||
|
RtlInitUnicodeString(&unicodeString, pCurrent);
|
|||
|
notification.SymbolicLinkName = &unicodeString;
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch the notification to the callback routine for the
|
|||
|
// appropriate session. Ignore the returned result for non-query
|
|||
|
// type events.
|
|||
|
//
|
|||
|
dispatchStatus = PiNotifyDriverCallback(CallbackRoutine,
|
|||
|
¬ification,
|
|||
|
Context,
|
|||
|
entry->SessionId,
|
|||
|
entry->OpaqueSession,
|
|||
|
&tempStatus);
|
|||
|
|
|||
|
//
|
|||
|
// ISSUE -2000/11/27 - JAMESCA: Overactive assert
|
|||
|
// ClusDisk failed here. The code in question is being
|
|||
|
// removed, but we don't we want to make sure we flush
|
|||
|
// anyone else out before we enable it again.
|
|||
|
//
|
|||
|
//ASSERT(NT_SUCCESS(dispatchStatus) && NT_SUCCESS(tempStatus));
|
|||
|
ASSERT(NT_SUCCESS(dispatchStatus));
|
|||
|
|
|||
|
pCurrent += (unicodeString.Length / sizeof(WCHAR)) + 1;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(pSymbolicLinks);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
*NotificationEntry = entry;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
clean0:
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
ObDereferenceObject(DriverObject);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopGetRelatedTargetDevice(
|
|||
|
IN PFILE_OBJECT FileObject,
|
|||
|
OUT PDEVICE_NODE *DeviceNode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
IopGetRelatedTargetDevice retrieves the device object associated with
|
|||
|
the specified file object and then sends a query device relations irp
|
|||
|
to that device object.
|
|||
|
|
|||
|
NOTE: The PDO associated with the returned device node has been referenced,
|
|||
|
and must be dereferenced when no longer needed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FileObject - Specifies the file object that is associated with the device
|
|||
|
object that will receive the query device relations irp.
|
|||
|
|
|||
|
DeviceNode - Returns the related target device node.
|
|||
|
|
|||
|
ReturnValue
|
|||
|
|
|||
|
Returns an NTSTATUS value.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
IO_STACK_LOCATION irpSp;
|
|||
|
PDEVICE_OBJECT deviceObject, targetDeviceObject;
|
|||
|
PDEVICE_RELATIONS deviceRelations;
|
|||
|
PDEVICE_NODE targetDeviceNode;
|
|||
|
|
|||
|
ASSERT(FileObject);
|
|||
|
|
|||
|
//
|
|||
|
// Retrieve the device object associated with this file handle.
|
|||
|
//
|
|||
|
|
|||
|
deviceObject = IoGetRelatedDeviceObject(FileObject);
|
|||
|
if (!deviceObject) {
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Query what the "actual" target device node should be for
|
|||
|
// this file object. Initialize the stack location to pass to
|
|||
|
// IopSynchronousCall() and then send the IRP to the device
|
|||
|
// object that's associated with the file handle.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
|
|||
|
irpSp.MajorFunction = IRP_MJ_PNP;
|
|||
|
irpSp.MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
|
|||
|
irpSp.Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
|
|||
|
irpSp.DeviceObject = deviceObject;
|
|||
|
irpSp.FileObject = FileObject;
|
|||
|
|
|||
|
status = IopSynchronousCall(deviceObject, &irpSp, (PULONG_PTR)&deviceRelations);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
#if 0
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_INFO_LEVEL,
|
|||
|
"IopGetRelatedTargetDevice: "
|
|||
|
"Contact dev owner for %WZ, which may not correctly support\n"
|
|||
|
"\tIRP_MN_QUERY_DEVICE_RELATIONS:TargetDeviceRelation\n",
|
|||
|
&deviceObject->DriverObject->DriverExtension->ServiceKeyName));
|
|||
|
//ASSERT(0);
|
|||
|
#endif
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(deviceRelations);
|
|||
|
|
|||
|
if (deviceRelations) {
|
|||
|
|
|||
|
ASSERT(deviceRelations->Count == 1);
|
|||
|
|
|||
|
if (deviceRelations->Count == 1) {
|
|||
|
|
|||
|
targetDeviceObject = deviceRelations->Objects[0];
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
targetDeviceObject = NULL;
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(deviceRelations);
|
|||
|
|
|||
|
if (targetDeviceObject) {
|
|||
|
|
|||
|
targetDeviceNode = (PDEVICE_NODE) targetDeviceObject->DeviceObjectExtension->DeviceNode;
|
|||
|
if (targetDeviceNode) {
|
|||
|
|
|||
|
*DeviceNode = targetDeviceNode;
|
|||
|
return status;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Definite driver screw up. If the verifier is enabled we will fail the
|
|||
|
// driver. Otherwise, we will ignore this. Note that we would have crashed
|
|||
|
// in Win2K!
|
|||
|
//
|
|||
|
PpvUtilFailDriver(
|
|||
|
PPVERROR_MISHANDLED_TARGET_DEVICE_RELATIONS,
|
|||
|
(PVOID) deviceObject->DriverObject->MajorFunction[IRP_MJ_PNP],
|
|||
|
deviceObject,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
*DeviceNode = NULL;
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IoGetRelatedTargetDevice(
|
|||
|
IN PFILE_OBJECT FileObject,
|
|||
|
OUT PDEVICE_OBJECT *DeviceObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
IoGetRelatedTargetDevice retrieves the device object associated with
|
|||
|
the specified file object and then sends a query device relations irp
|
|||
|
to that device object.
|
|||
|
|
|||
|
NOTE: The PDO associated with the returned device node has been referenced,
|
|||
|
and must be dereferenced when no longer needed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FileObject - Specifies the file object that is associated with the device
|
|||
|
object that will receive the query device relations irp.
|
|||
|
|
|||
|
DeviceObject - Returns the related target device object.
|
|||
|
|
|||
|
ReturnValue
|
|||
|
|
|||
|
Returns an NTSTATUS value.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_NODE deviceNode = NULL;
|
|||
|
|
|||
|
status = IopGetRelatedTargetDevice( FileObject, &deviceNode );
|
|||
|
if (NT_SUCCESS(status) && deviceNode != NULL) {
|
|||
|
*DeviceObject = deviceNode->PhysicalDeviceObject;
|
|||
|
}
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopNotifySetupDeviceArrival(
|
|||
|
PDEVICE_OBJECT PhysicalDeviceObject, // PDO of the device
|
|||
|
HANDLE EnumEntryKey, // Handle into the enum branch of the registry for this device
|
|||
|
BOOLEAN InstallDriver
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to notify setup (during text-mode setup) of arrivals
|
|||
|
of a particular device. It does not return until all interested parties have
|
|||
|
been notified.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
PhysicalDeviceObject - Supplies a pointer to the PDO of the newly arrived
|
|||
|
device.
|
|||
|
|
|||
|
EnumEntryKey - Supplies a handle to the key associated with the devide under
|
|||
|
the Enum\ branch of the registry. Can be NULL in which case the key
|
|||
|
will be opened here.
|
|||
|
|
|||
|
InstallDriver - Indicates whether setup should attempt to install a driver
|
|||
|
for this object. Device objects created through
|
|||
|
IoReportDetectedDevice() already have a driver but we want
|
|||
|
to indicate them to setup anyway.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
The contents of the notification structure *including* all pointers is only
|
|||
|
valid during the callback routine to which it was passed. If the data is
|
|||
|
required after the duration of the callback then it must be physically copied
|
|||
|
by the callback routine.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status, dispatchStatus;
|
|||
|
SETUP_DEVICE_ARRIVAL_NOTIFICATION notification;
|
|||
|
PDEVICE_NODE deviceNode;
|
|||
|
HANDLE enumKey = NULL;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Only perform notifications if someone has registered
|
|||
|
//
|
|||
|
|
|||
|
if (IopSetupNotifyData) {
|
|||
|
|
|||
|
if (!EnumEntryKey) {
|
|||
|
status = IopDeviceObjectToDeviceInstance(PhysicalDeviceObject,
|
|||
|
&enumKey,
|
|||
|
KEY_WRITE);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
EnumEntryKey = enumKey;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the notification structure
|
|||
|
//
|
|||
|
notification.Version = PNP_NOTIFICATION_VERSION;
|
|||
|
notification.Size = sizeof(SETUP_DEVICE_ARRIVAL_NOTIFICATION);
|
|||
|
notification.Event = GUID_SETUP_DEVICE_ARRIVAL;
|
|||
|
notification.PhysicalDeviceObject = PhysicalDeviceObject;
|
|||
|
notification.EnumEntryKey = EnumEntryKey;
|
|||
|
deviceNode = (PDEVICE_NODE) PhysicalDeviceObject->DeviceObjectExtension->DeviceNode;
|
|||
|
notification.EnumPath = &deviceNode->InstancePath;
|
|||
|
notification.InstallDriver = InstallDriver;
|
|||
|
|
|||
|
//
|
|||
|
// Note that the only setup notification callback currently supported
|
|||
|
// (setupdd.sys) is never in session space.
|
|||
|
//
|
|||
|
ASSERT(MmIsSessionAddress((PVOID)(IopSetupNotifyData->CallbackRoutine)) == FALSE);
|
|||
|
ASSERT(IopSetupNotifyData->SessionId == 0);
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch the notification to the callback routine for the
|
|||
|
// appropriate session.
|
|||
|
//
|
|||
|
dispatchStatus = PiNotifyDriverCallback(IopSetupNotifyData->CallbackRoutine,
|
|||
|
¬ification,
|
|||
|
IopSetupNotifyData->Context,
|
|||
|
IopSetupNotifyData->SessionId,
|
|||
|
IopSetupNotifyData->OpaqueSession,
|
|||
|
&status);
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(dispatchStatus));
|
|||
|
|
|||
|
//
|
|||
|
// Failure to dispatch setup notification should be reported as if a
|
|||
|
// match was not found, because the device has not been setup.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(dispatchStatus)) {
|
|||
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
if (enumKey) {
|
|||
|
ZwClose(enumKey);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IoNotifyPowerOperationVetoed(
|
|||
|
IN POWER_ACTION VetoedPowerOperation,
|
|||
|
IN PDEVICE_OBJECT TargetedDeviceObject OPTIONAL,
|
|||
|
IN PDEVICE_OBJECT VetoingDeviceObject
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called by the power subsystem to initiate user-mode
|
|||
|
notification of vetoed system power events. The power events are submitted
|
|||
|
into a serialized asynchronous queue. This queue is processed by a work
|
|||
|
item. This routine does not wait for the event to be processed.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
VetoedPowerOperation - Specifies the system-wide power action that was
|
|||
|
vetoed.
|
|||
|
|
|||
|
TargetedDeviceObject - Optionally, supplies the device object target of the
|
|||
|
vetoed operation.
|
|||
|
|
|||
|
VetoingDeviceObject - Specifies the device object responsible for vetoing
|
|||
|
the power operation.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the event was successfully
|
|||
|
inserted into the asynchronous event queue..
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_NODE deviceNode, vetoingDeviceNode;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// We have two types of power events, system wide (standby) and device
|
|||
|
// targetted (warm eject). Rather than have two different veto mechanisms,
|
|||
|
// we just retarget the operation against the root device if none is
|
|||
|
// specified (hey, someone's gotta represent the system, right?).
|
|||
|
//
|
|||
|
if (TargetedDeviceObject) {
|
|||
|
|
|||
|
deviceObject = TargetedDeviceObject;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
deviceObject = IopRootDeviceNode->PhysicalDeviceObject;
|
|||
|
}
|
|||
|
|
|||
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|||
|
if (!deviceNode) {
|
|||
|
return STATUS_INVALID_PARAMETER_2;
|
|||
|
}
|
|||
|
|
|||
|
vetoingDeviceNode = (PDEVICE_NODE)VetoingDeviceObject->DeviceObjectExtension->DeviceNode;
|
|||
|
if (!vetoingDeviceNode) {
|
|||
|
return STATUS_INVALID_PARAMETER_3;
|
|||
|
}
|
|||
|
|
|||
|
return PpSetPowerVetoEvent(
|
|||
|
VetoedPowerOperation,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
deviceObject,
|
|||
|
PNP_VetoDevice,
|
|||
|
&vetoingDeviceNode->InstancePath
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
IoPnPDeliverServicePowerNotification(
|
|||
|
IN POWER_ACTION PowerOperation,
|
|||
|
IN ULONG PowerNotificationCode,
|
|||
|
IN ULONG PowerNotificationData,
|
|||
|
IN BOOLEAN Synchronous
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called by the win32k driver to notify user-mode services of
|
|||
|
system power events. The power events are submitted into a serialized
|
|||
|
asynchronous queue. This queue is processed by a work item.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
PowerOperation - Specifies the system-wide power action that has occured.
|
|||
|
If the Synchronous parameter is TRUE, the event is a query for
|
|||
|
permission to perform the supplied power operation.
|
|||
|
|
|||
|
PowerNotificationCode - 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.)
|
|||
|
|
|||
|
PowerNotificationData - 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.)
|
|||
|
|
|||
|
Synchronous - Specifies whether this is a query operation. If the event is
|
|||
|
a query, this routine will wait for the result of the query before
|
|||
|
returning. If the query event is unsuccessful, this routine will
|
|||
|
initiate an appropriate veto event.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns a non-zero value if the event was successful, zero otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
KEVENT completionEvent;
|
|||
|
NTSTATUS completionStatus=STATUS_SUCCESS;
|
|||
|
PNP_VETO_TYPE vetoType = PNP_VetoTypeUnknown;
|
|||
|
UNICODE_STRING vetoName;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (Synchronous) {
|
|||
|
|
|||
|
vetoName.Buffer = ExAllocatePool (PagedPool,MAX_VETO_NAME_LENGTH*sizeof (WCHAR));
|
|||
|
|
|||
|
if (vetoName.Buffer) {
|
|||
|
vetoName.MaximumLength = MAX_VETO_NAME_LENGTH;
|
|||
|
}else {
|
|||
|
vetoName.MaximumLength = 0;
|
|||
|
}
|
|||
|
vetoName.Length = 0;
|
|||
|
|
|||
|
KeInitializeEvent(&completionEvent, NotificationEvent, FALSE);
|
|||
|
|
|||
|
status = PpSetPowerEvent(PowerNotificationCode,
|
|||
|
PowerNotificationData,
|
|||
|
&completionEvent,
|
|||
|
&completionStatus,
|
|||
|
&vetoType,
|
|||
|
&vetoName);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// PpSetPowerEvent returns success immediately after the event has
|
|||
|
// been successfully inserted into the event queue. Queued power
|
|||
|
// events are sent to user-mode via PiNotifyUserMode, which waits
|
|||
|
// for the the result. PiNotifyUserMode signals the completionEvent
|
|||
|
// below when the user response is received.
|
|||
|
//
|
|||
|
KeWaitForSingleObject( &completionEvent, Executive, KernelMode, FALSE, NULL );
|
|||
|
status = completionStatus;
|
|||
|
|
|||
|
//
|
|||
|
// We only have power event veto information to report if
|
|||
|
// user-mode responded to the event with failure.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(completionStatus)) {
|
|||
|
//
|
|||
|
// PpSetPowerVetoEvent requires a device object as the target of
|
|||
|
// the vetoed power operation. Since this is a system-wide
|
|||
|
// event, we just target the operation against the root device.
|
|||
|
//
|
|||
|
PpSetPowerVetoEvent(PowerOperation,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
IopRootDeviceNode->PhysicalDeviceObject,
|
|||
|
vetoType,
|
|||
|
&vetoName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (vetoName.Buffer) {
|
|||
|
ExFreePool (vetoName.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// No response is required for 'asynchronous' (non-query) events.
|
|||
|
// Just set the event and go.
|
|||
|
//
|
|||
|
status = PpSetPowerEvent(PowerNotificationCode,
|
|||
|
PowerNotificationData,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Since the user-mode power notification routine only returns a BOOLEAN
|
|||
|
// success value, PiNotifyUserMode only returns one of the following status
|
|||
|
// values:
|
|||
|
//
|
|||
|
ASSERT ((completionStatus == STATUS_SUCCESS) ||
|
|||
|
(completionStatus == STATUS_UNSUCCESSFUL));
|
|||
|
|
|||
|
//
|
|||
|
// The private code in Win32k that calls this, assumes that 0 is failure, !0 is success
|
|||
|
//
|
|||
|
return (NT_SUCCESS(completionStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
IopOrphanNotification(
|
|||
|
IN PDEVICE_NODE TargetNode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine releases the references to the device object for all the
|
|||
|
notifications entries of a device object, then fixes up the notification
|
|||
|
node to not point to a physical device object.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
TargetNode - Specifies the device node whose registered target device
|
|||
|
notification recipients are to be orphaned.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
The notification node will be released when IoUnregisterPlugPlayNotification
|
|||
|
is actually called, but the device object will already be gone.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PTARGET_DEVICE_NOTIFY_ENTRY entry;
|
|||
|
|
|||
|
IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
|
|||
|
while (!IsListEmpty(&TargetNode->TargetDeviceNotify)) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove all target device change notification entries for this devnode
|
|||
|
//
|
|||
|
|
|||
|
entry = (PTARGET_DEVICE_NOTIFY_ENTRY)
|
|||
|
RemoveHeadList(&TargetNode->TargetDeviceNotify);
|
|||
|
|
|||
|
ASSERT(entry->EventCategory == EventCategoryTargetDeviceChange);
|
|||
|
|
|||
|
//
|
|||
|
// Re-initialize the orphaned list entry so we don't attempt to remove
|
|||
|
// it from the list again.
|
|||
|
//
|
|||
|
|
|||
|
InitializeListHead((PLIST_ENTRY)entry);
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the target device object, and NULL it out so we don't
|
|||
|
// attempt to dereference it when the entry is actually unregistered.
|
|||
|
//
|
|||
|
|
|||
|
if (entry->PhysicalDeviceObject) {
|
|||
|
ObDereferenceObject(entry->PhysicalDeviceObject);
|
|||
|
entry->PhysicalDeviceObject = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PiNotifyDriverCallback(
|
|||
|
IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
|
|||
|
IN PVOID NotificationStructure,
|
|||
|
IN PVOID Context,
|
|||
|
IN ULONG SessionId,
|
|||
|
IN PVOID OpaqueSession OPTIONAL,
|
|||
|
OUT PNTSTATUS CallbackStatus OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine dispatches a plug and play notification event to a specified
|
|||
|
callback routine.
|
|||
|
|
|||
|
If the callback routine specifies an address outside of session space, or if
|
|||
|
the calling process is already in the context of the specified session, it
|
|||
|
will call the callback routine directly.
|
|||
|
|
|||
|
Otherwise, this routine will attempt to attach to the specified session and
|
|||
|
call the callback routine.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
CallbackRoutine - Entry point within the driver that will be called with
|
|||
|
information about the event that has occured.
|
|||
|
|
|||
|
NotificationStructure - Contains information about the event.
|
|||
|
|
|||
|
Context - Points to the context data supplied at registration.
|
|||
|
|
|||
|
SessionId - Specifies the ID of the session in which the specified
|
|||
|
callback is to be called.
|
|||
|
|
|||
|
OpqueSession - Optionally, specifies the opaque handle to the session that
|
|||
|
to attach to when the specified callback is called.
|
|||
|
|
|||
|
CallbackStatus - Optionally, supplies the address of a variable to receive
|
|||
|
the NTSTATUS code returned by the callback routine.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status code that indicates whether or not the function was successful.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Returns STATUS_NOT_FOUND if the specified session was not found.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status, CallStatus;
|
|||
|
KAPC_STATE ApcState;
|
|||
|
#if DBG
|
|||
|
KIRQL Irql;
|
|||
|
ULONG ApcDisable;
|
|||
|
#endif
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we have all the information we need to deliver notification.
|
|||
|
//
|
|||
|
if (!ARGUMENT_PRESENT(CallbackRoutine) ||
|
|||
|
!ARGUMENT_PRESENT(NotificationStructure)) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
//
|
|||
|
// Remember the current IRQL and ApcDisable count so we can make sure
|
|||
|
// the callback routine returns with these in tact.
|
|||
|
//
|
|||
|
Irql = KeGetCurrentIrql();
|
|||
|
ApcDisable = KeGetCurrentThread()->KernelApcDisable;
|
|||
|
#endif // DBG
|
|||
|
|
|||
|
if ((OpaqueSession == NULL) ||
|
|||
|
((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) &&
|
|||
|
(SessionId == PsGetCurrentProcessSessionId()))) {
|
|||
|
//
|
|||
|
// No session object was specified, or the current process is already in
|
|||
|
// the specified session, so just call the callback routine directly.
|
|||
|
//
|
|||
|
ASSERT(!MmIsSessionAddress((PVOID)CallbackRoutine) || OpaqueSession);
|
|||
|
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_TRACE_LEVEL,
|
|||
|
"PiNotifyDriverCallback: "
|
|||
|
"calling notification callback @ 0x%p directly\n",
|
|||
|
CallbackRoutine));
|
|||
|
|
|||
|
CallStatus = (CallbackRoutine)(NotificationStructure,
|
|||
|
Context);
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(CallbackStatus)) {
|
|||
|
*CallbackStatus = CallStatus;
|
|||
|
}
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Otherwise, call the callback routine in session space.
|
|||
|
//
|
|||
|
ASSERT(MmIsSessionAddress((PVOID)CallbackRoutine));
|
|||
|
|
|||
|
//
|
|||
|
// Attach to the specified session.
|
|||
|
//
|
|||
|
Status = MmAttachSession(OpaqueSession, &ApcState);
|
|||
|
ASSERT(NT_SUCCESS(Status));
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
//
|
|||
|
// Dispatch notification to the callback routine.
|
|||
|
//
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_TRACE_LEVEL,
|
|||
|
"PiNotifyDriverCallback: "
|
|||
|
"calling notification callback @ 0x%p for SessionId %d\n",
|
|||
|
CallbackRoutine,
|
|||
|
SessionId));
|
|||
|
|
|||
|
CallStatus = (CallbackRoutine)(NotificationStructure,
|
|||
|
Context);
|
|||
|
|
|||
|
//
|
|||
|
// Return the callback status.
|
|||
|
//
|
|||
|
if (ARGUMENT_PRESENT(CallbackStatus)) {
|
|||
|
*CallbackStatus = CallStatus;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Detach from the session.
|
|||
|
//
|
|||
|
Status = MmDetachSession(OpaqueSession, &ApcState);
|
|||
|
ASSERT(NT_SUCCESS(Status));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
//
|
|||
|
// Check the IRQL and ApcDisable count.
|
|||
|
//
|
|||
|
if (Irql != KeGetCurrentIrql()) {
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_ERROR_LEVEL,
|
|||
|
"PiNotifyDriverCallback: "
|
|||
|
"notification handler @ 0x%p returned at raised IRQL = %d, original = %d\n",
|
|||
|
CallbackRoutine,
|
|||
|
KeGetCurrentIrql(),
|
|||
|
Irql));
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
if (ApcDisable != KeGetCurrentThread()->KernelApcDisable) {
|
|||
|
IopDbgPrint((
|
|||
|
IOP_IOEVENT_ERROR_LEVEL,
|
|||
|
"PiNotifyDriverCallback: "
|
|||
|
"notification handler @ 0x%p returned with different KernelApcDisable = %d, original = %d\n",
|
|||
|
CallbackRoutine,
|
|||
|
KeGetCurrentThread()->KernelApcDisable,
|
|||
|
ApcDisable));
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
#endif // DBG
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|