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