Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2733 lines
86 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. notify.c
  5. Abstract:
  6. This module contains APIs and routines for handling device event
  7. notifications.
  8. Author:
  9. Environment:
  10. Kernel mode
  11. Revision History:
  12. --*/
  13. #include "pnpmgrp.h"
  14. #pragma hdrstop
  15. #include <pnpmgr.h>
  16. #include <pnpsetup.h>
  17. #define PNP_DEVICE_EVENT_ENTRY_TAG 'EEpP'
  18. //
  19. // Locks the list and inserts the entry at the tail.
  20. //
  21. #define PiLockedInsertTailList(list, lock, entry) { \
  22. IopAcquireNotifyLock((lock)); \
  23. InsertTailList((list), (entry)); \
  24. IopReleaseNotifyLock((lock)); \
  25. }
  26. #define PiGetSession(c,i) ((PVOID)(MmIsSessionAddress((PVOID)c)? MmGetSessionById(i) : NULL))
  27. typedef struct _ASYNC_TDC_WORK_ITEM {
  28. WORK_QUEUE_ITEM WorkItem;
  29. PDEVICE_OBJECT DeviceObject;
  30. PDEVICE_CHANGE_COMPLETE_CALLBACK Callback;
  31. PVOID Context;
  32. PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure;
  33. } ASYNC_TDC_WORK_ITEM, *PASYNC_TDC_WORK_ITEM;
  34. typedef struct _DEFERRED_REGISTRATION_ENTRY {
  35. LIST_ENTRY ListEntry;
  36. PNOTIFY_ENTRY_HEADER NotifyEntry;
  37. } DEFERRED_REGISTRATION_ENTRY, *PDEFERRED_REGISTRATION_ENTRY;
  38. //
  39. // Kernel mode notification data
  40. //
  41. #ifdef ALLOC_DATA_PRAGMA
  42. #pragma data_seg("PAGEDATA")
  43. #pragma const_seg("PAGECONST")
  44. #endif
  45. LIST_ENTRY IopDeviceClassNotifyList[NOTIFY_DEVICE_CLASS_HASH_BUCKETS] = {NULL};
  46. PSETUP_NOTIFY_DATA IopSetupNotifyData = NULL;
  47. LIST_ENTRY IopProfileNotifyList = {NULL};
  48. LIST_ENTRY IopDeferredRegistrationList = {NULL};
  49. #ifdef ALLOC_DATA_PRAGMA
  50. #pragma data_seg()
  51. #endif
  52. KGUARDED_MUTEX IopDeviceClassNotifyLock;
  53. KGUARDED_MUTEX IopTargetDeviceNotifyLock;
  54. KGUARDED_MUTEX IopHwProfileNotifyLock;
  55. KGUARDED_MUTEX IopDeferredRegistrationLock;
  56. BOOLEAN PiNotificationInProgress;
  57. KGUARDED_MUTEX PiNotificationInProgressLock;
  58. //
  59. // Prototypes
  60. //
  61. VOID
  62. IopDereferenceNotify(
  63. PNOTIFY_ENTRY_HEADER Notify
  64. );
  65. VOID
  66. IopInitializePlugPlayNotification(
  67. VOID
  68. );
  69. NTSTATUS
  70. PiNotifyUserMode(
  71. PPNP_DEVICE_EVENT_ENTRY DeviceEvent
  72. );
  73. NTSTATUS
  74. PiNotifyDriverCallback(
  75. IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
  76. IN PVOID NotificationStructure,
  77. IN PVOID Context,
  78. IN ULONG SessionId,
  79. IN PVOID OpaqueSession,
  80. OUT PNTSTATUS CallbackStatus OPTIONAL
  81. );
  82. VOID
  83. IopReferenceNotify(
  84. PNOTIFY_ENTRY_HEADER notify
  85. );
  86. VOID
  87. IopReportTargetDeviceChangeAsyncWorker(
  88. PVOID Context
  89. );
  90. NTSTATUS
  91. PiDeferNotification(
  92. IN PNOTIFY_ENTRY_HEADER Entry
  93. );
  94. #ifdef ALLOC_PRAGMA
  95. #pragma alloc_text(INIT, IopInitializePlugPlayNotification)
  96. #pragma alloc_text(PAGE, IoGetRelatedTargetDevice)
  97. #pragma alloc_text(PAGE, IoNotifyPowerOperationVetoed)
  98. #pragma alloc_text(PAGE, IoPnPDeliverServicePowerNotification)
  99. #pragma alloc_text(PAGE, IoRegisterPlugPlayNotification)
  100. #pragma alloc_text(PAGE, IoReportTargetDeviceChange)
  101. #pragma alloc_text(PAGE, IoUnregisterPlugPlayNotification)
  102. #pragma alloc_text(PAGE, IopDereferenceNotify)
  103. #pragma alloc_text(PAGE, IopGetRelatedTargetDevice)
  104. #pragma alloc_text(PAGE, IopNotifyDeviceClassChange)
  105. #pragma alloc_text(PAGE, IopNotifyHwProfileChange)
  106. #pragma alloc_text(PAGE, IopNotifySetupDeviceArrival)
  107. #pragma alloc_text(PAGE, IopNotifyTargetDeviceChange)
  108. #pragma alloc_text(PAGE, IopOrphanNotification)
  109. #pragma alloc_text(PAGE, IopProcessDeferredRegistrations)
  110. #pragma alloc_text(PAGE, IopReferenceNotify)
  111. #pragma alloc_text(PAGE, IopReportTargetDeviceChangeAsyncWorker)
  112. #pragma alloc_text(PAGE, IopRequestHwProfileChangeNotification)
  113. #pragma alloc_text(PAGE, PiNotifyDriverCallback)
  114. #pragma alloc_text(PAGE, PiDeferNotification)
  115. #endif // ALLOC_PRAGMA
  116. NTSTATUS
  117. IoUnregisterPlugPlayNotification(
  118. IN PVOID NotificationEntry
  119. )
  120. /*++
  121. Routine Description:
  122. This routine unregisters a notification previously registered via
  123. IoRegisterPlugPlayNotification. A driver cannot be unloaded until it has
  124. unregistered all of its notification handles.
  125. Parameters:
  126. NotificationEntry - This provices the cookie returned by IoRegisterPlugPlayNotification
  127. which identifies the registration in question.
  128. Return Value:
  129. Status code that indicates whether or not the function was successful.
  130. --*/
  131. {
  132. PNOTIFY_ENTRY_HEADER entry;
  133. PKGUARDED_MUTEX lock;
  134. BOOLEAN wasDeferred = FALSE;
  135. PLIST_ENTRY link;
  136. PDEFERRED_REGISTRATION_ENTRY deferredNode;
  137. PAGED_CODE();
  138. ASSERT(NotificationEntry);
  139. entry = (PNOTIFY_ENTRY_HEADER)NotificationEntry;
  140. lock = entry->Lock;
  141. KeAcquireGuardedMutex(&PiNotificationInProgressLock);
  142. if (PiNotificationInProgress) {
  143. //
  144. // Before unregistering the entry, we need to make sure that it's not sitting
  145. // around in the deferred registration list.
  146. //
  147. IopAcquireNotifyLock(&IopDeferredRegistrationLock);
  148. link = IopDeferredRegistrationList.Flink;
  149. while (link != (PLIST_ENTRY)&IopDeferredRegistrationList) {
  150. deferredNode = (PDEFERRED_REGISTRATION_ENTRY)link;
  151. ASSERT(deferredNode->NotifyEntry->Unregistered);
  152. if (deferredNode->NotifyEntry == entry) {
  153. wasDeferred = TRUE;
  154. if (lock) {
  155. IopAcquireNotifyLock(lock);
  156. }
  157. link = link->Flink;
  158. RemoveEntryList((PLIST_ENTRY)deferredNode);
  159. IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)deferredNode->NotifyEntry);
  160. if (lock) {
  161. IopReleaseNotifyLock(lock);
  162. }
  163. ExFreePool(deferredNode);
  164. } else {
  165. link = link->Flink;
  166. }
  167. }
  168. IopReleaseNotifyLock(&IopDeferredRegistrationLock);
  169. } else {
  170. //
  171. // If there is currently no notification in progress, the deferred
  172. // registration list must be empty.
  173. //
  174. ASSERT(IsListEmpty(&IopDeferredRegistrationList));
  175. }
  176. KeReleaseGuardedMutex(&PiNotificationInProgressLock);
  177. //
  178. // Acquire lock
  179. //
  180. if (lock) {
  181. IopAcquireNotifyLock(lock);
  182. }
  183. ASSERT(wasDeferred == entry->Unregistered);
  184. if (!entry->Unregistered || wasDeferred) {
  185. //
  186. // Dereference the entry if it is currently registered, or had its
  187. // registration pending completion of the notification in progress.
  188. //
  189. //
  190. // Mark the entry as unregistered so we don't notify on it
  191. //
  192. entry->Unregistered = TRUE;
  193. //
  194. // Dereference it thus deleting if no longer required
  195. //
  196. IopDereferenceNotify(entry);
  197. }
  198. //
  199. // Release the lock
  200. //
  201. if (lock) {
  202. IopReleaseNotifyLock(lock);
  203. }
  204. return STATUS_SUCCESS;
  205. }
  206. VOID
  207. IopProcessDeferredRegistrations(
  208. VOID
  209. )
  210. /*++
  211. Routine Description:
  212. This routine removes notification entries from the deferred registration
  213. list, marking them as "registered" so that they can receive notifications.
  214. Parameters:
  215. None.
  216. Return Value:
  217. None.
  218. --*/
  219. {
  220. PDEFERRED_REGISTRATION_ENTRY deferredNode;
  221. PKGUARDED_MUTEX lock;
  222. PAGED_CODE();
  223. IopAcquireNotifyLock(&IopDeferredRegistrationLock);
  224. while (!IsListEmpty(&IopDeferredRegistrationList)) {
  225. deferredNode = (PDEFERRED_REGISTRATION_ENTRY)RemoveHeadList(&IopDeferredRegistrationList);
  226. //
  227. // Acquire this entry's list lock.
  228. //
  229. lock = deferredNode->NotifyEntry->Lock;
  230. if (lock) {
  231. IopAcquireNotifyLock(lock);
  232. }
  233. //
  234. // Mark this entry as registered.
  235. //
  236. deferredNode->NotifyEntry->Unregistered = FALSE;
  237. //
  238. // Dereference the notification entry when removing it from the deferred
  239. // list, and free the node.
  240. //
  241. IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)deferredNode->NotifyEntry);
  242. ExFreePool(deferredNode);
  243. //
  244. // Release this entry's list lock.
  245. //
  246. if (lock) {
  247. IopReleaseNotifyLock(lock);
  248. lock = NULL;
  249. }
  250. }
  251. IopReleaseNotifyLock(&IopDeferredRegistrationLock);
  252. }
  253. NTSTATUS
  254. PiDeferNotification(
  255. IN PNOTIFY_ENTRY_HEADER Entry
  256. )
  257. /*++
  258. Routine Description:
  259. If a notification is currently in progress, this routine will insert the
  260. notification into the list of deferred notifications.
  261. Parameters:
  262. Entry - Notification entry.
  263. Return Value:
  264. Status code that indicates whether or not the function was successful.
  265. --*/
  266. {
  267. NTSTATUS status;
  268. PDEFERRED_REGISTRATION_ENTRY deferredNode;
  269. PAGED_CODE();
  270. status = STATUS_SUCCESS;
  271. KeAcquireGuardedMutex(&PiNotificationInProgressLock);
  272. if (PiNotificationInProgress) {
  273. //
  274. // If a notification is in progress, mark the entry as
  275. // Unregistered until after the current notification is
  276. // complete.
  277. //
  278. deferredNode = ExAllocatePool(PagedPool, sizeof(DEFERRED_REGISTRATION_ENTRY));
  279. if (deferredNode) {
  280. deferredNode->NotifyEntry = Entry;
  281. //
  282. // Consider this entry unregistered during the current
  283. // notification
  284. //
  285. Entry->Unregistered = TRUE;
  286. //
  287. // Reference the entry so that it doesn't go away until it has
  288. // been removed from the deferred registration list
  289. //
  290. IopReferenceNotify(Entry);
  291. //
  292. // Add this entry to the deferred registration list
  293. //
  294. IopAcquireNotifyLock(&IopDeferredRegistrationLock);
  295. InsertTailList(&IopDeferredRegistrationList, &deferredNode->ListEntry);
  296. IopReleaseNotifyLock(&IopDeferredRegistrationLock);
  297. } else {
  298. status = STATUS_INSUFFICIENT_RESOURCES;
  299. }
  300. } else {
  301. //
  302. // If there is currently no notification in progress, the deferred
  303. // registration list must be empty.
  304. //
  305. ASSERT(IsListEmpty(&IopDeferredRegistrationList));
  306. }
  307. KeReleaseGuardedMutex(&PiNotificationInProgressLock);
  308. return status;
  309. }
  310. NTSTATUS
  311. IoReportTargetDeviceChange(
  312. IN PDEVICE_OBJECT PhysicalDeviceObject,
  313. IN PVOID NotificationStructure // always begins with a PLUGPLAY_NOTIFICATION_HEADER
  314. )
  315. /*++
  316. Routine Description:
  317. This routine may be used to give notification of 3rd-party target device
  318. change events. This API will notify every driver that has registered for
  319. notification on a file object associated with PhysicalDeviceObject about
  320. the event indicated in the NotificationStructure.
  321. Parameters:
  322. PhysicalDeviceObject - Provides a pointer to the PDO that the change begin
  323. reported is associated with.
  324. NotificationStructure - Provides a pointer to the notification structure to be
  325. sent to all parties registered for notifications about changes to
  326. PhysicalDeviceObject.
  327. Return Value:
  328. Status code that indicates whether or not the function was successful.
  329. Note:
  330. This API may only be used to report non-PnP target device changes. In particular,
  331. it will fail if it's called with the NotificationStructure->Event field set to
  332. GUID_TARGET_DEVICE_QUERY_REMOVE, GUID_TARGET_DEVICE_REMOVE_CANCELLED, or
  333. GUID_TARGET_DEVICE_REMOVE_COMPLETE.
  334. --*/
  335. {
  336. NTSTATUS status = STATUS_SUCCESS;
  337. KEVENT completionEvent;
  338. NTSTATUS completionStatus;
  339. PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct;
  340. LONG dataSize;
  341. PAGED_CODE();
  342. notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
  343. ASSERT(notifyStruct);
  344. ASSERT_PDO(PhysicalDeviceObject);
  345. ASSERT(NULL == notifyStruct->FileObject);
  346. if (IopCompareGuid(&notifyStruct->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE) ||
  347. IopCompareGuid(&notifyStruct->Event, &GUID_TARGET_DEVICE_REMOVE_CANCELLED) ||
  348. IopCompareGuid(&notifyStruct->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
  349. //
  350. // Passed in an illegal value
  351. //
  352. IopDbgPrint((
  353. IOP_IOEVENT_ERROR_LEVEL,
  354. "IoReportTargetDeviceChange: "
  355. "Illegal Event type passed as custom notification\n"));
  356. return STATUS_INVALID_DEVICE_REQUEST;
  357. }
  358. if (notifyStruct->Size < FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer)) {
  359. return STATUS_INVALID_DEVICE_REQUEST;
  360. }
  361. dataSize = notifyStruct->Size - FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
  362. if (notifyStruct->NameBufferOffset != -1 && notifyStruct->NameBufferOffset > dataSize) {
  363. return STATUS_INVALID_DEVICE_REQUEST;
  364. }
  365. KeInitializeEvent(&completionEvent, NotificationEvent, FALSE);
  366. status = PpSetCustomTargetEvent( PhysicalDeviceObject,
  367. &completionEvent,
  368. (PULONG)&completionStatus,
  369. NULL,
  370. NULL,
  371. notifyStruct);
  372. if (NT_SUCCESS(status)) {
  373. KeWaitForSingleObject(&completionEvent, Executive, KernelMode, FALSE, NULL);
  374. status = completionStatus;
  375. }
  376. return status;
  377. }
  378. NTSTATUS
  379. IoReportTargetDeviceChangeAsynchronous(
  380. IN PDEVICE_OBJECT PhysicalDeviceObject,
  381. IN PVOID NotificationStructure, // always begins with a PLUGPLAY_NOTIFICATION_HEADER
  382. IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
  383. IN PVOID Context OPTIONAL
  384. )
  385. /*++
  386. Routine Description:
  387. This routine may be used to give notification of 3rd-party target device
  388. change events. This API will notify every driver that has registered for
  389. notification on a file object associated with PhysicalDeviceObject about
  390. the event indicated in the NotificationStructure.
  391. Parameters:
  392. PhysicalDeviceObject - Provides a pointer to the PDO that the change begin
  393. reported is associated with.
  394. NotificationStructure - Provides a pointer to the notification structure to be
  395. sent to all parties registered for notifications about changes to
  396. PhysicalDeviceObject.
  397. Return Value:
  398. Status code that indicates whether or not the function was successful.
  399. Note:
  400. This API may only be used to report non-PnP target device changes. In particular,
  401. it will fail if it's called with the NotificationStructure->Event field set to
  402. GUID_TARGET_DEVICE_QUERY_REMOVE, GUID_TARGET_DEVICE_REMOVE_CANCELLED, or
  403. GUID_TARGET_DEVICE_REMOVE_COMPLETE.
  404. --*/
  405. {
  406. PASYNC_TDC_WORK_ITEM asyncWorkItem;
  407. PWORK_QUEUE_ITEM workItem;
  408. NTSTATUS status;
  409. LONG dataSize;
  410. PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct;
  411. notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
  412. ASSERT(notifyStruct);
  413. ASSERT_PDO(PhysicalDeviceObject);
  414. ASSERT(NULL == notifyStruct->FileObject);
  415. if (IopCompareGuid(&notifyStruct->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE) ||
  416. IopCompareGuid(&notifyStruct->Event, &GUID_TARGET_DEVICE_REMOVE_CANCELLED) ||
  417. IopCompareGuid(&notifyStruct->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
  418. //
  419. // Passed in an illegal value
  420. //
  421. IopDbgPrint((
  422. IOP_IOEVENT_ERROR_LEVEL,
  423. "IoReportTargetDeviceChangeAsynchronous: "
  424. "Illegal Event type passed as custom notification\n"));
  425. return STATUS_INVALID_DEVICE_REQUEST;
  426. }
  427. if (notifyStruct->Size < FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer)) {
  428. return STATUS_INVALID_DEVICE_REQUEST;
  429. }
  430. dataSize = notifyStruct->Size - FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
  431. if (notifyStruct->NameBufferOffset != -1 && notifyStruct->NameBufferOffset > dataSize) {
  432. return STATUS_INVALID_DEVICE_REQUEST;
  433. }
  434. //
  435. // Since this routine can be called at DPC level we need to queue
  436. // a work item and process it when the irql drops.
  437. //
  438. asyncWorkItem = ExAllocatePool( NonPagedPool,
  439. sizeof(ASYNC_TDC_WORK_ITEM) + notifyStruct->Size);
  440. if (asyncWorkItem != NULL) {
  441. //
  442. // ISSUE-ADRIAO-2000/08/24 - We should use an IO work item here.
  443. //
  444. ObReferenceObject(PhysicalDeviceObject);
  445. asyncWorkItem->DeviceObject = PhysicalDeviceObject;
  446. asyncWorkItem->NotificationStructure =
  447. (PTARGET_DEVICE_CUSTOM_NOTIFICATION)((PUCHAR)asyncWorkItem + sizeof(ASYNC_TDC_WORK_ITEM));
  448. RtlCopyMemory( asyncWorkItem->NotificationStructure,
  449. notifyStruct,
  450. notifyStruct->Size);
  451. asyncWorkItem->Callback = Callback;
  452. asyncWorkItem->Context = Context;
  453. workItem = &asyncWorkItem->WorkItem;
  454. ExInitializeWorkItem(workItem, IopReportTargetDeviceChangeAsyncWorker, asyncWorkItem);
  455. //
  456. // Queue a work item to do the enumeration
  457. //
  458. ExQueueWorkItem(workItem, DelayedWorkQueue);
  459. status = STATUS_PENDING;
  460. } else {
  461. //
  462. // Failed to allocate memory for work item. Nothing we can do ...
  463. //
  464. status = STATUS_INSUFFICIENT_RESOURCES;
  465. }
  466. return status;
  467. }
  468. VOID
  469. IopReportTargetDeviceChangeAsyncWorker(
  470. PVOID Context
  471. )
  472. /*++
  473. Routine Description:
  474. This routine is the worker routine of IoInvalidateDeviceState.
  475. Its main purpose is to invoke IopSynchronousQueryDeviceState and release
  476. work item space.
  477. Parameters:
  478. Context - Supplies a pointer to the ASYNC_TDC_WORK_ITEM.
  479. ReturnValue:
  480. None.
  481. --*/
  482. {
  483. PASYNC_TDC_WORK_ITEM asyncWorkItem = (PASYNC_TDC_WORK_ITEM)Context;
  484. PAGED_CODE();
  485. PpSetCustomTargetEvent( asyncWorkItem->DeviceObject,
  486. NULL,
  487. NULL,
  488. asyncWorkItem->Callback,
  489. asyncWorkItem->Context,
  490. asyncWorkItem->NotificationStructure);
  491. ObDereferenceObject(asyncWorkItem->DeviceObject);
  492. ExFreePool(asyncWorkItem);
  493. }
  494. VOID
  495. IopInitializePlugPlayNotification(
  496. VOID
  497. )
  498. /*++
  499. Routine Description:
  500. This routine performs initialization required before any of the notification
  501. APIs can be called.
  502. Parameters:
  503. None
  504. Return Value:
  505. None
  506. --*/
  507. {
  508. ULONG count;
  509. PAGED_CODE();
  510. //
  511. // Initialize the notification structures
  512. //
  513. for (count = 0; count < NOTIFY_DEVICE_CLASS_HASH_BUCKETS; count++) {
  514. InitializeListHead(&IopDeviceClassNotifyList[count]);
  515. }
  516. //
  517. // Initialize the profile notification list
  518. //
  519. InitializeListHead(&IopProfileNotifyList);
  520. //
  521. // Initialize the deferred registration list
  522. //
  523. InitializeListHead(&IopDeferredRegistrationList);
  524. KeInitializeGuardedMutex(&IopDeviceClassNotifyLock);
  525. KeInitializeGuardedMutex(&IopTargetDeviceNotifyLock);
  526. KeInitializeGuardedMutex(&IopHwProfileNotifyLock);
  527. KeInitializeGuardedMutex(&IopDeferredRegistrationLock);
  528. }
  529. VOID
  530. IopReferenceNotify(
  531. PNOTIFY_ENTRY_HEADER Notify
  532. )
  533. /*++
  534. Routine Description:
  535. This routine increments the reference count for a notification entry.
  536. Parameters:
  537. Notify - Supplies a pointer to the notification entry to be referenced
  538. Return Value:
  539. None
  540. Note:
  541. The appropriate synchronization lock must be held on the notification
  542. list before this routine can be called
  543. --*/
  544. {
  545. PAGED_CODE();
  546. ASSERT(Notify);
  547. ASSERT(Notify->RefCount > 0);
  548. Notify->RefCount++;
  549. }
  550. VOID
  551. IopDereferenceNotify(
  552. PNOTIFY_ENTRY_HEADER Notify
  553. )
  554. /*++
  555. Routine Description:
  556. This routine decrements the reference count for a notification entry, removing
  557. the entry from the list and freeing the associated memory if there are no
  558. outstanding reference counts.
  559. Parameters:
  560. Notify - Supplies a pointer to the notification entry to be referenced
  561. Return Value:
  562. None
  563. Note:
  564. The appropriate synchronization lock must be held on the notification
  565. list before this routine can be called
  566. --*/
  567. {
  568. PAGED_CODE();
  569. ASSERT(Notify);
  570. ASSERT(Notify->RefCount > 0);
  571. Notify->RefCount--;
  572. if (Notify->RefCount == 0) {
  573. //
  574. // If the refcount is zero then the node should have been deregisterd
  575. // and is no longer needs to be in the list so remove and free it
  576. //
  577. ASSERT(Notify->Unregistered);
  578. //
  579. // Remove the notification entry from its list.
  580. //
  581. // Note that this MUST be done first, since the notification list head
  582. // for a target device notification entry resides in the target device
  583. // node, which may be freed immediately after the device object is
  584. // dereferenced. For notification entry types other than target device
  585. // change this is not critical, but still a good idea.
  586. //
  587. RemoveEntryList((PLIST_ENTRY)Notify);
  588. //
  589. // Dereference the driver object that registered for notifications
  590. //
  591. ObDereferenceObject(Notify->DriverObject);
  592. //
  593. // If this notification entry is for target device change, dereference
  594. // the PDO upon which this notification entry was hooked.
  595. //
  596. if (Notify->EventCategory == EventCategoryTargetDeviceChange) {
  597. PTARGET_DEVICE_NOTIFY_ENTRY entry = (PTARGET_DEVICE_NOTIFY_ENTRY)Notify;
  598. if (entry->PhysicalDeviceObject) {
  599. ObDereferenceObject(entry->PhysicalDeviceObject);
  600. entry->PhysicalDeviceObject = NULL;
  601. }
  602. }
  603. //
  604. // Dereference the opaque session object
  605. //
  606. if (Notify->OpaqueSession) {
  607. MmQuitNextSession(Notify->OpaqueSession);
  608. Notify->OpaqueSession = NULL;
  609. }
  610. //
  611. // Free the notification entry
  612. //
  613. ExFreePool(Notify);
  614. }
  615. }
  616. NTSTATUS
  617. IopRequestHwProfileChangeNotification(
  618. IN LPGUID EventGuid,
  619. IN PROFILE_NOTIFICATION_TIME NotificationTime,
  620. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  621. OUT PUNICODE_STRING VetoName OPTIONAL
  622. )
  623. /*++
  624. Routine Description:
  625. This routine is used to notify all registered drivers of a hardware profile
  626. change. If the operation is a HW provile change query then the operation
  627. is synchronous and the veto information is propagated. All other operations
  628. are asynchronous and veto information is not returned.
  629. Parameters:
  630. EventTypeGuid - The event that has occured
  631. NotificationTime - This is used to tell if we are already in an event
  632. when delivering a synchronous notification (ie,
  633. querying profile change to eject). It is one of
  634. three values:
  635. PROFILE_IN_PNPEVENT
  636. PROFILE_NOT_IN_PNPEVENT
  637. PROFILE_PERHAPS_IN_PNPEVENT
  638. VetoType - Type of vetoer.
  639. VetoName - Name of vetoer.
  640. Return Value:
  641. Status code that indicates whether or not the function was successful.
  642. Note:
  643. The contents of the notification structure *including* all pointers is only
  644. valid during the callback routine to which it was passed. If the data is
  645. required after the duration of the callback then it must be physically copied
  646. by the callback routine.
  647. --*/
  648. {
  649. NTSTATUS status=STATUS_SUCCESS,completionStatus;
  650. KEVENT completionEvent;
  651. ULONG dataSize,totalSize;
  652. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  653. PAGED_CODE();
  654. if ((!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE)) &&
  655. (!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_CHANGE_CANCELLED)) &&
  656. (!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_CHANGE_COMPLETE))) {
  657. //
  658. // Passed in an illegal value
  659. //
  660. IopDbgPrint((
  661. IOP_IOEVENT_ERROR_LEVEL,
  662. "IopRequestHwProfileChangeNotification: "
  663. "Illegal Event type passed as profile notification\n"));
  664. return STATUS_INVALID_DEVICE_REQUEST;
  665. }
  666. //
  667. // Only the query changes are synchronous, and in that case we must
  668. // know definitely whether we are nested within a Pnp event or not.
  669. //
  670. ASSERT((!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE))||
  671. (NotificationTime != PROFILE_PERHAPS_IN_PNPEVENT)) ;
  672. if (!IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE) ) {
  673. //
  674. // Asynchronous case. Very easy.
  675. //
  676. ASSERT(!ARGUMENT_PRESENT(VetoName));
  677. ASSERT(!ARGUMENT_PRESENT(VetoType));
  678. return PpSetHwProfileChangeEvent( EventGuid,
  679. NULL,
  680. NULL,
  681. NULL,
  682. NULL);
  683. }
  684. //
  685. // Query notifications are synchronous. Determine if we are currently
  686. // within an event, in which case we must do the notify here instead
  687. // of queueing it up.
  688. //
  689. if (NotificationTime == PROFILE_NOT_IN_PNPEVENT) {
  690. //
  691. // Queue up and block on the notification.
  692. //
  693. KeInitializeEvent(&completionEvent, NotificationEvent, FALSE);
  694. status = PpSetHwProfileChangeEvent( EventGuid,
  695. &completionEvent,
  696. &completionStatus,
  697. VetoType,
  698. VetoName);
  699. if (NT_SUCCESS(status)) {
  700. KeWaitForSingleObject( &completionEvent, Executive, KernelMode, FALSE, NULL );
  701. status = completionStatus;
  702. }
  703. return status;
  704. }
  705. //
  706. // Synchronous notify inside our Pnp event.
  707. //
  708. //
  709. // ISSUE-ADRIAO-1998/11/12 - We are MANUALLY sending the profile
  710. // query change notification because we are blocking inside a PnPEvent and
  711. // thus can't queue/wait on another!
  712. //
  713. ASSERT(PiNotificationInProgress == TRUE);
  714. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  715. totalSize = dataSize + FIELD_OFFSET (PNP_DEVICE_EVENT_ENTRY,Data);
  716. deviceEvent = ExAllocatePoolWithTag (PagedPool,
  717. totalSize,
  718. PNP_DEVICE_EVENT_ENTRY_TAG);
  719. if (NULL == deviceEvent) {
  720. return STATUS_INSUFFICIENT_RESOURCES;
  721. }
  722. //
  723. //Setup the PLUGPLAY_EVENT_BLOCK
  724. //
  725. RtlZeroMemory ((PVOID)deviceEvent,totalSize);
  726. deviceEvent->Data.EventCategory = HardwareProfileChangeEvent;
  727. RtlCopyMemory(&deviceEvent->Data.EventGuid, EventGuid, sizeof(GUID));
  728. deviceEvent->Data.TotalSize = dataSize;
  729. deviceEvent->CallerEvent = &completionEvent;
  730. deviceEvent->Data.Result = (PULONG)&completionStatus;
  731. deviceEvent->VetoType = VetoType;
  732. deviceEvent->VetoName = VetoName;
  733. //
  734. // Notify K-Mode
  735. //
  736. status = IopNotifyHwProfileChange(&deviceEvent->Data.EventGuid,
  737. VetoType,
  738. VetoName);
  739. if (!NT_SUCCESS(status)) {
  740. return status;
  741. }
  742. //
  743. // Notify user-mode (synchronously).
  744. //
  745. status = PiNotifyUserMode(deviceEvent);
  746. if (!NT_SUCCESS(status)) {
  747. //
  748. // Notify K-mode that the query has been cancelled.
  749. //
  750. IopNotifyHwProfileChange((LPGUID)&GUID_HWPROFILE_CHANGE_CANCELLED,
  751. NULL,
  752. NULL);
  753. }
  754. return status;
  755. }
  756. NTSTATUS
  757. IopNotifyHwProfileChange(
  758. IN LPGUID EventGuid,
  759. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  760. OUT PUNICODE_STRING VetoName OPTIONAL
  761. )
  762. /*++
  763. Routine Description:
  764. This routine is used to deliver the HWProfileNotifications. It is
  765. called from the worker thread only
  766. It does not return until all interested parties have been notified.
  767. Parameters:
  768. EventTypeGuid - The event that has occured
  769. Return Value:
  770. Status code that indicates whether or not the function was successful.
  771. Note:
  772. The contents of the notification structure *including* all pointers is only
  773. valid during the callback routine to which it was passed. If the data is
  774. required after the duration of the callback then it must be physically copied
  775. by the callback routine.
  776. --*/
  777. {
  778. NTSTATUS status = STATUS_SUCCESS, dispatchStatus;
  779. PHWPROFILE_NOTIFY_ENTRY pNotifyList, vetoEntry;
  780. PLIST_ENTRY link;
  781. PAGED_CODE();
  782. //Lock the Profile Notification List
  783. IopAcquireNotifyLock (&IopHwProfileNotifyLock);
  784. //
  785. // Grab the list head (inside the lock)
  786. //
  787. link = IopProfileNotifyList.Flink;
  788. pNotifyList=(PHWPROFILE_NOTIFY_ENTRY)link;
  789. //
  790. // Walk the circular list.
  791. //
  792. while (link != (PLIST_ENTRY)&IopProfileNotifyList) {
  793. if (!pNotifyList->Unregistered) {
  794. HWPROFILE_CHANGE_NOTIFICATION notification;
  795. //
  796. // Reference the entry so that no one deletes during the callback
  797. // and then release the lock
  798. //
  799. IopReferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
  800. IopReleaseNotifyLock(&IopHwProfileNotifyLock);
  801. //
  802. // Fill in the notification structure
  803. //
  804. notification.Version = PNP_NOTIFICATION_VERSION;
  805. notification.Size = sizeof(HWPROFILE_CHANGE_NOTIFICATION);
  806. notification.Event = *EventGuid;
  807. //
  808. // Dispatch the notification to the callback routine for the
  809. // appropriate session.
  810. //
  811. dispatchStatus = PiNotifyDriverCallback(pNotifyList->CallbackRoutine,
  812. &notification,
  813. pNotifyList->Context,
  814. pNotifyList->SessionId,
  815. pNotifyList->OpaqueSession,
  816. &status);
  817. ASSERT(NT_SUCCESS(dispatchStatus));
  818. //
  819. // Failure to dispatch the notification to the specified callback
  820. // should not be considered a veto.
  821. //
  822. if (!NT_SUCCESS(dispatchStatus)) {
  823. status = STATUS_SUCCESS;
  824. }
  825. //
  826. // If the caller returned anything other than success and it was a
  827. // query hardware profile change, we veto the query and send cancels
  828. // to all callers that already got the query.
  829. //
  830. if ((!NT_SUCCESS(status)) &&
  831. (IopCompareGuid(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE))) {
  832. if (VetoType) {
  833. *VetoType = PNP_VetoDriver;
  834. }
  835. if (VetoName) {
  836. VetoName->Length = 0;
  837. RtlCopyUnicodeString(VetoName, &pNotifyList->DriverObject->DriverName);
  838. }
  839. notification.Event = GUID_HWPROFILE_CHANGE_CANCELLED;
  840. notification.Size = sizeof(GUID_HWPROFILE_CHANGE_CANCELLED);
  841. //
  842. // Keep track of the entry that vetoed the query. We can't
  843. // dereference it just yet, because we may need to send it a
  844. // cancel-remove first. Since it's possible that the entry
  845. // may have been unregistered when the list was unlocked
  846. // during the query callback (removing all but the reference
  847. // we are currently holding), we need to make sure we don't
  848. // dereference it until we're absolutely done with it.
  849. //
  850. vetoEntry = pNotifyList;
  851. IopAcquireNotifyLock(&IopHwProfileNotifyLock);
  852. //
  853. // Make sure we are starting where we left off above, at the
  854. // vetoing entry.
  855. //
  856. ASSERT((PHWPROFILE_NOTIFY_ENTRY)link == vetoEntry);
  857. do {
  858. pNotifyList = (PHWPROFILE_NOTIFY_ENTRY)link;
  859. if (!pNotifyList->Unregistered) {
  860. IopReferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
  861. IopReleaseNotifyLock(&IopHwProfileNotifyLock);
  862. dispatchStatus = PiNotifyDriverCallback(pNotifyList->CallbackRoutine,
  863. &notification,
  864. pNotifyList->Context,
  865. pNotifyList->SessionId,
  866. pNotifyList->OpaqueSession,
  867. NULL);
  868. ASSERT(NT_SUCCESS(dispatchStatus));
  869. IopAcquireNotifyLock(&IopHwProfileNotifyLock);
  870. link = link->Blink;
  871. IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
  872. } else {
  873. link = link->Blink;
  874. }
  875. if (pNotifyList == vetoEntry) {
  876. //
  877. // Dereference the entry which vetoed the query change.
  878. //
  879. IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
  880. }
  881. } while (link != (PLIST_ENTRY)&IopProfileNotifyList);
  882. goto Clean0;
  883. }
  884. //
  885. // Reacquire the lock, walk forward, and dereference
  886. //
  887. IopAcquireNotifyLock (&IopHwProfileNotifyLock);
  888. link = link->Flink;
  889. IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)pNotifyList);
  890. pNotifyList=(PHWPROFILE_NOTIFY_ENTRY)link;
  891. } else {
  892. //
  893. //Walk forward if we hit an unregistered node
  894. //
  895. if (pNotifyList) {
  896. //
  897. //walk forward
  898. //
  899. link = link->Flink;
  900. pNotifyList=(PHWPROFILE_NOTIFY_ENTRY)link;
  901. }
  902. }
  903. }
  904. Clean0:
  905. //
  906. // UnLock the Profile Notification List
  907. //
  908. IopReleaseNotifyLock(&IopHwProfileNotifyLock);
  909. return status;
  910. }
  911. NTSTATUS
  912. IopNotifyTargetDeviceChange(
  913. IN LPCGUID EventGuid,
  914. IN PDEVICE_OBJECT DeviceObject,
  915. IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure OPTIONAL,
  916. OUT PDRIVER_OBJECT *VetoingDriver
  917. )
  918. /*++
  919. Routine Description:
  920. This routine is used to notify all registered drivers of a change to a
  921. particular device. It does not return until all interested parties have
  922. been notified.
  923. Parameters:
  924. EventGuid - The event guid to send to the drivers.
  925. DeviceObject - The device object for the affected device. The devnode for
  926. this device object contains a list of callback routines that have
  927. registered for notification of any changes on this device object.
  928. NotificationStructure - Custom notification structure to send to the
  929. registrants.
  930. VetoingDriver - Driver that vetoed the event if
  931. (EventGuid == GUID_TARGET_DEVICE_QUERY_REMOVE).
  932. Return Value:
  933. Status code that indicates whether or not the function was successful.
  934. Note:
  935. The contents of the notification structure *including* all pointers is only
  936. valid during the callback routine to which it was passed. If the data is
  937. required after the duration of the callback then it must be physically copied
  938. by the callback routine.
  939. --*/
  940. {
  941. NTSTATUS status, dispatchStatus;
  942. PLIST_ENTRY link;
  943. PTARGET_DEVICE_NOTIFY_ENTRY entry, vetoEntry;
  944. TARGET_DEVICE_REMOVAL_NOTIFICATION targetNotification;
  945. PVOID notification;
  946. PDEVICE_NODE deviceNode;
  947. BOOLEAN reverse;
  948. PAGED_CODE();
  949. ASSERT(DeviceObject != NULL);
  950. ASSERT(EventGuid != NULL);
  951. //
  952. // Reference the device object so it can't go away while we're doing notification
  953. //
  954. ObReferenceObject(DeviceObject);
  955. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  956. ASSERT(deviceNode != NULL);
  957. if (ARGUMENT_PRESENT(NotificationStructure)) {
  958. //
  959. // We're handling a custom notification
  960. //
  961. NotificationStructure->Version = PNP_NOTIFICATION_VERSION;
  962. } else {
  963. //
  964. // Fill in the notification structure
  965. //
  966. targetNotification.Version = PNP_NOTIFICATION_VERSION;
  967. targetNotification.Size = sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION);
  968. targetNotification.Event = *EventGuid;
  969. }
  970. //
  971. // Lock the notify list
  972. //
  973. IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
  974. //
  975. // Get the first entry
  976. //
  977. reverse = (BOOLEAN)IopCompareGuid(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_REMOVE_CANCELLED);
  978. if (reverse) {
  979. link = deviceNode->TargetDeviceNotify.Blink;
  980. } else {
  981. link = deviceNode->TargetDeviceNotify.Flink;
  982. }
  983. //
  984. // Iterate through the list
  985. //
  986. while (link != &deviceNode->TargetDeviceNotify) {
  987. entry = (PTARGET_DEVICE_NOTIFY_ENTRY)link;
  988. //
  989. // Only callback on registered nodes
  990. //
  991. if (!entry->Unregistered) {
  992. //
  993. // Reference the entry so that no one deletes during the callback
  994. // and then release the lock
  995. //
  996. IopReferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
  997. IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
  998. //
  999. // Select the notification structure to deliver and set the file
  1000. // object in the notification structure to that for the current
  1001. // entry
  1002. //
  1003. if (ARGUMENT_PRESENT(NotificationStructure)) {
  1004. NotificationStructure->FileObject = entry->FileObject;
  1005. notification = (PVOID)NotificationStructure;
  1006. } else {
  1007. targetNotification.FileObject = entry->FileObject;
  1008. notification = (PVOID)&targetNotification;
  1009. }
  1010. //
  1011. // Dispatch the notification to the callback routine for the
  1012. // appropriate session.
  1013. //
  1014. dispatchStatus = PiNotifyDriverCallback(entry->CallbackRoutine,
  1015. notification,
  1016. entry->Context,
  1017. entry->SessionId,
  1018. entry->OpaqueSession,
  1019. &status);
  1020. ASSERT(NT_SUCCESS(dispatchStatus));
  1021. //
  1022. // Failure to dispatch the notification to the specified callback
  1023. // should not be considered a veto.
  1024. //
  1025. if (!NT_SUCCESS(dispatchStatus)) {
  1026. status = STATUS_SUCCESS;
  1027. }
  1028. //
  1029. // If the caller returned anything other than success and it was
  1030. // a query remove, we veto the query remove and send cancels to
  1031. // all callers that already got the query remove.
  1032. //
  1033. if (!NT_SUCCESS(status)) {
  1034. if (IopCompareGuid(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_QUERY_REMOVE)) {
  1035. ASSERT(notification == (PVOID)&targetNotification);
  1036. if (VetoingDriver != NULL) {
  1037. *VetoingDriver = entry->DriverObject;
  1038. }
  1039. targetNotification.Event = GUID_TARGET_DEVICE_REMOVE_CANCELLED;
  1040. //
  1041. // Keep track of the entry that vetoed the query. We can't
  1042. // dereference it just yet, because we may need to send it a
  1043. // cancel-remove first. Since it's possible that the entry
  1044. // may have been unregistered when the list was unlocked
  1045. // during the query callback (removing all but the reference
  1046. // we are currently holding), we need to make sure we don't
  1047. // dereference it until we're absolutely done with it.
  1048. //
  1049. vetoEntry = entry;
  1050. IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
  1051. //
  1052. // Make sure we are starting where we left off above, at the
  1053. // vetoing entry.
  1054. //
  1055. ASSERT((PTARGET_DEVICE_NOTIFY_ENTRY)link == vetoEntry);
  1056. do {
  1057. entry = (PTARGET_DEVICE_NOTIFY_ENTRY)link;
  1058. if (!entry->Unregistered) {
  1059. //
  1060. // Reference the entry so that no one deletes during
  1061. // the callback and then release the lock
  1062. //
  1063. IopReferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
  1064. IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
  1065. //
  1066. // Set the file object in the notification structure
  1067. // to that for the current entry
  1068. //
  1069. targetNotification.FileObject = entry->FileObject;
  1070. //
  1071. // Dispatch the notification to the callback routine
  1072. // for the appropriate session.
  1073. //
  1074. dispatchStatus = PiNotifyDriverCallback(entry->CallbackRoutine,
  1075. &targetNotification,
  1076. entry->Context,
  1077. entry->SessionId,
  1078. entry->OpaqueSession,
  1079. NULL);
  1080. ASSERT(NT_SUCCESS(dispatchStatus));
  1081. //
  1082. // Reacquire the lock and dereference
  1083. //
  1084. IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
  1085. link = link->Blink;
  1086. IopDereferenceNotify( (PNOTIFY_ENTRY_HEADER) entry );
  1087. } else {
  1088. link = link->Blink;
  1089. }
  1090. if (entry == vetoEntry) {
  1091. //
  1092. // Dereference the entry which vetoed the query remove.
  1093. //
  1094. IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)vetoEntry);
  1095. }
  1096. } while (link != &deviceNode->TargetDeviceNotify);
  1097. goto Clean0;
  1098. } else {
  1099. ASSERT(notification == (PVOID)NotificationStructure);
  1100. IopDbgPrint((
  1101. IOP_IOEVENT_ERROR_LEVEL,
  1102. "IopNotifyTargetDeviceChange: "
  1103. "Driver %Z, handler @ 0x%p failed non-failable notification 0x%p with return code %x\n",
  1104. &entry->DriverObject->DriverName,
  1105. entry->CallbackRoutine,
  1106. notification,
  1107. status));
  1108. DbgBreakPoint();
  1109. }
  1110. }
  1111. //
  1112. // Reacquire the lock and dereference
  1113. //
  1114. IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
  1115. if (reverse) {
  1116. link = link->Blink;
  1117. } else {
  1118. link = link->Flink;
  1119. }
  1120. IopDereferenceNotify((PNOTIFY_ENTRY_HEADER)entry);
  1121. } else {
  1122. //
  1123. // Advance down the list
  1124. //
  1125. if (reverse) {
  1126. link = link->Blink;
  1127. } else {
  1128. link = link->Flink;
  1129. }
  1130. }
  1131. }
  1132. //
  1133. // If it's not a query, it can't be failed.
  1134. //
  1135. status = STATUS_SUCCESS;
  1136. Clean0:
  1137. //
  1138. // Release the lock and dereference the object
  1139. //
  1140. IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
  1141. ObDereferenceObject(DeviceObject);
  1142. return status;
  1143. }
  1144. NTSTATUS
  1145. IopNotifyDeviceClassChange(
  1146. LPGUID EventGuid,
  1147. LPGUID ClassGuid,
  1148. PUNICODE_STRING SymbolicLinkName
  1149. )
  1150. /*++
  1151. Routine Description:
  1152. This routine is used to notify all registered drivers of a changes to a
  1153. particular class of device. It does not return until all interested parties have
  1154. been notified.
  1155. Parameters:
  1156. EventTypeGuid - The event that has occured
  1157. ClassGuid - The device class this change has occured in
  1158. SymbolicLinkName - The kernel mode symbolic link name of the interface device
  1159. that changed
  1160. Return Value:
  1161. Status code that indicates whether or not the function was successful.
  1162. Note:
  1163. The contents of the notification structure *including* all pointers is only
  1164. valid during the callback routine to which it was passed. If the data is
  1165. required after the duration of the callback then it must be physically copied
  1166. by the callback routine.
  1167. --*/
  1168. {
  1169. NTSTATUS status, dispatchStatus;
  1170. PLIST_ENTRY link;
  1171. PDEVICE_CLASS_NOTIFY_ENTRY entry;
  1172. DEVICE_INTERFACE_CHANGE_NOTIFICATION notification;
  1173. ULONG hash;
  1174. PAGED_CODE();
  1175. //
  1176. // Fill in the notification structure
  1177. //
  1178. notification.Version = PNP_NOTIFICATION_VERSION;
  1179. notification.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
  1180. notification.Event = *EventGuid;
  1181. notification.InterfaceClassGuid = *ClassGuid;
  1182. notification.SymbolicLinkName = SymbolicLinkName;
  1183. //
  1184. // Lock the notify list
  1185. //
  1186. IopAcquireNotifyLock(&IopDeviceClassNotifyLock);
  1187. //
  1188. // Get the first entry
  1189. //
  1190. hash = IopHashGuid(ClassGuid);
  1191. link = IopDeviceClassNotifyList[hash].Flink;
  1192. //
  1193. // Iterate through the list
  1194. //
  1195. while (link != &IopDeviceClassNotifyList[hash]) {
  1196. entry = (PDEVICE_CLASS_NOTIFY_ENTRY)link;
  1197. //
  1198. // Only callback on registered nodes of the correct device class
  1199. //
  1200. if (!entry->Unregistered && IopCompareGuid(&(entry->ClassGuid), ClassGuid)) {
  1201. //
  1202. // Reference the entry so that no one deletes during the callback
  1203. // and then release the lock
  1204. //
  1205. IopReferenceNotify( (PNOTIFY_ENTRY_HEADER) entry );
  1206. IopReleaseNotifyLock(&IopDeviceClassNotifyLock);
  1207. //
  1208. // Dispatch the notification to the callback routine for the
  1209. // appropriate session. Ignore the returned result for non-query
  1210. // type events.
  1211. //
  1212. dispatchStatus = PiNotifyDriverCallback(entry->CallbackRoutine,
  1213. &notification,
  1214. entry->Context,
  1215. entry->SessionId,
  1216. entry->OpaqueSession,
  1217. &status);
  1218. ASSERT(NT_SUCCESS(dispatchStatus));
  1219. //
  1220. // ISSUE -2000/11/27 - JAMESCA: Overactive assert
  1221. // This assert is temporarily commented out until mountmgr is fixed.
  1222. //
  1223. // ASSERT(NT_SUCCESS(status));
  1224. //
  1225. // Reacquire the lock and dereference
  1226. //
  1227. IopAcquireNotifyLock(&IopDeviceClassNotifyLock);
  1228. link = link->Flink;
  1229. IopDereferenceNotify( (PNOTIFY_ENTRY_HEADER) entry );
  1230. } else {
  1231. //
  1232. // Advance down the list
  1233. //
  1234. link = link->Flink;
  1235. }
  1236. }
  1237. //
  1238. // Release the lock
  1239. //
  1240. IopReleaseNotifyLock(&IopDeviceClassNotifyLock);
  1241. return STATUS_SUCCESS;
  1242. }
  1243. NTSTATUS
  1244. IoRegisterPlugPlayNotification(
  1245. IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
  1246. IN ULONG EventCategoryFlags,
  1247. IN PVOID EventCategoryData OPTIONAL,
  1248. IN PDRIVER_OBJECT DriverObject,
  1249. IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
  1250. IN PVOID Context,
  1251. OUT PVOID *NotificationEntry
  1252. )
  1253. /*++
  1254. Routine Description:
  1255. IoRegisterPlugPlayNotification provides a mechanism by which WDM drivers may
  1256. receive notification (via callback) for a variety of Plug&Play events.
  1257. Arguments:
  1258. EventCategory - Specifies the event category being registered for. WDM drivers
  1259. may currently register for hard-ware profile changes, device class changes
  1260. (instance arrivals and removals), and target device changes (query-removal,
  1261. cancel-removal, removal-complete, as well as 3rd-party extensible events).
  1262. EventCategoryFlags - Supplies flags that modify the behavior of event registration.
  1263. There is a separate group of flags defined for each event category. Presently,
  1264. only the interface device change event category has any flags defined:
  1265. DEVICE_CLASS_NOTIFY_FOR_EXISTING_DEVICES -- Drivers wishing to retrieve a
  1266. complete list of all interface devices presently available, and keep
  1267. the list up-to-date (i.e., receive notification of interface device
  1268. arrivals and removals), may specify this flag. This will cause the
  1269. PnP manager to immediately notify the driver about every currently-existing
  1270. device of the specified interface class.
  1271. EventCategoryData - Used to 'filter' events of the desired category based on the
  1272. supplied criteria. Not all event categories will use this parameter. The
  1273. event categories presently defined use this information as fol-lows:
  1274. EventCategoryHardwareProfileChange -- this parameter is unused, and should be NULL.
  1275. EventCategoryDeviceClassChange -- LPGUID representing the interface class of interest
  1276. EventCategoryTargetDeviceChange -- PFILE_OBJECT of interest
  1277. DriverObject - The caller must supply a reference to its driver object (obtained via
  1278. ObReferenceObject), to prevent the driver from being unloaded while registered for
  1279. notification. The PnP Manager will dereference the driver object when the driver
  1280. unregisters for notification via IoUnregisterPlugPlayNotification).
  1281. CallbackRoutine - Entry point within the driver that the PnP manager should call
  1282. whenever an applicable PnP event occurs. The entry point must have the
  1283. following prototype:
  1284. typedef
  1285. NTSTATUS
  1286. (*PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) (
  1287. IN PVOID NotificationStructure,
  1288. IN PVOID Context
  1289. );
  1290. where NotificationStructure contains information about the event. Each event
  1291. GUID within an event category may potentially have its own notification structure
  1292. format, but the buffer must al-ways begin with a PLUGPLAY_NOTIFICATION_HEADER,
  1293. which indicates the size and ver-sion of the structure, as well as the GUID for
  1294. the event.
  1295. The Context parameter provides the callback with the same context data that the
  1296. caller passed in during registration.
  1297. Context - Points to the context data passed to the callback upon event notification.
  1298. NotificationEntry - Upon success, receives a handle representing the notification
  1299. registration. This handle may be used to unregister for notification via
  1300. IoUnregisterPlugPlayNotification.
  1301. --*/
  1302. {
  1303. NTSTATUS status;
  1304. PAGED_CODE();
  1305. ASSERT(NotificationEntry);
  1306. //
  1307. // Initialize out parameters
  1308. //
  1309. *NotificationEntry = NULL;
  1310. //
  1311. // Reference the driver object so it doesn't go away while we still have
  1312. // a pointer outstanding
  1313. //
  1314. status = ObReferenceObjectByPointer(DriverObject,
  1315. 0,
  1316. IoDriverObjectType,
  1317. KernelMode);
  1318. if (!NT_SUCCESS(status)) {
  1319. return status;
  1320. }
  1321. switch (EventCategory) {
  1322. case EventCategoryReserved:
  1323. {
  1324. //
  1325. // This is currently supported only for setupdd.sys in textmode setup.
  1326. //
  1327. if (ExpInTextModeSetup) {
  1328. PSETUP_NOTIFY_DATA setupData;
  1329. ASSERT(IopSetupNotifyData == NULL);
  1330. //
  1331. // Note that the only setup notification callback currently supported
  1332. // (setupdd.sys) is never in session space.
  1333. //
  1334. ASSERT(MmIsSessionAddress((PVOID)CallbackRoutine) == FALSE);
  1335. ASSERT(MmGetSessionId(PsGetCurrentProcess()) == 0);
  1336. //
  1337. // Allocate space for the setup data
  1338. //
  1339. setupData = ExAllocatePool(PagedPool, sizeof(SETUP_NOTIFY_DATA));
  1340. if (!setupData) {
  1341. status = STATUS_INSUFFICIENT_RESOURCES;
  1342. goto clean0;
  1343. }
  1344. //
  1345. // Store the required information
  1346. //
  1347. InitializeListHead(&(setupData->ListEntry));
  1348. setupData->EventCategory = EventCategory;
  1349. setupData->SessionId = MmGetSessionId(PsGetCurrentProcess());
  1350. setupData->CallbackRoutine = CallbackRoutine;
  1351. setupData->Context = Context;
  1352. setupData->RefCount = 1;
  1353. setupData->Unregistered = FALSE;
  1354. setupData->Lock = NULL;
  1355. setupData->DriverObject = DriverObject;
  1356. setupData->OpaqueSession = PiGetSession(CallbackRoutine, setupData->SessionId);
  1357. //
  1358. // Activate the notifications
  1359. //
  1360. IopSetupNotifyData = setupData;
  1361. }
  1362. //
  1363. // Explicitly NULL out the returned entry as you can *NOT* unregister
  1364. // for setup notifications
  1365. //
  1366. *NotificationEntry = NULL;
  1367. break;
  1368. }
  1369. case EventCategoryHardwareProfileChange:
  1370. {
  1371. PHWPROFILE_NOTIFY_ENTRY entry;
  1372. //
  1373. // new entry
  1374. //
  1375. entry =ExAllocatePool (PagedPool,sizeof (HWPROFILE_NOTIFY_ENTRY));
  1376. if (!entry) {
  1377. status = STATUS_INSUFFICIENT_RESOURCES;
  1378. goto clean0;
  1379. }
  1380. //
  1381. // Initialize the entry.
  1382. //
  1383. entry->EventCategory = EventCategory;
  1384. entry->SessionId = MmGetSessionId(PsGetCurrentProcess());
  1385. entry->CallbackRoutine = CallbackRoutine;
  1386. entry->Context = Context;
  1387. entry->RefCount = 1;
  1388. entry->Unregistered = FALSE;
  1389. entry->Lock = &IopHwProfileNotifyLock;
  1390. entry->DriverObject = DriverObject;
  1391. entry->OpaqueSession = PiGetSession(CallbackRoutine, entry->SessionId);
  1392. //
  1393. // Defer notification if neccessary.
  1394. //
  1395. status = PiDeferNotification((PNOTIFY_ENTRY_HEADER)entry);
  1396. if (!NT_SUCCESS(status)) {
  1397. ExFreePool(entry);
  1398. goto clean0;
  1399. }
  1400. //
  1401. // Insert the entry into its list.
  1402. //
  1403. PiLockedInsertTailList(&IopProfileNotifyList, &IopHwProfileNotifyLock, &entry->ListEntry);
  1404. *NotificationEntry = entry;
  1405. break;
  1406. }
  1407. case EventCategoryTargetDeviceChange:
  1408. {
  1409. PTARGET_DEVICE_NOTIFY_ENTRY entry;
  1410. PDEVICE_NODE deviceNode;
  1411. ASSERT(EventCategoryData);
  1412. //
  1413. // Allocate a new list entry
  1414. //
  1415. entry = ExAllocatePool(PagedPool, sizeof(TARGET_DEVICE_NOTIFY_ENTRY));
  1416. if (!entry) {
  1417. status = STATUS_INSUFFICIENT_RESOURCES;
  1418. goto clean0;
  1419. }
  1420. //
  1421. // Retrieve the device object associated with this file handle.
  1422. //
  1423. status = IopGetRelatedTargetDevice((PFILE_OBJECT)EventCategoryData,
  1424. &deviceNode);
  1425. if (!NT_SUCCESS(status)) {
  1426. ExFreePool(entry);
  1427. goto clean0;
  1428. }
  1429. //
  1430. // Fill out the entry
  1431. //
  1432. entry->EventCategory = EventCategory;
  1433. entry->SessionId = MmGetSessionId(PsGetCurrentProcess());
  1434. entry->CallbackRoutine = CallbackRoutine;
  1435. entry->Context = Context;
  1436. entry->DriverObject = DriverObject;
  1437. entry->RefCount = 1;
  1438. entry->Unregistered = FALSE;
  1439. entry->Lock = &IopTargetDeviceNotifyLock;
  1440. entry->FileObject = (PFILE_OBJECT)EventCategoryData;
  1441. entry->OpaqueSession = PiGetSession(CallbackRoutine, entry->SessionId);
  1442. //
  1443. // The PDO associated with the devnode we got back from
  1444. // IopGetRelatedTargetDevice has already been referenced by that
  1445. // routine. Store this reference away in the notification entry,
  1446. // so we can deref it later when the notification entry is unregistered.
  1447. //
  1448. ASSERT(deviceNode->PhysicalDeviceObject);
  1449. entry->PhysicalDeviceObject = deviceNode->PhysicalDeviceObject;
  1450. status = PiDeferNotification((PNOTIFY_ENTRY_HEADER)entry);
  1451. if (!NT_SUCCESS(status)) {
  1452. ExFreePool(entry);
  1453. goto clean0;
  1454. }
  1455. //
  1456. // Insert the entry into its list.
  1457. //
  1458. PiLockedInsertTailList(&deviceNode->TargetDeviceNotify, &IopTargetDeviceNotifyLock, &entry->ListEntry);
  1459. *NotificationEntry = entry;
  1460. break;
  1461. }
  1462. case EventCategoryDeviceInterfaceChange:
  1463. {
  1464. PDEVICE_CLASS_NOTIFY_ENTRY entry;
  1465. ASSERT(EventCategoryData);
  1466. //
  1467. // Allocate a new list entry
  1468. //
  1469. entry = ExAllocatePool(PagedPool, sizeof(DEVICE_CLASS_NOTIFY_ENTRY));
  1470. if (!entry) {
  1471. status = STATUS_INSUFFICIENT_RESOURCES;
  1472. goto clean0;
  1473. }
  1474. //
  1475. // Fill out the entry
  1476. //
  1477. entry->EventCategory = EventCategory;
  1478. entry->SessionId = MmGetSessionId(PsGetCurrentProcess());
  1479. entry->CallbackRoutine = CallbackRoutine;
  1480. entry->Context = Context;
  1481. entry->ClassGuid = *((LPGUID) EventCategoryData);
  1482. entry->RefCount = 1;
  1483. entry->Unregistered = FALSE;
  1484. entry->Lock = &IopDeviceClassNotifyLock;
  1485. entry->DriverObject = DriverObject;
  1486. entry->OpaqueSession = PiGetSession(CallbackRoutine, entry->SessionId);
  1487. //
  1488. // Defer notification if needed.
  1489. //
  1490. status = PiDeferNotification((PNOTIFY_ENTRY_HEADER)entry);
  1491. if (!NT_SUCCESS(status)) {
  1492. ExFreePool(entry);
  1493. goto clean0;
  1494. }
  1495. //
  1496. // Insert the entry into its list.
  1497. //
  1498. PiLockedInsertTailList(
  1499. (PLIST_ENTRY)&IopDeviceClassNotifyList[IopHashGuid(&(entry->ClassGuid))],
  1500. &IopDeviceClassNotifyLock,
  1501. &entry->ListEntry);
  1502. //
  1503. // See if we need to notify for all the device classes already present
  1504. //
  1505. if (EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES) {
  1506. PWCHAR pSymbolicLinks, pCurrent;
  1507. DEVICE_INTERFACE_CHANGE_NOTIFICATION notification;
  1508. UNICODE_STRING unicodeString;
  1509. //
  1510. // Fill in the notification structure
  1511. //
  1512. notification.Version = PNP_NOTIFICATION_VERSION;
  1513. notification.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
  1514. notification.Event = GUID_DEVICE_INTERFACE_ARRIVAL;
  1515. notification.InterfaceClassGuid = entry->ClassGuid;
  1516. //
  1517. // Get the list of all the devices of this function class that are
  1518. // already in the system
  1519. //
  1520. status = IoGetDeviceInterfaces(&(entry->ClassGuid),
  1521. NULL,
  1522. 0,
  1523. &pSymbolicLinks);
  1524. if (!NT_SUCCESS(status)) {
  1525. //
  1526. // No buffer will have been returned so just return status
  1527. //
  1528. goto clean0;
  1529. }
  1530. //
  1531. // Callback for each device currently in the system
  1532. //
  1533. pCurrent = pSymbolicLinks;
  1534. while(*pCurrent != UNICODE_NULL) {
  1535. NTSTATUS dispatchStatus, tempStatus;
  1536. RtlInitUnicodeString(&unicodeString, pCurrent);
  1537. notification.SymbolicLinkName = &unicodeString;
  1538. //
  1539. // Dispatch the notification to the callback routine for the
  1540. // appropriate session. Ignore the returned result for non-query
  1541. // type events.
  1542. //
  1543. dispatchStatus = PiNotifyDriverCallback(CallbackRoutine,
  1544. &notification,
  1545. Context,
  1546. entry->SessionId,
  1547. entry->OpaqueSession,
  1548. &tempStatus);
  1549. //
  1550. // ISSUE -2000/11/27 - JAMESCA: Overactive assert
  1551. // ClusDisk failed here. The code in question is being
  1552. // removed, but we don't we want to make sure we flush
  1553. // anyone else out before we enable it again.
  1554. //
  1555. //ASSERT(NT_SUCCESS(dispatchStatus) && NT_SUCCESS(tempStatus));
  1556. ASSERT(NT_SUCCESS(dispatchStatus));
  1557. pCurrent += (unicodeString.Length / sizeof(WCHAR)) + 1;
  1558. }
  1559. ExFreePool(pSymbolicLinks);
  1560. }
  1561. *NotificationEntry = entry;
  1562. }
  1563. break;
  1564. }
  1565. clean0:
  1566. if (!NT_SUCCESS(status)) {
  1567. ObDereferenceObject(DriverObject);
  1568. }
  1569. return status;
  1570. }
  1571. NTSTATUS
  1572. IopGetRelatedTargetDevice(
  1573. IN PFILE_OBJECT FileObject,
  1574. OUT PDEVICE_NODE *DeviceNode
  1575. )
  1576. /*++
  1577. Routine Description:
  1578. IopGetRelatedTargetDevice retrieves the device object associated with
  1579. the specified file object and then sends a query device relations irp
  1580. to that device object.
  1581. NOTE: The PDO associated with the returned device node has been referenced,
  1582. and must be dereferenced when no longer needed.
  1583. Arguments:
  1584. FileObject - Specifies the file object that is associated with the device
  1585. object that will receive the query device relations irp.
  1586. DeviceNode - Returns the related target device node.
  1587. ReturnValue
  1588. Returns an NTSTATUS value.
  1589. --*/
  1590. {
  1591. NTSTATUS status;
  1592. IO_STACK_LOCATION irpSp;
  1593. PDEVICE_OBJECT deviceObject, targetDeviceObject;
  1594. PDEVICE_RELATIONS deviceRelations;
  1595. PDEVICE_NODE targetDeviceNode;
  1596. PAGED_CODE();
  1597. ASSERT(FileObject);
  1598. *DeviceNode = NULL;
  1599. //
  1600. // Retrieve the device object associated with this file handle.
  1601. //
  1602. deviceObject = IoGetRelatedDeviceObject(FileObject);
  1603. if (!deviceObject) {
  1604. return STATUS_NO_SUCH_DEVICE;
  1605. }
  1606. //
  1607. // Query what the "actual" target device node should be for
  1608. // this file object. Initialize the stack location to pass to
  1609. // IopSynchronousCall() and then send the IRP to the device
  1610. // object that's associated with the file handle.
  1611. //
  1612. RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
  1613. irpSp.MajorFunction = IRP_MJ_PNP;
  1614. irpSp.MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
  1615. irpSp.Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
  1616. irpSp.DeviceObject = deviceObject;
  1617. irpSp.FileObject = FileObject;
  1618. status = IopSynchronousCall(deviceObject, &irpSp, (PULONG_PTR)&deviceRelations);
  1619. if (!NT_SUCCESS(status)) {
  1620. return status;
  1621. }
  1622. ASSERT(deviceRelations);
  1623. if (deviceRelations) {
  1624. ASSERT(deviceRelations->Count == 1);
  1625. if (deviceRelations->Count == 1) {
  1626. targetDeviceObject = deviceRelations->Objects[0];
  1627. } else {
  1628. targetDeviceObject = NULL;
  1629. }
  1630. ExFreePool(deviceRelations);
  1631. if (targetDeviceObject) {
  1632. targetDeviceNode = (PDEVICE_NODE) targetDeviceObject->DeviceObjectExtension->DeviceNode;
  1633. if (targetDeviceNode) {
  1634. *DeviceNode = targetDeviceNode;
  1635. return status;
  1636. }
  1637. }
  1638. }
  1639. //
  1640. // Definite driver screw up. If the verifier is enabled we will fail the
  1641. // driver. Otherwise, we will ignore this. Note that we would have crashed
  1642. // in Win2K!
  1643. //
  1644. PpvUtilFailDriver(
  1645. PPVERROR_MISHANDLED_TARGET_DEVICE_RELATIONS,
  1646. (PVOID) deviceObject->DriverObject->MajorFunction[IRP_MJ_PNP],
  1647. deviceObject,
  1648. NULL);
  1649. return STATUS_NO_SUCH_DEVICE;
  1650. }
  1651. NTSTATUS
  1652. IoGetRelatedTargetDevice(
  1653. IN PFILE_OBJECT FileObject,
  1654. OUT PDEVICE_OBJECT *DeviceObject
  1655. )
  1656. /*++
  1657. Routine Description:
  1658. IoGetRelatedTargetDevice retrieves the device object associated with
  1659. the specified file object and then sends a query device relations irp
  1660. to that device object.
  1661. NOTE: The PDO associated with the returned device node has been referenced,
  1662. and must be dereferenced when no longer needed.
  1663. Arguments:
  1664. FileObject - Specifies the file object that is associated with the device
  1665. object that will receive the query device relations irp.
  1666. DeviceObject - Returns the related target device object.
  1667. ReturnValue
  1668. Returns an NTSTATUS value.
  1669. --*/
  1670. {
  1671. NTSTATUS status;
  1672. PDEVICE_NODE deviceNode = NULL;
  1673. PAGED_CODE();
  1674. status = IopGetRelatedTargetDevice(FileObject, &deviceNode);
  1675. if (NT_SUCCESS(status) && deviceNode != NULL) {
  1676. *DeviceObject = deviceNode->PhysicalDeviceObject;
  1677. }
  1678. return status;
  1679. }
  1680. NTSTATUS
  1681. IopNotifySetupDeviceArrival(
  1682. PDEVICE_OBJECT PhysicalDeviceObject, // PDO of the device
  1683. HANDLE EnumEntryKey, // Handle into the enum branch of the registry for this device
  1684. BOOLEAN InstallDriver
  1685. )
  1686. /*++
  1687. Routine Description:
  1688. This routine is used to notify setup (during text-mode setup) of arrivals
  1689. of a particular device. It does not return until all interested parties have
  1690. been notified.
  1691. Parameters:
  1692. PhysicalDeviceObject - Supplies a pointer to the PDO of the newly arrived
  1693. device.
  1694. EnumEntryKey - Supplies a handle to the key associated with the devide under
  1695. the Enum\ branch of the registry. Can be NULL in which case the key
  1696. will be opened here.
  1697. InstallDriver - Indicates whether setup should attempt to install a driver
  1698. for this object. Device objects created through
  1699. IoReportDetectedDevice() already have a driver but we want
  1700. to indicate them to setup anyway.
  1701. Return Value:
  1702. Status code that indicates whether or not the function was successful.
  1703. Note:
  1704. The contents of the notification structure *including* all pointers is only
  1705. valid during the callback routine to which it was passed. If the data is
  1706. required after the duration of the callback then it must be physically copied
  1707. by the callback routine.
  1708. --*/
  1709. {
  1710. NTSTATUS status, dispatchStatus;
  1711. SETUP_DEVICE_ARRIVAL_NOTIFICATION notification;
  1712. PDEVICE_NODE deviceNode;
  1713. HANDLE enumKey = NULL;
  1714. PAGED_CODE();
  1715. //
  1716. // Only perform notifications if someone has registered
  1717. //
  1718. if (IopSetupNotifyData) {
  1719. if (!EnumEntryKey) {
  1720. status = IopDeviceObjectToDeviceInstance(PhysicalDeviceObject,
  1721. &enumKey,
  1722. KEY_WRITE);
  1723. if (!NT_SUCCESS(status)) {
  1724. return status;
  1725. }
  1726. EnumEntryKey = enumKey;
  1727. }
  1728. //
  1729. // Fill in the notification structure
  1730. //
  1731. notification.Version = PNP_NOTIFICATION_VERSION;
  1732. notification.Size = sizeof(SETUP_DEVICE_ARRIVAL_NOTIFICATION);
  1733. notification.Event = GUID_SETUP_DEVICE_ARRIVAL;
  1734. notification.PhysicalDeviceObject = PhysicalDeviceObject;
  1735. notification.EnumEntryKey = EnumEntryKey;
  1736. deviceNode = (PDEVICE_NODE) PhysicalDeviceObject->DeviceObjectExtension->DeviceNode;
  1737. notification.EnumPath = &deviceNode->InstancePath;
  1738. notification.InstallDriver = InstallDriver;
  1739. //
  1740. // Note that the only setup notification callback currently supported
  1741. // (setupdd.sys) is never in session space.
  1742. //
  1743. ASSERT(MmIsSessionAddress((PVOID)(IopSetupNotifyData->CallbackRoutine)) == FALSE);
  1744. ASSERT(IopSetupNotifyData->SessionId == 0);
  1745. //
  1746. // Dispatch the notification to the callback routine for the
  1747. // appropriate session.
  1748. //
  1749. dispatchStatus = PiNotifyDriverCallback(IopSetupNotifyData->CallbackRoutine,
  1750. &notification,
  1751. IopSetupNotifyData->Context,
  1752. IopSetupNotifyData->SessionId,
  1753. IopSetupNotifyData->OpaqueSession,
  1754. &status);
  1755. ASSERT(NT_SUCCESS(dispatchStatus));
  1756. //
  1757. // Failure to dispatch setup notification should be reported as if a
  1758. // match was not found, because the device has not been setup.
  1759. //
  1760. if (!NT_SUCCESS(dispatchStatus)) {
  1761. status = STATUS_OBJECT_NAME_NOT_FOUND;
  1762. }
  1763. if (enumKey) {
  1764. ZwClose(enumKey);
  1765. }
  1766. return status;
  1767. } else {
  1768. return STATUS_OBJECT_NAME_NOT_FOUND;
  1769. }
  1770. }
  1771. NTSTATUS
  1772. IoNotifyPowerOperationVetoed(
  1773. IN POWER_ACTION VetoedPowerOperation,
  1774. IN PDEVICE_OBJECT TargetedDeviceObject OPTIONAL,
  1775. IN PDEVICE_OBJECT VetoingDeviceObject
  1776. )
  1777. /*++
  1778. Routine Description:
  1779. This routine is called by the power subsystem to initiate user-mode
  1780. notification of vetoed system power events. The power events are submitted
  1781. into a serialized asynchronous queue. This queue is processed by a work
  1782. item. This routine does not wait for the event to be processed.
  1783. Parameters:
  1784. VetoedPowerOperation - Specifies the system-wide power action that was
  1785. vetoed.
  1786. TargetedDeviceObject - Optionally, supplies the device object target of the
  1787. vetoed operation.
  1788. VetoingDeviceObject - Specifies the device object responsible for vetoing
  1789. the power operation.
  1790. Return Value:
  1791. Status code that indicates whether or not the event was successfully
  1792. inserted into the asynchronous event queue..
  1793. --*/
  1794. {
  1795. PDEVICE_NODE deviceNode, vetoingDeviceNode;
  1796. PDEVICE_OBJECT deviceObject;
  1797. PAGED_CODE();
  1798. //
  1799. // We have two types of power events, system wide (standby) and device
  1800. // targetted (warm eject). Rather than have two different veto mechanisms,
  1801. // we just retarget the operation against the root device if none is
  1802. // specified (hey, someone's gotta represent the system, right?).
  1803. //
  1804. if (TargetedDeviceObject) {
  1805. deviceObject = TargetedDeviceObject;
  1806. } else {
  1807. deviceObject = IopRootDeviceNode->PhysicalDeviceObject;
  1808. }
  1809. deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
  1810. if (!deviceNode) {
  1811. return STATUS_INVALID_PARAMETER_2;
  1812. }
  1813. vetoingDeviceNode = (PDEVICE_NODE)VetoingDeviceObject->DeviceObjectExtension->DeviceNode;
  1814. if (!vetoingDeviceNode) {
  1815. return STATUS_INVALID_PARAMETER_3;
  1816. }
  1817. return PpSetPowerVetoEvent(
  1818. VetoedPowerOperation,
  1819. NULL,
  1820. NULL,
  1821. deviceObject,
  1822. PNP_VetoDevice,
  1823. &vetoingDeviceNode->InstancePath);
  1824. }
  1825. ULONG
  1826. IoPnPDeliverServicePowerNotification(
  1827. IN POWER_ACTION PowerOperation,
  1828. IN ULONG PowerNotificationCode,
  1829. IN ULONG PowerNotificationData,
  1830. IN BOOLEAN Synchronous
  1831. )
  1832. /*++
  1833. Routine Description:
  1834. This routine is called by the win32k driver to notify user-mode services of
  1835. system power events. The power events are submitted into a serialized
  1836. asynchronous queue. This queue is processed by a work item.
  1837. Parameters:
  1838. PowerOperation - Specifies the system-wide power action that has occured.
  1839. If the Synchronous parameter is TRUE, the event is a query for
  1840. permission to perform the supplied power operation.
  1841. PowerNotificationCode - Supplies the power event code that is to be
  1842. communicated to user-mode components.
  1843. (Specifically, this event code is actually one of the PBT_APM* user-mode
  1844. power event ids, as defined in sdk\inc\winuser.h. It is typically used
  1845. as the WPARAM data associated with WM_POWERBROADCAST user-mode window
  1846. messages. It is supplied to kernel-mode PnP, directly from win32k, for
  1847. the explicit purpose of user-mode power event notification.)
  1848. PowerNotificationData - Specifies additional event-specific data for the specified
  1849. power event id.
  1850. (Specifically, this event data is the LPARAM data for the corresponding
  1851. PBT_APM* user-mode power event id, specified above.)
  1852. Synchronous - Specifies whether this is a query operation. If the event is
  1853. a query, this routine will wait for the result of the query before
  1854. returning. If the query event is unsuccessful, this routine will
  1855. initiate an appropriate veto event.
  1856. Return Value:
  1857. Returns a non-zero value if the event was successful, zero otherwise.
  1858. --*/
  1859. {
  1860. NTSTATUS status = STATUS_SUCCESS;
  1861. KEVENT completionEvent;
  1862. NTSTATUS completionStatus=STATUS_SUCCESS;
  1863. PNP_VETO_TYPE vetoType = PNP_VetoTypeUnknown;
  1864. UNICODE_STRING vetoName;
  1865. PAGED_CODE();
  1866. if (Synchronous) {
  1867. vetoName.Buffer = ExAllocatePool (PagedPool,MAX_VETO_NAME_LENGTH*sizeof (WCHAR));
  1868. if (vetoName.Buffer) {
  1869. vetoName.MaximumLength = MAX_VETO_NAME_LENGTH;
  1870. }else {
  1871. vetoName.MaximumLength = 0;
  1872. }
  1873. vetoName.Length = 0;
  1874. KeInitializeEvent(&completionEvent, NotificationEvent, FALSE);
  1875. status = PpSetPowerEvent(PowerNotificationCode,
  1876. PowerNotificationData,
  1877. &completionEvent,
  1878. &completionStatus,
  1879. &vetoType,
  1880. &vetoName);
  1881. if (NT_SUCCESS(status)) {
  1882. //
  1883. // PpSetPowerEvent returns success immediately after the event has
  1884. // been successfully inserted into the event queue. Queued power
  1885. // events are sent to user-mode via PiNotifyUserMode, which waits
  1886. // for the the result. PiNotifyUserMode signals the completionEvent
  1887. // below when the user response is received.
  1888. //
  1889. KeWaitForSingleObject( &completionEvent, Executive, KernelMode, FALSE, NULL );
  1890. status = completionStatus;
  1891. //
  1892. // We only have power event veto information to report if
  1893. // user-mode responded to the event with failure.
  1894. //
  1895. if (!NT_SUCCESS(completionStatus)) {
  1896. //
  1897. // PpSetPowerVetoEvent requires a device object as the target of
  1898. // the vetoed power operation. Since this is a system-wide
  1899. // event, we just target the operation against the root device.
  1900. //
  1901. PpSetPowerVetoEvent(PowerOperation,
  1902. NULL,
  1903. NULL,
  1904. IopRootDeviceNode->PhysicalDeviceObject,
  1905. vetoType,
  1906. &vetoName);
  1907. }
  1908. }
  1909. if (vetoName.Buffer) {
  1910. ExFreePool (vetoName.Buffer);
  1911. }
  1912. } else {
  1913. //
  1914. // No response is required for 'asynchronous' (non-query) events.
  1915. // Just set the event and go.
  1916. //
  1917. status = PpSetPowerEvent(PowerNotificationCode,
  1918. PowerNotificationData,
  1919. NULL,
  1920. NULL,
  1921. NULL,
  1922. NULL);
  1923. }
  1924. //
  1925. // Since the user-mode power notification routine only returns a BOOLEAN
  1926. // success value, PiNotifyUserMode only returns one of the following status
  1927. // values:
  1928. //
  1929. ASSERT ((completionStatus == STATUS_SUCCESS) ||
  1930. (completionStatus == STATUS_UNSUCCESSFUL));
  1931. //
  1932. // The private code in Win32k that calls this, assumes that 0 is failure, !0 is success
  1933. //
  1934. return (NT_SUCCESS(completionStatus));
  1935. }
  1936. VOID
  1937. IopOrphanNotification(
  1938. IN PDEVICE_NODE TargetNode
  1939. )
  1940. /*++
  1941. Routine Description:
  1942. This routine releases the references to the device object for all the
  1943. notifications entries of a device object, then fixes up the notification
  1944. node to not point to a physical device object.
  1945. Parameters:
  1946. TargetNode - Specifies the device node whose registered target device
  1947. notification recipients are to be orphaned.
  1948. Return Value:
  1949. None.
  1950. Notes:
  1951. The notification node will be released when IoUnregisterPlugPlayNotification
  1952. is actually called, but the device object will already be gone.
  1953. --*/
  1954. {
  1955. PTARGET_DEVICE_NOTIFY_ENTRY entry;
  1956. PAGED_CODE();
  1957. IopAcquireNotifyLock(&IopTargetDeviceNotifyLock);
  1958. while (!IsListEmpty(&TargetNode->TargetDeviceNotify)) {
  1959. //
  1960. // Remove all target device change notification entries for this devnode
  1961. //
  1962. entry = (PTARGET_DEVICE_NOTIFY_ENTRY)
  1963. RemoveHeadList(&TargetNode->TargetDeviceNotify);
  1964. ASSERT(entry->EventCategory == EventCategoryTargetDeviceChange);
  1965. //
  1966. // Re-initialize the orphaned list entry so we don't attempt to remove
  1967. // it from the list again.
  1968. //
  1969. InitializeListHead((PLIST_ENTRY)entry);
  1970. //
  1971. // Dereference the target device object, and NULL it out so we don't
  1972. // attempt to dereference it when the entry is actually unregistered.
  1973. //
  1974. if (entry->PhysicalDeviceObject) {
  1975. ObDereferenceObject(entry->PhysicalDeviceObject);
  1976. entry->PhysicalDeviceObject = NULL;
  1977. }
  1978. }
  1979. IopReleaseNotifyLock(&IopTargetDeviceNotifyLock);
  1980. return;
  1981. }
  1982. NTSTATUS
  1983. PiNotifyDriverCallback(
  1984. IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
  1985. IN PVOID NotificationStructure,
  1986. IN PVOID Context,
  1987. IN ULONG SessionId,
  1988. IN PVOID OpaqueSession OPTIONAL,
  1989. OUT PNTSTATUS CallbackStatus OPTIONAL
  1990. )
  1991. /*++
  1992. Routine Description:
  1993. This routine dispatches a plug and play notification event to a specified
  1994. callback routine.
  1995. If the callback routine specifies an address outside of session space, or if
  1996. the calling process is already in the context of the specified session, it
  1997. will call the callback routine directly.
  1998. Otherwise, this routine will attempt to attach to the specified session and
  1999. call the callback routine.
  2000. Parameters:
  2001. CallbackRoutine - Entry point within the driver that will be called with
  2002. information about the event that has occured.
  2003. NotificationStructure - Contains information about the event.
  2004. Context - Points to the context data supplied at registration.
  2005. SessionId - Specifies the ID of the session in which the specified
  2006. callback is to be called.
  2007. OpqueSession - Optionally, specifies the opaque handle to the session that
  2008. to attach to when the specified callback is called.
  2009. CallbackStatus - Optionally, supplies the address of a variable to receive
  2010. the NTSTATUS code returned by the callback routine.
  2011. Return Value:
  2012. Status code that indicates whether or not the function was successful.
  2013. Notes:
  2014. Returns STATUS_NOT_FOUND if the specified session was not found.
  2015. --*/
  2016. {
  2017. NTSTATUS Status, CallStatus;
  2018. KAPC_STATE ApcState;
  2019. #if DBG
  2020. KIRQL Irql;
  2021. ULONG CombinedApcDisable;
  2022. #endif
  2023. PAGED_CODE();
  2024. //
  2025. // Make sure we have all the information we need to deliver notification.
  2026. //
  2027. if (!ARGUMENT_PRESENT(CallbackRoutine) ||
  2028. !ARGUMENT_PRESENT(NotificationStructure)) {
  2029. return STATUS_INVALID_PARAMETER;
  2030. }
  2031. #if DBG
  2032. //
  2033. // Remember the current IRQL and ApcDisable count so we can make sure
  2034. // the callback routine returns with these in tact.
  2035. //
  2036. Irql = KeGetCurrentIrql();
  2037. CombinedApcDisable = KeGetCurrentThread()->CombinedApcDisable;
  2038. #endif // DBG
  2039. if ((OpaqueSession == NULL) ||
  2040. ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) &&
  2041. (SessionId == PsGetCurrentProcessSessionId()))) {
  2042. //
  2043. // No session object was specified, or the current process is already in
  2044. // the specified session, so just call the callback routine directly.
  2045. //
  2046. ASSERT(!MmIsSessionAddress((PVOID)CallbackRoutine) || OpaqueSession);
  2047. IopDbgPrint((
  2048. IOP_IOEVENT_TRACE_LEVEL,
  2049. "PiNotifyDriverCallback: "
  2050. "calling notification callback @ 0x%p directly\n",
  2051. CallbackRoutine));
  2052. CallStatus = (CallbackRoutine)(NotificationStructure,
  2053. Context);
  2054. if (ARGUMENT_PRESENT(CallbackStatus)) {
  2055. *CallbackStatus = CallStatus;
  2056. }
  2057. Status = STATUS_SUCCESS;
  2058. } else {
  2059. //
  2060. // Otherwise, call the callback routine in session space.
  2061. //
  2062. ASSERT(MmIsSessionAddress((PVOID)CallbackRoutine));
  2063. //
  2064. // Attach to the specified session.
  2065. //
  2066. Status = MmAttachSession(OpaqueSession, &ApcState);
  2067. ASSERT(NT_SUCCESS(Status));
  2068. if (NT_SUCCESS(Status)) {
  2069. //
  2070. // Dispatch notification to the callback routine.
  2071. //
  2072. IopDbgPrint((
  2073. IOP_IOEVENT_TRACE_LEVEL,
  2074. "PiNotifyDriverCallback: "
  2075. "calling notification callback @ 0x%p for SessionId %d\n",
  2076. CallbackRoutine,
  2077. SessionId));
  2078. CallStatus = (CallbackRoutine)(NotificationStructure,
  2079. Context);
  2080. //
  2081. // Return the callback status.
  2082. //
  2083. if (ARGUMENT_PRESENT(CallbackStatus)) {
  2084. *CallbackStatus = CallStatus;
  2085. }
  2086. //
  2087. // Detach from the session.
  2088. //
  2089. Status = MmDetachSession(OpaqueSession, &ApcState);
  2090. ASSERT(NT_SUCCESS(Status));
  2091. }
  2092. }
  2093. #if DBG
  2094. //
  2095. // Check the IRQL and ApcDisable count.
  2096. //
  2097. if (Irql != KeGetCurrentIrql()) {
  2098. IopDbgPrint((
  2099. IOP_IOEVENT_ERROR_LEVEL,
  2100. "PiNotifyDriverCallback: "
  2101. "notification handler @ 0x%p returned at raised IRQL = %d, original = %d\n",
  2102. CallbackRoutine,
  2103. KeGetCurrentIrql(),
  2104. Irql));
  2105. DbgBreakPoint();
  2106. }
  2107. if (CombinedApcDisable != KeGetCurrentThread()->CombinedApcDisable) {
  2108. IopDbgPrint((
  2109. IOP_IOEVENT_ERROR_LEVEL,
  2110. "PiNotifyDriverCallback: "
  2111. "notification handler @ 0x%p returned with different CombinedApcDisable = %d, original = %d\n",
  2112. CallbackRoutine,
  2113. KeGetCurrentThread()->CombinedApcDisable,
  2114. CombinedApcDisable));
  2115. DbgBreakPoint();
  2116. }
  2117. #endif // DBG
  2118. return Status;
  2119. }
  2120. #ifdef ALLOC_DATA_PRAGMA
  2121. #pragma const_seg()
  2122. #endif