Source code of Windows XP (NT5)
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.

2934 lines
88 KiB

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