9f73218bcf0141d5df2ef8d96f8c6e0013abc1be
[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_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 ExFreePool(NotificationStructure);
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 }
196 default:
197 {
198 DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
199 break;
200 }
201 }
202
203 /* Move to the next element now, as callback may unregister itself */
204 ListEntry = ListEntry->Flink;
205 /* FIXME: If ListEntry was the last element and that callback registers
206 * new notifications, those won't be checked... */
207
208 if (CallCurrentEntry)
209 {
210 /* Call entry into new allocated memory */
211 DPRINT("IopNotifyPlugPlayNotification(): found suitable callback %p\n",
212 ChangeEntry);
213
214 KeReleaseGuardedMutex(&PnpNotifyListLock);
215 (ChangeEntry->PnpNotificationProc)(
216 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 }