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