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

5386 lines
166 KiB

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