xref: /reactos/ntoskrnl/io/pnpmgr/pnpnotify.c (revision cc7cf826)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * COPYRIGHT:       GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/io/pnpmgr/pnpnotify.c
5  * PURPOSE:         Plug & Play notification functions
6  * PROGRAMMERS:     Filip Navara (xnavara@volny.cz)
7  *                  Herv� Poussineau (hpoussin@reactos.org)
8  *                  Pierre Schweitzer
9  */
10 
11 /* INCLUDES ******************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* TYPES *******************************************************************/
18 
19 typedef struct _PNP_NOTIFY_ENTRY
20 {
21     LIST_ENTRY PnpNotifyList;
22     IO_NOTIFICATION_EVENT_CATEGORY EventCategory;
23     PVOID Context;
24     UNICODE_STRING Guid;
25     PFILE_OBJECT FileObject;
26     PDRIVER_OBJECT DriverObject;
27     PDRIVER_NOTIFICATION_CALLBACK_ROUTINE PnpNotificationProc;
28 } PNP_NOTIFY_ENTRY, *PPNP_NOTIFY_ENTRY;
29 
30 KGUARDED_MUTEX PnpNotifyListLock;
31 LIST_ENTRY PnpNotifyListHead;
32 
33 /* FUNCTIONS *****************************************************************/
34 
35 VOID
36 IopNotifyPlugPlayNotification(
37     IN PDEVICE_OBJECT DeviceObject,
38     IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
39     IN LPCGUID Event,
40     IN PVOID EventCategoryData1,
41     IN PVOID EventCategoryData2)
42 {
43     PPNP_NOTIFY_ENTRY ChangeEntry;
44     PLIST_ENTRY ListEntry;
45     PVOID NotificationStructure;
46     BOOLEAN CallCurrentEntry;
47     UNICODE_STRING GuidString;
48     NTSTATUS Status;
49     PDEVICE_OBJECT EntryDeviceObject = NULL;
50 
51     ASSERT(DeviceObject);
52 
53     KeAcquireGuardedMutex(&PnpNotifyListLock);
54     if (IsListEmpty(&PnpNotifyListHead))
55     {
56         KeReleaseGuardedMutex(&PnpNotifyListLock);
57         return;
58     }
59 
60     switch (EventCategory)
61     {
62         case EventCategoryDeviceInterfaceChange:
63         {
64             PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos;
65             NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
66                 PagedPool,
67                 sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION),
68                 TAG_PNP_NOTIFY);
69             if (!NotificationInfos)
70             {
71                 KeReleaseGuardedMutex(&PnpNotifyListLock);
72                 return;
73             }
74             NotificationInfos->Version = 1;
75             NotificationInfos->Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
76             RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
77             RtlCopyMemory(&NotificationInfos->InterfaceClassGuid, EventCategoryData1, sizeof(GUID));
78             NotificationInfos->SymbolicLinkName = (PUNICODE_STRING)EventCategoryData2;
79             Status = RtlStringFromGUID(&NotificationInfos->InterfaceClassGuid, &GuidString);
80             if (!NT_SUCCESS(Status))
81             {
82                 KeReleaseGuardedMutex(&PnpNotifyListLock);
83                 ExFreePoolWithTag(NotificationStructure, TAG_PNP_NOTIFY);
84                 return;
85             }
86             break;
87         }
88         case EventCategoryHardwareProfileChange:
89         {
90             PHWPROFILE_CHANGE_NOTIFICATION NotificationInfos;
91             NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
92                 PagedPool,
93                 sizeof(HWPROFILE_CHANGE_NOTIFICATION),
94                 TAG_PNP_NOTIFY);
95             if (!NotificationInfos)
96             {
97                 KeReleaseGuardedMutex(&PnpNotifyListLock);
98                 return;
99             }
100             NotificationInfos->Version = 1;
101             NotificationInfos->Size = sizeof(HWPROFILE_CHANGE_NOTIFICATION);
102             RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
103             break;
104         }
105         case EventCategoryTargetDeviceChange:
106         {
107             if (Event != &GUID_PNP_CUSTOM_NOTIFICATION)
108             {
109                 PTARGET_DEVICE_REMOVAL_NOTIFICATION NotificationInfos;
110                 NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
111                     PagedPool,
112                     sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION),
113                     TAG_PNP_NOTIFY);
114                 if (!NotificationInfos)
115                 {
116                     KeReleaseGuardedMutex(&PnpNotifyListLock);
117                     return;
118                 }
119                 NotificationInfos->Version = 1;
120                 NotificationInfos->Size = sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION);
121                 RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
122             }
123             else
124             {
125                 PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationInfos;
126                 NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
127                     PagedPool,
128                     sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION),
129                     TAG_PNP_NOTIFY);
130                 if (!NotificationInfos)
131                 {
132                     KeReleaseGuardedMutex(&PnpNotifyListLock);
133                     return;
134                 }
135                 RtlCopyMemory(NotificationInfos, EventCategoryData1, sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION));
136             }
137             break;
138         }
139         default:
140         {
141             DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
142             KeReleaseGuardedMutex(&PnpNotifyListLock);
143             return;
144         }
145     }
146 
147     /* Loop through procedures registred in PnpNotifyListHead
148      * list to find those that meet some criteria.
149      */
150     ListEntry = PnpNotifyListHead.Flink;
151     while (ListEntry != &PnpNotifyListHead)
152     {
153         ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY, PnpNotifyList);
154         CallCurrentEntry = FALSE;
155 
156         if (ChangeEntry->EventCategory != EventCategory)
157         {
158             ListEntry = ListEntry->Flink;
159             continue;
160         }
161 
162         switch (EventCategory)
163         {
164             case EventCategoryDeviceInterfaceChange:
165             {
166                 if (RtlCompareUnicodeString(&ChangeEntry->Guid, &GuidString, FALSE) == 0)
167                 {
168                     CallCurrentEntry = TRUE;
169                 }
170                 break;
171             }
172             case EventCategoryHardwareProfileChange:
173             {
174                 CallCurrentEntry = TRUE;
175                 break;
176             }
177             case EventCategoryTargetDeviceChange:
178             {
179                 Status = IoGetRelatedTargetDevice(ChangeEntry->FileObject, &EntryDeviceObject);
180                 if (NT_SUCCESS(Status))
181                 {
182                     if (DeviceObject == EntryDeviceObject)
183                     {
184                         if (Event == &GUID_PNP_CUSTOM_NOTIFICATION)
185                         {
186                             ((PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure)->FileObject = ChangeEntry->FileObject;
187                         }
188                         else
189                         {
190                             ((PTARGET_DEVICE_REMOVAL_NOTIFICATION)NotificationStructure)->FileObject = ChangeEntry->FileObject;
191                         }
192                         CallCurrentEntry = TRUE;
193                     }
194                 }
195                 break;
196             }
197             default:
198             {
199                 DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
200                 break;
201             }
202         }
203 
204         /* Move to the next element now, as callback may unregister itself */
205         ListEntry = ListEntry->Flink;
206         /* FIXME: If ListEntry was the last element and that callback registers
207          * new notifications, those won't be checked... */
208 
209         if (CallCurrentEntry)
210         {
211             /* Call entry into new allocated memory */
212             DPRINT("IopNotifyPlugPlayNotification(): found suitable callback %p\n",
213                 ChangeEntry);
214 
215             KeReleaseGuardedMutex(&PnpNotifyListLock);
216             (ChangeEntry->PnpNotificationProc)(NotificationStructure,
217                                                ChangeEntry->Context);
218             KeAcquireGuardedMutex(&PnpNotifyListLock);
219         }
220 
221     }
222     KeReleaseGuardedMutex(&PnpNotifyListLock);
223     ExFreePoolWithTag(NotificationStructure, TAG_PNP_NOTIFY);
224     if (EventCategory == EventCategoryDeviceInterfaceChange)
225         RtlFreeUnicodeString(&GuidString);
226 }
227 
228 /* PUBLIC FUNCTIONS **********************************************************/
229 
230 /*
231  * @unimplemented
232  */
233 ULONG
234 NTAPI
235 IoPnPDeliverServicePowerNotification(ULONG VetoedPowerOperation OPTIONAL,
236                                      ULONG PowerNotification,
237                                      ULONG Unknown OPTIONAL,
238                                      BOOLEAN Synchronous)
239 {
240     UNIMPLEMENTED;
241     return 0;
242 }
243 
244 /*
245  * @implemented
246  */
247 NTSTATUS
248 NTAPI
249 IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
250                                IN ULONG EventCategoryFlags,
251                                IN PVOID EventCategoryData OPTIONAL,
252                                IN PDRIVER_OBJECT DriverObject,
253                                IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
254                                IN PVOID Context,
255                                OUT PVOID *NotificationEntry)
256 {
257     PPNP_NOTIFY_ENTRY Entry;
258     PWSTR SymbolicLinkList;
259     NTSTATUS Status;
260     PAGED_CODE();
261 
262     DPRINT("%s(EventCategory 0x%x, EventCategoryFlags 0x%lx, DriverObject %p) called.\n",
263            __FUNCTION__,
264            EventCategory,
265            EventCategoryFlags,
266            DriverObject);
267 
268     ObReferenceObject(DriverObject);
269 
270     /* Try to allocate entry for notification before sending any notification */
271     Entry = ExAllocatePoolWithTag(NonPagedPool,
272                                   sizeof(PNP_NOTIFY_ENTRY),
273                                   TAG_PNP_NOTIFY);
274 
275     if (!Entry)
276     {
277         DPRINT("ExAllocatePool() failed\n");
278         ObDereferenceObject(DriverObject);
279         return STATUS_INSUFFICIENT_RESOURCES;
280     }
281 
282     if (EventCategory == EventCategoryDeviceInterfaceChange &&
283         EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES)
284     {
285         DEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos;
286         UNICODE_STRING SymbolicLinkU;
287         PWSTR SymbolicLink;
288 
289         Status = IoGetDeviceInterfaces((LPGUID)EventCategoryData,
290                                        NULL, /* PhysicalDeviceObject OPTIONAL */
291                                        0, /* Flags */
292                                        &SymbolicLinkList);
293         if (NT_SUCCESS(Status))
294         {
295             /* Enumerate SymbolicLinkList */
296             NotificationInfos.Version = 1;
297             NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
298             RtlCopyMemory(&NotificationInfos.Event,
299                           &GUID_DEVICE_INTERFACE_ARRIVAL,
300                           sizeof(GUID));
301             RtlCopyMemory(&NotificationInfos.InterfaceClassGuid,
302                           EventCategoryData,
303                           sizeof(GUID));
304             NotificationInfos.SymbolicLinkName = &SymbolicLinkU;
305 
306             for (SymbolicLink = SymbolicLinkList;
307                  *SymbolicLink;
308                  SymbolicLink += wcslen(SymbolicLink) + 1)
309             {
310                 RtlInitUnicodeString(&SymbolicLinkU, SymbolicLink);
311                 DPRINT("Calling callback routine for %S\n", SymbolicLink);
312                 (*CallbackRoutine)(&NotificationInfos, Context);
313             }
314 
315             ExFreePool(SymbolicLinkList);
316         }
317     }
318 
319     Entry->PnpNotificationProc = CallbackRoutine;
320     Entry->EventCategory = EventCategory;
321     Entry->Context = Context;
322     Entry->DriverObject = DriverObject;
323     switch (EventCategory)
324     {
325         case EventCategoryDeviceInterfaceChange:
326         {
327             Status = RtlStringFromGUID(EventCategoryData, &Entry->Guid);
328             if (!NT_SUCCESS(Status))
329             {
330                 ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY);
331                 ObDereferenceObject(DriverObject);
332                 return Status;
333             }
334             break;
335         }
336         case EventCategoryHardwareProfileChange:
337         {
338             /* nothing to do */
339            break;
340         }
341         case EventCategoryTargetDeviceChange:
342         {
343             Entry->FileObject = (PFILE_OBJECT)EventCategoryData;
344             break;
345         }
346         default:
347         {
348             DPRINT1("%s: unknown EventCategory 0x%x UNIMPLEMENTED\n",
349                     __FUNCTION__, EventCategory);
350             break;
351         }
352     }
353 
354     KeAcquireGuardedMutex(&PnpNotifyListLock);
355     InsertHeadList(&PnpNotifyListHead,
356                    &Entry->PnpNotifyList);
357     KeReleaseGuardedMutex(&PnpNotifyListLock);
358 
359     DPRINT("%s returns NotificationEntry %p\n", __FUNCTION__, Entry);
360 
361     *NotificationEntry = Entry;
362 
363     return STATUS_SUCCESS;
364 }
365 
366 /*
367  * @implemented
368  */
369 NTSTATUS
370 NTAPI
371 IoUnregisterPlugPlayNotification(IN PVOID NotificationEntry)
372 {
373     PPNP_NOTIFY_ENTRY Entry;
374     PAGED_CODE();
375 
376     Entry = (PPNP_NOTIFY_ENTRY)NotificationEntry;
377     DPRINT("%s(NotificationEntry %p) called\n", __FUNCTION__, Entry);
378 
379     KeAcquireGuardedMutex(&PnpNotifyListLock);
380     RemoveEntryList(&Entry->PnpNotifyList);
381     KeReleaseGuardedMutex(&PnpNotifyListLock);
382 
383     RtlFreeUnicodeString(&Entry->Guid);
384 
385     ObDereferenceObject(Entry->DriverObject);
386 
387     ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY);
388 
389     return STATUS_SUCCESS;
390 }
391