ReactOS  0.4.15-dev-2536-g90b3a9c
pnpnotify.c
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS Kernel
3  * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE: Plug & Play notification functions
5  * COPYRIGHT: Copyright 2003 Filip Navara <xnavara@volny.cz>
6  * Copyright 2005-2006 HervĂ© Poussineau <hpoussin@reactos.org>
7  * Copyright 2010 Pierre Schweitzer <pierre@reactos.org>
8  * Copyright 2020 Victor Perevertkin <victor.perevertkin@reactos.org>
9  */
10 
11 /* INCLUDES ******************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* DATA **********************************************************************/
18 
22 
24 LIST_ENTRY PiNotifyHwProfileListHead;
25 
27 LIST_ENTRY PiNotifyDeviceInterfaceListHead;
28 
29 /* TYPES *********************************************************************/
30 
31 typedef struct _PNP_NOTIFY_ENTRY
32 {
33  LIST_ENTRY PnpNotifyList;
34  PVOID Context;
36  PDRIVER_NOTIFICATION_CALLBACK_ROUTINE PnpNotificationProc;
37  union
38  {
39  GUID Guid; // for EventCategoryDeviceInterfaceChange
40  struct
41  {
42  PFILE_OBJECT FileObject; // for EventCategoryTargetDeviceChange
44  };
45  };
46  IO_NOTIFICATION_EVENT_CATEGORY EventCategory;
47  UINT8 RefCount;
50 
51 /* FUNCTIONS *****************************************************************/
52 
53 CODE_SEG("INIT")
54 VOID
56 {
60  InitializeListHead(&PiNotifyHwProfileListHead);
61  InitializeListHead(&PiNotifyDeviceInterfaceListHead);
62 }
63 
64 static
65 CODE_SEG("PAGE")
66 VOID
69 {
70  PAGED_CODE();
71  ASSERT(Entry->RefCount > 0);
72 
73  ObDereferenceObject(Entry->DriverObject);
74  Entry->RefCount--;
75  if (Entry->RefCount == 0)
76  {
77  ASSERT(Entry->Deleted);
78 
79  RemoveEntryList(&Entry->PnpNotifyList);
80  if (Entry->EventCategory == EventCategoryTargetDeviceChange)
81  {
82  // IopGetRelatedTargetDevice referenced the device upon the notification registration
83  ObDereferenceObject(Entry->DeviceObject);
84  }
86  }
87 }
88 
89 static
90 CODE_SEG("PAGE")
91 VOID
94 {
95  PAGED_CODE();
96  ObReferenceObject(Entry->DriverObject);
97  Entry->RefCount++;
98 }
99 
103 static
104 CODE_SEG("PAGE")
105 VOID
110 {
111  PAGED_CODE();
112 #if DBG
113  KIRQL oldIrql = KeGetCurrentIrql();
114  ULONG oldApcDisable = KeGetCurrentThread()->CombinedApcDisable;
115 #endif
116 
118 
119  ASSERT(oldIrql == KeGetCurrentIrql() &&
120  oldApcDisable == KeGetCurrentThread()->CombinedApcDisable);
121 }
122 
123 static
124 CODE_SEG("PAGE")
126 VOID
127 PiProcessSingleNotification(
131  _Out_ PLIST_ENTRY *NextEntry)
132 {
133  PAGED_CODE();
134 
135  // the notification may be unregistered inside the procedure
136  // thus reference the entry so we may proceed
138 
139  // release the lock because the notification routine has to be called without any
140  // limitations regarding APCs
142  PiCallNotifyProc(Entry->PnpNotificationProc, NotificationStructure, Entry->Context);
144 
145  // take the next entry link only after the callback finishes
146  // the lock is not held there, so Entry may have changed at this point
147  *NextEntry = Entry->PnpNotifyList.Flink;
149 }
150 
159 CODE_SEG("PAGE")
160 VOID
165 {
166  PAGED_CODE();
167 
169  notifyStruct = ExAllocatePoolWithTag(PagedPool, sizeof(*notifyStruct), TAG_PNP_NOTIFY);
170  if (!notifyStruct)
171  {
172  return;
173  }
174 
175  *notifyStruct = (DEVICE_INTERFACE_CHANGE_NOTIFICATION) {
176  .Version = 1,
177  .Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION),
178  .Event = *Event,
181  };
182 
183  DPRINT("Delivering a DeviceInterfaceChange PnP event\n");
184 
186 
187  PLIST_ENTRY entry = PiNotifyDeviceInterfaceListHead.Flink;
188  while (entry != &PiNotifyDeviceInterfaceListHead)
189  {
190  PPNP_NOTIFY_ENTRY nEntry = CONTAINING_RECORD(entry, PNP_NOTIFY_ENTRY, PnpNotifyList);
191 
192  if (!IsEqualGUID(&notifyStruct->InterfaceClassGuid, &nEntry->Guid))
193  {
194  entry = entry->Flink;
195  continue;
196  }
197 
198  PiProcessSingleNotification(nEntry, notifyStruct, &PiNotifyDeviceInterfaceLock, &entry);
199  }
200 
202  ExFreePoolWithTag(notifyStruct, TAG_PNP_NOTIFY);
203 }
204 
205 
212 CODE_SEG("PAGE")
213 VOID
216 {
217  PAGED_CODE();
218 
219  PHWPROFILE_CHANGE_NOTIFICATION notifyStruct;
220  notifyStruct = ExAllocatePoolWithTag(PagedPool, sizeof(*notifyStruct), TAG_PNP_NOTIFY);
221  if (!notifyStruct)
222  {
223  return;
224  }
225 
226  *notifyStruct = (HWPROFILE_CHANGE_NOTIFICATION) {
227  .Version = 1,
228  .Size = sizeof(HWPROFILE_CHANGE_NOTIFICATION),
229  .Event = *Event
230  };
231 
232  DPRINT("Delivering a HardwareProfileChange PnP event\n");
233 
235 
236  PLIST_ENTRY entry = PiNotifyHwProfileListHead.Flink;
237  while (entry != &PiNotifyHwProfileListHead)
238  {
239  PPNP_NOTIFY_ENTRY nEntry = CONTAINING_RECORD(entry, PNP_NOTIFY_ENTRY, PnpNotifyList);
240 
241  PiProcessSingleNotification(nEntry, notifyStruct, &PiNotifyHwProfileLock, &entry);
242  }
243 
245  ExFreePoolWithTag(notifyStruct, TAG_PNP_NOTIFY);
246 }
247 
256 CODE_SEG("PAGE")
257 VOID
261  _In_opt_ PTARGET_DEVICE_CUSTOM_NOTIFICATION CustomNotification)
262 {
263  PAGED_CODE();
264 
265  PVOID notificationStruct;
266  // just in case our device is removed during the operation
268 
270  ASSERT(deviceNode);
271 
272  if (!IsEqualGUID(Event, &GUID_PNP_CUSTOM_NOTIFICATION))
273  {
275  notifStruct = ExAllocatePoolWithTag(PagedPool, sizeof(*notifStruct), TAG_PNP_NOTIFY);
276  if (!notifStruct)
277  {
278  return;
279  }
280 
281  *notifStruct = (TARGET_DEVICE_REMOVAL_NOTIFICATION) {
282  .Version = 1,
283  .Size = sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION),
284  .Event = *Event
285  };
286 
287  notificationStruct = notifStruct;
288 
289  DPRINT("Delivering a (non-custom) TargetDeviceChange PnP event\n");
290  }
291  else
292  {
293  ASSERT(CustomNotification);
294  // assuming everythng else is correct
295 
296  notificationStruct = CustomNotification;
297 
298  DPRINT("Delivering a (custom) TargetDeviceChange PnP event\n");
299  }
300 
302 
304  while (entry != &deviceNode->TargetDeviceNotify)
305  {
306  PPNP_NOTIFY_ENTRY nEntry = CONTAINING_RECORD(entry, PNP_NOTIFY_ENTRY, PnpNotifyList);
307 
308  // put the file object from our saved entry to this particular notification's struct
309  ((PTARGET_DEVICE_REMOVAL_NOTIFICATION)notificationStruct)->FileObject = nEntry->FileObject;
310  // so you don't need to look at the definition ;)
313 
314  PiProcessSingleNotification(nEntry, notificationStruct, &PiNotifyTargetDeviceLock, &entry);
315  }
316 
318  ExFreePoolWithTag(notificationStruct, TAG_PNP_NOTIFY);
320 }
321 
322 /* PUBLIC FUNCTIONS **********************************************************/
323 
324 /*
325  * @unimplemented
326  */
327 ULONG
328 NTAPI
330  _In_ ULONG VetoedPowerOperation,
331  _In_ ULONG PowerNotificationCode,
332  _In_ ULONG PowerNotificationData,
333  _In_ BOOLEAN Synchronous)
334 {
336  return 0;
337 }
338 
339 /*
340  * @implemented
341  */
342 CODE_SEG("PAGE")
343 NTSTATUS
344 NTAPI
346  _In_ IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
353 {
357  PAGED_CODE();
358 
359  DPRINT("%s(EventCategory 0x%x, EventCategoryFlags 0x%lx, DriverObject %p) called.\n",
360  __FUNCTION__, EventCategory, EventCategoryFlags, DriverObject);
361 
363 
364  /* Try to allocate entry for notification before sending any notification */
366  if (!Entry)
367  {
368  DPRINT("ExAllocatePool() failed\n");
371  }
372 
373  *Entry = (PNP_NOTIFY_ENTRY) {
374  .PnpNotificationProc = CallbackRoutine,
375  .Context = Context,
376  .DriverObject = DriverObject,
377  .EventCategory = EventCategory,
378  .RefCount = 1
379  };
380 
381  switch (EventCategory)
382  {
384  {
385  Entry->Guid = *(LPGUID)EventCategoryData;
386 
387  // first register the notification
389  InsertTailList(&PiNotifyDeviceInterfaceListHead, &Entry->PnpNotifyList);
391 
392  // then process existing interfaces if asked
394  {
395  DEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos;
396  UNICODE_STRING SymbolicLinkU;
398 
400  NULL, /* PhysicalDeviceObject OPTIONAL */
401  0, /* Flags */
403  if (NT_SUCCESS(Status))
404  {
405  /* Enumerate SymbolicLinkList */
406  NotificationInfos.Version = 1;
407  NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
408  NotificationInfos.Event = GUID_DEVICE_INTERFACE_ARRIVAL;
409  NotificationInfos.InterfaceClassGuid = *(LPGUID)EventCategoryData;
410  NotificationInfos.SymbolicLinkName = &SymbolicLinkU;
411 
413  *SymbolicLink;
414  SymbolicLink += (SymbolicLinkU.Length / sizeof(WCHAR)) + 1)
415  {
416  RtlInitUnicodeString(&SymbolicLinkU, SymbolicLink);
417  DPRINT("Calling callback routine for %S\n", SymbolicLink);
418  PiCallNotifyProc(CallbackRoutine, &NotificationInfos, Context);
419  }
420 
422  }
423  }
424  break;
425  }
427  {
429  InsertTailList(&PiNotifyHwProfileListHead, &Entry->PnpNotifyList);
431  break;
432  }
434  {
435  PDEVICE_NODE deviceNode;
436  Entry->FileObject = (PFILE_OBJECT)EventCategoryData;
437 
438  // NOTE: the device node's PDO is referenced here
439  Status = IopGetRelatedTargetDevice(Entry->FileObject, &deviceNode);
440  if (!NT_SUCCESS(Status))
441  {
444  return Status;
445  }
446  // save it so we can dereference it later
447  Entry->DeviceObject = deviceNode->PhysicalDeviceObject;
448 
449  // each DEVICE_NODE has its own registered notifications list
451  InsertTailList(&deviceNode->TargetDeviceNotify, &Entry->PnpNotifyList);
453  break;
454  }
455  default:
456  {
457  DPRINT1("%s: unknown EventCategory 0x%x UNIMPLEMENTED\n",
458  __FUNCTION__, EventCategory);
459 
462  return STATUS_NOT_SUPPORTED;
463  }
464  }
465 
466  DPRINT("%s returns NotificationEntry %p\n", __FUNCTION__, Entry);
467 
469 
470  return STATUS_SUCCESS;
471 }
472 
473 /*
474  * @implemented
475  */
476 CODE_SEG("PAGE")
477 NTSTATUS
478 NTAPI
481 {
484 
485  PAGED_CODE();
486 
487  DPRINT("%s(NotificationEntry %p) called\n", __FUNCTION__, Entry);
488 
489  switch (Entry->EventCategory)
490  {
493  break;
496  break;
499  break;
500  default:
501  UNREACHABLE;
502  return STATUS_NOT_SUPPORTED;
503  }
504 
506  if (!Entry->Deleted)
507  {
508  Entry->Deleted = TRUE; // so it can't be unregistered two times
510  }
511  else
512  {
513  DPRINT1("IoUnregisterPlugPlayNotification called two times for 0x%p\n", NotificationEntry);
514  }
516 
517  return STATUS_SUCCESS;
518 }
#define KeGetCurrentIrql()
Definition: env_spec_w32.h:706
_In_ PVOID NotificationStructure
Definition: iofuncs.h:1206
struct _TARGET_DEVICE_REMOVAL_NOTIFICATION TARGET_DEVICE_REMOVAL_NOTIFICATION
return STATUS_NOT_SUPPORTED
DRIVER_NOTIFICATION_CALLBACK_ROUTINE * PDRIVER_NOTIFICATION_CALLBACK_ROUTINE
Definition: iotypes.h:1247
PDEVICE_OBJECT PhysicalDeviceObject
Definition: iotypes.h:850
const GUID GUID_DEVICE_INTERFACE_ARRIVAL
Definition: deviface.c:14
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
_In_opt_ PDEVICE_OBJECT _In_ ULONG _Outptr_result_nullonfailure_ _At_ * SymbolicLinkList(return==0, __drv_allocatesMem(Mem))) PZZWSTR *SymbolicLinkList
struct _TARGET_DEVICE_REMOVAL_NOTIFICATION * PTARGET_DEVICE_REMOVAL_NOTIFICATION
struct _Entry Entry
Definition: kefuncs.h:627
LIST_ENTRY TargetDeviceNotify
Definition: iotypes.h:864
VOID FASTCALL KeAcquireGuardedMutex(IN PKGUARDED_MUTEX GuardedMutex)
Definition: gmutex.c:42
_In_ ULONG _In_opt_ PVOID EventCategoryData
Definition: iofuncs.h:1149
#define TRUE
Definition: types.h:120
uint16_t * PWSTR
Definition: typedefs.h:56
VOID PiNotifyHardwareProfileChange(_In_ LPCGUID Event)
Delivers the event to all drivers subscribed to EventCategoryHardwareProfileChange PnP event.
Definition: pnpnotify.c:214
LONG NTSTATUS
Definition: precomp.h:26
NTSTATUS NTAPI IopGetRelatedTargetDevice(IN PFILE_OBJECT FileObject, OUT PDEVICE_NODE *DeviceNode)
Definition: device.c:653
NTSTATUS NTAPI IoGetDeviceInterfaces(IN CONST GUID *InterfaceClassGuid, IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL, IN ULONG Flags, OUT PWSTR *SymbolicLinkList)
Definition: deviface.c:454
#define PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
Definition: iotypes.h:1239
_In_ ULONG EventCategoryFlags
Definition: iofuncs.h:1149
static VOID PiDereferencePnpNotifyEntry(_In_ PPNP_NOTIFY_ENTRY Entry)
Definition: pnpnotify.c:67
#define InsertTailList(ListHead, Entry)
static VOID PiCallNotifyProc(_In_ PDRIVER_NOTIFICATION_CALLBACK_ROUTINE Proc, _In_ PVOID NotificationStructure, _In_ PVOID Context)
Calls PnP notification routine and makes some checks to detect faulty drivers.
Definition: pnpnotify.c:106
enum _IO_NOTIFICATION_EVENT_CATEGORY IO_NOTIFICATION_EVENT_CATEGORY
#define _In_opt_
Definition: no_sal2.h:212
FORCEINLINE BOOLEAN RemoveEntryList(_In_ PLIST_ENTRY Entry)
Definition: rtlfuncs.h:105
UCHAR KIRQL
Definition: env_spec_w32.h:591
_Must_inspect_result_ _In_ PFLT_GET_OPERATION_STATUS_CALLBACK CallbackRoutine
Definition: fltkernel.h:1035
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
struct _HWPROFILE_CHANGE_NOTIFICATION HWPROFILE_CHANGE_NOTIFICATION
_In_ ULONG _In_opt_ PVOID _In_ PDRIVER_OBJECT _In_ PDRIVER_NOTIFICATION_CALLBACK_ROUTINE _Inout_opt_ __drv_aliasesMem PVOID _Outptr_result_nullonfailure_ _At_ * NotificationEntry(return==0, __drv_allocatesMem(Mem))) PVOID *NotificationEntry
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
ULONG NTAPI IoPnPDeliverServicePowerNotification(_In_ ULONG VetoedPowerOperation, _In_ ULONG PowerNotificationCode, _In_ ULONG PowerNotificationData, _In_ BOOLEAN Synchronous)
Definition: pnpnotify.c:329
unsigned char BOOLEAN
static _Requires_lock_held_(Lock)
Definition: pnpnotify.c:125
#define _Out_
Definition: no_sal2.h:160
#define C_ASSERT(e)
Definition: intsafe.h:71
CODE_SEG("INIT")
Definition: Interface.c:1810
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
Definition: Messaging.c:560
_In_ WDFREQUEST _In_ WDFFILEOBJECT FileObject
Definition: wdfdevice.h:547
* PPNP_NOTIFY_ENTRY
Definition: pnpnotify.c:49
_Must_inspect_result_ _In_ PDRIVER_OBJECT DriverObject
Definition: wdfdriver.h:213
Status
Definition: gdiplustypes.h:24
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
#define ASSERT(a)
Definition: mode.c:45
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
_Must_inspect_result_ _In_ WDFOBJECT _In_ CONST GUID * Guid
Definition: wdfobject.h:760
#define ObDereferenceObject
Definition: obfuncs.h:203
VOID PiInitializeNotifications(VOID)
Definition: pnpnotify.c:55
* PFILE_OBJECT
Definition: iotypes.h:1998
_Guarded_by_(PiNotifyHwProfileLock)
Definition: pnpnotify.c:23
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
NTSTATUS NTAPI IoUnregisterPlugPlayNotification(_In_ PVOID NotificationEntry)
Definition: pnpnotify.c:479
uint32_t entry
Definition: isohybrid.c:63
Definition: typedefs.h:119
KGUARDED_MUTEX PiNotifyTargetDeviceLock
Definition: pnpnotify.c:19
PNP_NOTIFY_ENTRY
Definition: pnpnotify.c:49
#define _In_
Definition: no_sal2.h:158
#define TAG_PNP_NOTIFY
Definition: tag.h:92
VOID FASTCALL KeInitializeGuardedMutex(OUT PKGUARDED_MUTEX GuardedMutex)
Definition: gmutex.c:31
VOID PiNotifyTargetDeviceChange(_In_ LPCGUID Event, _In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PTARGET_DEVICE_CUSTOM_NOTIFICATION CustomNotification)
Delivers the event to all drivers subscribed to EventCategoryTargetDeviceChange PnP event.
Definition: pnpnotify.c:258
KGUARDED_MUTEX PiNotifyHwProfileLock
Definition: pnpnotify.c:20
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
_In_ CONST GUID * InterfaceClassGuid
Definition: iofuncs.h:1136
#define NULL
Definition: types.h:112
BOOL WINAPI IsEqualGUID(REFGUID rguid1, REFGUID rguid2)
Definition: compobj.c:4112
struct _DEVICE_INTERFACE_CHANGE_NOTIFICATION DEVICE_INTERFACE_CHANGE_NOTIFICATION
VOID FASTCALL KeReleaseGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
Definition: gmutex.c:53
#define DPRINT1
Definition: precomp.h:8
struct tagContext Context
Definition: acpixf.h:1034
#define ObReferenceObject
Definition: obfuncs.h:204
_Must_inspect_result_ _In_opt_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWAITLOCK * Lock
Definition: wdfsync.h:124
unsigned int ULONG
Definition: retypes.h:1
NTSYSAPI VOID NTAPI RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
#define UNIMPLEMENTED
Definition: debug.h:115
#define UNREACHABLE
NTSTATUS NTAPI IoRegisterPlugPlayNotification(_In_ IO_NOTIFICATION_EVENT_CATEGORY EventCategory, _In_ ULONG EventCategoryFlags, _In_opt_ PVOID EventCategoryData, _In_ PDRIVER_OBJECT DriverObject, _In_ PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine, _Inout_opt_ PVOID Context, _Out_ PVOID *NotificationEntry)
Definition: pnpnotify.c:345
#define STATUS_SUCCESS
Definition: shellext.h:65
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1099
#define DPRINT
Definition: sndvol32.h:71
#define KeGetCurrentThread
Definition: hal.h:50
VOID PiNotifyDeviceInterfaceChange(_In_ LPCGUID Event, _In_ LPCGUID InterfaceClassGuid, _In_ PUNICODE_STRING SymbolicLinkName)
Delivers the event to all drivers subscribed to EventCategoryDeviceInterfaceChange.
Definition: pnpnotify.c:161
static VOID PiReferencePnpNotifyEntry(_In_ PPNP_NOTIFY_ENTRY Entry)
Definition: pnpnotify.c:92
unsigned char UINT8
#define __FUNCTION__
Definition: types.h:112
base of all file and directory entries
Definition: entries.h:82
#define ExFreePool(addr)
Definition: env_spec_w32.h:352
static const WCHAR SymbolicLink[]
Definition: interface.c:31
PDEVICE_NODE FASTCALL IopGetDeviceNode(IN PDEVICE_OBJECT DeviceObject)
#define _Inout_opt_
Definition: no_sal2.h:216
_Must_inspect_result_ _In_ WDFDEVICE _In_ PCUNICODE_STRING SymbolicLinkName
Definition: wdfdevice.h:3736
#define PAGED_CODE()
GUID * LPGUID
Definition: guiddef.h:81
KGUARDED_MUTEX PiNotifyDeviceInterfaceLock
Definition: pnpnotify.c:21