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.

5183 lines
153 KiB

  1. /*++
  2. Copyright (c) 1995-2000 Microsoft Corporation
  3. Module Name:
  4. pnpevent.c
  5. Abstract:
  6. Routines dealing with Plug and Play event management/notification.
  7. Author:
  8. Lonny McMichael (lonnym) 02/14/95
  9. Paula Tomlinson (paulat) 07/01/96
  10. Revision History:
  11. --*/
  12. #include "pnpmgrp.h"
  13. #pragma hdrstop
  14. #include <wdmguid.h>
  15. #include <pnpmgr.h>
  16. #include <pnpsetup.h>
  17. /*
  18. * Design notes:
  19. *
  20. * When UmPnpMgr needs to initiate an action (which it might do to complete
  21. * a CmXxx API), it calls NtPlugPlayControl. NtPlugPlayControl then usually
  22. * invokes one of the PpSetXxx functions. Similarly, if Io routines need to
  23. * initiate such an action (say due a hardware initiated eject), they call one
  24. * of the following PpSetXxx functions (or an intermediate):
  25. *
  26. * Operations synchronized via the queue
  27. * PpSetDeviceClassChange (async)
  28. * PpSetTargetDeviceRemove (optional event)
  29. * PpSetCustomTargetEvent (optional event)
  30. * PpSetHwProfileChangeEvent (optional event)
  31. * PpSetPowerEvent (optional event)
  32. * PpSetPlugPlayEvent (async)
  33. * PpSetDeviceRemovalSafe (optional event)
  34. * PpSetBlockedDriverEvent (async)
  35. * PpSynchronizeDeviceEventQueue (sync, enqueues a noop to flush queue)
  36. *
  37. * The PpSetXxx functions enqueue items to be processed into the Pnp event
  38. * queue (via PiInsertEventInQueue). Whenever one of these events are inserted
  39. * into the queue a worker routine is ensured to be available to process it
  40. * (PiWalkDeviceList).
  41. *
  42. * In general, events processed in PiWalkDeviceList fall into two categories -
  43. * those that are notifications for user mode (queued by kernel mode), and those
  44. * that are queued operations.
  45. *
  46. * User mode notifications are sent by invoking PiNotifyUserMode. That routine
  47. * gets UmPnpMgr's attention and copies up a buffer for it to digest. This
  48. * operation is synchronous, PiNotifyUserMode waits until UmPnpMgr.Dll signals
  49. * it is done (NtPlugPlayControl calls PiUserResponse) before returning.
  50. *
  51. * Queued operations (such as PiProcessQueryRemoveAndEject) may be very involved
  52. * and could generate other events solely for user mode (via calls to
  53. * PiNotifyUserMode, PiNotifyUserModeRemoveVetoed). These operations may also
  54. * need to synchronously call kernel and user mode code that registered for the
  55. * appropriate events (via the IopNotifyXxx functions).
  56. *
  57. */
  58. //
  59. // Pool Tags
  60. //
  61. #define PNP_DEVICE_EVENT_LIST_TAG 'LEpP'
  62. #define PNP_DEVICE_EVENT_ENTRY_TAG 'EEpP'
  63. #define PNP_USER_BLOCK_TAG 'BUpP'
  64. #define PNP_DEVICE_WORK_ITEM_TAG 'IWpP'
  65. #define PNP_POOL_EVENT_BUFFER 'BEpP'
  66. //
  67. // PNP_USER_BLOCK
  68. //
  69. // The caller block contains info describing the caller of
  70. // NtGetPlugPlayEvent. There's only one caller block.
  71. //
  72. typedef struct _PNP_USER_BLOCK {
  73. NTSTATUS Status;
  74. ULONG Result;
  75. PPNP_VETO_TYPE VetoType;
  76. PUNICODE_STRING VetoName;
  77. ERESOURCE Lock;
  78. KEVENT Registered;
  79. KEVENT NotifyUserEvent;
  80. KEVENT UserResultEvent;
  81. PPLUGPLAY_EVENT_BLOCK EventBuffer;
  82. ULONG EventBufferSize;
  83. PVOID PoolBuffer;
  84. ULONG PoolUsed;
  85. ULONG PoolSize;
  86. BOOLEAN Deferred;
  87. } PNP_USER_BLOCK, *PPNP_USER_BLOCK;
  88. //
  89. // Local (private) function prototypes
  90. //
  91. NTSTATUS
  92. PiInsertEventInQueue(
  93. IN PPNP_DEVICE_EVENT_ENTRY DeviceEvent
  94. );
  95. VOID
  96. PiWalkDeviceList(
  97. IN PVOID Context
  98. );
  99. NTSTATUS
  100. PiNotifyUserMode(
  101. PPNP_DEVICE_EVENT_ENTRY DeviceEvent
  102. );
  103. NTSTATUS
  104. PiNotifyUserModeDeviceRemoval(
  105. IN PPNP_DEVICE_EVENT_ENTRY TemplateDeviceEvent,
  106. IN CONST GUID *EventGuid,
  107. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  108. OUT PUNICODE_STRING VetoName OPTIONAL
  109. );
  110. NTSTATUS
  111. PiNotifyUserModeRemoveVetoed(
  112. IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
  113. IN PDEVICE_OBJECT DeviceObject,
  114. IN PNP_VETO_TYPE VetoType,
  115. IN PUNICODE_STRING VetoName OPTIONAL
  116. );
  117. NTSTATUS
  118. PiNotifyUserModeRemoveVetoedByList(
  119. IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
  120. IN PDEVICE_OBJECT DeviceObject,
  121. IN PNP_VETO_TYPE VetoType,
  122. IN PWSTR MultiSzVetoList
  123. );
  124. NTSTATUS
  125. PiNotifyUserModeKernelInitiatedEject(
  126. IN PDEVICE_OBJECT DeviceObject,
  127. OUT PNP_VETO_TYPE *VetoType,
  128. OUT PUNICODE_STRING VetoName
  129. );
  130. NTSTATUS
  131. PiProcessQueryRemoveAndEject(
  132. IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
  133. );
  134. NTSTATUS
  135. PiProcessTargetDeviceEvent(
  136. IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
  137. );
  138. NTSTATUS
  139. PiProcessCustomDeviceEvent(
  140. IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
  141. );
  142. NTSTATUS
  143. PiResizeTargetDeviceBlock(
  144. IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent,
  145. IN PLUGPLAY_DEVICE_DELETE_TYPE DeleteType,
  146. IN PRELATION_LIST RelationsList,
  147. IN BOOLEAN ExcludeIndirectRelations
  148. );
  149. VOID
  150. PiBuildUnsafeRemovalDeviceBlock(
  151. IN PPNP_DEVICE_EVENT_ENTRY OriginalDeviceEvent,
  152. IN PRELATION_LIST RelationsList,
  153. OUT PPNP_DEVICE_EVENT_ENTRY *AllocatedDeviceEvent
  154. );
  155. VOID
  156. PiFinalizeVetoedRemove(
  157. IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
  158. IN PNP_VETO_TYPE VetoType,
  159. IN PUNICODE_STRING VetoName OPTIONAL
  160. );
  161. #if DBG
  162. VOID
  163. LookupGuid(
  164. IN CONST GUID *Guid,
  165. IN OUT PCHAR String,
  166. IN ULONG StringLength
  167. );
  168. VOID
  169. DumpMultiSz(
  170. IN PWCHAR MultiSz
  171. );
  172. VOID
  173. DumpPnpEvent(
  174. IN PPLUGPLAY_EVENT_BLOCK EventBlock
  175. );
  176. VOID
  177. PiDumpPdoHandlesToDebugger(
  178. IN PDEVICE_OBJECT *DeviceObjectArray,
  179. IN ULONG ArrayCount,
  180. IN BOOLEAN KnownHandleFailure
  181. );
  182. BOOLEAN
  183. PiDumpPdoHandlesToDebuggerCallBack(
  184. IN PDEVICE_OBJECT DeviceObject,
  185. IN PEPROCESS Process,
  186. IN PFILE_OBJECT FileObject,
  187. IN HANDLE HandleId,
  188. IN PVOID Context
  189. );
  190. #define DUMP_PDO_HANDLES(array, count, knownfailure) \
  191. PiDumpPdoHandlesToDebugger(array, count, knownfailure)
  192. #else // DBG
  193. #define DUMP_PDO_HANDLES(array, count, knownfailure)
  194. #endif // DBG
  195. #ifdef ALLOC_PRAGMA
  196. #if DBG
  197. #pragma alloc_text(PAGE, DumpMultiSz)
  198. #pragma alloc_text(PAGE, DumpPnpEvent)
  199. #pragma alloc_text(PAGE, LookupGuid)
  200. #pragma alloc_text(PAGE, PiDumpPdoHandlesToDebugger)
  201. #pragma alloc_text(PAGE, PiDumpPdoHandlesToDebuggerCallBack)
  202. #endif
  203. #pragma alloc_text(PAGE, NtGetPlugPlayEvent)
  204. #pragma alloc_text(PAGE, PiCompareGuid)
  205. #pragma alloc_text(PAGE, PiInsertEventInQueue)
  206. #pragma alloc_text(PAGE, PiNotifyUserMode)
  207. #pragma alloc_text(PAGE, PiNotifyUserModeDeviceRemoval)
  208. #pragma alloc_text(PAGE, PiNotifyUserModeKernelInitiatedEject)
  209. #pragma alloc_text(PAGE, PiNotifyUserModeRemoveVetoed)
  210. #pragma alloc_text(PAGE, PiNotifyUserModeRemoveVetoedByList)
  211. #pragma alloc_text(PAGE, PiProcessCustomDeviceEvent)
  212. #pragma alloc_text(PAGE, PiProcessQueryRemoveAndEject)
  213. #pragma alloc_text(PAGE, PiProcessTargetDeviceEvent)
  214. #pragma alloc_text(PAGE, PiResizeTargetDeviceBlock)
  215. #pragma alloc_text(PAGE, PiBuildUnsafeRemovalDeviceBlock)
  216. #pragma alloc_text(PAGE, PiUserResponse)
  217. #pragma alloc_text(PAGE, PiWalkDeviceList)
  218. #pragma alloc_text(PAGE, PiFinalizeVetoedRemove)
  219. #pragma alloc_text(PAGE, PpCompleteDeviceEvent)
  220. #pragma alloc_text(PAGE, PpInitializeNotification)
  221. #pragma alloc_text(PAGE, PpNotifyUserModeRemovalSafe)
  222. #pragma alloc_text(PAGE, PpSetCustomTargetEvent)
  223. #pragma alloc_text(PAGE, PpSetDeviceClassChange)
  224. #pragma alloc_text(PAGE, PpSetDeviceRemovalSafe)
  225. #pragma alloc_text(PAGE, PpSetHwProfileChangeEvent)
  226. #pragma alloc_text(PAGE, PpSetBlockedDriverEvent)
  227. #pragma alloc_text(PAGE, PpSetPlugPlayEvent)
  228. #pragma alloc_text(PAGE, PpSetPowerEvent)
  229. #pragma alloc_text(PAGE, PpSetPowerVetoEvent)
  230. #pragma alloc_text(PAGE, PpSetTargetDeviceRemove)
  231. #pragma alloc_text(PAGE, PpSynchronizeDeviceEventQueue)
  232. #pragma alloc_text(PAGE, PiAllocateCriticalMemory)
  233. #endif
  234. //
  235. // Global Data
  236. //
  237. #ifdef ALLOC_DATA_PRAGMA
  238. #pragma data_seg("PAGEDATA")
  239. #endif
  240. PPNP_DEVICE_EVENT_LIST PpDeviceEventList = NULL;
  241. PPNP_USER_BLOCK PpUserBlock = NULL;
  242. BOOLEAN UserModeRunning = FALSE;
  243. BOOLEAN PiNotificationInProgress = FALSE;
  244. #ifdef ALLOC_DATA_PRAGMA
  245. #pragma data_seg()
  246. #endif
  247. FAST_MUTEX PiNotificationInProgressLock;
  248. #if DBG
  249. BOOLEAN PiDumpVetoedHandles = FALSE;
  250. #endif
  251. NTSTATUS
  252. NtGetPlugPlayEvent(
  253. IN HANDLE EventHandle,
  254. IN PVOID Context OPTIONAL,
  255. OUT PPLUGPLAY_EVENT_BLOCK EventBlock,
  256. IN ULONG EventBufferSize
  257. )
  258. /*++
  259. Routine Description:
  260. FRONT-END
  261. This Plug and Play Manager API allows the user-mode PnP manager to
  262. receive notification of a (kernel-mode) PnP hardware event.
  263. THIS API IS ONLY CALLABLE BY THE USER-MODE PNP MANAGER. IF ANY OTHER
  264. COMPONENT CALLS THIS API, THE DELIVERED EVENT WILL BE LOST TO THE REST
  265. OF THE OPERATING SYSTEM. FURTHERMORE, THERE IS COMPLEX SYNCHRONIZATION
  266. BETWEEN THE USER-MODE AND KERNEL-MODE PNP MANAGERS, ANYONE ELSE CALLING
  267. THIS API WILL EVENTUALLY DEADLOCK THE SYSTEM.
  268. Arguments:
  269. EventHandle - Supplies an event handle that is signalled when an event
  270. is ready to be delivered to user-mode.
  271. EventBlock - Pointer to a PLUGPLAY_EVENT_BLOCK structure that will receive
  272. information on the hardware event that has occurred.
  273. EventBufferLength - Specifies the size, in bytes, of the EventBuffer field
  274. in the PLUGPLAY_EVENT_BLOCK pointed to by EventBlock.
  275. Return Value:
  276. NTSTATUS code indicating whether or not the function was successful
  277. --*/
  278. {
  279. NTSTATUS status = STATUS_SUCCESS;
  280. UNREFERENCED_PARAMETER( Context );
  281. UNREFERENCED_PARAMETER( EventHandle );
  282. PAGED_CODE();
  283. //
  284. // This routine only supports user-mode callers.
  285. //
  286. if (KeGetPreviousMode() != UserMode) {
  287. IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
  288. "NtGetPlugPlayEvent: Only allows user-mode callers\n"));
  289. return STATUS_ACCESS_DENIED;
  290. }
  291. //
  292. // Does the caller have "trusted computer base" privilge?
  293. //
  294. if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) {
  295. IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
  296. "NtGetPlugPlayEvent: SecurityCheck failed\n"));
  297. return STATUS_PRIVILEGE_NOT_HELD;
  298. }
  299. UserModeRunning = TRUE;
  300. try {
  301. //
  302. // Probe user buffer parameters.
  303. //
  304. ProbeForWrite(EventBlock,
  305. EventBufferSize,
  306. sizeof(ULONG)
  307. );
  308. } except(EXCEPTION_EXECUTE_HANDLER) {
  309. status = GetExceptionCode();
  310. IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
  311. "NtGetPlugPlayEvent: Exception 0x%08X\n",
  312. status));
  313. }
  314. if (!NT_SUCCESS(status)) {
  315. return status;
  316. }
  317. PpUserBlock->EventBuffer = EventBlock;
  318. PpUserBlock->EventBufferSize = EventBufferSize;
  319. //
  320. // Wait for the NotifyUserEvent (signalled when there's a device event
  321. // ready to be delivered to user-mode.
  322. //
  323. if (!PpUserBlock->Deferred) {
  324. //
  325. // Make it a UserMode wait so the terminate APC will unblock us,
  326. // and we can leave, which cleans up the thread
  327. //
  328. PpUserBlock->PoolUsed = 0;
  329. //
  330. // Tell kernel we have a waiter.
  331. //
  332. KeSetEvent(&PpUserBlock->Registered, 0, FALSE);
  333. //
  334. // Wait for kernel to fill in data for us to copy.
  335. //
  336. status = KeWaitForSingleObject(&PpUserBlock->NotifyUserEvent,
  337. Executive,
  338. UserMode,
  339. FALSE,
  340. NULL);
  341. }
  342. //
  343. // On deferral, we know we've returned STATUS_BUFFER_TOO_SMALL
  344. // so, the PpUserBlock->PoolBuffer still points to a valid block
  345. // check that the new event buffer is big enough.
  346. // copy the data out, (w/o waiting for a kernel event)
  347. //
  348. if (NT_SUCCESS(status) && (status != STATUS_USER_APC) ) {
  349. //
  350. // Validate user buffer size.
  351. //
  352. //
  353. // DON'T do this
  354. // ASSERT (PpUserBlock->EventBufferSize >= PpUserBlock->PoolSize);
  355. //
  356. // Because the user block might just still be too small!
  357. //
  358. if (PpUserBlock->EventBufferSize < PpUserBlock->PoolUsed) {
  359. PpUserBlock->Deferred=TRUE;
  360. IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
  361. "NtGetPlugPlayEvent: User-mode buffer too small for event\n"));
  362. status = STATUS_BUFFER_TOO_SMALL;
  363. } else {
  364. status = PpUserBlock->Status;
  365. PpUserBlock->Deferred = FALSE;
  366. if (PpUserBlock->PoolBuffer != NULL && NT_SUCCESS (status)) {
  367. RtlCopyMemory(PpUserBlock->EventBuffer,
  368. PpUserBlock->PoolBuffer,
  369. PpUserBlock->PoolUsed);
  370. } else {
  371. IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
  372. "NtGetPlugPlayEvent: Invalid event buffer\n"));
  373. status = STATUS_UNSUCCESSFUL;
  374. }
  375. }
  376. }
  377. KeClearEvent(&PpUserBlock->Registered);
  378. #if DBG
  379. {
  380. CHAR guidString[256];
  381. LookupGuid(&PpUserBlock->EventBuffer->EventGuid, guidString, sizeof(guidString));
  382. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  383. "NtGetPlugPlayEvent: Returning event - EventGuid = %s\n",
  384. guidString));
  385. }
  386. #endif
  387. return status;
  388. } // NtGetPlugPlayEvent
  389. NTSTATUS
  390. PpInitializeNotification(
  391. VOID
  392. )
  393. /*++
  394. Routine Description:
  395. This routine performs initialization required before any of the notification
  396. events can be processed. This routine performs init for the master device
  397. event queue processing.
  398. Parameters:
  399. None
  400. Return Value:
  401. Returns a STATUS_Xxx value that indicates whether the function succeeded
  402. or not.
  403. --*/
  404. {
  405. NTSTATUS status = STATUS_SUCCESS;
  406. PAGED_CODE();
  407. //
  408. // Allocate and initialize the master device event list.
  409. //
  410. PpDeviceEventList = ExAllocatePoolWithTag(NonPagedPool,
  411. sizeof(PNP_DEVICE_EVENT_LIST),
  412. PNP_DEVICE_EVENT_LIST_TAG);
  413. if (PpDeviceEventList == NULL) {
  414. status = STATUS_INSUFFICIENT_RESOURCES;
  415. goto Clean0;
  416. }
  417. KeInitializeMutex(&PpDeviceEventList->EventQueueMutex, 0);
  418. ExInitializeFastMutex(&PpDeviceEventList->Lock);
  419. InitializeListHead(&(PpDeviceEventList->List));
  420. PpDeviceEventList->Status = STATUS_PENDING;
  421. //
  422. // Intialize the PpUserBlock buffer - this buffer contains info about
  423. // the user-mode caller for NtGetPlugPlayEvent and describes who in user
  424. // mode we pass the events to.
  425. //
  426. PpUserBlock = ExAllocatePoolWithTag(NonPagedPool,
  427. sizeof(PNP_USER_BLOCK),
  428. PNP_USER_BLOCK_TAG);
  429. if (PpUserBlock == NULL) {
  430. status = STATUS_INSUFFICIENT_RESOURCES;
  431. goto Clean0;
  432. }
  433. RtlZeroMemory(PpUserBlock, sizeof(PNP_USER_BLOCK));
  434. PpUserBlock->PoolSize = sizeof (PLUGPLAY_EVENT_BLOCK)+
  435. sizeof (PNP_DEVICE_EVENT_ENTRY);
  436. PpUserBlock->PoolBuffer = ExAllocatePoolWithTag(NonPagedPool,
  437. PpUserBlock->PoolSize,
  438. PNP_USER_BLOCK_TAG);
  439. if (PpUserBlock->PoolBuffer == NULL ) {
  440. status = STATUS_INSUFFICIENT_RESOURCES;
  441. PpUserBlock->PoolSize = 0;
  442. goto Clean0;
  443. }
  444. KeInitializeEvent(&PpUserBlock->Registered, SynchronizationEvent, FALSE);
  445. KeInitializeEvent(&PpUserBlock->NotifyUserEvent, SynchronizationEvent, FALSE);
  446. KeInitializeEvent(&PpUserBlock->UserResultEvent, SynchronizationEvent, FALSE);
  447. ExInitializeResourceLite(&PpUserBlock->Lock);
  448. // PpUserBlock->Status = STATUS_SUCCESS;
  449. // PpUserBlock->Result = 0;
  450. // PpUserBlock->EventBuffer = NULL;
  451. // PpUserBlock->EventBufferSize = 0;
  452. // PpUserBlock->PoolUsed = 0;
  453. ExInitializeFastMutex(&PiNotificationInProgressLock);
  454. Clean0:
  455. return status;
  456. } // PpInitializeNotification
  457. NTSTATUS
  458. PiInsertEventInQueue(
  459. IN PPNP_DEVICE_EVENT_ENTRY DeviceEvent
  460. )
  461. {
  462. PWORK_QUEUE_ITEM workItem;
  463. NTSTATUS status;
  464. PAGED_CODE();
  465. workItem = NULL;
  466. status = STATUS_SUCCESS;
  467. #if DBG
  468. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  469. "PiInsertEventInQueue: Event queued\n"));
  470. DumpPnpEvent(&DeviceEvent->Data);
  471. #endif
  472. //
  473. // Check if a new work item needs to be kicked off. A new work item gets
  474. // kicked off iff this is the first event in the list.
  475. //
  476. ExAcquireFastMutex(&PpDeviceEventList->Lock);
  477. ExAcquireFastMutex(&PiNotificationInProgressLock);
  478. if (!PiNotificationInProgress) {
  479. workItem = ExAllocatePoolWithTag(NonPagedPool,
  480. sizeof(WORK_QUEUE_ITEM),
  481. PNP_DEVICE_WORK_ITEM_TAG);
  482. if (workItem) {
  483. PiNotificationInProgress = TRUE;
  484. KeClearEvent(&PiEventQueueEmpty);
  485. } else {
  486. IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
  487. "PiInsertEventInQueue: Could not allocate memory to kick off a worker thread\n"));
  488. status = STATUS_INSUFFICIENT_RESOURCES;
  489. }
  490. } else {
  491. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  492. "PiInsertEventInQueue: Worker thread already running\n"));
  493. }
  494. //
  495. // Insert the event iff successfull so far.
  496. //
  497. InsertTailList(&PpDeviceEventList->List, &DeviceEvent->ListEntry);
  498. ExReleaseFastMutex(&PiNotificationInProgressLock);
  499. ExReleaseFastMutex(&PpDeviceEventList->Lock);
  500. //
  501. // Queue the work item if any.
  502. //
  503. if (workItem) {
  504. ExInitializeWorkItem(workItem, PiWalkDeviceList, workItem);
  505. ExQueueWorkItem(workItem, DelayedWorkQueue);
  506. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  507. "PiInsertEventInQueue: Kicked off worker thread\n"));
  508. }
  509. return status;
  510. }
  511. NTSTATUS
  512. PpSetDeviceClassChange(
  513. IN CONST GUID *EventGuid,
  514. IN CONST GUID *ClassGuid,
  515. IN PUNICODE_STRING SymbolicLinkName
  516. )
  517. /*++
  518. Routine Description:
  519. This routine is called by user-mode pnp manager and drivers (indirectly) to
  520. submit device interface change events into a serialized asynchronous queue.
  521. This queue is processed by a work item.
  522. Arguments:
  523. EventGuid - Indicates what event is triggered has occured.
  524. ClassGuid - Indicates the class of the device interface that changed.
  525. SymbolicLinkName - Specifies the symbolic link name associated with the
  526. interface device.
  527. Return Value:
  528. None.
  529. --*/
  530. {
  531. NTSTATUS status = STATUS_SUCCESS;
  532. ULONG dataSize, totalSize;
  533. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  534. PAGED_CODE();
  535. if (PpPnpShuttingDown) {
  536. return STATUS_TOO_LATE;
  537. }
  538. #if DBG
  539. {
  540. CHAR eventGuidString[80];
  541. CHAR classGuidString[80];
  542. LookupGuid(EventGuid, eventGuidString, sizeof(eventGuidString));
  543. LookupGuid(ClassGuid, classGuidString, sizeof(classGuidString));
  544. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  545. "PpSetDeviceClassChange: Entered\n EventGuid = %s\n ClassGuid = %s\n SymbolicLinkName = %wZ\n",
  546. eventGuidString,
  547. classGuidString,
  548. SymbolicLinkName));
  549. }
  550. #endif
  551. try {
  552. ASSERT(EventGuid != NULL);
  553. ASSERT(ClassGuid != NULL);
  554. ASSERT(SymbolicLinkName != NULL);
  555. //
  556. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  557. // we record in the TotalSize field later (the length doesn't count the
  558. // terminating null but we're already counting the first index into the
  559. // SymbolicLinkName field so it works out.
  560. //
  561. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) + SymbolicLinkName->Length;
  562. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  563. deviceEvent = ExAllocatePoolWithTag( PagedPool,
  564. totalSize,
  565. PNP_DEVICE_EVENT_ENTRY_TAG);
  566. if (deviceEvent == NULL) {
  567. status = STATUS_NO_MEMORY;
  568. goto Clean0;
  569. }
  570. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  571. RtlCopyMemory(&deviceEvent->Data.EventGuid, EventGuid, sizeof(GUID));
  572. deviceEvent->Data.EventCategory = DeviceClassChangeEvent;
  573. //deviceEvent->Data.Result = NULL;
  574. //deviceEvent->Data.Flags = 0;
  575. deviceEvent->Data.TotalSize = dataSize;
  576. RtlCopyMemory(&deviceEvent->Data.u.DeviceClass.ClassGuid, ClassGuid, sizeof(GUID));
  577. RtlCopyMemory(&deviceEvent->Data.u.DeviceClass.SymbolicLinkName,
  578. SymbolicLinkName->Buffer,
  579. SymbolicLinkName->Length);
  580. deviceEvent->Data.u.DeviceClass.SymbolicLinkName[SymbolicLinkName->Length/sizeof(WCHAR)] = 0x0;
  581. status = PiInsertEventInQueue(deviceEvent);
  582. Clean0:
  583. ;
  584. } except(EXCEPTION_EXECUTE_HANDLER) {
  585. IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
  586. "PpSetDeviceClassChange: Exception 0x%08X\n", GetExceptionCode()));
  587. }
  588. return status;
  589. } // PpSetDeviceClassChange
  590. NTSTATUS
  591. PpSetCustomTargetEvent(
  592. IN PDEVICE_OBJECT DeviceObject,
  593. IN PKEVENT SyncEvent OPTIONAL,
  594. OUT PULONG Result OPTIONAL,
  595. IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
  596. IN PVOID Context OPTIONAL,
  597. IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure
  598. )
  599. /*++
  600. Routine Description:
  601. This routine is called by user-mode pnp manager and drivers (indirectly) to
  602. submit target device change events into a serialized asynchronous queue.
  603. This queue is processed by a work item.
  604. Arguments:
  605. DeviceObject - Indicates the device object for the device that changed.
  606. SyncEvent - Optionally, specifies a kernel-mode event that will be set when the
  607. event is finished processing.
  608. Result - Supplies a pointer to a ULONG that will be filled in with the status
  609. after the event has actual completed (notification finished and the
  610. event processed). This value is not used when SyncEvent is NULL and
  611. is REQUIRED when SyncEvent is supplied.
  612. NotificationStructure - Specifies the custom Notification to be processed.
  613. Return Value:
  614. None.
  615. --*/
  616. {
  617. NTSTATUS status = STATUS_SUCCESS;
  618. PDEVICE_NODE deviceNode;
  619. ULONG dataSize, totalSize;
  620. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  621. PAGED_CODE();
  622. ASSERT(NotificationStructure != NULL);
  623. ASSERT(DeviceObject != NULL);
  624. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  625. "PpSetCustomTargetEvent: DeviceObject = 0x%p, SyncEvent = 0x%p, Result = 0x%p\n",
  626. DeviceObject,
  627. SyncEvent,
  628. Result));
  629. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  630. " Callback = 0x%p, Context = 0x%p, NotificationStructure = 0x%p\n",
  631. Callback,
  632. Context,
  633. NotificationStructure));
  634. if (SyncEvent) {
  635. ASSERT(Result);
  636. *Result = STATUS_PENDING;
  637. }
  638. if (PpPnpShuttingDown) {
  639. return STATUS_TOO_LATE;
  640. }
  641. //
  642. // Reference the device object so it can't go away until after we're
  643. // done with notification.
  644. //
  645. ObReferenceObject(DeviceObject);
  646. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  647. ASSERT(deviceNode);
  648. //
  649. // This is a custom event block, so build up the PLUGPLAY_EVENT_BLOCK
  650. // but copy the Notification Structure and put that in the EventBlock
  651. // so we can dig it out in the handler later
  652. //
  653. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) + deviceNode->InstancePath.Length + sizeof(UNICODE_NULL);
  654. //
  655. // We need to ensure that the Notification structure remains aligned
  656. // so round up dataSize to a multiple of sizeof(PVOID).
  657. //
  658. dataSize += sizeof(PVOID) - 1;
  659. dataSize &= ~(sizeof(PVOID) - 1);
  660. dataSize += NotificationStructure->Size;
  661. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  662. deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
  663. if (deviceEvent == NULL) {
  664. ObDereferenceObject(DeviceObject);
  665. return STATUS_INSUFFICIENT_RESOURCES;
  666. }
  667. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  668. deviceEvent->CallerEvent = SyncEvent;
  669. deviceEvent->Callback = Callback;
  670. deviceEvent->Context = Context;
  671. deviceEvent->Data.EventGuid = GUID_PNP_CUSTOM_NOTIFICATION;
  672. deviceEvent->Data.EventCategory = CustomDeviceEvent;
  673. deviceEvent->Data.Result = Result;
  674. deviceEvent->Data.Flags = 0;
  675. deviceEvent->Data.TotalSize = dataSize;
  676. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  677. if (deviceNode->InstancePath.Length != 0) {
  678. RtlCopyMemory((PVOID)deviceEvent->Data.u.CustomNotification.DeviceIds,
  679. (PVOID)deviceNode->InstancePath.Buffer,
  680. deviceNode->InstancePath.Length);
  681. //
  682. // No need to NUL terminate this string since we initially zeroed the
  683. // buffer after allocation.
  684. //
  685. }
  686. //
  687. // Point the custom notification block to the extra space at the
  688. // end of the allocation
  689. //
  690. deviceEvent->Data.u.CustomNotification.NotificationStructure =
  691. (PVOID)((PUCHAR)deviceEvent + totalSize - NotificationStructure->Size);
  692. RtlCopyMemory(deviceEvent->Data.u.CustomNotification.NotificationStructure,
  693. NotificationStructure,
  694. NotificationStructure->Size);
  695. status = PiInsertEventInQueue(deviceEvent);
  696. return status;
  697. } // PpSetCustomTargetEvent
  698. NTSTATUS
  699. PpSetTargetDeviceRemove(
  700. IN PDEVICE_OBJECT DeviceObject,
  701. IN BOOLEAN KernelInitiated,
  702. IN BOOLEAN NoRestart,
  703. IN BOOLEAN DoEject,
  704. IN ULONG Problem,
  705. IN PKEVENT SyncEvent OPTIONAL,
  706. OUT PULONG Result OPTIONAL,
  707. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  708. OUT PUNICODE_STRING VetoName OPTIONAL
  709. )
  710. /*++
  711. Routine Description:
  712. This routine is called by user-mode pnp manager and drivers (indirectly) to
  713. submit target device change events into a serialized asynchronous queue.
  714. This queue is processed by a work item.
  715. Arguments:
  716. EventGuid - Indicates what event is triggered has occured.
  717. DeviceObject - Indicates the device object for the device that changed.
  718. SyncEvent - Optionally, specifies a kernel-mode event that will be set when the
  719. event is finished processing.
  720. Result - Supplies a pointer to a ULONG that will be filled in with the status
  721. after the event has actual completed (notification finished and the
  722. event processed). This value is not used when SyncEvent is NULL and
  723. is REQUIRED when SyncEvent is supplied.
  724. Flags - Current can be set to the following flags (bitfields)
  725. TDF_PERFORMACTION
  726. TDF_DEVICEEJECTABLE.
  727. NotificationStructure - If present, implies that EventGuid is NULL, and specifies
  728. a custom Notification to be processed. By definition it cannot be an event of
  729. type GUID_TARGET_DEVICE_QUERY_REMOVE, GUID_TARGET_DEVICE_REMOVE_CANCELLED, or
  730. GUID_TARGET_DEVICE_REMOVE_COMPLETE.
  731. Return Value:
  732. None.
  733. --*/
  734. {
  735. NTSTATUS status = STATUS_SUCCESS;
  736. PDEVICE_NODE deviceNode;
  737. ULONG dataSize, totalSize, i;
  738. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  739. PAGED_CODE();
  740. ASSERT(DeviceObject != NULL);
  741. if (SyncEvent) {
  742. ASSERT(Result);
  743. *Result = STATUS_PENDING;
  744. }
  745. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  746. "PpSetTargetDeviceRemove: Entered\n"));
  747. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  748. " DeviceObject = 0x%p, NoRestart = %d, Problem = %d\n",
  749. DeviceObject,
  750. NoRestart,
  751. Problem));
  752. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  753. " SyncEvent = 0x%p, Result = 0x%p\n",
  754. SyncEvent,
  755. Result));
  756. if (PpPnpShuttingDown) {
  757. return STATUS_TOO_LATE;
  758. }
  759. //
  760. // Reference the device object so it can't go away until after we're
  761. // done with notification.
  762. //
  763. ObReferenceObject(DeviceObject);
  764. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  765. ASSERT(deviceNode);
  766. //
  767. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  768. // we record in the TotalSize field later (the length doesn't count the
  769. // terminating null but we're already counting the first index into the
  770. // DeviceId field so it works out. Add one more for double-null term.
  771. //
  772. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  773. dataSize += deviceNode->InstancePath.Length + sizeof(WCHAR);
  774. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  775. deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
  776. if (deviceEvent == NULL) {
  777. ObDereferenceObject(DeviceObject);
  778. return STATUS_INSUFFICIENT_RESOURCES;
  779. }
  780. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  781. deviceEvent->CallerEvent = SyncEvent;
  782. deviceEvent->Argument = Problem;
  783. deviceEvent->VetoType = VetoType;
  784. deviceEvent->VetoName = VetoName;
  785. deviceEvent->Data.EventGuid = DoEject ? GUID_DEVICE_EJECT : GUID_DEVICE_QUERY_AND_REMOVE;
  786. deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
  787. deviceEvent->Data.Result = Result;
  788. if (NoRestart) {
  789. deviceEvent->Data.Flags |= TDF_NO_RESTART;
  790. }
  791. if (KernelInitiated) {
  792. deviceEvent->Data.Flags |= TDF_KERNEL_INITIATED;
  793. }
  794. deviceEvent->Data.TotalSize = dataSize;
  795. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  796. if (deviceNode->InstancePath.Length != 0) {
  797. RtlCopyMemory((PVOID)deviceEvent->Data.u.TargetDevice.DeviceIds,
  798. (PVOID)deviceNode->InstancePath.Buffer,
  799. deviceNode->InstancePath.Length);
  800. }
  801. i = deviceNode->InstancePath.Length/sizeof(WCHAR);
  802. deviceEvent->Data.u.TargetDevice.DeviceIds[i] = L'\0';
  803. status = PiInsertEventInQueue(deviceEvent);
  804. return status;
  805. } // PpSetTargetDeviceRemove
  806. NTSTATUS
  807. PpSetDeviceRemovalSafe(
  808. IN PDEVICE_OBJECT DeviceObject,
  809. IN PKEVENT SyncEvent OPTIONAL,
  810. OUT PULONG Result OPTIONAL
  811. )
  812. /*++
  813. Routine Description:
  814. This routine is called to notify user mode a device can be removed. The IO
  815. system may queue this event when a hardware initiated eject has completed.
  816. Arguments:
  817. DeviceObject - Indicates the device object for the device that changed.
  818. SyncEvent - Optionally, specifies a kernel-mode event that will be set when the
  819. event is finished processing.
  820. Result - Supplies a pointer to a ULONG that will be filled in with the status
  821. after the event has actual completed (notification finished and the
  822. event processed). This value is not used when SyncEvent is NULL and
  823. is REQUIRED when SyncEvent is supplied.
  824. Return Value:
  825. None.
  826. --*/
  827. {
  828. NTSTATUS status;
  829. PDEVICE_NODE deviceNode;
  830. ULONG dataSize, totalSize, i;
  831. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  832. PAGED_CODE();
  833. ASSERT(DeviceObject != NULL);
  834. if (SyncEvent) {
  835. ASSERT(Result);
  836. *Result = STATUS_PENDING;
  837. }
  838. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  839. "PpSetDeviceRemovalSafe: Entered\n"));
  840. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  841. " DeviceObject = 0x%p, SyncEvent = 0x%p, Result = 0x%p\n",
  842. DeviceObject,
  843. SyncEvent,
  844. Result));
  845. if (PpPnpShuttingDown) {
  846. return STATUS_TOO_LATE;
  847. }
  848. //
  849. // Reference the device object so it can't go away until after we're
  850. // done with notification.
  851. //
  852. ObReferenceObject(DeviceObject);
  853. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  854. ASSERT(deviceNode);
  855. //
  856. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  857. // we record in the TotalSize field later (the length doesn't count the
  858. // terminating null but we're already counting the first index into the
  859. // DeviceId field so it works out. Add one more for double-null term.
  860. //
  861. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  862. dataSize += deviceNode->InstancePath.Length + sizeof(WCHAR);
  863. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  864. deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
  865. if (deviceEvent == NULL) {
  866. ObDereferenceObject(DeviceObject);
  867. return STATUS_INSUFFICIENT_RESOURCES;
  868. }
  869. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  870. deviceEvent->CallerEvent = SyncEvent;
  871. deviceEvent->Argument = 0;
  872. deviceEvent->VetoType = NULL;
  873. deviceEvent->VetoName = NULL;
  874. deviceEvent->Data.EventGuid = GUID_DEVICE_SAFE_REMOVAL;
  875. deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
  876. deviceEvent->Data.Result = Result;
  877. deviceEvent->Data.Flags = 0;
  878. deviceEvent->Data.TotalSize = dataSize;
  879. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  880. if (deviceNode->InstancePath.Length != 0) {
  881. RtlCopyMemory((PVOID)deviceEvent->Data.u.TargetDevice.DeviceIds,
  882. (PVOID)deviceNode->InstancePath.Buffer,
  883. deviceNode->InstancePath.Length);
  884. }
  885. i = deviceNode->InstancePath.Length/sizeof(WCHAR);
  886. deviceEvent->Data.u.TargetDevice.DeviceIds[i] = L'\0';
  887. status = PiInsertEventInQueue(deviceEvent);
  888. return status;
  889. } // PpSetDeviceRemovalSafe
  890. NTSTATUS
  891. PpSetHwProfileChangeEvent(
  892. IN GUID CONST *EventTypeGuid,
  893. IN PKEVENT CompletionEvent OPTIONAL,
  894. OUT PNTSTATUS CompletionStatus OPTIONAL,
  895. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  896. OUT PUNICODE_STRING VetoName OPTIONAL
  897. )
  898. {
  899. ULONG dataSize,totalSize;
  900. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  901. NTSTATUS status = STATUS_SUCCESS;
  902. PAGED_CODE();
  903. #if DBG
  904. {
  905. CHAR eventGuidString[80];
  906. LookupGuid(EventTypeGuid, eventGuidString, sizeof(eventGuidString));
  907. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  908. "PpSetHwProfileChangeEvent: Entered\n EventGuid = %s\n\n",
  909. eventGuidString));
  910. }
  911. #endif
  912. if (PpPnpShuttingDown) {
  913. return STATUS_TOO_LATE;
  914. }
  915. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  916. totalSize = dataSize + FIELD_OFFSET (PNP_DEVICE_EVENT_ENTRY,Data);
  917. deviceEvent = ExAllocatePoolWithTag (PagedPool,
  918. totalSize,
  919. PNP_DEVICE_EVENT_ENTRY_TAG);
  920. if (NULL == deviceEvent) {
  921. return STATUS_INSUFFICIENT_RESOURCES;
  922. }
  923. //
  924. // Setup the PLUGPLAY_EVENT_BLOCK
  925. //
  926. RtlZeroMemory ((PVOID)deviceEvent,totalSize);
  927. deviceEvent->CallerEvent = CompletionEvent;
  928. deviceEvent->VetoType = VetoType;
  929. deviceEvent->VetoName = VetoName;
  930. deviceEvent->Data.EventCategory = HardwareProfileChangeEvent;
  931. RtlCopyMemory(&deviceEvent->Data.EventGuid, EventTypeGuid, sizeof(GUID));
  932. deviceEvent->Data.TotalSize = dataSize;
  933. deviceEvent->Data.Result = (PULONG)CompletionStatus;
  934. status = PiInsertEventInQueue(deviceEvent);
  935. return status;
  936. } // PpSetHwProfileChangeEvent
  937. NTSTATUS
  938. PpSetBlockedDriverEvent(
  939. IN GUID CONST *BlockedDriverGuid
  940. )
  941. /*++
  942. Routine Description:
  943. This routine is called to notify user mode of blocked driver events.
  944. Arguments:
  945. BlockedDriverGuid - Specifies the GUID which identifies the blocked driver.
  946. Return Value:
  947. Returns the status of inserting the event into the synchronized pnp event
  948. queue.
  949. --*/
  950. {
  951. ULONG dataSize, totalSize;
  952. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  953. NTSTATUS status;
  954. PAGED_CODE();
  955. if (PpPnpShuttingDown) {
  956. return STATUS_TOO_LATE;
  957. }
  958. //
  959. // Allocate a device event entry.
  960. //
  961. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  962. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  963. deviceEvent = ExAllocatePoolWithTag(PagedPool,
  964. totalSize,
  965. PNP_DEVICE_EVENT_ENTRY_TAG);
  966. if (deviceEvent == NULL) {
  967. return STATUS_INSUFFICIENT_RESOURCES;
  968. }
  969. //
  970. // Setup the PLUGPLAY_EVENT_BLOCK
  971. //
  972. RtlZeroMemory ((PVOID)deviceEvent, totalSize);
  973. deviceEvent->Data.EventGuid = GUID_DRIVER_BLOCKED;
  974. deviceEvent->Data.EventCategory = BlockedDriverEvent;
  975. deviceEvent->Data.TotalSize = dataSize;
  976. RtlCopyMemory(&deviceEvent->Data.u.BlockedDriverNotification.BlockedDriverGuid,
  977. BlockedDriverGuid,
  978. sizeof(GUID));
  979. //
  980. // Insert the event into the queue.
  981. //
  982. status = PiInsertEventInQueue(deviceEvent);
  983. return status;
  984. } // PpSetBlockedDriverEvent
  985. NTSTATUS
  986. PpSetPowerEvent(
  987. IN ULONG EventCode,
  988. IN ULONG EventData,
  989. IN PKEVENT CompletionEvent OPTIONAL,
  990. OUT PNTSTATUS CompletionStatus OPTIONAL,
  991. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  992. OUT PUNICODE_STRING VetoName OPTIONAL
  993. )
  994. /*++
  995. Routine Description:
  996. This routine is called to notify user mode of system-wide power events.
  997. Arguments:
  998. EventCode - Supplies the power event code that is to be communicated
  999. to user-mode components.
  1000. (Specifically, this event code is actually one of the PBT_APM*
  1001. user-mode power event ids, as defined in sdk\inc\winuser.h. It is
  1002. typically used as the WPARAM data associated with WM_POWERBROADCAST
  1003. user-mode window messages. It is supplied to kernel-mode PnP,
  1004. directly from win32k, for the explicit purpose of user-mode power
  1005. event notification.)
  1006. EventData - Specifies additional event-specific data for the specified
  1007. power event id.
  1008. (Specifically, this event data is the LPARAM data for the
  1009. corresponding PBT_APM* user-mode power event id, specified above.)
  1010. CompletionEvent - Optionally, specifies a kernel-mode event that will be set when the
  1011. event is finished processing.
  1012. CompletionStatus - Supplies a pointer to a ULONG that will be filled in with the status
  1013. after the event has actual completed (notification finished and the
  1014. event processed). This value is not used when SyncEvent is NULL and
  1015. is REQUIRED when SyncEvent is supplied.
  1016. VetoType - Optionally, if the specified EventCode is a query-type operation,
  1017. this argument supplies a pointer to an address that will receive the
  1018. type of the vetoing user-mode component, in the event that the
  1019. request is denied.
  1020. VetoName - Optionally, if the specified EventCode is a query-type operation,
  1021. this argument supplies a pointer to a UNICODE_STRING that will
  1022. receive the name of the vetoing user-mode component, in the event
  1023. that the request is denied.
  1024. Return Value:
  1025. Returns the status of inserting the event into the synchronized pnp event
  1026. queue.
  1027. For the final status of a synchronous power event, check the value at the
  1028. location specified by CompletionStatus, once the supplied CompletionEvent
  1029. has been set.
  1030. --*/
  1031. {
  1032. ULONG dataSize,totalSize;
  1033. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1034. NTSTATUS status = STATUS_SUCCESS;
  1035. PAGED_CODE();
  1036. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1037. "PpSetPowerEvent: Entered\n\n") );
  1038. if (PpPnpShuttingDown) {
  1039. return STATUS_TOO_LATE;
  1040. }
  1041. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  1042. totalSize = dataSize + FIELD_OFFSET (PNP_DEVICE_EVENT_ENTRY,Data);
  1043. deviceEvent = ExAllocatePoolWithTag(PagedPool,
  1044. totalSize,
  1045. PNP_DEVICE_EVENT_ENTRY_TAG);
  1046. if (NULL == deviceEvent) {
  1047. return STATUS_INSUFFICIENT_RESOURCES;
  1048. }
  1049. //
  1050. //Setup the PLUGPLAY_EVENT_BLOCK
  1051. //
  1052. RtlZeroMemory ((PVOID)deviceEvent,totalSize);
  1053. deviceEvent->CallerEvent = CompletionEvent;
  1054. deviceEvent->VetoType = VetoType;
  1055. deviceEvent->VetoName = VetoName;
  1056. deviceEvent->Data.EventCategory = PowerEvent;
  1057. deviceEvent->Data.EventGuid = GUID_PNP_POWER_NOTIFICATION;
  1058. deviceEvent->Data.TotalSize = dataSize;
  1059. deviceEvent->Data.Result = (PULONG)CompletionStatus;
  1060. deviceEvent->Data.u.PowerNotification.NotificationCode = EventCode;
  1061. deviceEvent->Data.u.PowerNotification.NotificationData = EventData;
  1062. status = PiInsertEventInQueue(deviceEvent);
  1063. return status;
  1064. } // PpSetPowerEvent
  1065. NTSTATUS
  1066. PpSetPowerVetoEvent(
  1067. IN POWER_ACTION VetoedPowerOperation,
  1068. IN PKEVENT CompletionEvent OPTIONAL,
  1069. OUT PNTSTATUS CompletionStatus OPTIONAL,
  1070. IN PDEVICE_OBJECT DeviceObject,
  1071. IN PNP_VETO_TYPE VetoType,
  1072. IN PUNICODE_STRING VetoName OPTIONAL
  1073. )
  1074. /*++
  1075. --*/
  1076. {
  1077. ULONG dataSize, totalSize, i;
  1078. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1079. PDEVICE_NODE deviceNode;
  1080. PWCHAR vetoData;
  1081. NTSTATUS status;
  1082. if (PpPnpShuttingDown) {
  1083. return STATUS_TOO_LATE;
  1084. }
  1085. //
  1086. // Reference the device object so it can't go away until after we're
  1087. // done with notification.
  1088. //
  1089. ObReferenceObject(DeviceObject);
  1090. //
  1091. // Given the pdo, retrieve the devnode (the device instance string is
  1092. // attached to the devnode in the InstancePath field).
  1093. //
  1094. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  1095. if (!deviceNode) {
  1096. ObDereferenceObject(DeviceObject);
  1097. return STATUS_INVALID_PARAMETER_2;
  1098. }
  1099. //
  1100. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  1101. // we record in the TotalSize field later (because of the first index into
  1102. // the DeviceIdVetoNameBuffer, this is double null terminated).
  1103. //
  1104. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) +
  1105. deviceNode->InstancePath.Length +
  1106. (VetoName ? VetoName->Length : 0) +
  1107. sizeof(WCHAR)*2;
  1108. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  1109. deviceEvent = ExAllocatePoolWithTag(PagedPool,
  1110. totalSize,
  1111. PNP_DEVICE_EVENT_ENTRY_TAG);
  1112. if (deviceEvent == NULL) {
  1113. ObDereferenceObject(DeviceObject);
  1114. return STATUS_INSUFFICIENT_RESOURCES;
  1115. }
  1116. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  1117. deviceEvent->CallerEvent = CompletionEvent;
  1118. deviceEvent->Data.TotalSize = dataSize;
  1119. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  1120. deviceEvent->Data.Result = (PULONG)CompletionStatus;
  1121. deviceEvent->Data.u.VetoNotification.VetoType = VetoType;
  1122. //
  1123. // You can think of this as a MultiSz string where the first entry is the
  1124. // DeviceId for the device being removed, and the next Id's all corrospond
  1125. // to the vetoers.
  1126. //
  1127. RtlCopyMemory(
  1128. deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer,
  1129. deviceNode->InstancePath.Buffer,
  1130. deviceNode->InstancePath.Length
  1131. );
  1132. i = deviceNode->InstancePath.Length;
  1133. deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)] = UNICODE_NULL;
  1134. if (VetoName) {
  1135. vetoData = (&deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)])+1;
  1136. RtlCopyMemory(vetoData, VetoName->Buffer, VetoName->Length);
  1137. vetoData[VetoName->Length/sizeof(WCHAR)] = UNICODE_NULL;
  1138. }
  1139. //
  1140. // No need to NULL terminate the entry after the last one as we prezero'd
  1141. // the buffer. Now set the appropriate GUID so the UI looks right.
  1142. //
  1143. if (VetoedPowerOperation == PowerActionWarmEject) {
  1144. deviceEvent->Data.EventGuid = GUID_DEVICE_WARM_EJECT_VETOED;
  1145. } else if (VetoedPowerOperation == PowerActionHibernate) {
  1146. deviceEvent->Data.EventGuid = GUID_DEVICE_HIBERNATE_VETOED;
  1147. } else {
  1148. deviceEvent->Data.EventGuid = GUID_DEVICE_STANDBY_VETOED;
  1149. }
  1150. deviceEvent->Data.EventCategory = VetoEvent;
  1151. status = PiInsertEventInQueue(deviceEvent);
  1152. return status;
  1153. }
  1154. VOID
  1155. PpSetPlugPlayEvent(
  1156. IN CONST GUID *EventGuid,
  1157. IN PDEVICE_OBJECT DeviceObject
  1158. )
  1159. /*++
  1160. Routine Description:
  1161. This routine allows kernel mode enumerator to inform Plug and Play Manager
  1162. on the events triggered by enumeration, i.e., DeviceArrived and DeviceRemoved.
  1163. The PnP manager can then inform user-mode about the event.
  1164. Arguments:
  1165. EventId - Indicates what event is triggered by enumeration.
  1166. Return Value:
  1167. None.
  1168. --*/
  1169. {
  1170. ULONG dataSize, totalSize;
  1171. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1172. PDEVICE_NODE deviceNode;
  1173. PAGED_CODE();
  1174. ASSERT(EventGuid != NULL);
  1175. ASSERT(DeviceObject != NULL);
  1176. #if DBG
  1177. {
  1178. CHAR eventGuidString[80];
  1179. LookupGuid(EventGuid, eventGuidString, sizeof(eventGuidString));
  1180. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1181. "PpSetPlugPlayEvent: Entered\n EventGuid = %s\n",
  1182. eventGuidString));
  1183. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1184. " DeviceObject = 0x%p\n",
  1185. DeviceObject));
  1186. }
  1187. #endif
  1188. if (PpPnpShuttingDown) {
  1189. return;
  1190. }
  1191. //
  1192. // Reference the device object so it can't go away until after we're
  1193. // done with notification.
  1194. //
  1195. ObReferenceObject(DeviceObject);
  1196. //
  1197. // Given the pdo, retrieve the devnode (the device instance string is
  1198. // attached to the devnode in the InstancePath field).
  1199. //
  1200. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  1201. if (!deviceNode) {
  1202. ObDereferenceObject(DeviceObject);
  1203. return;
  1204. }
  1205. //
  1206. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  1207. // we record in the TotalSize field later (the length doesn't count the
  1208. // terminating null but we're already counting the first index into the
  1209. // DeviceId field. also include a final terminating null, in case this is
  1210. // a TargetDevice event, where DeviceIds is a multi-sz list).
  1211. //
  1212. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) + deviceNode->InstancePath.Length + sizeof(WCHAR);
  1213. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  1214. deviceEvent = ExAllocatePoolWithTag(PagedPool,
  1215. totalSize,
  1216. PNP_DEVICE_EVENT_ENTRY_TAG);
  1217. if (deviceEvent == NULL) {
  1218. ObDereferenceObject(DeviceObject);
  1219. return;
  1220. }
  1221. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  1222. RtlCopyMemory(&deviceEvent->Data.EventGuid, EventGuid, sizeof(GUID));
  1223. deviceEvent->Data.TotalSize = dataSize;
  1224. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  1225. if (PiCompareGuid(EventGuid, &GUID_DEVICE_ENUMERATED)) {
  1226. //
  1227. // GUID_DEVICE_ENUMERATED events are device installation requests for
  1228. // user-mode, and are sent using the DeviceInstallEvent event type.
  1229. //
  1230. deviceEvent->Data.EventCategory = DeviceInstallEvent;
  1231. RtlCopyMemory(&deviceEvent->Data.u.InstallDevice.DeviceId,
  1232. deviceNode->InstancePath.Buffer,
  1233. deviceNode->InstancePath.Length);
  1234. deviceEvent->Data.u.InstallDevice.DeviceId[deviceNode->InstancePath.Length/sizeof(WCHAR)] = 0x0;
  1235. } else {
  1236. //
  1237. // All other target events are sent using the TargetDeviceChangeEvent
  1238. // event type, and are distinguished by the EventGuid. Note that
  1239. // DeviceIds is a multi-sz list.
  1240. //
  1241. deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
  1242. RtlCopyMemory(&deviceEvent->Data.u.TargetDevice.DeviceIds,
  1243. deviceNode->InstancePath.Buffer,
  1244. deviceNode->InstancePath.Length);
  1245. deviceEvent->Data.u.TargetDevice.DeviceIds[deviceNode->InstancePath.Length/sizeof(WCHAR)] = 0x0;
  1246. deviceEvent->Data.u.TargetDevice.DeviceIds[deviceNode->InstancePath.Length/sizeof(WCHAR)+1] = 0x0;
  1247. }
  1248. PiInsertEventInQueue(deviceEvent);
  1249. return;
  1250. } // PpSetPlugPlayEvent
  1251. NTSTATUS
  1252. PpSynchronizeDeviceEventQueue(
  1253. VOID
  1254. )
  1255. {
  1256. NTSTATUS status;
  1257. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1258. KEVENT event;
  1259. ULONG result;
  1260. PAGED_CODE();
  1261. //
  1262. // Note this is the only queuing function which is valid when PpShuttingDown
  1263. // is TRUE.
  1264. //
  1265. deviceEvent = ExAllocatePoolWithTag( PagedPool,
  1266. sizeof(PNP_DEVICE_EVENT_ENTRY),
  1267. PNP_DEVICE_EVENT_ENTRY_TAG);
  1268. if (deviceEvent == NULL) {
  1269. return STATUS_NO_MEMORY;
  1270. }
  1271. KeInitializeEvent(&event, NotificationEvent, FALSE);
  1272. RtlZeroMemory((PVOID)deviceEvent, sizeof(PNP_DEVICE_EVENT_ENTRY));
  1273. deviceEvent->CallerEvent = &event;
  1274. deviceEvent->Data.EventGuid = GUID_DEVICE_NOOP;
  1275. deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
  1276. deviceEvent->Data.Result = &result;
  1277. deviceEvent->Data.TotalSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  1278. status = PiInsertEventInQueue(deviceEvent);
  1279. if (NT_SUCCESS(status)) {
  1280. status = KeWaitForSingleObject( &event,
  1281. Executive,
  1282. KernelMode,
  1283. FALSE, // not alertable
  1284. 0); // infinite
  1285. }
  1286. return status;
  1287. }
  1288. VOID
  1289. PiWalkDeviceList(
  1290. IN PVOID Context
  1291. )
  1292. /*++
  1293. Routine Description:
  1294. If the master device list contains any device events, empty the list now.
  1295. This is a worker item thread routine (queued by PiPostNotify). We walk the
  1296. list - this will cause the oldest device event on the list to be sent to
  1297. all registered recipients and then the device event will be removed (if at
  1298. least one recipient received it).
  1299. Order Rules:
  1300. Interface Devices - kernel mode first, user-mode second
  1301. Hardware profile changes - user-mode first, kernel-mode second
  1302. Target device changes (query remove, remove) : user-mode first, send
  1303. (cancel remove) : kernel-mode first, post
  1304. (custom) : kernel-mode first, post
  1305. Arguments:
  1306. NONE.
  1307. Return Value:
  1308. NONE.
  1309. --*/
  1310. {
  1311. NTSTATUS status;
  1312. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1313. PLIST_ENTRY current;
  1314. UNICODE_STRING tempString;
  1315. PAGED_CODE();
  1316. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1317. "PiWalkDeviceList: Worker thread entered\n"));
  1318. //
  1319. // Empty the device event list, remove entries from the head of the list
  1320. // (deliver oldest entries first).
  1321. //
  1322. status = KeWaitForSingleObject(&PpDeviceEventList->EventQueueMutex,
  1323. Executive,
  1324. KernelMode,
  1325. FALSE, // not alertable
  1326. 0); // infinite
  1327. if (!NT_SUCCESS(status)) {
  1328. ExAcquireFastMutex(&PiNotificationInProgressLock);
  1329. KeSetEvent(&PiEventQueueEmpty, 0, FALSE);
  1330. PiNotificationInProgress = FALSE;
  1331. ExReleaseFastMutex(&PiNotificationInProgressLock);
  1332. return;
  1333. }
  1334. for ( ; ; ) {
  1335. ExAcquireFastMutex(&PpDeviceEventList->Lock);
  1336. if (!IsListEmpty(&PpDeviceEventList->List)) {
  1337. current = RemoveHeadList(&PpDeviceEventList->List);
  1338. ExReleaseFastMutex(&PpDeviceEventList->Lock);
  1339. deviceEvent = CONTAINING_RECORD(current, // address
  1340. PNP_DEVICE_EVENT_ENTRY, // type
  1341. ListEntry); // field
  1342. #if DBG
  1343. {
  1344. CHAR guidString[256];
  1345. LookupGuid(&deviceEvent->Data.EventGuid, guidString, sizeof(guidString));
  1346. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1347. "PiWalkDeviceList: Processing queued event - EventGuid = %s\n",
  1348. guidString));
  1349. }
  1350. #endif
  1351. switch (deviceEvent->Data.EventCategory) {
  1352. case DeviceClassChangeEvent: {
  1353. //
  1354. // Notify kernel-mode (synchronous).
  1355. //
  1356. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1357. "PiWalkDeviceList: DeviceClassChangeEvent - notifying kernel-mode\n"));
  1358. RtlInitUnicodeString(&tempString, deviceEvent->Data.u.DeviceClass.SymbolicLinkName);
  1359. IopNotifyDeviceClassChange(&deviceEvent->Data.EventGuid,
  1360. &deviceEvent->Data.u.DeviceClass.ClassGuid,
  1361. &tempString);
  1362. //
  1363. // Notify user-mode (synchronous).
  1364. //
  1365. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1366. "PiWalkDeviceList: DeviceClassChangeEvent - user kernel-mode\n"));
  1367. PiNotifyUserMode(deviceEvent);
  1368. status = STATUS_SUCCESS;
  1369. break;
  1370. }
  1371. case CustomDeviceEvent: {
  1372. status = PiProcessCustomDeviceEvent(&deviceEvent);
  1373. break;
  1374. }
  1375. case TargetDeviceChangeEvent: {
  1376. status = PiProcessTargetDeviceEvent(&deviceEvent);
  1377. break;
  1378. }
  1379. case DeviceInstallEvent: {
  1380. //
  1381. // Notify user-mode (synchronous).
  1382. //
  1383. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1384. "PiWalkDeviceList: DeviceInstallEvent - notifying user-mode\n"));
  1385. PiNotifyUserMode(deviceEvent);
  1386. status = STATUS_SUCCESS;
  1387. break;
  1388. }
  1389. case HardwareProfileChangeEvent: {
  1390. //
  1391. // Notify user-mode (synchronous).
  1392. //
  1393. status = PiNotifyUserMode(deviceEvent);
  1394. if (NT_SUCCESS(status)) {
  1395. //
  1396. // Notify K-Mode
  1397. //
  1398. IopNotifyHwProfileChange(&deviceEvent->Data.EventGuid,
  1399. deviceEvent->VetoType,
  1400. deviceEvent->VetoName);
  1401. }
  1402. break;
  1403. }
  1404. case PowerEvent: {
  1405. //
  1406. // Notify user-mode (synchronous).
  1407. //
  1408. status = PiNotifyUserMode(deviceEvent);
  1409. break;
  1410. }
  1411. case VetoEvent: {
  1412. //
  1413. // Forward onto user-mode.
  1414. //
  1415. status = PiNotifyUserMode(deviceEvent);
  1416. break;
  1417. }
  1418. case BlockedDriverEvent: {
  1419. //
  1420. // Forward onto user-mode.
  1421. //
  1422. status = PiNotifyUserMode(deviceEvent);
  1423. break;
  1424. }
  1425. default: {
  1426. //
  1427. // These should never be queued to kernel mode. They are
  1428. // notifications for user mode, and should only be seen
  1429. // through the PiNotifyUserModeXxx functions.
  1430. //
  1431. ASSERT(0);
  1432. status = STATUS_UNSUCCESSFUL;
  1433. break;
  1434. }
  1435. }
  1436. if (status != STATUS_PENDING) {
  1437. PpCompleteDeviceEvent(deviceEvent, status);
  1438. }
  1439. //
  1440. // Commit pending registrations after processing each event.
  1441. //
  1442. IopProcessDeferredRegistrations();
  1443. } else {
  1444. ExAcquireFastMutex(&PiNotificationInProgressLock);
  1445. KeSetEvent(&PiEventQueueEmpty, 0, FALSE);
  1446. PiNotificationInProgress = FALSE;
  1447. //
  1448. // Commit pending registrations after processing all queued events.
  1449. //
  1450. IopProcessDeferredRegistrations();
  1451. ExReleaseFastMutex(&PiNotificationInProgressLock);
  1452. ExReleaseFastMutex(&PpDeviceEventList->Lock);
  1453. break;
  1454. }
  1455. }
  1456. if (Context != NULL) {
  1457. ExFreePool(Context);
  1458. }
  1459. KeReleaseMutex(&PpDeviceEventList->EventQueueMutex, FALSE);
  1460. return;
  1461. } // PiWalkDeviceList
  1462. VOID
  1463. PpCompleteDeviceEvent(
  1464. IN OUT PPNP_DEVICE_EVENT_ENTRY DeviceEvent,
  1465. IN NTSTATUS FinalStatus
  1466. )
  1467. /*++
  1468. Routine Description:
  1469. Arguments:
  1470. DeviceEvent - Event to complete.
  1471. FinalStatus - Final status for this event.
  1472. Return Value:
  1473. NONE.
  1474. --*/
  1475. {
  1476. #if DBG
  1477. CHAR guidString[256];
  1478. #endif
  1479. PAGED_CODE();
  1480. #if DBG
  1481. LookupGuid(&DeviceEvent->Data.EventGuid, guidString, sizeof(guidString));
  1482. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1483. "PpCompleteDeviceEvent: Completing queued event - EventGuid = %s with %08lx\n",
  1484. guidString,
  1485. FinalStatus));
  1486. #endif
  1487. //
  1488. // If synchronous, signal the user-supplied event.
  1489. //
  1490. if (DeviceEvent->CallerEvent) {
  1491. *DeviceEvent->Data.Result = FinalStatus;
  1492. KeSetEvent(DeviceEvent->CallerEvent, 0, FALSE);
  1493. }
  1494. if (DeviceEvent->Callback) {
  1495. DeviceEvent->Callback(DeviceEvent->Context);
  1496. }
  1497. //
  1498. // Release the reference we took for this device object during
  1499. // the PpSetCustomTargetEvent call.
  1500. //
  1501. if (DeviceEvent->Data.DeviceObject != NULL) {
  1502. ObDereferenceObject(DeviceEvent->Data.DeviceObject);
  1503. }
  1504. //
  1505. // Assume device event was delivered successfully, get rid of it.
  1506. //
  1507. ExFreePool(DeviceEvent);
  1508. return;
  1509. } // PpCompleteDeviceEvent
  1510. NTSTATUS
  1511. PiNotifyUserMode(
  1512. PPNP_DEVICE_EVENT_ENTRY DeviceEvent
  1513. )
  1514. /*++
  1515. Routine Description:
  1516. This routine dispatches the device event to user-mode for processing.
  1517. Arguments:
  1518. DeviceEvent - Data describing what change and how.
  1519. Return Value:
  1520. Retuns an NTSTATUS value.
  1521. --*/
  1522. {
  1523. NTSTATUS status = STATUS_SUCCESS, status1 = STATUS_SUCCESS;
  1524. PAGED_CODE();
  1525. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1526. "PiNotifyUserMode: Entered\n"));
  1527. //
  1528. // First make sure user-mode is up and running before attempting to deliver
  1529. // an event. If not running yet, skip user-mode for this event.
  1530. //
  1531. if (UserModeRunning) {
  1532. //
  1533. // User-mode notification is a single-shot model, once user-mode is
  1534. // running, I need to wait until user-mode is ready to take the next
  1535. // event (i.e, wait until we're sitting in another NtGetPlugPlayEvent
  1536. // call).
  1537. //
  1538. status1 = KeWaitForSingleObject(&PpUserBlock->Registered,
  1539. Executive,
  1540. KernelMode,
  1541. FALSE,
  1542. NULL);
  1543. ASSERT (PpUserBlock->Deferred == FALSE);
  1544. //
  1545. // Change the status after the wait.
  1546. //
  1547. PpUserBlock->Status = STATUS_SUCCESS;
  1548. if (NT_SUCCESS(status1)) {
  1549. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1550. "PiNotifyUserMode: User-mode ready\n"));
  1551. //
  1552. // Make sure we can handle it in the pool buffer and copy it out
  1553. //
  1554. if (PpUserBlock->PoolSize < DeviceEvent->Data.TotalSize) {
  1555. //
  1556. //Allocate a new block (well, conceptually grow the block)
  1557. // only when it's not big enough, so that we know we've always got
  1558. // room for normal events, and in the very low memory case, we can
  1559. // fail custom events, but keep the system running
  1560. //
  1561. PVOID pHold;
  1562. pHold = ExAllocatePoolWithTag(NonPagedPool,
  1563. DeviceEvent->Data.TotalSize,
  1564. PNP_POOL_EVENT_BUFFER);
  1565. if (!pHold) {
  1566. IopDbgPrint((IOP_IOEVENT_ERROR_LEVEL,
  1567. "PiNotifyUserMode: Out of NonPagedPool!!\n"));
  1568. PpUserBlock->Status = STATUS_INSUFFICIENT_RESOURCES;
  1569. return STATUS_INSUFFICIENT_RESOURCES;
  1570. }
  1571. PpUserBlock->PoolSize = DeviceEvent->Data.TotalSize;
  1572. ExFreePool (PpUserBlock->PoolBuffer );
  1573. PpUserBlock->PoolBuffer = pHold;
  1574. }
  1575. PpUserBlock->PoolUsed = DeviceEvent->Data.TotalSize;
  1576. RtlCopyMemory(PpUserBlock->PoolBuffer,
  1577. &DeviceEvent->Data,
  1578. PpUserBlock->PoolUsed);
  1579. }
  1580. //
  1581. // Veto information is only propogated where needed, ie
  1582. // QUERY_REMOVE's, Profile change requests and PowerEvents.
  1583. //
  1584. if (PiCompareGuid(&DeviceEvent->Data.EventGuid,
  1585. &GUID_TARGET_DEVICE_QUERY_REMOVE) ||
  1586. PiCompareGuid(&DeviceEvent->Data.EventGuid,
  1587. &GUID_HWPROFILE_QUERY_CHANGE) ||
  1588. PiCompareGuid(&DeviceEvent->Data.EventGuid,
  1589. &GUID_DEVICE_KERNEL_INITIATED_EJECT) ||
  1590. (DeviceEvent->Data.EventCategory == PowerEvent)) {
  1591. PpUserBlock->VetoType = DeviceEvent->VetoType;
  1592. PpUserBlock->VetoName = DeviceEvent->VetoName;
  1593. } else {
  1594. PpUserBlock->VetoType = NULL;
  1595. PpUserBlock->VetoName = NULL;
  1596. }
  1597. //
  1598. // Set the system event that causes NtGetPlugPlayEvent to return to caller.
  1599. //
  1600. KeSetEvent(&PpUserBlock->NotifyUserEvent, 0, FALSE);
  1601. //
  1602. // Wait until we get an answer back from user-mode.
  1603. //
  1604. status1 = KeWaitForSingleObject(&PpUserBlock->UserResultEvent,
  1605. Executive,
  1606. KernelMode,
  1607. TRUE,
  1608. NULL);
  1609. //
  1610. // Check the result from this user-mode notification.
  1611. //
  1612. if (status1 == STATUS_ALERTED || status1 == STATUS_SUCCESS) {
  1613. if (!PpUserBlock->Result) {
  1614. //
  1615. // For query-remove case, any errors are treated as a
  1616. // failure during notification (since it may result in our
  1617. // inability to let a registered caller vote in the query-remove)
  1618. // and the PpUserBlock->Result is set accordingly.
  1619. //
  1620. //
  1621. // Note! User mode ONLY returns a 0 or !0 response.
  1622. // if 1 then it succeeded.
  1623. //
  1624. status = STATUS_UNSUCCESSFUL;
  1625. }
  1626. }
  1627. PpUserBlock->VetoType = NULL;
  1628. PpUserBlock->VetoName = NULL;
  1629. }
  1630. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1631. "PiNotifyUserMode: User-mode returned, status = 0x%08X, status1 = 0x%08X, Result = 0x%08X\n",
  1632. status,
  1633. status1,
  1634. PpUserBlock->Result));
  1635. return status;
  1636. } // PiNotifyUserMode
  1637. VOID
  1638. PiUserResponse(
  1639. IN ULONG Response,
  1640. IN PNP_VETO_TYPE VetoType,
  1641. IN LPWSTR VetoName,
  1642. IN ULONG VetoNameLength
  1643. )
  1644. /*++
  1645. Routine Description:
  1646. This routine is called when user-mode pnp manager is signalling that it's
  1647. done processing an event; the result of the event processing is passed in
  1648. the Response parameter.
  1649. Arguments:
  1650. Response - Result of event processing in user-mode.
  1651. Return Value:
  1652. None.
  1653. --*/
  1654. {
  1655. UNICODE_STRING vetoString;
  1656. PAGED_CODE();
  1657. PpUserBlock->Result = Response;
  1658. if (PpUserBlock->VetoType != NULL) {
  1659. *PpUserBlock->VetoType = VetoType;
  1660. }
  1661. if (PpUserBlock->VetoName != NULL) {
  1662. ASSERT(VetoNameLength == (USHORT)VetoNameLength);
  1663. vetoString.MaximumLength = (USHORT)VetoNameLength;
  1664. vetoString.Length = (USHORT)VetoNameLength;
  1665. vetoString.Buffer = VetoName;
  1666. RtlCopyUnicodeString(PpUserBlock->VetoName, &vetoString);
  1667. }
  1668. KeSetEvent(&PpUserBlock->UserResultEvent, 0, FALSE);
  1669. } // PiUserResponse
  1670. NTSTATUS
  1671. PiNotifyUserModeDeviceRemoval(
  1672. IN PPNP_DEVICE_EVENT_ENTRY TemplateDeviceEvent,
  1673. IN CONST GUID *EventGuid,
  1674. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  1675. OUT PUNICODE_STRING VetoName OPTIONAL
  1676. )
  1677. /*++
  1678. Routine Description:
  1679. This routine tells user mode to perform a specific device removal
  1680. operation.
  1681. Arguments:
  1682. TemplateDeviceEvent - Device event containing information about the
  1683. intended event (includes a list of devices.) The
  1684. event is temporarily used by this function, and is
  1685. restored before this function returns.
  1686. EventGuid - Points to the event user mode should process:
  1687. GUID_TARGET_DEVICE_QUERY_REMOVE
  1688. GUID_TARGET_DEVICE_REMOVE_CANCELLED
  1689. GUID_DEVICE_REMOVE_PENDING
  1690. GUID_TARGET_DEVICE_REMOVE_COMPLETE
  1691. GUID_DEVICE_SURPRISE_REMOVAL
  1692. VetoType - Pointer to address that receives the veto type if the operation
  1693. failed.
  1694. VetoName - Pointer to a unicode string that will receive data appropriate
  1695. to the veto type.
  1696. Return Value:
  1697. NTSTATUS.
  1698. --*/
  1699. {
  1700. NTSTATUS status;
  1701. GUID oldGuid;
  1702. PPNP_VETO_TYPE oldVetoType;
  1703. PUNICODE_STRING oldVetoName;
  1704. #if DBG
  1705. CHAR guidString[256];
  1706. #endif
  1707. PAGED_CODE();
  1708. #if DBG
  1709. LookupGuid(EventGuid, guidString, sizeof(guidString));
  1710. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  1711. "PiNotifyUserModeDeviceRemoval: %s - notifying user-mode\n",
  1712. guidString));
  1713. #endif
  1714. //
  1715. // Save the old guid so we can use the template without making a copy. We
  1716. // preserve it so that the removal veto UI can use the original event GUID
  1717. // to let the UI differentiate disables from ejects, etc.
  1718. //
  1719. RtlCopyMemory(&oldGuid, &TemplateDeviceEvent->Data.EventGuid, sizeof(GUID));
  1720. //
  1721. // Do the same with the vetoname and vetobuffer.
  1722. //
  1723. oldVetoType = TemplateDeviceEvent->VetoType;
  1724. oldVetoName = TemplateDeviceEvent->VetoName;
  1725. //
  1726. // Copy in the new data.
  1727. //
  1728. RtlCopyMemory(&TemplateDeviceEvent->Data.EventGuid, EventGuid, sizeof(GUID));
  1729. TemplateDeviceEvent->VetoType = VetoType;
  1730. TemplateDeviceEvent->VetoName = VetoName;
  1731. //
  1732. // Send it.
  1733. //
  1734. status = PiNotifyUserMode(TemplateDeviceEvent);
  1735. //
  1736. // Restore the old info.
  1737. //
  1738. RtlCopyMemory(&TemplateDeviceEvent->Data.EventGuid, &oldGuid, sizeof(GUID));
  1739. TemplateDeviceEvent->VetoType = oldVetoType;
  1740. TemplateDeviceEvent->VetoName = oldVetoName;
  1741. return status;
  1742. }
  1743. NTSTATUS
  1744. PiNotifyUserModeKernelInitiatedEject(
  1745. IN PDEVICE_OBJECT DeviceObject,
  1746. OUT PNP_VETO_TYPE *VetoType,
  1747. OUT PUNICODE_STRING VetoName
  1748. )
  1749. /*++
  1750. Routine Description:
  1751. This routine is called to notify user mode a device has a kenel-mode
  1752. initated eject outstanding. UmPnpMgr might decide to veto the event if
  1753. a user with the appropriate permissions hasn't logged on locally.
  1754. Arguments:
  1755. DeviceObject - Indicates the device object is to be ejected.
  1756. Return Value:
  1757. None.
  1758. --*/
  1759. {
  1760. NTSTATUS status;
  1761. PDEVICE_NODE deviceNode;
  1762. ULONG dataSize, totalSize, i;
  1763. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1764. PAGED_CODE();
  1765. ASSERT(DeviceObject != NULL);
  1766. //
  1767. // Reference the device object so it can't go away until after we're
  1768. // done with notification.
  1769. //
  1770. ObReferenceObject(DeviceObject);
  1771. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  1772. ASSERT(deviceNode);
  1773. //
  1774. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  1775. // we record in the TotalSize field later (the length doesn't count the
  1776. // terminating null but we're already counting the first index into the
  1777. // DeviceId field so it works out. Add one more for double-null term.
  1778. //
  1779. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  1780. dataSize += deviceNode->InstancePath.Length + sizeof(WCHAR);
  1781. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  1782. deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
  1783. if (deviceEvent == NULL) {
  1784. ObDereferenceObject(DeviceObject);
  1785. return STATUS_NO_MEMORY;
  1786. }
  1787. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  1788. deviceEvent->CallerEvent = NULL;
  1789. deviceEvent->Argument = 0;
  1790. deviceEvent->VetoType = VetoType;
  1791. deviceEvent->VetoName = VetoName;
  1792. deviceEvent->Data.EventGuid = GUID_DEVICE_KERNEL_INITIATED_EJECT;
  1793. deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
  1794. deviceEvent->Data.Result = 0;
  1795. deviceEvent->Data.Flags = 0;
  1796. deviceEvent->Data.TotalSize = dataSize;
  1797. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  1798. if (deviceNode->InstancePath.Length != 0) {
  1799. RtlCopyMemory((PVOID)deviceEvent->Data.u.TargetDevice.DeviceIds,
  1800. (PVOID)deviceNode->InstancePath.Buffer,
  1801. deviceNode->InstancePath.Length);
  1802. }
  1803. i = deviceNode->InstancePath.Length/sizeof(WCHAR);
  1804. deviceEvent->Data.u.TargetDevice.DeviceIds[i] = L'\0';
  1805. status = PiNotifyUserMode(deviceEvent);
  1806. ExFreePool(deviceEvent);
  1807. ObDereferenceObject(DeviceObject);
  1808. return status;
  1809. } // PiNotifyUserModeKernelInitiatedEject
  1810. NTSTATUS
  1811. PiNotifyUserModeRemoveVetoed(
  1812. IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
  1813. IN PDEVICE_OBJECT DeviceObject,
  1814. IN PNP_VETO_TYPE VetoType,
  1815. IN PUNICODE_STRING VetoName OPTIONAL
  1816. )
  1817. /*++
  1818. --*/
  1819. {
  1820. ULONG dataSize, totalSize, i;
  1821. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1822. PDEVICE_NODE deviceNode;
  1823. PWCHAR vetoData;
  1824. NTSTATUS status;
  1825. //
  1826. // This device should be locked during this operation, but all good designs
  1827. // includes healthy doses of paranoia.
  1828. //
  1829. ObReferenceObject(DeviceObject);
  1830. //
  1831. // Given the pdo, retrieve the devnode (the device instance string is
  1832. // attached to the devnode in the InstancePath field).
  1833. //
  1834. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  1835. if (!deviceNode) {
  1836. ObDereferenceObject(DeviceObject);
  1837. return STATUS_INVALID_PARAMETER_1;
  1838. }
  1839. //
  1840. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  1841. // we record in the TotalSize field later (because of the first index into
  1842. // the DeviceIdVetoNameBuffer, this is double null terminated).
  1843. //
  1844. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) +
  1845. deviceNode->InstancePath.Length +
  1846. (VetoName ? VetoName->Length : 0) +
  1847. sizeof(WCHAR)*2;
  1848. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  1849. deviceEvent = ExAllocatePoolWithTag(PagedPool,
  1850. totalSize,
  1851. PNP_DEVICE_EVENT_ENTRY_TAG);
  1852. if (deviceEvent == NULL) {
  1853. ObDereferenceObject(DeviceObject);
  1854. return STATUS_INSUFFICIENT_RESOURCES;
  1855. }
  1856. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  1857. deviceEvent->Data.TotalSize = dataSize;
  1858. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  1859. deviceEvent->Data.u.VetoNotification.VetoType = VetoType;
  1860. //
  1861. // You can think of this as a MultiSz string where the first entry is the
  1862. // DeviceId for the device being removed, and the next Id's all corrospond
  1863. // to the vetoers.
  1864. //
  1865. RtlCopyMemory(
  1866. deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer,
  1867. deviceNode->InstancePath.Buffer,
  1868. deviceNode->InstancePath.Length
  1869. );
  1870. i = deviceNode->InstancePath.Length;
  1871. deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)] = UNICODE_NULL;
  1872. if (VetoName) {
  1873. vetoData = (&deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)])+1;
  1874. RtlCopyMemory(vetoData, VetoName->Buffer, VetoName->Length);
  1875. vetoData[VetoName->Length/sizeof(WCHAR)] = UNICODE_NULL;
  1876. }
  1877. //
  1878. // No need to NULL terminate the entry after the last one as we prezero'd
  1879. // the buffer. Now set the appropriate GUID so the UI looks right.
  1880. //
  1881. if (PiCompareGuid(&VetoedDeviceEvent->Data.EventGuid, &GUID_DEVICE_EJECT)) {
  1882. deviceEvent->Data.EventGuid = GUID_DEVICE_EJECT_VETOED;
  1883. } else {
  1884. ASSERT(PiCompareGuid(&VetoedDeviceEvent->Data.EventGuid, &GUID_DEVICE_QUERY_AND_REMOVE));
  1885. deviceEvent->Data.EventGuid = GUID_DEVICE_REMOVAL_VETOED;
  1886. }
  1887. deviceEvent->Data.EventCategory = VetoEvent;
  1888. status = PiNotifyUserMode(deviceEvent);
  1889. ExFreePool(deviceEvent);
  1890. ObDereferenceObject(DeviceObject);
  1891. return status;
  1892. }
  1893. NTSTATUS
  1894. PiNotifyUserModeRemoveVetoedByList(
  1895. IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
  1896. IN PDEVICE_OBJECT DeviceObject,
  1897. IN PNP_VETO_TYPE VetoType,
  1898. IN PWSTR MultiSzVetoList
  1899. )
  1900. /*++
  1901. --*/
  1902. {
  1903. ULONG dataSize, totalSize, i, vetoListLength;
  1904. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1905. PDEVICE_NODE deviceNode;
  1906. PWCHAR vetoData;
  1907. NTSTATUS status;
  1908. //
  1909. // This device should be locked during this operation, but all good designs
  1910. // includes healthy doses of paranoia.
  1911. //
  1912. ObReferenceObject(DeviceObject);
  1913. //
  1914. // Given the pdo, retrieve the devnode (the device instance string is
  1915. // attached to the devnode in the InstancePath field).
  1916. //
  1917. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  1918. if (!deviceNode) {
  1919. ObDereferenceObject(DeviceObject);
  1920. return STATUS_INVALID_PARAMETER_1;
  1921. }
  1922. //
  1923. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  1924. // we record in the TotalSize field later (because of the first index into
  1925. // the DeviceIdVetoNameBuffer, this is double null terminated).
  1926. //
  1927. for(vetoData = MultiSzVetoList; *vetoData; vetoData += vetoListLength) {
  1928. vetoListLength = (ULONG)(wcslen(vetoData) + 1);
  1929. }
  1930. vetoListLength = ((ULONG)(vetoData - MultiSzVetoList))*sizeof(WCHAR);
  1931. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK) +
  1932. deviceNode->InstancePath.Length +
  1933. vetoListLength +
  1934. sizeof(WCHAR);
  1935. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  1936. deviceEvent = ExAllocatePoolWithTag(PagedPool,
  1937. totalSize,
  1938. PNP_DEVICE_EVENT_ENTRY_TAG);
  1939. if (deviceEvent == NULL) {
  1940. ObDereferenceObject(DeviceObject);
  1941. return STATUS_INSUFFICIENT_RESOURCES;
  1942. }
  1943. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  1944. deviceEvent->Data.TotalSize = dataSize;
  1945. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  1946. deviceEvent->Data.u.VetoNotification.VetoType = VetoType;
  1947. //
  1948. // You can think of this as a MultiSz string where the first entry is the
  1949. // DeviceId for the device being removed, and the next Id's all corrospond
  1950. // to the vetoers.
  1951. //
  1952. RtlCopyMemory(
  1953. deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer,
  1954. deviceNode->InstancePath.Buffer,
  1955. deviceNode->InstancePath.Length
  1956. );
  1957. i = deviceNode->InstancePath.Length;
  1958. deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)] = UNICODE_NULL;
  1959. vetoData = (&deviceEvent->Data.u.VetoNotification.DeviceIdVetoNameBuffer[i/sizeof(WCHAR)])+1;
  1960. RtlCopyMemory(vetoData, MultiSzVetoList, vetoListLength);
  1961. vetoData[vetoListLength/sizeof(WCHAR)] = UNICODE_NULL;
  1962. //
  1963. // No need to NULL terminate the entry after the last one as we prezero'd
  1964. // the buffer. Now set the appropriate GUID so the UI looks right.
  1965. //
  1966. if (PiCompareGuid(&VetoedDeviceEvent->Data.EventGuid, &GUID_DEVICE_EJECT)) {
  1967. deviceEvent->Data.EventGuid = GUID_DEVICE_EJECT_VETOED;
  1968. } else {
  1969. ASSERT(PiCompareGuid(&VetoedDeviceEvent->Data.EventGuid, &GUID_DEVICE_QUERY_AND_REMOVE));
  1970. deviceEvent->Data.EventGuid = GUID_DEVICE_REMOVAL_VETOED;
  1971. }
  1972. deviceEvent->Data.EventCategory = VetoEvent;
  1973. status = PiNotifyUserMode(deviceEvent);
  1974. ExFreePool(deviceEvent);
  1975. ObDereferenceObject(DeviceObject);
  1976. return status;
  1977. }
  1978. NTSTATUS
  1979. PpNotifyUserModeRemovalSafe(
  1980. IN PDEVICE_OBJECT DeviceObject
  1981. )
  1982. /*++
  1983. Routine Description:
  1984. This routine is called to notify user mode a device can be removed. This
  1985. is similar to PpSetDeviceRemovalSafe except that we are already in a
  1986. kernel mode PnP device event we must complete, from which this function
  1987. will piggyback a notification up to just user mode.
  1988. Arguments:
  1989. DeviceObject - Indicates the device object is ready for removal.
  1990. Return Value:
  1991. None.
  1992. --*/
  1993. {
  1994. NTSTATUS status;
  1995. PDEVICE_NODE deviceNode;
  1996. ULONG dataSize, totalSize, i;
  1997. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  1998. PAGED_CODE();
  1999. ASSERT(DeviceObject != NULL);
  2000. //
  2001. // Reference the device object so it can't go away until after we're
  2002. // done with notification.
  2003. //
  2004. ObReferenceObject(DeviceObject);
  2005. deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
  2006. ASSERT(deviceNode);
  2007. //
  2008. // Calculate the size of the PLUGPLAY_EVENT_BLOCK, this is the size that
  2009. // we record in the TotalSize field later (the length doesn't count the
  2010. // terminating null but we're already counting the first index into the
  2011. // DeviceId field so it works out. Add one more for double-null term.
  2012. //
  2013. dataSize = sizeof(PLUGPLAY_EVENT_BLOCK);
  2014. dataSize += deviceNode->InstancePath.Length + sizeof(WCHAR);
  2015. totalSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) + dataSize;
  2016. deviceEvent = ExAllocatePoolWithTag(PagedPool, totalSize, PNP_DEVICE_EVENT_ENTRY_TAG);
  2017. if (deviceEvent == NULL) {
  2018. ObDereferenceObject(DeviceObject);
  2019. return STATUS_INSUFFICIENT_RESOURCES;
  2020. }
  2021. RtlZeroMemory((PVOID)deviceEvent, totalSize);
  2022. deviceEvent->CallerEvent = NULL;
  2023. deviceEvent->Argument = 0;
  2024. deviceEvent->VetoType = NULL;
  2025. deviceEvent->VetoName = NULL;
  2026. deviceEvent->Data.EventGuid = GUID_DEVICE_SAFE_REMOVAL;
  2027. deviceEvent->Data.EventCategory = TargetDeviceChangeEvent;
  2028. deviceEvent->Data.Result = 0;
  2029. deviceEvent->Data.Flags = 0;
  2030. deviceEvent->Data.TotalSize = dataSize;
  2031. deviceEvent->Data.DeviceObject = (PVOID)DeviceObject;
  2032. if (deviceNode->InstancePath.Length != 0) {
  2033. RtlCopyMemory((PVOID)deviceEvent->Data.u.TargetDevice.DeviceIds,
  2034. (PVOID)deviceNode->InstancePath.Buffer,
  2035. deviceNode->InstancePath.Length);
  2036. }
  2037. i = deviceNode->InstancePath.Length/sizeof(WCHAR);
  2038. deviceEvent->Data.u.TargetDevice.DeviceIds[i] = L'\0';
  2039. status = PiNotifyUserMode(deviceEvent);
  2040. ExFreePool(deviceEvent);
  2041. ObDereferenceObject(DeviceObject);
  2042. return status;
  2043. } // PpNotifyUserModeRemovalSafe
  2044. NTSTATUS
  2045. PiProcessQueryRemoveAndEject(
  2046. IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
  2047. )
  2048. /*++
  2049. Routine Description:
  2050. This routine processes the various flavours of remove: Eject, SurpriseRemove,
  2051. Remove, and QueryRemove.
  2052. Eject
  2053. Retrieve bus, removal, and eject relations.
  2054. Do queries on all relations
  2055. Send IRP_MN_REMOVE_DEVICE to all the relations.
  2056. Queue the pending eject
  2057. Once the eject happens
  2058. Reenumerate all the indirect relation's parents
  2059. SurpriseRemove
  2060. Retrieve bus, removal, and eject relations.
  2061. Send IRP_MN_SURPRISE_REMOVAL to all the direct relations.
  2062. Notify everyone that the device is gone.
  2063. Reenumerate the parents of all the indirect relations.
  2064. Remove the indirect relations from the relations list.
  2065. Queue the pending surprise removal.
  2066. Once the last handle is closed.
  2067. Send IRP_MN_REMOVE_DEVICE to all the direct relations.
  2068. RemoveFailedDevice
  2069. Retrieve bus and removal relations.
  2070. Notify everyone that the device is going.
  2071. Reenumerate the parents of all the indirect relations.
  2072. Remove the indirect relations from the relations list.
  2073. Queue as a pending surprise removal.
  2074. Once the last handle is closed.
  2075. Send IRP_MN_REMOVE_DEVICE to all the direct relations.
  2076. RemoveUnstartedFailedDevice
  2077. Retrieve bus relations.
  2078. Notify everyone that the device is going.
  2079. Send IRP_MN_REMOVE_DEVICE to all the direct relations.
  2080. Remove
  2081. Use the relations from the last QueryRemove or retrieve new bus, removal,
  2082. and eject relations if the device wasn't already QueryRemoved.
  2083. Send IRP_MN_REMOVE_DEVICE to all the relations.
  2084. Arguments:
  2085. Response - Result of event processing in user-mode.
  2086. Return Value:
  2087. NTSTATUS code.
  2088. --*/
  2089. {
  2090. PPNP_DEVICE_EVENT_ENTRY deviceEvent, tempDeviceEvent;
  2091. PPNP_DEVICE_EVENT_ENTRY surpriseRemovalEvent;
  2092. PLUGPLAY_DEVICE_DELETE_TYPE deleteType;
  2093. PPENDING_RELATIONS_LIST_ENTRY pendingRelations;
  2094. PNP_VETO_TYPE vetoType;
  2095. PDEVICE_OBJECT deviceObject, relatedDeviceObject;
  2096. PDEVICE_OBJECT *pdoList;
  2097. PDEVICE_NODE deviceNode, relatedDeviceNode;
  2098. PRELATION_LIST relationsList;
  2099. ULONG relationCount;
  2100. NTSTATUS status;
  2101. ULONG marker;
  2102. BOOLEAN directDescendant;
  2103. PDEVICE_OBJECT vetoingDevice = NULL;
  2104. PDRIVER_OBJECT vetoingDriver = NULL;
  2105. LONG index;
  2106. BOOLEAN possibleProfileChangeInProgress = FALSE;
  2107. BOOLEAN subsumingProfileChange = FALSE;
  2108. BOOLEAN hotEjectSupported;
  2109. BOOLEAN warmEjectSupported;
  2110. BOOLEAN excludeIndirectRelations;
  2111. UNICODE_STRING singleVetoListItem;
  2112. PWSTR vetoList;
  2113. UNICODE_STRING internalVetoString;
  2114. PWSTR internalVetoBuffer;
  2115. PDOCK_INTERFACE dockInterface = NULL;
  2116. PAGED_CODE();
  2117. deviceEvent = *DeviceEvent;
  2118. deviceObject = (PDEVICE_OBJECT)deviceEvent->Data.DeviceObject;
  2119. deviceNode = deviceObject->DeviceObjectExtension->DeviceNode;
  2120. surpriseRemovalEvent = NULL;
  2121. PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
  2122. if (PiCompareGuid(&deviceEvent->Data.EventGuid, &GUID_DEVICE_EJECT)) {
  2123. deleteType = EjectDevice;
  2124. } else if (deviceEvent->Data.Flags & TDF_KERNEL_INITIATED) {
  2125. if (!(deviceNode->Flags & DNF_ENUMERATED)) {
  2126. ASSERT(deviceNode->State == DeviceNodeAwaitingQueuedDeletion);
  2127. if ((deviceNode->PreviousState == DeviceNodeStarted) ||
  2128. (deviceNode->PreviousState == DeviceNodeStopped) ||
  2129. (deviceNode->PreviousState == DeviceNodeRestartCompletion)) {
  2130. deleteType = SurpriseRemoveDevice;
  2131. } else {
  2132. deleteType = RemoveDevice;
  2133. }
  2134. } else {
  2135. ASSERT(deviceNode->State == DeviceNodeAwaitingQueuedRemoval);
  2136. if ((deviceNode->PreviousState == DeviceNodeStarted) ||
  2137. (deviceNode->PreviousState == DeviceNodeStopped) ||
  2138. (deviceNode->PreviousState == DeviceNodeRestartCompletion)) {
  2139. deleteType = RemoveFailedDevice;
  2140. } else {
  2141. deleteType = RemoveUnstartedFailedDevice;
  2142. }
  2143. }
  2144. } else {
  2145. deleteType = QueryRemoveDevice;
  2146. }
  2147. if (deleteType == QueryRemoveDevice || deleteType == EjectDevice) {
  2148. if (deviceNode->Flags & DNF_LEGACY_DRIVER) {
  2149. PiFinalizeVetoedRemove(
  2150. deviceEvent,
  2151. PNP_VetoLegacyDevice,
  2152. &deviceNode->InstancePath
  2153. );
  2154. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2155. return STATUS_PLUGPLAY_QUERY_VETOED;
  2156. }
  2157. }
  2158. if (deleteType == QueryRemoveDevice && deviceEvent->Argument == CM_PROB_DISABLED) {
  2159. //
  2160. // if we're trying to remove the device to disable the device
  2161. //
  2162. if (deviceNode->DisableableDepends > 0) {
  2163. //
  2164. // we should have caught this before (in usermode PnP)
  2165. // but a rare scenario can exist where the device becomes non-disableable
  2166. // There is still a potential gap, if the device hasn't got around
  2167. // to marking itself as non-disableable yet
  2168. //
  2169. PiFinalizeVetoedRemove(
  2170. deviceEvent,
  2171. PNP_VetoNonDisableable,
  2172. &deviceNode->InstancePath
  2173. );
  2174. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2175. return STATUS_PLUGPLAY_QUERY_VETOED;
  2176. }
  2177. }
  2178. //
  2179. // Allocate room for a possible veto buffer.
  2180. //
  2181. internalVetoBuffer = (PWSTR) PiAllocateCriticalMemory(
  2182. deleteType,
  2183. PagedPool,
  2184. MAX_VETO_NAME_LENGTH * sizeof(WCHAR),
  2185. 0
  2186. );
  2187. if (internalVetoBuffer == NULL) {
  2188. PiFinalizeVetoedRemove(
  2189. deviceEvent,
  2190. PNP_VetoTypeUnknown,
  2191. NULL
  2192. );
  2193. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2194. return STATUS_PLUGPLAY_QUERY_VETOED;
  2195. }
  2196. //
  2197. // Preinit the veto information
  2198. //
  2199. vetoType = PNP_VetoTypeUnknown;
  2200. internalVetoString.MaximumLength = MAX_VETO_NAME_LENGTH;
  2201. internalVetoString.Length = 0;
  2202. internalVetoString.Buffer = internalVetoBuffer;
  2203. if (deleteType == EjectDevice) {
  2204. if (deviceNode->Flags & DNF_LOCKED_FOR_EJECT) {
  2205. //
  2206. // Either this node or one of its ancestors is already being ejected.
  2207. //
  2208. ExFreePool(internalVetoBuffer);
  2209. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2210. return STATUS_SUCCESS;
  2211. }
  2212. if (deviceEvent->Data.Flags & TDF_KERNEL_INITIATED) {
  2213. //
  2214. // Check permissions.
  2215. //
  2216. status = PiNotifyUserModeKernelInitiatedEject(
  2217. deviceObject,
  2218. &vetoType,
  2219. &internalVetoString
  2220. );
  2221. if (!NT_SUCCESS(status)) {
  2222. PiFinalizeVetoedRemove(
  2223. deviceEvent,
  2224. vetoType,
  2225. &internalVetoString
  2226. );
  2227. ExFreePool(internalVetoBuffer);
  2228. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2229. return STATUS_PLUGPLAY_QUERY_VETOED;
  2230. }
  2231. }
  2232. if ((deviceNode->DockInfo.DockStatus == DOCK_DEPARTING) ||
  2233. (deviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED)) {
  2234. //
  2235. // We already have an eject queued against this device. Don't allow
  2236. // another eject to break into the middle of a queue/cancel warm
  2237. // eject sequence.
  2238. //
  2239. ExFreePool(internalVetoBuffer);
  2240. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2241. return STATUS_SUCCESS;
  2242. }
  2243. //
  2244. // What types of ejection can we do? (warm/hot)
  2245. //
  2246. if (!IopDeviceNodeFlagsToCapabilities(deviceNode)->Removable) {
  2247. //
  2248. // This device is neither ejectable, nor even removable.
  2249. //
  2250. PiFinalizeVetoedRemove(
  2251. deviceEvent,
  2252. PNP_VetoIllegalDeviceRequest,
  2253. &deviceNode->InstancePath
  2254. );
  2255. ExFreePool(internalVetoBuffer);
  2256. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2257. return STATUS_PLUGPLAY_QUERY_VETOED;
  2258. }
  2259. }
  2260. if ((deleteType == QueryRemoveDevice) && (!PipAreDriversLoaded(deviceNode))) {
  2261. //
  2262. // The device doesn't have an FDO.
  2263. //
  2264. status = STATUS_SUCCESS;
  2265. if ((deviceNode->State == DeviceNodeInitialized) ||
  2266. (deviceNode->State == DeviceNodeRemoved)) {
  2267. //
  2268. // The rules are:
  2269. // 1) !TDF_NO_RESTART means clear the devnode and get it ready
  2270. // as long as the problem is user resettable. Ignore the passed
  2271. // in problem code (probably either CM_PROB_WILL_BE_REMOVED or
  2272. // CM_PROB_DEVICE_NOT_THERE), as it means nothing.
  2273. // 2) TDF_NO_RESTART means change the problem code over if you can.
  2274. // If the problem code is not user resettable, the problem code
  2275. // won't change.
  2276. //
  2277. //
  2278. // In all cases we try to clear the problem.
  2279. //
  2280. if (PipDoesDevNodeHaveProblem(deviceNode)) {
  2281. if (!PipIsProblemReadonly(deviceNode->Problem)) {
  2282. PipClearDevNodeProblem(deviceNode);
  2283. }
  2284. }
  2285. if (!PipDoesDevNodeHaveProblem(deviceNode)) {
  2286. if (!(deviceEvent->Data.Flags & TDF_NO_RESTART)) {
  2287. //
  2288. // This is a reset attempt. Mark the devnode so that it
  2289. // comes online next enumeration.
  2290. //
  2291. IopRestartDeviceNode(deviceNode);
  2292. } else {
  2293. //
  2294. // We're changing or setting problem codes. Note that the
  2295. // device is still in DeviceNodeInitialized or
  2296. // DeviceNodeRemoved.
  2297. //
  2298. PipSetDevNodeProblem(deviceNode, deviceEvent->Argument);
  2299. }
  2300. } else {
  2301. //
  2302. // The problem is fixed, so the devnode state is immutable
  2303. // as far as user mode is concerned. Here we fail the call
  2304. // if we can't bring the devnode back online. We always succeed
  2305. // the call if it was an attempt to change the code, as the
  2306. // user either wants to prepare the device for ejection (done),
  2307. // or wants to disable it (as good as done.)
  2308. //
  2309. if (!(deviceEvent->Data.Flags & TDF_NO_RESTART)) {
  2310. status = STATUS_INVALID_PARAMETER;
  2311. }
  2312. }
  2313. }
  2314. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2315. ExFreePool(internalVetoBuffer);
  2316. return status;
  2317. }
  2318. status = IopBuildRemovalRelationList( deviceObject,
  2319. deleteType,
  2320. &vetoType,
  2321. &internalVetoString,
  2322. &relationsList);
  2323. if (!NT_SUCCESS(status)) {
  2324. PiFinalizeVetoedRemove(
  2325. deviceEvent,
  2326. vetoType,
  2327. &internalVetoString
  2328. );
  2329. ExFreePool(internalVetoBuffer);
  2330. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2331. return STATUS_PLUGPLAY_QUERY_VETOED;
  2332. }
  2333. ASSERT(relationsList != NULL);
  2334. //
  2335. // Resize the event buffer and add these device instance strings to the
  2336. // list to notify.
  2337. //
  2338. relationCount = IopGetRelationsCount( relationsList );
  2339. ASSERT(!IopGetRelationsTaggedCount( relationsList ));
  2340. //
  2341. // PdoList will become a list of devices that must be queried. This is
  2342. // a subset of all the devices that might disappear, all of which appear
  2343. // in the relations list.
  2344. //
  2345. pdoList = (PDEVICE_OBJECT *) PiAllocateCriticalMemory(
  2346. deleteType,
  2347. NonPagedPool,
  2348. relationCount * sizeof(PDEVICE_OBJECT),
  2349. 0
  2350. );
  2351. if (pdoList != NULL) {
  2352. relationCount = 0;
  2353. marker = 0;
  2354. while (IopEnumerateRelations( relationsList,
  2355. &marker,
  2356. &relatedDeviceObject,
  2357. &directDescendant,
  2358. NULL,
  2359. TRUE)) {
  2360. //
  2361. // Here is a list of what operations retrieve what relations,
  2362. // who they query, and who/how they notify.
  2363. //
  2364. // Operation Relations Queries Notifies
  2365. // --------- --------- ------- --------
  2366. // EjectDevice Ejection Everyone Everyone (Remove)
  2367. // SurpriseRemoveDevice Ejection NA Descendants (SurpriseRemove)
  2368. // RemoveDevice Ejection NA Descendants (Remove)
  2369. // RemoveFailedDevice Removal NA Descendants (SurpriseRemove)
  2370. // RemoveUnstartedFailedDevice Removal NA Descendants (Remove)
  2371. // QueryRemoveDevice Removal Everyone Everyone (Remove)
  2372. //
  2373. //
  2374. // N.B.
  2375. // We do not send SurpriseRemove's to removal relations.
  2376. // While doing so might seem to be the correct behavior, many
  2377. // drivers do not handle this well. Simply reenumerating the
  2378. // parents of the removal relations works much better. Similarly
  2379. // ejection relations have their parents reenumerated (which
  2380. // makes sense, as they are speculative in nature anyway).
  2381. //
  2382. // If we get in a case where a *parent* of a dock gets
  2383. // into the RemoveFailedDevice case (ie, failed restart,
  2384. // responded to QueryDeviceState with Removed, etc), then we
  2385. // will be shortly losing the children when we stop the parent.
  2386. // However, we want to eject the dock child, not just remove it
  2387. // as starting and ejecting are symmetric here. Note that
  2388. // currently the only such parent would be the root ACPI devnode.
  2389. //
  2390. // Ejection relations of a device (eg dock) that has been
  2391. // surprise removed are not notified that they *may* have been
  2392. // pulled (remember, ejection relations are speculative). We
  2393. // will notify only DirectDescendants and queue an enumeration
  2394. // on every parent of the ejection relations. If they really
  2395. // disappeared, they will get their notification, albeit a bit
  2396. // later than some of the other devices in the tree.
  2397. //
  2398. if (directDescendant || deleteType == EjectDevice || deleteType == QueryRemoveDevice) {
  2399. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  2400. //
  2401. // PiProcessQueryRemoveAndEject will be called twice for
  2402. // the dock during an eject. Once with EjectDevice, and
  2403. // after the dock is listed as missing once more with
  2404. // RemoveDevice. We don't want to start a profile change
  2405. // for RemoveDevice as we are already in one, and we would
  2406. // deadlock if we tried. We don't start one for QueryRemove
  2407. // either as the dock isn't *physically* going away.
  2408. //
  2409. ASSERT(relatedDeviceNode->DockInfo.DockStatus != DOCK_ARRIVING);
  2410. if (deleteType != RemoveDevice &&
  2411. deleteType != QueryRemoveDevice) {
  2412. if (relatedDeviceNode->DockInfo.DockStatus == DOCK_QUIESCENT) {
  2413. possibleProfileChangeInProgress = TRUE;
  2414. } else if (relatedDeviceNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) {
  2415. subsumingProfileChange = TRUE;
  2416. }
  2417. }
  2418. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  2419. if (deleteType == QueryRemoveDevice || deleteType == EjectDevice) {
  2420. if (relatedDeviceNode->Flags & DNF_LEGACY_DRIVER) {
  2421. PiFinalizeVetoedRemove(
  2422. deviceEvent,
  2423. PNP_VetoLegacyDevice,
  2424. &relatedDeviceNode->InstancePath
  2425. );
  2426. status = STATUS_UNSUCCESSFUL;
  2427. break;
  2428. }
  2429. if (relatedDeviceNode->State == DeviceNodeRemovePendingCloses) {
  2430. PiFinalizeVetoedRemove(
  2431. deviceEvent,
  2432. PNP_VetoOutstandingOpen,
  2433. &relatedDeviceNode->InstancePath
  2434. );
  2435. status = STATUS_UNSUCCESSFUL;
  2436. break;
  2437. }
  2438. }
  2439. pdoList[ relationCount++ ] = relatedDeviceObject;
  2440. }
  2441. }
  2442. } else {
  2443. status = STATUS_INSUFFICIENT_RESOURCES;
  2444. }
  2445. if (NT_SUCCESS(status)) {
  2446. excludeIndirectRelations =
  2447. (deleteType == SurpriseRemoveDevice ||
  2448. deleteType == RemoveFailedDevice ||
  2449. deleteType == RemoveUnstartedFailedDevice ||
  2450. deleteType == RemoveDevice);
  2451. status = PiResizeTargetDeviceBlock( DeviceEvent,
  2452. deleteType,
  2453. relationsList,
  2454. excludeIndirectRelations );
  2455. deviceEvent = *DeviceEvent;
  2456. if (deleteType == SurpriseRemoveDevice) {
  2457. PiBuildUnsafeRemovalDeviceBlock(
  2458. deviceEvent,
  2459. relationsList,
  2460. &surpriseRemovalEvent
  2461. );
  2462. }
  2463. }
  2464. if (!NT_SUCCESS(status)) {
  2465. IopFreeRelationList(relationsList);
  2466. if (pdoList) {
  2467. ExFreePool(pdoList);
  2468. }
  2469. ExFreePool(internalVetoBuffer);
  2470. PiFinalizeVetoedRemove(
  2471. deviceEvent,
  2472. PNP_VetoTypeUnknown,
  2473. NULL
  2474. );
  2475. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2476. return status;
  2477. }
  2478. //
  2479. // We may need to take the hardware profile change semaphore, and also
  2480. // broadcast a hardware profile change request...
  2481. //
  2482. if (possibleProfileChangeInProgress) {
  2483. PpProfileBeginHardwareProfileTransition(subsumingProfileChange);
  2484. //
  2485. // Walk the list of docks who are going to disappear and mark them as
  2486. // in profile transition.
  2487. //
  2488. for (index = relationCount - 1; index >= 0; index--) {
  2489. relatedDeviceObject = pdoList[ index ];
  2490. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  2491. ASSERT(relatedDeviceNode->DockInfo.DockStatus != DOCK_ARRIVING);
  2492. if (relatedDeviceNode->DockInfo.DockStatus == DOCK_QUIESCENT) {
  2493. PpProfileIncludeInHardwareProfileTransition(
  2494. relatedDeviceNode,
  2495. DOCK_DEPARTING
  2496. );
  2497. }
  2498. }
  2499. //
  2500. // We can only be in one of the following deleteType situations
  2501. //
  2502. // 1) EjectDevice - Good, normal ejection request by our user
  2503. // (we are using EjectionRelations)
  2504. //
  2505. // 2) SurpriseRemoveDevice - Someone yanked the dock out.
  2506. // (we are using EjectionRelations)
  2507. //
  2508. // 3) RemoveFailedDevice - A start failed after a stop on a parent or
  2509. // even our device. This case is not handled
  2510. // correctly. We assert for now, and we
  2511. // maroon the dock, ie lose it's devnode but
  2512. // the dock stays physically present and is
  2513. // in the users eye's unejectable.
  2514. //
  2515. // 4) RemoveDevice - This occurs in three cases:
  2516. // a) A removed device is disappearing.
  2517. // b) A device is being removed but has not
  2518. // been started.
  2519. // c) A device has failed start.
  2520. //
  2521. // We pass through case a) during a normal
  2522. // ejection and as part of a profile
  2523. // transition begun earlier. c) is similar
  2524. // to a) but the transition was begun by the
  2525. // start code. For case b) we don't want to
  2526. // turn it into an eject, as the OS might be
  2527. // removing our parent as a normal part of
  2528. // setup, and we wouldn't want to undock then
  2529. // (and we probably aren't changing profiles
  2530. // anyway).
  2531. //
  2532. // 5) QueryRemoveDevice This should never be the case here per the
  2533. // explicit veto in the IopEnumerateRelations
  2534. // code above.
  2535. //
  2536. //
  2537. // RemoveFailedDevice is a PathTrap - the only parent of a dock is
  2538. // the ACPI root devnode right now. We shouldn't get into that case.
  2539. //
  2540. ASSERT(deleteType != QueryRemoveDevice &&
  2541. deleteType != RemoveFailedDevice);
  2542. if (deleteType == EjectDevice) {
  2543. //
  2544. // Are there any legacy drivers in the system?
  2545. //
  2546. status = IoGetLegacyVetoList(&vetoList, &vetoType);
  2547. if (NT_SUCCESS(status) &&
  2548. (vetoType != PNP_VetoTypeUnknown)) {
  2549. //
  2550. // Release any docks in profile transition
  2551. //
  2552. PpProfileCancelHardwareProfileTransition();
  2553. IopFreeRelationList(relationsList);
  2554. //
  2555. // Failure occured, notify user mode as appropriate, or fill in
  2556. // the veto buffer.
  2557. //
  2558. if (deviceEvent->VetoType != NULL) {
  2559. *deviceEvent->VetoType = vetoType;
  2560. }
  2561. if (deviceEvent->VetoName == NULL) {
  2562. //
  2563. // If there is not a VetoName passed in then call user mode
  2564. // to display the eject veto notification to the user.
  2565. //
  2566. PiNotifyUserModeRemoveVetoedByList(
  2567. deviceEvent,
  2568. deviceObject,
  2569. vetoType,
  2570. vetoList
  2571. );
  2572. } else {
  2573. //
  2574. // The veto data in the PNP_DEVICE_EVENT_ENTRY block is
  2575. // a UNICODE_STRING field. Since that type of data structure
  2576. // cannot handle Multi-Sz data, we cull the information down
  2577. // to one entry here.
  2578. //
  2579. RtlCopyUnicodeString(deviceEvent->VetoName, &singleVetoListItem);
  2580. RtlInitUnicodeString(&singleVetoListItem, vetoList);
  2581. }
  2582. ExFreePool(vetoList);
  2583. ExFreePool(pdoList);
  2584. ExFreePool(internalVetoBuffer);
  2585. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2586. return STATUS_PLUGPLAY_QUERY_VETOED;
  2587. }
  2588. //
  2589. // Broadcast the query for a profile change against our current
  2590. // list of docks in transition...
  2591. //
  2592. status = PpProfileQueryHardwareProfileChange(
  2593. subsumingProfileChange,
  2594. PROFILE_IN_PNPEVENT,
  2595. &vetoType,
  2596. &internalVetoString
  2597. );
  2598. if (!NT_SUCCESS(status)) {
  2599. //
  2600. // Release any docks in profile transition
  2601. //
  2602. PpProfileCancelHardwareProfileTransition();
  2603. IopFreeRelationList(relationsList);
  2604. PiFinalizeVetoedRemove(
  2605. deviceEvent,
  2606. vetoType,
  2607. &internalVetoString
  2608. );
  2609. ExFreePool(pdoList);
  2610. ExFreePool(internalVetoBuffer);
  2611. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2612. return STATUS_PLUGPLAY_QUERY_VETOED;
  2613. }
  2614. }
  2615. }
  2616. if (deleteType == QueryRemoveDevice || deleteType == EjectDevice) {
  2617. //
  2618. // Send query notification to user-mode.
  2619. //
  2620. status = PiNotifyUserModeDeviceRemoval(
  2621. deviceEvent,
  2622. &GUID_TARGET_DEVICE_QUERY_REMOVE,
  2623. &vetoType,
  2624. &internalVetoString
  2625. );
  2626. if (NT_SUCCESS(status)) {
  2627. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  2628. "PiProcessQueryRemoveAndEject: QUERY_REMOVE - notifying kernel-mode\n"));
  2629. //
  2630. // Send query notification to kernel-mode drivers.
  2631. //
  2632. for (index = 0; index < (LONG)relationCount; index++) {
  2633. relatedDeviceObject = pdoList[ index ];
  2634. status = IopNotifyTargetDeviceChange( &GUID_TARGET_DEVICE_QUERY_REMOVE,
  2635. relatedDeviceObject,
  2636. NULL,
  2637. &vetoingDriver);
  2638. if (!NT_SUCCESS(status)) {
  2639. vetoType = PNP_VetoDriver;
  2640. if (vetoingDriver != NULL) {
  2641. RtlCopyUnicodeString(&internalVetoString, &vetoingDriver->DriverName);
  2642. } else {
  2643. RtlInitUnicodeString(&internalVetoString, NULL);
  2644. }
  2645. for (index--; index >= 0; index--) {
  2646. relatedDeviceObject = pdoList[ index ];
  2647. IopNotifyTargetDeviceChange( &GUID_TARGET_DEVICE_REMOVE_CANCELLED,
  2648. relatedDeviceObject,
  2649. NULL,
  2650. NULL);
  2651. }
  2652. break;
  2653. }
  2654. }
  2655. if (NT_SUCCESS(status)) {
  2656. //
  2657. // If we haven't already performed the action yet (a query remove
  2658. // to the target device, in this case), then do it now.
  2659. //
  2660. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  2661. "PiProcessQueryRemoveAndEject: QueryRemove DevNodes\n"));
  2662. status = IopDeleteLockedDeviceNodes(deviceObject,
  2663. relationsList,
  2664. QueryRemoveDevice,
  2665. TRUE,
  2666. 0,
  2667. &vetoType,
  2668. &internalVetoString);
  2669. if (NT_SUCCESS(status)) {
  2670. //
  2671. // Everyone has been notified and had a chance to close their handles.
  2672. // Since no one has vetoed it yet, let's see if there are any open
  2673. // references.
  2674. //
  2675. if (IopNotifyPnpWhenChainDereferenced( pdoList, relationCount, TRUE, &vetoingDevice )) {
  2676. DUMP_PDO_HANDLES(pdoList, relationCount, FALSE);
  2677. vetoType = PNP_VetoOutstandingOpen;
  2678. if (vetoingDevice != NULL) {
  2679. relatedDeviceNode = (PDEVICE_NODE)vetoingDevice->DeviceObjectExtension->DeviceNode;
  2680. ASSERT(relatedDeviceNode != NULL);
  2681. RtlCopyUnicodeString(&internalVetoString, &relatedDeviceNode->InstancePath);
  2682. } else {
  2683. RtlInitUnicodeString(&internalVetoString, NULL);
  2684. }
  2685. //
  2686. // Send cancel remove to the target devices.
  2687. //
  2688. IopDeleteLockedDeviceNodes(deviceObject,
  2689. relationsList,
  2690. CancelRemoveDevice,
  2691. TRUE,
  2692. 0,
  2693. NULL,
  2694. NULL);
  2695. status = STATUS_UNSUCCESSFUL;
  2696. }
  2697. } else {
  2698. DUMP_PDO_HANDLES(pdoList, relationCount, FALSE);
  2699. }
  2700. if (!NT_SUCCESS(status)) {
  2701. //
  2702. // Send cancel notification to kernel-mode drivers.
  2703. //
  2704. for (index = relationCount - 1; index >= 0; index--) {
  2705. relatedDeviceObject = pdoList[ index ];
  2706. IopNotifyTargetDeviceChange( &GUID_TARGET_DEVICE_REMOVE_CANCELLED,
  2707. relatedDeviceObject,
  2708. NULL,
  2709. NULL);
  2710. }
  2711. }
  2712. }
  2713. if (!NT_SUCCESS(status)) {
  2714. IopDbgPrint((IOP_IOEVENT_WARNING_LEVEL,
  2715. "PiProcessQueryRemoveAndEject: Vetoed by \"%wZ\" (type 0x%x)\n",
  2716. &internalVetoString,
  2717. vetoType));
  2718. PiFinalizeVetoedRemove(
  2719. deviceEvent,
  2720. vetoType,
  2721. &internalVetoString
  2722. );
  2723. //
  2724. // A driver vetoed the query remove, go back and send
  2725. // cancels to user-mode (cancels already sent to drivers
  2726. // that received the query).
  2727. //
  2728. PiNotifyUserModeDeviceRemoval(
  2729. deviceEvent,
  2730. &GUID_TARGET_DEVICE_REMOVE_CANCELLED,
  2731. NULL,
  2732. NULL
  2733. );
  2734. }
  2735. } else {
  2736. PiFinalizeVetoedRemove(
  2737. deviceEvent,
  2738. vetoType,
  2739. &internalVetoString
  2740. );
  2741. }
  2742. if (!NT_SUCCESS(status)) {
  2743. //
  2744. // Broadcast a cancel HW profile change event if appropriate.
  2745. //
  2746. if (possibleProfileChangeInProgress) {
  2747. //
  2748. // Release any docks in profile transition. We also broadcast
  2749. // the cancel.
  2750. //
  2751. PpProfileCancelHardwareProfileTransition();
  2752. }
  2753. //
  2754. // User-mode vetoed the request (cancels already sent
  2755. // to user-mode callers that received the query).
  2756. //
  2757. IopFreeRelationList(relationsList);
  2758. ExFreePool(pdoList);
  2759. ExFreePool(internalVetoBuffer);
  2760. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2761. return STATUS_PLUGPLAY_QUERY_VETOED;
  2762. }
  2763. } else if (deleteType == SurpriseRemoveDevice || deleteType == RemoveFailedDevice) {
  2764. //
  2765. // Send IRP_MN_SURPRISE_REMOVAL, IopDeleteLockDeviceNodes ignores
  2766. // indirect descendants for SurpriseRemoveDevice.
  2767. //
  2768. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  2769. "PiProcessQueryRemoveAndEject: QueryRemove DevNodes\n"));
  2770. IopDeleteLockedDeviceNodes( deviceObject,
  2771. relationsList,
  2772. SurpriseRemoveDevice,
  2773. FALSE,
  2774. 0,
  2775. NULL,
  2776. NULL);
  2777. }
  2778. //
  2779. // Notify user-mode and drivers that a remove is happening. User-mode
  2780. // sees this as a remove pending if it's user initiated, we don't give
  2781. // them the "remove" until it's actually gone.
  2782. //
  2783. if (deleteType != SurpriseRemoveDevice) {
  2784. //
  2785. // ISSUE - 2000/08/20 - ADRIAO: Busted message path
  2786. // We send GUID_DEVICE_REMOVE_PENDING to devices that are already
  2787. // dead in the case of RemoveFailedDevice.
  2788. //
  2789. PiNotifyUserModeDeviceRemoval(
  2790. deviceEvent,
  2791. &GUID_DEVICE_REMOVE_PENDING,
  2792. NULL,
  2793. NULL
  2794. );
  2795. } else {
  2796. if (surpriseRemovalEvent) {
  2797. PiNotifyUserModeDeviceRemoval(
  2798. surpriseRemovalEvent,
  2799. &GUID_DEVICE_SURPRISE_REMOVAL,
  2800. NULL,
  2801. NULL
  2802. );
  2803. ExFreePool(surpriseRemovalEvent);
  2804. }
  2805. PiNotifyUserModeDeviceRemoval(
  2806. deviceEvent,
  2807. &GUID_TARGET_DEVICE_REMOVE_COMPLETE,
  2808. NULL,
  2809. NULL
  2810. );
  2811. }
  2812. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  2813. "PiProcessQueryRemoveAndEject: REMOVE_COMPLETE - notifying kernel-mode\n"));
  2814. for (index = 0; index < (LONG)relationCount; index++) {
  2815. relatedDeviceObject = pdoList[ index ];
  2816. status = IopNotifyTargetDeviceChange( &GUID_TARGET_DEVICE_REMOVE_COMPLETE,
  2817. relatedDeviceObject,
  2818. NULL,
  2819. NULL);
  2820. ASSERT(NT_SUCCESS(status));
  2821. }
  2822. if (deleteType == RemoveDevice ||
  2823. deleteType == RemoveFailedDevice ||
  2824. deleteType == SurpriseRemoveDevice) {
  2825. //
  2826. // For these operations indirect relations are speculative.
  2827. //
  2828. // So for each of the indirect relations, invalidate their parents and
  2829. // remove them from the relations list.
  2830. //
  2831. IopInvalidateRelationsInList( relationsList, deleteType, TRUE, FALSE );
  2832. IopRemoveIndirectRelationsFromList( relationsList );
  2833. }
  2834. if (deleteType == RemoveFailedDevice ||
  2835. deleteType == SurpriseRemoveDevice) {
  2836. //
  2837. // We've sent the surprise remove IRP to the original device and all its
  2838. // direct descendants. We've also notified user-mode.
  2839. //
  2840. //
  2841. // Unlock the device relations list.
  2842. //
  2843. // Note there could be a potential race condition here between
  2844. // unlocking the devnodes in the relation list and completing the
  2845. // execution of IopNotifyPnpWhenChainDereferenced. If an enumeration
  2846. // takes places (we've unlocked the devnode) before the eventual remove
  2847. // is sent then problems could occur.
  2848. //
  2849. // This is prevented by the setting of DNF_REMOVE_PENDING_CLOSES when
  2850. // we sent the IRP_MN_SURPRISE_REMOVAL.
  2851. //
  2852. // We do need to do it prior to calling IopQueuePendingSurpriseRemoval
  2853. // since we lose ownership of the relation list in that call. Also
  2854. // IopNotifyPnpWhenChainDereferenced may cause the relation list to be
  2855. // freed before it returns.
  2856. //
  2857. // If this is a RemoveFailedDevice then we don't want to remove the
  2858. // device node from the tree but we do want to remove children without
  2859. // resources.
  2860. //
  2861. IopUnlinkDeviceRemovalRelations( deviceObject,
  2862. relationsList,
  2863. deleteType == SurpriseRemoveDevice ?
  2864. UnlinkAllDeviceNodesPendingClose :
  2865. UnlinkOnlyChildDeviceNodesPendingClose);
  2866. //
  2867. // Add the relation list to a list of pending surprise removals.
  2868. //
  2869. IopQueuePendingSurpriseRemoval( deviceObject, relationsList, deviceEvent->Argument );
  2870. //
  2871. // Release the engine lock *before* IopNotifyPnpWhenChainDereferenced,
  2872. // as it may call back into us...
  2873. //
  2874. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  2875. IopNotifyPnpWhenChainDereferenced( pdoList, relationCount, FALSE, NULL );
  2876. ExFreePool(pdoList);
  2877. ExFreePool(internalVetoBuffer);
  2878. return STATUS_SUCCESS;
  2879. }
  2880. if (deviceNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) {
  2881. status = IopQueryDockRemovalInterface(
  2882. deviceObject,
  2883. &dockInterface
  2884. );
  2885. if (dockInterface) {
  2886. //
  2887. // Make sure updates don't occur on removes during an ejection.
  2888. // We may change this to PDS_UPDATE_ON_EJECT *after* the remove
  2889. // IRPs go through (as only then do we know our power
  2890. // constraints)
  2891. //
  2892. dockInterface->ProfileDepartureSetMode(
  2893. dockInterface->Context,
  2894. PDS_UPDATE_ON_INTERFACE
  2895. );
  2896. }
  2897. }
  2898. //
  2899. // Send the remove to the devnode tree.
  2900. //
  2901. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  2902. "PiProcessQueryRemoveAndEject: RemoveDevice DevNodes\n"));
  2903. status = IopDeleteLockedDeviceNodes(deviceObject,
  2904. relationsList,
  2905. RemoveDevice,
  2906. (BOOLEAN)(deleteType == QueryRemoveDevice || deleteType == EjectDevice),
  2907. deviceEvent->Argument,
  2908. NULL,
  2909. NULL);
  2910. hotEjectSupported =
  2911. (BOOLEAN) IopDeviceNodeFlagsToCapabilities(deviceNode)->EjectSupported;
  2912. warmEjectSupported =
  2913. (BOOLEAN) IopDeviceNodeFlagsToCapabilities(deviceNode)->WarmEjectSupported;
  2914. if (deleteType != EjectDevice) {
  2915. if (!(deviceEvent->Data.Flags & TDF_NO_RESTART)) {
  2916. //
  2917. // Set a flag to let kernel-mode know we'll be wanting to
  2918. // restart these devnodes, eventually.
  2919. //
  2920. marker = 0;
  2921. while (IopEnumerateRelations( relationsList,
  2922. &marker,
  2923. &relatedDeviceObject,
  2924. NULL,
  2925. NULL,
  2926. TRUE)) {
  2927. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  2928. if (relatedDeviceNode &&
  2929. relatedDeviceNode->State == DeviceNodeRemoved &&
  2930. PipIsDevNodeProblem(relatedDeviceNode, CM_PROB_WILL_BE_REMOVED)) {
  2931. PipClearDevNodeProblem(relatedDeviceNode);
  2932. IopRestartDeviceNode(relatedDeviceNode);
  2933. }
  2934. }
  2935. }
  2936. //
  2937. // Unlock the device relations list.
  2938. //
  2939. IopUnlinkDeviceRemovalRelations( deviceObject,
  2940. relationsList,
  2941. UnlinkRemovedDeviceNodes );
  2942. IopFreeRelationList(relationsList);
  2943. } else if (hotEjectSupported || warmEjectSupported) {
  2944. //
  2945. // From this point on we cannot return any sort of failure without
  2946. // going through IopEjectDevice or cancelling any outstanding profile
  2947. // change.
  2948. //
  2949. //
  2950. // Set a flag to let kernel-mode know we'll be wanting to
  2951. // restart these devnodes, eventually.
  2952. //
  2953. marker = 0;
  2954. while (IopEnumerateRelations( relationsList,
  2955. &marker,
  2956. &relatedDeviceObject,
  2957. NULL,
  2958. NULL,
  2959. TRUE)) {
  2960. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  2961. if (relatedDeviceNode) {
  2962. relatedDeviceNode->Flags |= DNF_LOCKED_FOR_EJECT;
  2963. }
  2964. }
  2965. IopUnlinkDeviceRemovalRelations( deviceObject,
  2966. relationsList,
  2967. UnlinkRemovedDeviceNodes );
  2968. //
  2969. // Send the eject
  2970. //
  2971. pendingRelations = ExAllocatePool( NonPagedPool, sizeof(PENDING_RELATIONS_LIST_ENTRY) );
  2972. if (pendingRelations == NULL) {
  2973. //
  2974. // It's cleanup time. Free up everything that matters
  2975. //
  2976. if (dockInterface) {
  2977. dockInterface->ProfileDepartureSetMode(
  2978. dockInterface->Context,
  2979. PDS_UPDATE_DEFAULT
  2980. );
  2981. dockInterface->InterfaceDereference(dockInterface->Context);
  2982. }
  2983. ExFreePool(pdoList);
  2984. ExFreePool(internalVetoBuffer);
  2985. if (possibleProfileChangeInProgress) {
  2986. //
  2987. // Release any docks in profile transition. We also broadcast
  2988. // the cancel.
  2989. //
  2990. PpProfileCancelHardwareProfileTransition();
  2991. }
  2992. //
  2993. // This will bring back online the devices that were held offline
  2994. // for the duration of the undock.
  2995. //
  2996. IopInvalidateRelationsInList(relationsList, deleteType, FALSE, TRUE);
  2997. //
  2998. // Free the relations list
  2999. //
  3000. IopFreeRelationList(relationsList);
  3001. //
  3002. // Let the user know we were unable to process the request.
  3003. //
  3004. PiFinalizeVetoedRemove(
  3005. deviceEvent,
  3006. PNP_VetoTypeUnknown,
  3007. NULL
  3008. );
  3009. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  3010. return STATUS_PLUGPLAY_QUERY_VETOED;
  3011. }
  3012. //
  3013. // Fill out the pending eject information.
  3014. //
  3015. ObReferenceObject(deviceObject);
  3016. pendingRelations->DeviceEvent = deviceEvent;
  3017. pendingRelations->DeviceObject = deviceObject;
  3018. pendingRelations->RelationsList = relationsList;
  3019. pendingRelations->ProfileChangingEject = possibleProfileChangeInProgress;
  3020. pendingRelations->DisplaySafeRemovalDialog =
  3021. (BOOLEAN)(deviceEvent->VetoName == NULL);
  3022. pendingRelations->DockInterface = dockInterface;
  3023. //
  3024. // Now that we've removed all the devices that won't be present
  3025. // in the new hardware profile state (eg batteries, etc),
  3026. //
  3027. status = PoGetLightestSystemStateForEject(
  3028. possibleProfileChangeInProgress,
  3029. hotEjectSupported,
  3030. warmEjectSupported,
  3031. &pendingRelations->LightestSleepState
  3032. );
  3033. if (!NT_SUCCESS(status)) {
  3034. if (status == STATUS_INSUFFICIENT_POWER) {
  3035. PiFinalizeVetoedRemove(
  3036. deviceEvent,
  3037. PNP_VetoInsufficientPower,
  3038. NULL
  3039. );
  3040. } else {
  3041. IopDbgPrint((IOP_IOEVENT_WARNING_LEVEL,
  3042. "PiProcessQueryRemoveAndEject: Vetoed by power system (%x)\n",
  3043. status));
  3044. PiFinalizeVetoedRemove(
  3045. deviceEvent,
  3046. PNP_VetoTypeUnknown,
  3047. NULL
  3048. );
  3049. }
  3050. //
  3051. // We'll complete this one ourselves thank you.
  3052. //
  3053. pendingRelations->DeviceEvent = NULL;
  3054. pendingRelations->DisplaySafeRemovalDialog = FALSE;
  3055. //
  3056. // Release any profile transitions.
  3057. //
  3058. InitializeListHead( &pendingRelations->Link );
  3059. IopProcessCompletedEject((PVOID) pendingRelations);
  3060. ExFreePool(pdoList);
  3061. ExFreePool(internalVetoBuffer);
  3062. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  3063. return STATUS_PLUGPLAY_QUERY_VETOED;
  3064. }
  3065. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  3066. //
  3067. // Completion routine for the eject IRP handles display of the
  3068. // safe removal dialog and completion of the event. Returning
  3069. // STATUS_PENDING does let other events get processed though.
  3070. //
  3071. IopEjectDevice( deviceObject, pendingRelations );
  3072. ExFreePool(pdoList);
  3073. ExFreePool(internalVetoBuffer);
  3074. return STATUS_PENDING;
  3075. } else {
  3076. //
  3077. // All docks must be hot or warm ejectable.
  3078. //
  3079. ASSERT(!dockInterface);
  3080. //
  3081. // Unlock the device relations list.
  3082. //
  3083. IopUnlinkDeviceRemovalRelations( deviceObject,
  3084. relationsList,
  3085. UnlinkRemovedDeviceNodes );
  3086. IopFreeRelationList(relationsList);
  3087. //
  3088. // This hardware supports neither hot nor warm eject, but it is
  3089. // removable. It can therefore be thought of as a "user assisted" hot
  3090. // eject. In this case we do *not* want to wait around for the user to
  3091. // "complete the eject" and then put up the message. So we piggyback a
  3092. // safe removal notification while UmPnPMgr is alert and waiting in
  3093. // user mode, and the user gets the dialog now.
  3094. //
  3095. if (deviceEvent->VetoName == NULL) {
  3096. PpNotifyUserModeRemovalSafe(deviceObject);
  3097. }
  3098. }
  3099. if (deleteType == RemoveDevice) {
  3100. //
  3101. // Notify user-mode one last time that everything is actually done.
  3102. //
  3103. PiNotifyUserModeDeviceRemoval(
  3104. deviceEvent,
  3105. &GUID_TARGET_DEVICE_REMOVE_COMPLETE,
  3106. NULL,
  3107. NULL
  3108. );
  3109. }
  3110. ExFreePool(pdoList);
  3111. if (dockInterface) {
  3112. dockInterface->ProfileDepartureSetMode(
  3113. dockInterface->Context,
  3114. PDS_UPDATE_DEFAULT
  3115. );
  3116. dockInterface->InterfaceDereference(dockInterface->Context);
  3117. }
  3118. ExFreePool(internalVetoBuffer);
  3119. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  3120. return STATUS_SUCCESS;
  3121. }
  3122. NTSTATUS
  3123. PiProcessTargetDeviceEvent(
  3124. IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
  3125. )
  3126. /*++
  3127. Routine Description:
  3128. This routine processes each type of event in the target device category.
  3129. These events may have been initiated by either user-mode or kernel mode.
  3130. Arguments:
  3131. deviceEvent - Data describing the type of target device event and the
  3132. target device itself.
  3133. Return Value:
  3134. None.
  3135. --*/
  3136. {
  3137. NTSTATUS status = STATUS_SUCCESS;
  3138. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  3139. PAGED_CODE();
  3140. deviceEvent = *DeviceEvent;
  3141. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3142. "PiProcessTargetDeviceEvent: Entered\n"));
  3143. //-----------------------------------------------------------------
  3144. // QUERY and REMOVE
  3145. //-----------------------------------------------------------------
  3146. if (PiCompareGuid(&deviceEvent->Data.EventGuid,
  3147. &GUID_DEVICE_QUERY_AND_REMOVE)) {
  3148. status = PiProcessQueryRemoveAndEject(DeviceEvent);
  3149. }
  3150. //-----------------------------------------------------------------
  3151. // EJECT
  3152. //-----------------------------------------------------------------
  3153. else if (PiCompareGuid(&deviceEvent->Data.EventGuid,
  3154. &GUID_DEVICE_EJECT)) {
  3155. status = PiProcessQueryRemoveAndEject(DeviceEvent);
  3156. }
  3157. //-----------------------------------------------------------------
  3158. // ARRIVAL
  3159. //-----------------------------------------------------------------
  3160. else if (PiCompareGuid(&deviceEvent->Data.EventGuid,
  3161. &GUID_DEVICE_ARRIVAL)) {
  3162. //
  3163. // Notify user-mode (not drivers) that an arrival just happened.
  3164. //
  3165. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3166. "PiProcessTargetDeviceEvent: ARRIVAL - notifying user-mode\n"));
  3167. PiNotifyUserMode(deviceEvent);
  3168. }
  3169. //-----------------------------------------------------------------
  3170. // NO-OP REQUEST (to flush device event queue)
  3171. //-----------------------------------------------------------------
  3172. else if (PiCompareGuid(&deviceEvent->Data.EventGuid,
  3173. &GUID_DEVICE_NOOP)) {
  3174. status = STATUS_SUCCESS;
  3175. }
  3176. //-----------------------------------------------------------------
  3177. // SAFE REMOVAL NOTIFICATION
  3178. //-----------------------------------------------------------------
  3179. else if (PiCompareGuid(&deviceEvent->Data.EventGuid, &GUID_DEVICE_SAFE_REMOVAL)) {
  3180. //
  3181. // Notify user-mode (and nobody else) that it is now safe to remove
  3182. // someone.
  3183. //
  3184. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3185. "PiProcessTargetDeviceEvent: SAFE_REMOVAL - notifying user-mode\n"));
  3186. PiNotifyUserMode(deviceEvent);
  3187. }
  3188. return status;
  3189. } // PiProcessTargetDeviceEvent
  3190. NTSTATUS
  3191. PiProcessCustomDeviceEvent(
  3192. IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent
  3193. )
  3194. /*++
  3195. Routine Description:
  3196. This routine processes each type of event in the custom device category.
  3197. These events may have been initiated by either user-mode or kernel mode.
  3198. Arguments:
  3199. deviceEvent - Data describing the type of custom device event and the
  3200. target device itself.
  3201. Return Value:
  3202. None.
  3203. --*/
  3204. {
  3205. PPNP_DEVICE_EVENT_ENTRY deviceEvent;
  3206. PTARGET_DEVICE_CUSTOM_NOTIFICATION customNotification;
  3207. PDEVICE_OBJECT deviceObject;
  3208. PAGED_CODE();
  3209. deviceEvent = *DeviceEvent;
  3210. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3211. "PiProcessCustomDeviceEvent: Entered\n"));
  3212. ASSERT(PiCompareGuid(&deviceEvent->Data.EventGuid,
  3213. &GUID_PNP_CUSTOM_NOTIFICATION));
  3214. deviceObject = (PDEVICE_OBJECT)deviceEvent->Data.DeviceObject;
  3215. customNotification = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)deviceEvent->Data.u.CustomNotification.NotificationStructure;
  3216. //
  3217. // Notify user-mode that something just happened.
  3218. //
  3219. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3220. "PiProcessCustomDeviceEvent: CUSTOM_NOTIFICATION - notifying user-mode\n"));
  3221. PiNotifyUserMode(deviceEvent);
  3222. //
  3223. // Notify K-mode
  3224. //
  3225. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3226. "PiProcessCustomDeviceEvent: CUSTOM_NOTIFICATION - notifying kernel-mode\n"));
  3227. IopNotifyTargetDeviceChange( &customNotification->Event,
  3228. deviceObject,
  3229. customNotification,
  3230. NULL);
  3231. return STATUS_SUCCESS;
  3232. } // PiProcessCustomDeviceEvent
  3233. NTSTATUS
  3234. PiResizeTargetDeviceBlock(
  3235. IN OUT PPNP_DEVICE_EVENT_ENTRY *DeviceEvent,
  3236. IN PLUGPLAY_DEVICE_DELETE_TYPE DeleteType,
  3237. IN PRELATION_LIST RelationsList,
  3238. IN BOOLEAN ExcludeIndirectRelations
  3239. )
  3240. /*++
  3241. Routine Description:
  3242. This routine takes the passed in device event block and resizes it to
  3243. hold a multisz list of device instance strings in the DeviceIds field.
  3244. This list includes the original target device id plus the device id
  3245. for all the device objects in the specified DeviceRelations struct.
  3246. Arguments:
  3247. DeviceEvent - On entry, contains the original device event block, on
  3248. return it contains the newly allocated device event block and
  3249. a complete list of related device id strings.
  3250. DeviceRelations - structure that contains a list of related device objects.
  3251. Return Value:
  3252. NTSTATUS value.
  3253. --*/
  3254. {
  3255. PDEVICE_NODE relatedDeviceNode;
  3256. PDEVICE_OBJECT relatedDeviceObject;
  3257. ULONG newSize, currentSize;
  3258. PPNP_DEVICE_EVENT_ENTRY newDeviceEvent;
  3259. LPWSTR targetDevice, p;
  3260. ULONG marker;
  3261. BOOLEAN directDescendant;
  3262. PAGED_CODE();
  3263. if (RelationsList == NULL) {
  3264. return STATUS_SUCCESS; // nothing to do
  3265. }
  3266. targetDevice = (*DeviceEvent)->Data.u.TargetDevice.DeviceIds;
  3267. //
  3268. // Calculate the size of the PNP_DEVICE_EVENT_ENTRY block
  3269. //
  3270. currentSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) +
  3271. (*DeviceEvent)->Data.TotalSize;
  3272. newSize = currentSize;
  3273. newSize -= (ULONG)((wcslen(targetDevice)+1) * sizeof(WCHAR));
  3274. marker = 0;
  3275. while (IopEnumerateRelations( RelationsList,
  3276. &marker,
  3277. &relatedDeviceObject,
  3278. &directDescendant,
  3279. NULL,
  3280. FALSE)) {
  3281. if (!ExcludeIndirectRelations || directDescendant) {
  3282. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  3283. if (relatedDeviceNode != NULL) {
  3284. if (relatedDeviceNode->InstancePath.Length != 0) {
  3285. newSize += relatedDeviceNode->InstancePath.Length + sizeof(WCHAR);
  3286. }
  3287. }
  3288. }
  3289. }
  3290. ASSERT(newSize >= currentSize);
  3291. if (newSize == currentSize) {
  3292. return STATUS_SUCCESS;
  3293. } else if (newSize < currentSize) {
  3294. newSize = currentSize;
  3295. }
  3296. newDeviceEvent = (PPNP_DEVICE_EVENT_ENTRY) PiAllocateCriticalMemory(
  3297. DeleteType,
  3298. PagedPool,
  3299. newSize,
  3300. PNP_DEVICE_EVENT_ENTRY_TAG
  3301. );
  3302. if (newDeviceEvent == NULL) {
  3303. return STATUS_INSUFFICIENT_RESOURCES;
  3304. }
  3305. RtlZeroMemory((PVOID)newDeviceEvent, newSize);
  3306. //
  3307. // Copy the old buffer into the new buffer, it's only the new stuff at the
  3308. // end that changes.
  3309. //
  3310. RtlCopyMemory(newDeviceEvent, *DeviceEvent, currentSize);
  3311. //
  3312. // Update the size of the PLUGPLAY_EVENT_BLOCK
  3313. //
  3314. newDeviceEvent->Data.TotalSize = newSize - FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data);
  3315. //
  3316. // Add device instance string for each device relation to the list.
  3317. // Leave the target device first in the list, and skip it during the
  3318. // enumeration below.
  3319. //
  3320. marker = 0;
  3321. p = newDeviceEvent->Data.u.TargetDevice.DeviceIds + wcslen(targetDevice) + 1;
  3322. while (IopEnumerateRelations( RelationsList,
  3323. &marker,
  3324. &relatedDeviceObject,
  3325. &directDescendant,
  3326. NULL,
  3327. FALSE)) {
  3328. if ((relatedDeviceObject != newDeviceEvent->Data.DeviceObject) &&
  3329. (!ExcludeIndirectRelations || directDescendant)) {
  3330. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  3331. if (relatedDeviceNode != NULL) {
  3332. if (relatedDeviceNode->InstancePath.Length != 0) {
  3333. RtlCopyMemory(p,
  3334. relatedDeviceNode->InstancePath.Buffer,
  3335. relatedDeviceNode->InstancePath.Length);
  3336. p += relatedDeviceNode->InstancePath.Length / sizeof(WCHAR) + 1;
  3337. }
  3338. }
  3339. }
  3340. }
  3341. *p = UNICODE_NULL;
  3342. ExFreePool(*DeviceEvent);
  3343. *DeviceEvent = newDeviceEvent;
  3344. return STATUS_SUCCESS;
  3345. } // PiResizeTargetDeviceBlock
  3346. VOID
  3347. PiBuildUnsafeRemovalDeviceBlock(
  3348. IN PPNP_DEVICE_EVENT_ENTRY OriginalDeviceEvent,
  3349. IN PRELATION_LIST RelationsList,
  3350. OUT PPNP_DEVICE_EVENT_ENTRY *AllocatedDeviceEvent
  3351. )
  3352. /*++
  3353. Routine Description:
  3354. This routine builds a device event block to send to user mode in case of
  3355. unsafe removal.
  3356. Arguments:
  3357. OriginalDeviceEvent - Contains the original device event block.
  3358. RelationList - structure that contains a list of related device objects.
  3359. AllocatedDeviceEvent - Receives the new device event, NULL on error or
  3360. no entries.
  3361. Return Value:
  3362. None.
  3363. --*/
  3364. {
  3365. PDEVICE_NODE relatedDeviceNode;
  3366. PDEVICE_OBJECT relatedDeviceObject;
  3367. ULONG dataSize, eventSize, headerSize;
  3368. PPNP_DEVICE_EVENT_ENTRY newDeviceEvent;
  3369. LPWSTR targetDevice, p;
  3370. ULONG marker;
  3371. BOOLEAN directDescendant;
  3372. PAGED_CODE();
  3373. //
  3374. // Preinit
  3375. //
  3376. *AllocatedDeviceEvent = NULL;
  3377. if (RelationsList == NULL) {
  3378. return; // nothing to do
  3379. }
  3380. targetDevice = OriginalDeviceEvent->Data.u.TargetDevice.DeviceIds;
  3381. //
  3382. // Calculate the size of the PNP_DEVICE_EVENT_ENTRY block
  3383. //
  3384. dataSize = 0;
  3385. marker = 0;
  3386. while (IopEnumerateRelations( RelationsList,
  3387. &marker,
  3388. &relatedDeviceObject,
  3389. &directDescendant,
  3390. NULL,
  3391. FALSE)) {
  3392. if (!directDescendant) {
  3393. continue;
  3394. }
  3395. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  3396. if ((relatedDeviceNode == NULL) ||
  3397. PipIsBeingRemovedSafely(relatedDeviceNode)) {
  3398. continue;
  3399. }
  3400. if (relatedDeviceNode->InstancePath.Length != 0) {
  3401. dataSize += relatedDeviceNode->InstancePath.Length + sizeof(WCHAR);
  3402. }
  3403. }
  3404. if (dataSize == 0) {
  3405. //
  3406. // No entries, bail.
  3407. //
  3408. return;
  3409. }
  3410. //
  3411. // Add the terminating MultiSz NULL.
  3412. //
  3413. dataSize += sizeof(WCHAR);
  3414. headerSize = FIELD_OFFSET(PNP_DEVICE_EVENT_ENTRY, Data) +
  3415. FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, u);
  3416. eventSize = dataSize + headerSize;
  3417. //
  3418. // If we can't get memory, there simply won't be a message sent.
  3419. //
  3420. newDeviceEvent = ExAllocatePoolWithTag(
  3421. PagedPool,
  3422. eventSize,
  3423. PNP_DEVICE_EVENT_ENTRY_TAG
  3424. );
  3425. if (newDeviceEvent == NULL) {
  3426. return;
  3427. }
  3428. RtlZeroMemory((PVOID)newDeviceEvent, eventSize);
  3429. //
  3430. // Copy the header into the new buffer.
  3431. //
  3432. RtlCopyMemory(newDeviceEvent, OriginalDeviceEvent, headerSize);
  3433. //
  3434. // Update the size of the PLUGPLAY_EVENT_BLOCK
  3435. //
  3436. newDeviceEvent->Data.TotalSize = dataSize + FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, u);
  3437. //
  3438. // Add device instance string for each device relation to the list.
  3439. //
  3440. marker = 0;
  3441. p = newDeviceEvent->Data.u.TargetDevice.DeviceIds;
  3442. while (IopEnumerateRelations( RelationsList,
  3443. &marker,
  3444. &relatedDeviceObject,
  3445. &directDescendant,
  3446. NULL,
  3447. FALSE)) {
  3448. if (!directDescendant) {
  3449. continue;
  3450. }
  3451. relatedDeviceNode = (PDEVICE_NODE)relatedDeviceObject->DeviceObjectExtension->DeviceNode;
  3452. if ((relatedDeviceNode == NULL) ||
  3453. PipIsBeingRemovedSafely(relatedDeviceNode)) {
  3454. continue;
  3455. }
  3456. if (relatedDeviceNode->InstancePath.Length != 0) {
  3457. RtlCopyMemory(p,
  3458. relatedDeviceNode->InstancePath.Buffer,
  3459. relatedDeviceNode->InstancePath.Length);
  3460. p += relatedDeviceNode->InstancePath.Length / sizeof(WCHAR) + 1;
  3461. }
  3462. }
  3463. *p = UNICODE_NULL;
  3464. *AllocatedDeviceEvent = newDeviceEvent;
  3465. return;
  3466. } // PiBuildUnsafeRemovalDeviceBlock
  3467. VOID
  3468. PiFinalizeVetoedRemove(
  3469. IN PPNP_DEVICE_EVENT_ENTRY VetoedDeviceEvent,
  3470. IN PNP_VETO_TYPE VetoType,
  3471. IN PUNICODE_STRING VetoName OPTIONAL
  3472. )
  3473. /*++
  3474. Routine Description:
  3475. This routine takes care of updating the event results with the veto
  3476. information, puts up UI if neccessary, and dumps failure information to
  3477. the debugger for debugging purposes.
  3478. Arguments:
  3479. VetoedDeviceEvent - Data describing the device event failed.
  3480. VetoType - The veto code best describing why the operation failed.
  3481. VetoName - A unicode string appropriate to the veto code that describes
  3482. the vetoer.
  3483. Return Value:
  3484. None.
  3485. --*/
  3486. {
  3487. PDEVICE_OBJECT deviceObject;
  3488. #if DBG
  3489. PUNICODE_STRING devNodeName;
  3490. const char *failureReason;
  3491. #endif
  3492. deviceObject = (PDEVICE_OBJECT) VetoedDeviceEvent->Data.DeviceObject;
  3493. #if DBG
  3494. devNodeName = &((PDEVICE_NODE) deviceObject->DeviceObjectExtension->DeviceNode)->InstancePath;
  3495. switch(VetoType) {
  3496. case PNP_VetoTypeUnknown:
  3497. failureReason = "for unspecified reason";
  3498. break;
  3499. case PNP_VetoLegacyDevice:
  3500. failureReason = "due to legacy device";
  3501. break;
  3502. case PNP_VetoPendingClose:
  3503. //
  3504. // ADRIAO N.B. 07/10/2000 - I believe this case is vestigal...
  3505. //
  3506. ASSERT(0);
  3507. failureReason = "due to pending close";
  3508. break;
  3509. case PNP_VetoWindowsApp:
  3510. failureReason = "due to windows application";
  3511. break;
  3512. case PNP_VetoWindowsService:
  3513. failureReason = "due to service";
  3514. break;
  3515. case PNP_VetoOutstandingOpen:
  3516. failureReason = "due to outstanding handles on device";
  3517. break;
  3518. case PNP_VetoDevice:
  3519. failureReason = "by device";
  3520. break;
  3521. case PNP_VetoDriver:
  3522. failureReason = "by driver";
  3523. break;
  3524. case PNP_VetoIllegalDeviceRequest:
  3525. failureReason = "as the request was invalid for the device";
  3526. break;
  3527. case PNP_VetoInsufficientPower:
  3528. failureReason = "because there would be insufficient system power to continue";
  3529. break;
  3530. case PNP_VetoNonDisableable:
  3531. failureReason = "due to non-disableable device";
  3532. break;
  3533. case PNP_VetoLegacyDriver:
  3534. failureReason = "due to legacy driver";
  3535. break;
  3536. case PNP_VetoInsufficientRights:
  3537. failureReason = "insufficient permissions";
  3538. break;
  3539. default:
  3540. ASSERT(0);
  3541. failureReason = "due to uncoded reason";
  3542. break;
  3543. }
  3544. if (VetoName != NULL) {
  3545. IopDbgPrint((IOP_IOEVENT_WARNING_LEVEL,
  3546. "PiFinalizeVetoedRemove: Removal of %wZ vetoed %s %wZ.\n",
  3547. devNodeName,
  3548. failureReason,
  3549. VetoName
  3550. ));
  3551. } else {
  3552. IopDbgPrint((IOP_IOEVENT_WARNING_LEVEL,
  3553. "PiFinalizeVetoedRemove: Removal of %wZ vetoed %s.\n",
  3554. devNodeName,
  3555. failureReason
  3556. ));
  3557. }
  3558. #endif
  3559. //
  3560. // Update the vetoType field if the caller is interested.
  3561. //
  3562. if (VetoedDeviceEvent->VetoType != NULL) {
  3563. *VetoedDeviceEvent->VetoType = VetoType;
  3564. }
  3565. //
  3566. // The VetoName field tells us whether UI should be displayed (if NULL,
  3567. // kernel mode UI is implicitely requested.)
  3568. //
  3569. if (VetoedDeviceEvent->VetoName != NULL) {
  3570. if (VetoName != NULL) {
  3571. RtlCopyUnicodeString(VetoedDeviceEvent->VetoName, VetoName);
  3572. }
  3573. } else {
  3574. //
  3575. // If there is not a VetoName passed in then call user mode to display the
  3576. // eject veto notification to the user
  3577. //
  3578. PiNotifyUserModeRemoveVetoed(
  3579. VetoedDeviceEvent,
  3580. deviceObject,
  3581. VetoType,
  3582. VetoName
  3583. );
  3584. }
  3585. }
  3586. BOOLEAN
  3587. PiCompareGuid(
  3588. CONST GUID *Guid1,
  3589. CONST GUID *Guid2
  3590. )
  3591. /*++
  3592. Routine Description:
  3593. This routine compares two guids.
  3594. Arguments:
  3595. Guid1 - First guid to compare
  3596. Guid2 - Second guid to compare
  3597. Return Value:
  3598. Returns TRUE if the guids are equal and FALSE if they're different.
  3599. --*/
  3600. {
  3601. PAGED_CODE();
  3602. if (RtlCompareMemory((PVOID)Guid1, (PVOID)Guid2, sizeof(GUID)) == sizeof(GUID)) {
  3603. return TRUE;
  3604. }
  3605. return FALSE;
  3606. } // PiCompareGuid
  3607. PVOID
  3608. PiAllocateCriticalMemory(
  3609. IN PLUGPLAY_DEVICE_DELETE_TYPE DeleteType,
  3610. IN POOL_TYPE PoolType,
  3611. IN SIZE_T Size,
  3612. IN ULONG Tag
  3613. )
  3614. /*++
  3615. Routine Description:
  3616. This function allocates memory and never fails if the DeleteType isn't
  3617. QueryRemoveDevice or EjectDevice. This function will disappear in the next
  3618. version of the PnP engine as we will instead requeue failed operations
  3619. (which will also result in a second attempt to allocate the memory) or
  3620. preallocate the required memory when bringing new devnode's into the world.
  3621. Arguments:
  3622. DeleteType - Operation (EjectDevice, SurpriseRemoveDevice, ...)
  3623. PoolType - PagedPool, NonPagedPool
  3624. Size - Size
  3625. Tag - Allocation tag
  3626. Return Value:
  3627. Allocation, NULL due to insufficient resources.
  3628. --*/
  3629. {
  3630. PVOID memory;
  3631. LARGE_INTEGER timeOut;
  3632. PAGED_CODE();
  3633. //
  3634. // Retries only have a hope of succeeding if we are at PASSIVE_LEVEL
  3635. //
  3636. ASSERT(KeGetCurrentIrql() != DISPATCH_LEVEL);
  3637. while(1) {
  3638. memory = ExAllocatePoolWithTag(PoolType, Size, Tag);
  3639. if (memory ||
  3640. (DeleteType == QueryRemoveDevice) ||
  3641. (DeleteType == EjectDevice)) {
  3642. //
  3643. // Either we got memory or the op was failable. Get out of here.
  3644. //
  3645. break;
  3646. }
  3647. //
  3648. // We're stuck until more memory comes along. Let some other
  3649. // threads run before we get another shot...
  3650. //
  3651. timeOut.QuadPart = Int32x32To64( 1, -10000 );
  3652. KeDelayExecutionThread(KernelMode, FALSE, &timeOut);
  3653. }
  3654. return memory;
  3655. }
  3656. #if DBG
  3657. struct {
  3658. CONST GUID *Guid;
  3659. PCHAR Name;
  3660. } EventGuidTable[] = {
  3661. { &GUID_HWPROFILE_QUERY_CHANGE, "GUID_HWPROFILE_QUERY_CHANGE" },
  3662. { &GUID_HWPROFILE_CHANGE_CANCELLED, "GUID_HWPROFILE_CHANGE_CANCELLED" },
  3663. { &GUID_HWPROFILE_CHANGE_COMPLETE, "GUID_HWPROFILE_CHANGE_COMPLETE" },
  3664. { &GUID_DEVICE_INTERFACE_ARRIVAL, "GUID_DEVICE_INTERFACE_ARRIVAL" },
  3665. { &GUID_DEVICE_INTERFACE_REMOVAL, "GUID_DEVICE_INTERFACE_REMOVAL" },
  3666. { &GUID_TARGET_DEVICE_QUERY_REMOVE, "GUID_TARGET_DEVICE_QUERY_REMOVE" },
  3667. { &GUID_TARGET_DEVICE_REMOVE_CANCELLED, "GUID_TARGET_DEVICE_REMOVE_CANCELLED" },
  3668. { &GUID_TARGET_DEVICE_REMOVE_COMPLETE, "GUID_TARGET_DEVICE_REMOVE_COMPLETE" },
  3669. { &GUID_PNP_CUSTOM_NOTIFICATION, "GUID_PNP_CUSTOM_NOTIFICATION" },
  3670. { &GUID_DEVICE_ARRIVAL, "GUID_DEVICE_ARRIVAL" },
  3671. { &GUID_DEVICE_ENUMERATED, "GUID_DEVICE_ENUMERATED" },
  3672. { &GUID_DEVICE_ENUMERATE_REQUEST, "GUID_DEVICE_ENUMERATE_REQUEST" },
  3673. { &GUID_DEVICE_START_REQUEST, "GUID_DEVICE_START_REQUEST" },
  3674. { &GUID_DEVICE_REMOVE_PENDING, "GUID_DEVICE_REMOVE_PENDING" },
  3675. { &GUID_DEVICE_QUERY_AND_REMOVE, "GUID_DEVICE_QUERY_AND_REMOVE" },
  3676. { &GUID_DEVICE_EJECT, "GUID_DEVICE_EJECT" },
  3677. { &GUID_DEVICE_NOOP, "GUID_DEVICE_NOOP" },
  3678. { &GUID_DEVICE_SURPRISE_REMOVAL, "GUID_DEVICE_SURPRISE_REMOVAL" },
  3679. { &GUID_DEVICE_SAFE_REMOVAL, "GUID_DEVICE_SAFE_REMOVAL" },
  3680. { &GUID_DEVICE_EJECT_VETOED, "GUID_DEVICE_EJECT_VETOED" },
  3681. { &GUID_DEVICE_REMOVAL_VETOED, "GUID_DEVICE_REMOVAL_VETOED" },
  3682. };
  3683. #define EVENT_GUID_TABLE_SIZE (sizeof(EventGuidTable) / sizeof(EventGuidTable[0]))
  3684. VOID
  3685. LookupGuid(
  3686. IN CONST GUID *Guid,
  3687. IN OUT PCHAR String,
  3688. IN ULONG StringLength
  3689. )
  3690. {
  3691. int i;
  3692. PAGED_CODE();
  3693. for (i = 0; i < EVENT_GUID_TABLE_SIZE; i++) {
  3694. if (PiCompareGuid(Guid, EventGuidTable[i].Guid)) {
  3695. strncpy(String, EventGuidTable[i].Name, StringLength - 1);
  3696. String[StringLength - 1] = '\0';
  3697. return;
  3698. }
  3699. }
  3700. _snprintf( String, StringLength, "%08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X",
  3701. Guid->Data1,
  3702. Guid->Data2,
  3703. Guid->Data3,
  3704. Guid->Data4[0],
  3705. Guid->Data4[1],
  3706. Guid->Data4[2],
  3707. Guid->Data4[3],
  3708. Guid->Data4[4],
  3709. Guid->Data4[5],
  3710. Guid->Data4[6],
  3711. Guid->Data4[7] );
  3712. }
  3713. VOID
  3714. DumpMultiSz(
  3715. IN PWCHAR MultiSz
  3716. )
  3717. {
  3718. PWCHAR p = MultiSz;
  3719. ULONG length;
  3720. PAGED_CODE();
  3721. while (*p) {
  3722. length = (ULONG)wcslen(p);
  3723. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3724. " %S\n", p));
  3725. p += length + 1;
  3726. }
  3727. }
  3728. VOID
  3729. DumpPnpEvent(
  3730. IN PPLUGPLAY_EVENT_BLOCK EventBlock
  3731. )
  3732. {
  3733. CHAR guidString[256];
  3734. PAGED_CODE();
  3735. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3736. "PlugPlay Event Block @ 0x%p\n", EventBlock));
  3737. LookupGuid(&EventBlock->EventGuid, guidString, sizeof(guidString));
  3738. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3739. " EventGuid = %s\n", guidString));
  3740. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3741. " DeviceObject = 0x%p\n", EventBlock->DeviceObject));
  3742. switch (EventBlock->EventCategory) {
  3743. case HardwareProfileChangeEvent:
  3744. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3745. " HardwareProfileChangeEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
  3746. EventBlock->Result,
  3747. EventBlock->Flags,
  3748. EventBlock->TotalSize));
  3749. break;
  3750. case TargetDeviceChangeEvent:
  3751. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3752. " TargetDeviceChangeEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
  3753. EventBlock->Result,
  3754. EventBlock->Flags,
  3755. EventBlock->TotalSize));
  3756. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3757. " DeviceIds:\n"));
  3758. DumpMultiSz( EventBlock->u.TargetDevice.DeviceIds );
  3759. break;
  3760. case DeviceClassChangeEvent:
  3761. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3762. " DeviceClassChangeEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
  3763. EventBlock->Result,
  3764. EventBlock->Flags,
  3765. EventBlock->TotalSize));
  3766. LookupGuid(&EventBlock->u.DeviceClass.ClassGuid, guidString, sizeof(guidString));
  3767. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3768. " ClassGuid = %s\n",
  3769. guidString));
  3770. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3771. " SymbolicLinkName = %S\n",
  3772. EventBlock->u.DeviceClass.SymbolicLinkName));
  3773. break;
  3774. case CustomDeviceEvent:
  3775. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3776. " CustomDeviceEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
  3777. EventBlock->Result,
  3778. EventBlock->Flags,
  3779. EventBlock->TotalSize));
  3780. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3781. " NotificationStructure = 0x%p\n DeviceIds:\n",
  3782. EventBlock->u.CustomNotification.NotificationStructure));
  3783. DumpMultiSz( EventBlock->u.CustomNotification.DeviceIds );
  3784. break;
  3785. case DeviceInstallEvent:
  3786. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3787. " DeviceInstallEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
  3788. EventBlock->Result,
  3789. EventBlock->Flags,
  3790. EventBlock->TotalSize));
  3791. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3792. " DeviceId = %S\n", EventBlock->u.InstallDevice.DeviceId));
  3793. break;
  3794. case DeviceArrivalEvent:
  3795. IopDbgPrint((IOP_IOEVENT_INFO_LEVEL,
  3796. " DeviceArrivalEvent, Result = 0x%p, Flags = 0x%08X, TotalSize = %d\n",
  3797. EventBlock->Result,
  3798. EventBlock->Flags,
  3799. EventBlock->TotalSize));
  3800. break;
  3801. }
  3802. }
  3803. VOID
  3804. PiDumpPdoHandlesToDebugger(
  3805. IN PDEVICE_OBJECT *DeviceObjectArray,
  3806. IN ULONG ArrayCount,
  3807. IN BOOLEAN KnownHandleFailure
  3808. )
  3809. /*++
  3810. Routine Description:
  3811. This helper routine dumps any handles opened against the passed in array of
  3812. device objects to the debugger console.
  3813. Arguments:
  3814. DeviceObjectArray - Array of Physical Device Objects.
  3815. ArrayCount - Number of device objects in the passed in array
  3816. KnownHandleFailure - TRUE if the removal was vetoed due to open handles,
  3817. FALSE if not.
  3818. Return Value:
  3819. None.
  3820. --*/
  3821. {
  3822. ULONG i, handleCount;
  3823. //
  3824. // If we have enabled the dumping flag, or the user ran oh.exe, spit all
  3825. // handles on a veto to the debugger.
  3826. //
  3827. if (!(PiDumpVetoedHandles || (NtGlobalFlag & FLG_MAINTAIN_OBJECT_TYPELIST))) {
  3828. return;
  3829. }
  3830. DbgPrint("Beginning handle dump:\n");
  3831. if (!KnownHandleFailure) {
  3832. DbgPrint(" (Failed Query-Remove - *Might* by due to leaked handles)\n");
  3833. }
  3834. for(i=handleCount=0; i<ArrayCount; i++) {
  3835. PpHandleEnumerateHandlesAgainstPdoStack(
  3836. DeviceObjectArray[i],
  3837. PiDumpPdoHandlesToDebuggerCallBack,
  3838. (PVOID) &handleCount
  3839. );
  3840. }
  3841. DbgPrint("Dump complete - %d total handles found.\n", handleCount);
  3842. }
  3843. BOOLEAN
  3844. PiDumpPdoHandlesToDebuggerCallBack(
  3845. IN PDEVICE_OBJECT DeviceObject,
  3846. IN PEPROCESS Process,
  3847. IN PFILE_OBJECT FileObject,
  3848. IN HANDLE HandleId,
  3849. IN PVOID Context
  3850. )
  3851. /*++
  3852. Routine Description:
  3853. This helper routine for PiDumpPdoHandlesToDebuggerCallBack. It gets called
  3854. back for each handle opened against a given device object.
  3855. Arguments:
  3856. DeviceObject - Device Object handle was against. Will be valid (referenced)
  3857. Process - Process handle was against. Will be valid (referenced)
  3858. FileObject - File object pertaining to handle - might not be valid
  3859. HandleId - Handle relating to open device - might not be valid
  3860. Context - Context passed in to PpHandleEnumerateHandlesAgainstPdoStack.
  3861. Return Value:
  3862. TRUE if the enumeration should be stopped, FALSE otherwise.
  3863. --*/
  3864. {
  3865. PULONG handleCount;
  3866. //
  3867. // Display the handle.
  3868. //
  3869. DbgPrint(
  3870. " DeviceObject:%p ProcessID:%dT FileObject:%p Handle:%dT\n",
  3871. DeviceObject,
  3872. Process->UniqueProcessId,
  3873. FileObject,
  3874. HandleId
  3875. );
  3876. handleCount = (PULONG) Context;
  3877. (*handleCount)++;
  3878. return FALSE;
  3879. }
  3880. #endif // DBG