mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 13:01:40 +00:00
390 lines
13 KiB
C
390 lines
13 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/io/pnpmgr/pnpnotify.c
|
|
* PURPOSE: Plug & Play notification functions
|
|
* PROGRAMMERS: Filip Navara (xnavara@volny.cz)
|
|
* Hervé Poussineau (hpoussin@reactos.org)
|
|
* Pierre Schweitzer
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* TYPES *******************************************************************/
|
|
|
|
typedef struct _PNP_NOTIFY_ENTRY
|
|
{
|
|
LIST_ENTRY PnpNotifyList;
|
|
IO_NOTIFICATION_EVENT_CATEGORY EventCategory;
|
|
PVOID Context;
|
|
UNICODE_STRING Guid;
|
|
PFILE_OBJECT FileObject;
|
|
PDRIVER_OBJECT DriverObject;
|
|
PDRIVER_NOTIFICATION_CALLBACK_ROUTINE PnpNotificationProc;
|
|
} PNP_NOTIFY_ENTRY, *PPNP_NOTIFY_ENTRY;
|
|
|
|
KGUARDED_MUTEX PnpNotifyListLock;
|
|
LIST_ENTRY PnpNotifyListHead;
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
VOID
|
|
IopNotifyPlugPlayNotification(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
|
|
IN LPCGUID Event,
|
|
IN PVOID EventCategoryData1,
|
|
IN PVOID EventCategoryData2)
|
|
{
|
|
PPNP_NOTIFY_ENTRY ChangeEntry;
|
|
PLIST_ENTRY ListEntry;
|
|
PVOID NotificationStructure;
|
|
BOOLEAN CallCurrentEntry;
|
|
UNICODE_STRING GuidString;
|
|
NTSTATUS Status;
|
|
PDEVICE_OBJECT EntryDeviceObject = NULL;
|
|
|
|
ASSERT(DeviceObject);
|
|
|
|
KeAcquireGuardedMutex(&PnpNotifyListLock);
|
|
if (IsListEmpty(&PnpNotifyListHead))
|
|
{
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
return;
|
|
}
|
|
|
|
switch (EventCategory)
|
|
{
|
|
case EventCategoryDeviceInterfaceChange:
|
|
{
|
|
PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos;
|
|
NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION),
|
|
TAG_PNP_NOTIFY);
|
|
if (!NotificationInfos)
|
|
{
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
return;
|
|
}
|
|
NotificationInfos->Version = 1;
|
|
NotificationInfos->Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
|
|
RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
|
|
RtlCopyMemory(&NotificationInfos->InterfaceClassGuid, EventCategoryData1, sizeof(GUID));
|
|
NotificationInfos->SymbolicLinkName = (PUNICODE_STRING)EventCategoryData2;
|
|
Status = RtlStringFromGUID(&NotificationInfos->InterfaceClassGuid, &GuidString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
ExFreePoolWithTag(NotificationStructure, TAG_PNP_NOTIFY);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case EventCategoryHardwareProfileChange:
|
|
{
|
|
PHWPROFILE_CHANGE_NOTIFICATION NotificationInfos;
|
|
NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(HWPROFILE_CHANGE_NOTIFICATION),
|
|
TAG_PNP_NOTIFY);
|
|
if (!NotificationInfos)
|
|
{
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
return;
|
|
}
|
|
NotificationInfos->Version = 1;
|
|
NotificationInfos->Size = sizeof(HWPROFILE_CHANGE_NOTIFICATION);
|
|
RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
|
|
break;
|
|
}
|
|
case EventCategoryTargetDeviceChange:
|
|
{
|
|
if (Event != &GUID_PNP_CUSTOM_NOTIFICATION)
|
|
{
|
|
PTARGET_DEVICE_REMOVAL_NOTIFICATION NotificationInfos;
|
|
NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION),
|
|
TAG_PNP_NOTIFY);
|
|
if (!NotificationInfos)
|
|
{
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
return;
|
|
}
|
|
NotificationInfos->Version = 1;
|
|
NotificationInfos->Size = sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION);
|
|
RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
|
|
}
|
|
else
|
|
{
|
|
PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationInfos;
|
|
NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION),
|
|
TAG_PNP_NOTIFY);
|
|
if (!NotificationInfos)
|
|
{
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
return;
|
|
}
|
|
RtlCopyMemory(NotificationInfos, EventCategoryData1, sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION));
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Loop through procedures registred in PnpNotifyListHead
|
|
* list to find those that meet some criteria.
|
|
*/
|
|
ListEntry = PnpNotifyListHead.Flink;
|
|
while (ListEntry != &PnpNotifyListHead)
|
|
{
|
|
ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY, PnpNotifyList);
|
|
CallCurrentEntry = FALSE;
|
|
|
|
if (ChangeEntry->EventCategory != EventCategory)
|
|
{
|
|
ListEntry = ListEntry->Flink;
|
|
continue;
|
|
}
|
|
|
|
switch (EventCategory)
|
|
{
|
|
case EventCategoryDeviceInterfaceChange:
|
|
{
|
|
if (RtlCompareUnicodeString(&ChangeEntry->Guid, &GuidString, FALSE) == 0)
|
|
{
|
|
CallCurrentEntry = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
case EventCategoryHardwareProfileChange:
|
|
{
|
|
CallCurrentEntry = TRUE;
|
|
break;
|
|
}
|
|
case EventCategoryTargetDeviceChange:
|
|
{
|
|
Status = IoGetRelatedTargetDevice(ChangeEntry->FileObject, &EntryDeviceObject);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (DeviceObject == EntryDeviceObject)
|
|
{
|
|
if (Event == &GUID_PNP_CUSTOM_NOTIFICATION)
|
|
{
|
|
((PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure)->FileObject = ChangeEntry->FileObject;
|
|
}
|
|
else
|
|
{
|
|
((PTARGET_DEVICE_REMOVAL_NOTIFICATION)NotificationStructure)->FileObject = ChangeEntry->FileObject;
|
|
}
|
|
CallCurrentEntry = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Move to the next element now, as callback may unregister itself */
|
|
ListEntry = ListEntry->Flink;
|
|
/* FIXME: If ListEntry was the last element and that callback registers
|
|
* new notifications, those won't be checked... */
|
|
|
|
if (CallCurrentEntry)
|
|
{
|
|
/* Call entry into new allocated memory */
|
|
DPRINT("IopNotifyPlugPlayNotification(): found suitable callback %p\n",
|
|
ChangeEntry);
|
|
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
(ChangeEntry->PnpNotificationProc)(NotificationStructure,
|
|
ChangeEntry->Context);
|
|
KeAcquireGuardedMutex(&PnpNotifyListLock);
|
|
}
|
|
|
|
}
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
ExFreePoolWithTag(NotificationStructure, TAG_PNP_NOTIFY);
|
|
if (EventCategory == EventCategoryDeviceInterfaceChange)
|
|
RtlFreeUnicodeString(&GuidString);
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
ULONG
|
|
NTAPI
|
|
IoPnPDeliverServicePowerNotification(ULONG VetoedPowerOperation OPTIONAL,
|
|
ULONG PowerNotification,
|
|
ULONG Unknown OPTIONAL,
|
|
BOOLEAN Synchronous)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
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)
|
|
{
|
|
PPNP_NOTIFY_ENTRY Entry;
|
|
PWSTR SymbolicLinkList;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
DPRINT("%s(EventCategory 0x%x, EventCategoryFlags 0x%lx, DriverObject %p) called.\n",
|
|
__FUNCTION__,
|
|
EventCategory,
|
|
EventCategoryFlags,
|
|
DriverObject);
|
|
|
|
ObReferenceObject(DriverObject);
|
|
|
|
/* Try to allocate entry for notification before sending any notification */
|
|
Entry = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(PNP_NOTIFY_ENTRY),
|
|
TAG_PNP_NOTIFY);
|
|
|
|
if (!Entry)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
ObDereferenceObject(DriverObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (EventCategory == EventCategoryDeviceInterfaceChange &&
|
|
EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES)
|
|
{
|
|
DEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos;
|
|
UNICODE_STRING SymbolicLinkU;
|
|
PWSTR SymbolicLink;
|
|
|
|
Status = IoGetDeviceInterfaces((LPGUID)EventCategoryData,
|
|
NULL, /* PhysicalDeviceObject OPTIONAL */
|
|
0, /* Flags */
|
|
&SymbolicLinkList);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Enumerate SymbolicLinkList */
|
|
NotificationInfos.Version = 1;
|
|
NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
|
|
RtlCopyMemory(&NotificationInfos.Event,
|
|
&GUID_DEVICE_INTERFACE_ARRIVAL,
|
|
sizeof(GUID));
|
|
RtlCopyMemory(&NotificationInfos.InterfaceClassGuid,
|
|
EventCategoryData,
|
|
sizeof(GUID));
|
|
NotificationInfos.SymbolicLinkName = &SymbolicLinkU;
|
|
|
|
for (SymbolicLink = SymbolicLinkList;
|
|
*SymbolicLink;
|
|
SymbolicLink += wcslen(SymbolicLink) + 1)
|
|
{
|
|
RtlInitUnicodeString(&SymbolicLinkU, SymbolicLink);
|
|
DPRINT("Calling callback routine for %S\n", SymbolicLink);
|
|
(*CallbackRoutine)(&NotificationInfos, Context);
|
|
}
|
|
|
|
ExFreePool(SymbolicLinkList);
|
|
}
|
|
}
|
|
|
|
Entry->PnpNotificationProc = CallbackRoutine;
|
|
Entry->EventCategory = EventCategory;
|
|
Entry->Context = Context;
|
|
Entry->DriverObject = DriverObject;
|
|
switch (EventCategory)
|
|
{
|
|
case EventCategoryDeviceInterfaceChange:
|
|
{
|
|
Status = RtlStringFromGUID(EventCategoryData, &Entry->Guid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY);
|
|
ObDereferenceObject(DriverObject);
|
|
return Status;
|
|
}
|
|
break;
|
|
}
|
|
case EventCategoryHardwareProfileChange:
|
|
{
|
|
/* nothing to do */
|
|
break;
|
|
}
|
|
case EventCategoryTargetDeviceChange:
|
|
{
|
|
Entry->FileObject = (PFILE_OBJECT)EventCategoryData;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DPRINT1("%s: unknown EventCategory 0x%x UNIMPLEMENTED\n",
|
|
__FUNCTION__, EventCategory);
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeAcquireGuardedMutex(&PnpNotifyListLock);
|
|
InsertHeadList(&PnpNotifyListHead,
|
|
&Entry->PnpNotifyList);
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
|
|
DPRINT("%s returns NotificationEntry %p\n", __FUNCTION__, Entry);
|
|
|
|
*NotificationEntry = Entry;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoUnregisterPlugPlayNotification(IN PVOID NotificationEntry)
|
|
{
|
|
PPNP_NOTIFY_ENTRY Entry;
|
|
PAGED_CODE();
|
|
|
|
Entry = (PPNP_NOTIFY_ENTRY)NotificationEntry;
|
|
DPRINT("%s(NotificationEntry %p) called\n", __FUNCTION__, Entry);
|
|
|
|
KeAcquireGuardedMutex(&PnpNotifyListLock);
|
|
RemoveEntryList(&Entry->PnpNotifyList);
|
|
KeReleaseGuardedMutex(&PnpNotifyListLock);
|
|
|
|
RtlFreeUnicodeString(&Entry->Guid);
|
|
|
|
ObDereferenceObject(Entry->DriverObject);
|
|
|
|
ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|