eedf193e62f49e930ffef482d9ad6f9c7334440f
[reactos.git] / 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_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)(
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("%s(EventCategory 0x%x, EventCategoryFlags 0x%lx, DriverObject %p) called.\n",
264 __FUNCTION__,
265 EventCategory,
266 EventCategoryFlags,
267 DriverObject);
268
269 ObReferenceObject(DriverObject);
270
271 /* Try to allocate entry for notification before sending any notification */
272 Entry = ExAllocatePoolWithTag(NonPagedPool,
273 sizeof(PNP_NOTIFY_ENTRY),
274 TAG_PNP_NOTIFY);
275
276 if (!Entry)
277 {
278 DPRINT("ExAllocatePool() failed\n");
279 ObDereferenceObject(DriverObject);
280 return STATUS_INSUFFICIENT_RESOURCES;
281 }
282
283 if (EventCategory == EventCategoryDeviceInterfaceChange &&
284 EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES)
285 {
286 DEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos;
287 UNICODE_STRING SymbolicLinkU;
288 PWSTR SymbolicLink;
289
290 Status = IoGetDeviceInterfaces((LPGUID)EventCategoryData,
291 NULL, /* PhysicalDeviceObject OPTIONAL */
292 0, /* Flags */
293 &SymbolicLinkList);
294 if (NT_SUCCESS(Status))
295 {
296 /* Enumerate SymbolicLinkList */
297 NotificationInfos.Version = 1;
298 NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
299 RtlCopyMemory(&NotificationInfos.Event,
300 &GUID_DEVICE_INTERFACE_ARRIVAL,
301 sizeof(GUID));
302 RtlCopyMemory(&NotificationInfos.InterfaceClassGuid,
303 EventCategoryData,
304 sizeof(GUID));
305 NotificationInfos.SymbolicLinkName = &SymbolicLinkU;
306
307 for (SymbolicLink = SymbolicLinkList;
308 *SymbolicLink;
309 SymbolicLink += wcslen(SymbolicLink) + 1)
310 {
311 RtlInitUnicodeString(&SymbolicLinkU, SymbolicLink);
312 DPRINT("Calling callback routine for %S\n", SymbolicLink);
313 (*CallbackRoutine)(&NotificationInfos, Context);
314 }
315
316 ExFreePool(SymbolicLinkList);
317 }
318 }
319
320 Entry->PnpNotificationProc = CallbackRoutine;
321 Entry->EventCategory = EventCategory;
322 Entry->Context = Context;
323 Entry->DriverObject = DriverObject;
324 switch (EventCategory)
325 {
326 case EventCategoryDeviceInterfaceChange:
327 {
328 Status = RtlStringFromGUID(EventCategoryData, &Entry->Guid);
329 if (!NT_SUCCESS(Status))
330 {
331 ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY);
332 ObDereferenceObject(DriverObject);
333 return Status;
334 }
335 break;
336 }
337 case EventCategoryHardwareProfileChange:
338 {
339 /* nothing to do */
340 break;
341 }
342 case EventCategoryTargetDeviceChange:
343 {
344 Entry->FileObject = (PFILE_OBJECT)EventCategoryData;
345 break;
346 }
347 default:
348 {
349 DPRINT1("%s: unknown EventCategory 0x%x UNIMPLEMENTED\n",
350 __FUNCTION__, EventCategory);
351 break;
352 }
353 }
354
355 KeAcquireGuardedMutex(&PnpNotifyListLock);
356 InsertHeadList(&PnpNotifyListHead,
357 &Entry->PnpNotifyList);
358 KeReleaseGuardedMutex(&PnpNotifyListLock);
359
360 DPRINT("%s returns NotificationEntry %p\n", __FUNCTION__, Entry);
361
362 *NotificationEntry = Entry;
363
364 return STATUS_SUCCESS;
365 }
366
367 /*
368 * @implemented
369 */
370 NTSTATUS
371 NTAPI
372 IoUnregisterPlugPlayNotification(IN PVOID NotificationEntry)
373 {
374 PPNP_NOTIFY_ENTRY Entry;
375 PAGED_CODE();
376
377 Entry = (PPNP_NOTIFY_ENTRY)NotificationEntry;
378 DPRINT("%s(NotificationEntry %p) called\n", __FUNCTION__, Entry);
379
380 KeAcquireGuardedMutex(&PnpNotifyListLock);
381 RemoveEntryList(&Entry->PnpNotifyList);
382 KeReleaseGuardedMutex(&PnpNotifyListLock);
383
384 RtlFreeUnicodeString(&Entry->Guid);
385
386 ObDereferenceObject(Entry->DriverObject);
387
388 ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY);
389
390 return STATUS_SUCCESS;
391 }