8a6dce72e9572fbf35d149b9c13b9932b13c6ff3
[reactos.git] / reactos / ntoskrnl / io / pnpmgr / pnpnotify.c
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_NOTIFICATION_CALLBACK_ROUTINE PnpNotificationProc;
27 } PNP_NOTIFY_ENTRY, *PPNP_NOTIFY_ENTRY;
28
29 KGUARDED_MUTEX PnpNotifyListLock;
30 LIST_ENTRY PnpNotifyListHead;
31
32 /* FUNCTIONS *****************************************************************/
33
34 VOID
35 IopNotifyPlugPlayNotification(
36 IN PDEVICE_OBJECT DeviceObject,
37 IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
38 IN LPCGUID Event,
39 IN PVOID EventCategoryData1,
40 IN PVOID EventCategoryData2)
41 {
42 PPNP_NOTIFY_ENTRY ChangeEntry;
43 PLIST_ENTRY ListEntry;
44 PVOID NotificationStructure;
45 BOOLEAN CallCurrentEntry;
46 UNICODE_STRING GuidString;
47 NTSTATUS Status;
48 PDEVICE_OBJECT EntryDeviceObject = NULL;
49
50 ASSERT(DeviceObject);
51
52 KeAcquireGuardedMutex(&PnpNotifyListLock);
53 if (IsListEmpty(&PnpNotifyListHead))
54 {
55 KeReleaseGuardedMutex(&PnpNotifyListLock);
56 return;
57 }
58
59 switch (EventCategory)
60 {
61 case EventCategoryDeviceInterfaceChange:
62 {
63 PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos;
64 NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
65 PagedPool,
66 sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION),
67 TAG_PNP_NOTIFY);
68 if (!NotificationInfos)
69 {
70 KeReleaseGuardedMutex(&PnpNotifyListLock);
71 return;
72 }
73 NotificationInfos->Version = 1;
74 NotificationInfos->Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
75 RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
76 RtlCopyMemory(&NotificationInfos->InterfaceClassGuid, EventCategoryData1, sizeof(GUID));
77 NotificationInfos->SymbolicLinkName = (PUNICODE_STRING)EventCategoryData2;
78 Status = RtlStringFromGUID(&NotificationInfos->InterfaceClassGuid, &GuidString);
79 if (!NT_SUCCESS(Status))
80 {
81 KeReleaseGuardedMutex(&PnpNotifyListLock);
82 ExFreePool(NotificationStructure);
83 return;
84 }
85 break;
86 }
87 case EventCategoryHardwareProfileChange:
88 {
89 PHWPROFILE_CHANGE_NOTIFICATION NotificationInfos;
90 NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
91 PagedPool,
92 sizeof(HWPROFILE_CHANGE_NOTIFICATION),
93 TAG_PNP_NOTIFY);
94 if (!NotificationInfos)
95 {
96 KeReleaseGuardedMutex(&PnpNotifyListLock);
97 return;
98 }
99 NotificationInfos->Version = 1;
100 NotificationInfos->Size = sizeof(HWPROFILE_CHANGE_NOTIFICATION);
101 RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
102 break;
103 }
104 case EventCategoryTargetDeviceChange:
105 {
106 if (Event != &GUID_PNP_CUSTOM_NOTIFICATION)
107 {
108 PTARGET_DEVICE_REMOVAL_NOTIFICATION NotificationInfos;
109 NotificationStructure = NotificationInfos = ExAllocatePoolWithTag(
110 PagedPool,
111 sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION),
112 TAG_PNP_NOTIFY);
113 if (!NotificationInfos)
114 {
115 KeReleaseGuardedMutex(&PnpNotifyListLock);
116 return;
117 }
118 NotificationInfos->Version = 1;
119 NotificationInfos->Size = sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION);
120 RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID));
121 NotificationInfos->FileObject = (PFILE_OBJECT)EventCategoryData1;
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 if (Event != &GUID_PNP_CUSTOM_NOTIFICATION)
180 {
181 if (ChangeEntry->FileObject == (PFILE_OBJECT)EventCategoryData1)
182 CallCurrentEntry = TRUE;
183 }
184 else
185 {
186 Status = IoGetRelatedTargetDevice(ChangeEntry->FileObject, &EntryDeviceObject);
187 if (NT_SUCCESS(Status))
188 {
189 if (DeviceObject == EntryDeviceObject)
190 {
191 ((PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure)->FileObject = ChangeEntry->FileObject;
192 CallCurrentEntry = TRUE;
193 }
194 }
195 }
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)(
217 NotificationStructure,
218 ChangeEntry->Context);
219 KeAcquireGuardedMutex(&PnpNotifyListLock);
220 }
221
222 }
223 KeReleaseGuardedMutex(&PnpNotifyListLock);
224 ExFreePoolWithTag(NotificationStructure, TAG_PNP_NOTIFY);
225 if (EventCategory == EventCategoryDeviceInterfaceChange)
226 RtlFreeUnicodeString(&GuidString);
227 }
228
229 /* PUBLIC FUNCTIONS **********************************************************/
230
231 /*
232 * @unimplemented
233 */
234 ULONG
235 NTAPI
236 IoPnPDeliverServicePowerNotification(ULONG VetoedPowerOperation OPTIONAL,
237 ULONG PowerNotification,
238 ULONG Unknown OPTIONAL,
239 BOOLEAN Synchronous)
240 {
241 UNIMPLEMENTED;
242 return 0;
243 }
244
245 /*
246 * @implemented
247 */
248 NTSTATUS
249 NTAPI
250 IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
251 IN ULONG EventCategoryFlags,
252 IN PVOID EventCategoryData OPTIONAL,
253 IN PDRIVER_OBJECT DriverObject,
254 IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
255 IN PVOID Context,
256 OUT PVOID *NotificationEntry)
257 {
258 PPNP_NOTIFY_ENTRY Entry;
259 PWSTR SymbolicLinkList;
260 NTSTATUS Status;
261 PAGED_CODE();
262
263 DPRINT("__FUNCTION__(EventCategory 0x%x, EventCategoryFlags 0x%lx, DriverObject %p) called.\n",
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 DPRINT("IoGetDeviceInterfaces() failed with status 0x%08lx\n",
296 Status);
297 ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY);
298 ObDereferenceObject(DriverObject);
299 return Status;
300 }
301
302 /* Enumerate SymbolicLinkList */
303 NotificationInfos.Version = 1;
304 NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
305 RtlCopyMemory(&NotificationInfos.Event,
306 &GUID_DEVICE_INTERFACE_ARRIVAL,
307 sizeof(GUID));
308 RtlCopyMemory(&NotificationInfos.InterfaceClassGuid,
309 EventCategoryData,
310 sizeof(GUID));
311 NotificationInfos.SymbolicLinkName = &SymbolicLinkU;
312
313 for (SymbolicLink = SymbolicLinkList;
314 *SymbolicLink;
315 SymbolicLink += wcslen(SymbolicLink) + 1)
316 {
317 RtlInitUnicodeString(&SymbolicLinkU, SymbolicLink);
318 DPRINT("Calling callback routine for %S\n", SymbolicLink);
319 (*CallbackRoutine)(&NotificationInfos, Context);
320 }
321
322 ExFreePool(SymbolicLinkList);
323 }
324
325 Entry->PnpNotificationProc = CallbackRoutine;
326 Entry->EventCategory = EventCategory;
327 Entry->Context = Context;
328 switch (EventCategory)
329 {
330 case EventCategoryDeviceInterfaceChange:
331 {
332 Status = RtlStringFromGUID(EventCategoryData, &Entry->Guid);
333 if (!NT_SUCCESS(Status))
334 {
335 ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY);
336 ObDereferenceObject(DriverObject);
337 return Status;
338 }
339 break;
340 }
341 case EventCategoryHardwareProfileChange:
342 {
343 /* nothing to do */
344 break;
345 }
346 case EventCategoryTargetDeviceChange:
347 {
348 Entry->FileObject = (PFILE_OBJECT)EventCategoryData;
349 break;
350 }
351 default:
352 {
353 DPRINT1("__FUNCTION__(): unknown EventCategory 0x%x UNIMPLEMENTED\n",
354 EventCategory);
355 break;
356 }
357 }
358
359 KeAcquireGuardedMutex(&PnpNotifyListLock);
360 InsertHeadList(&PnpNotifyListHead,
361 &Entry->PnpNotifyList);
362 KeReleaseGuardedMutex(&PnpNotifyListLock);
363
364 DPRINT("IoRegisterPlugPlayNotification() returns NotificationEntry %p\n",
365 Entry);
366
367 *NotificationEntry = Entry;
368
369 return STATUS_SUCCESS;
370 }
371
372 /*
373 * @implemented
374 */
375 NTSTATUS
376 NTAPI
377 IoUnregisterPlugPlayNotification(IN PVOID NotificationEntry)
378 {
379 PPNP_NOTIFY_ENTRY Entry;
380 PAGED_CODE();
381
382 Entry = (PPNP_NOTIFY_ENTRY)NotificationEntry;
383 DPRINT("__FUNCTION__(NotificationEntry %p) called\n", Entry);
384
385 KeAcquireGuardedMutex(&PnpNotifyListLock);
386 RtlFreeUnicodeString(&Entry->Guid);
387 RemoveEntryList(&Entry->PnpNotifyList);
388 KeReleaseGuardedMutex(&PnpNotifyListLock);
389
390 return STATUS_SUCCESS;
391 }