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.

991 lines
27 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. notify.c
  5. Abstract:
  6. Po/Driver Notify functions
  7. Author:
  8. Bryan Willman (bryanwi) 11-Mar-97
  9. Revision History:
  10. --*/
  11. #include "pop.h"
  12. //
  13. // constants
  14. //
  15. #define POP_RECURSION_LIMIT 30
  16. //
  17. // macros
  18. //
  19. #define IS_DO_PDO(DeviceObject) \
  20. ((DeviceObject->Flags & DO_BUS_ENUMERATED_DEVICE) && (DeviceObject->DeviceObjectExtension->DeviceNode))
  21. //
  22. // procedures private to notification
  23. //
  24. NTSTATUS
  25. PopEnterNotification(
  26. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  27. PDEVICE_OBJECT DeviceObject,
  28. PPO_NOTIFY NotificationFunction,
  29. PVOID NotificationContext,
  30. ULONG NotificationType,
  31. PDEVICE_POWER_STATE DeviceState,
  32. PVOID *NotificationHandle
  33. );
  34. NTSTATUS
  35. PopBuildPowerChannel(
  36. PDEVICE_OBJECT DeviceObject,
  37. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  38. ULONG RecursionThrottle
  39. );
  40. NTSTATUS
  41. PopCompleteFindIrp(
  42. IN PDEVICE_OBJECT DeviceObject,
  43. IN PIRP Irp,
  44. IN PVOID Context
  45. );
  46. NTSTATUS
  47. PopFindPowerDependencies(
  48. PDEVICE_OBJECT DeviceObject,
  49. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  50. ULONG RecursionThrottle
  51. );
  52. VOID
  53. PopPresentNotify(
  54. PDEVICE_OBJECT DeviceObject,
  55. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  56. ULONG NotificationType
  57. );
  58. //
  59. // Speced (public) entry points
  60. //
  61. NTKERNELAPI
  62. NTSTATUS
  63. PoRegisterDeviceNotify (
  64. IN PDEVICE_OBJECT DeviceObject,
  65. IN PPO_NOTIFY NotificationFunction,
  66. IN PVOID NotificationContext,
  67. IN ULONG NotificationType,
  68. OUT PDEVICE_POWER_STATE DeviceState,
  69. OUT PVOID *NotificationHandle
  70. )
  71. /*++
  72. Routine Description:
  73. Registers the caller to receive notification of power state changes or
  74. dependency invalidations related to the "channel" underneath and including
  75. the Physical Device Object (PDO) refereced via DeviceObject.
  76. The "channel" is the set of PDOs, always including the one supplied
  77. by DeviceObject, which form the hardware stack necessary to perform
  78. operations. The channel is on when all of these PDOs are on. It is off
  79. when any of them is not on.
  80. Arguments:
  81. DeviceObject - supplies a PDO
  82. NotificationFunction - routine to call to post the notification
  83. NotificationContext - argument passed through as is to the NotificationFunction
  84. NotificationType - mask of the notifications that the caller wishes to recieve.
  85. Note that INVALID will always be reported, regardless of whether the
  86. caller asked for it.
  87. DeviceState - the current state of the PDO, as last reported to the
  88. system via PoSetPowerState
  89. NotificationHandle - reference to the notification instance, used to
  90. cancel the notification.
  91. Return Value:
  92. Standard NTSTATUS values, including:
  93. STATUS_INVALID_PARAMETER if DeviceObject is not a PDO, or
  94. any other parameter is nonsense.
  95. STATUS_SUCCESS
  96. STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
  97. --*/
  98. {
  99. NTSTATUS status;
  100. PPOWER_CHANNEL_SUMMARY pchannel;
  101. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  102. //
  103. // return error for nonsense parameters, or DeviceObject not PDO
  104. //
  105. if ( (NotificationFunction == NULL) ||
  106. (NotificationType == 0) ||
  107. (NotificationHandle == NULL) ||
  108. (DeviceState == NULL) ||
  109. (DeviceObject == NULL) )
  110. {
  111. return STATUS_INVALID_PARAMETER;
  112. }
  113. if ( ! (IS_DO_PDO(DeviceObject)) ) {
  114. return STATUS_INVALID_PARAMETER;
  115. }
  116. //
  117. // acquire the notification channel lock, since we will be
  118. // changing channel structures
  119. //
  120. ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
  121. //
  122. // if the channel isn't already in place, create it
  123. //
  124. if (!PopGetDope(DeviceObject)) {
  125. ExReleaseResourceLite(&PopNotifyLock);
  126. return STATUS_INSUFFICIENT_RESOURCES;
  127. }
  128. pchannel = &(DeviceObject->DeviceObjectExtension->Dope->PowerChannelSummary);
  129. if (pchannel->Signature == 0) {
  130. //
  131. // we do NOT already have a channel, bug GetDope has
  132. // inited the notify list and set the signature and owner for us
  133. //
  134. if (!NT_SUCCESS(status = PopBuildPowerChannel(DeviceObject, pchannel, 0))) {
  135. ExReleaseResourceLite(&PopNotifyLock);
  136. return status;
  137. }
  138. pchannel->Signature = (ULONG)POP_PNCS_TAG;
  139. }
  140. //
  141. // since we are here, pchannel points to a filled in power channel for
  142. // the request PDO, so we just add this notification instance to it
  143. // and we are done
  144. //
  145. status = PopEnterNotification(
  146. pchannel,
  147. DeviceObject,
  148. NotificationFunction,
  149. NotificationContext,
  150. NotificationType,
  151. DeviceState,
  152. NotificationHandle
  153. );
  154. ExReleaseResourceLite(&PopNotifyLock);
  155. return status;
  156. }
  157. NTKERNELAPI
  158. NTSTATUS
  159. PoCancelDeviceNotify (
  160. IN PVOID NotificationHandle
  161. )
  162. /*++
  163. Routine Description:
  164. Check that NotificationHandle points to a notify block and that
  165. it makes sense to cancel it. Decrement ref count. If new ref count
  166. is 0, blast the entry, cut it from the list, and free its memory.
  167. Arguments:
  168. NotificationHandle - reference to the notification list entry of interest
  169. Return Value:
  170. --*/
  171. {
  172. PPOWER_NOTIFY_BLOCK pnb;
  173. KIRQL OldIrql;
  174. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  175. pnb = (PPOWER_NOTIFY_BLOCK)NotificationHandle;
  176. ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
  177. PopLockDopeGlobal(&OldIrql);
  178. //
  179. // check for blatant errors
  180. //
  181. if ( (!pnb) ||
  182. (pnb->Signature != (ULONG)POP_PNB_TAG) ||
  183. (pnb->RefCount < 0) )
  184. {
  185. ASSERT(0); // force a break on a debug build
  186. ExReleaseResourceLite(&PopNotifyLock);
  187. PopUnlockDopeGlobal(OldIrql);
  188. return STATUS_INVALID_HANDLE;
  189. }
  190. //
  191. // decrement ref count. if it's 0 afterwards we are done with this node
  192. //
  193. pnb->RefCount--;
  194. if (pnb->RefCount == 0) {
  195. //
  196. // blast it all, just to be paranoid (it's a low freq operation)
  197. //
  198. RemoveEntryList(&(pnb->NotifyList));
  199. pnb->Signature = POP_NONO;
  200. pnb->RefCount = -1;
  201. pnb->NotificationFunction = NULL;
  202. pnb->NotificationContext = 0L;
  203. pnb->NotificationType = 0;
  204. InitializeListHead(&(pnb->NotifyList));
  205. if (pnb->Invalidated) {
  206. PopInvalidNotifyBlockCount--;
  207. }
  208. ExFreePool(pnb);
  209. }
  210. PopUnlockDopeGlobal(OldIrql);
  211. ExReleaseResourceLite(&PopNotifyLock);
  212. return STATUS_SUCCESS;
  213. }
  214. //
  215. // worker code
  216. //
  217. NTSTATUS
  218. PopEnterNotification(
  219. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  220. PDEVICE_OBJECT DeviceObject,
  221. PPO_NOTIFY NotificationFunction,
  222. PVOID NotificationContext,
  223. ULONG NotificationType,
  224. PDEVICE_POWER_STATE DeviceState,
  225. PVOID *NotificationHandle
  226. )
  227. /*++
  228. Routine Description:
  229. Scans the Power Notification Instance list of the power channel
  230. looking for one that matches the parameters, which we'll use
  231. if possible.
  232. If no candidate is found, make a new one and put it on the list.
  233. Arguments:
  234. PowerChannelSummary - pointer to the power channel structure for the devobj
  235. DeviceObject - supplies a PDO
  236. NotificationFunction - routine to call to post the notification
  237. NotificationContext - argument passed through as is to the NotificationFunction
  238. NotificationType - mask of the notifications that the caller wishes to recieve.
  239. Note that INVALID will always be reported, regardless of whether the
  240. caller asked for it.
  241. DeviceState - the current state of the PDO, as last reported to the
  242. system via PoSetPowerState
  243. NotificationHandle - reference to the notification instance, used to
  244. cancel the notification.
  245. Return Value:
  246. Standard NTSTATUS values, including:
  247. STATUS_SUCCESS
  248. STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
  249. --*/
  250. {
  251. PLIST_ENTRY plist;
  252. PPOWER_NOTIFY_BLOCK pnb;
  253. KIRQL oldIrql;
  254. PopLockDopeGlobal(&oldIrql);
  255. //
  256. // run the notify list looking for an existing instance to use
  257. //
  258. for (plist = PowerChannelSummary->NotifyList.Flink;
  259. plist != &(PowerChannelSummary->NotifyList);
  260. plist = plist->Flink)
  261. {
  262. pnb = CONTAINING_RECORD(plist, POWER_NOTIFY_BLOCK, NotifyList);
  263. if ( (pnb->NotificationFunction == NotificationFunction) &&
  264. (pnb->NotificationContext == NotificationContext) &&
  265. (pnb->NotificationType == NotificationType) )
  266. {
  267. //
  268. // we have found an existing list entry that works for us
  269. //
  270. pnb->RefCount++;
  271. *DeviceState = PopLockGetDoDevicePowerState(DeviceObject->DeviceObjectExtension);
  272. *NotificationHandle = (PVOID)pnb;
  273. return STATUS_SUCCESS;
  274. }
  275. }
  276. //
  277. // didn't find an instance we can use, so make a new one
  278. //
  279. pnb = ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_NOTIFY_BLOCK), POP_PNB_TAG);
  280. if (!pnb) {
  281. PopUnlockDopeGlobal(oldIrql);
  282. return STATUS_INSUFFICIENT_RESOURCES;
  283. }
  284. pnb->Signature = (ULONG)(POP_PNB_TAG);
  285. pnb->RefCount = 1;
  286. pnb->Invalidated = FALSE;
  287. InitializeListHead(&(pnb->NotifyList));
  288. pnb->NotificationFunction = NotificationFunction;
  289. pnb->NotificationContext = NotificationContext;
  290. pnb->NotificationType = NotificationType;
  291. pnb->PowerChannel = PowerChannelSummary;
  292. InsertHeadList(&(PowerChannelSummary->NotifyList), &(pnb->NotifyList));
  293. *DeviceState = PopLockGetDoDevicePowerState(DeviceObject->DeviceObjectExtension);
  294. *NotificationHandle = (PVOID)pnb;
  295. PopUnlockDopeGlobal(oldIrql);
  296. return STATUS_SUCCESS;
  297. }
  298. NTSTATUS
  299. PopBuildPowerChannel(
  300. PDEVICE_OBJECT DeviceObject,
  301. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  302. ULONG RecursionThrottle
  303. )
  304. /*++
  305. Routine Description:
  306. Adds DeviceObject to the power notify channel focused at PowerChannelSummary,
  307. and then repeat on dependencies.
  308. Arguments:
  309. DeviceObject - supplies a PDO
  310. PowerChannelSummary - the power channel structure to add to the PDO's run list
  311. RecursionThrottle - number of times we're recursed into this routine,
  312. punt if it exceeds threshold
  313. Return Value:
  314. Standard NTSTATUS values, including:
  315. STATUS_SUCCESS
  316. STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
  317. --*/
  318. {
  319. PLIST_ENTRY pSourceHead;
  320. PPOWER_NOTIFY_SOURCE pSourceEntry, pEntry;
  321. PPOWER_NOTIFY_TARGET pTargetEntry;
  322. KIRQL OldIrql;
  323. PDEVICE_OBJECT_POWER_EXTENSION pdope;
  324. PLIST_ENTRY plink;
  325. //
  326. // bugcheck if we get all confused
  327. //
  328. if ( ! (IS_DO_PDO(DeviceObject))) {
  329. PopInternalAddToDumpFile ( PowerChannelSummary, sizeof(POWER_CHANNEL_SUMMARY), DeviceObject, NULL, NULL, NULL );
  330. //
  331. // subcode 2 is used alot here (including the call in _PopInternalError, which is
  332. // used all over the place. So it's essentially undiagnosable. Cut our losses
  333. // here and start using subcode POP_SYS.
  334. //
  335. // KeBugCheckEx(INTERNAL_POWER_ERROR, 2, 1, (ULONG_PTR)DeviceObject, (ULONG_PTR)PowerChannelSummary);
  336. KeBugCheckEx( INTERNAL_POWER_ERROR, // bugcheck code
  337. POP_SYS, // subcode
  338. 0x100, // unique identifier
  339. (ULONG_PTR)DeviceObject,
  340. (ULONG_PTR)PowerChannelSummary);
  341. }
  342. if (RecursionThrottle > POP_RECURSION_LIMIT) {
  343. ASSERT(0);
  344. return STATUS_STACK_OVERFLOW;
  345. }
  346. if (!PopGetDope(DeviceObject)) {
  347. return STATUS_INSUFFICIENT_RESOURCES;
  348. }
  349. //
  350. // allocate entries in case we need them
  351. //
  352. pSourceEntry =
  353. ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_NOTIFY_SOURCE), POP_PNSC_TAG);
  354. pTargetEntry =
  355. ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_NOTIFY_TARGET), POP_PNTG_TAG);
  356. if ((!pSourceEntry) || (!pTargetEntry)) {
  357. if (pSourceEntry) ExFreePool(pSourceEntry);
  358. if (pTargetEntry) ExFreePool(pTargetEntry);
  359. return STATUS_INSUFFICIENT_RESOURCES;
  360. }
  361. //
  362. // run the source list
  363. //
  364. PopLockDopeGlobal(&OldIrql);
  365. pdope = DeviceObject->DeviceObjectExtension->Dope;
  366. pSourceHead = &(pdope->NotifySourceList);
  367. for (plink = pSourceHead->Flink;
  368. plink != pSourceHead;
  369. plink = plink->Flink)
  370. {
  371. pEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
  372. if (pEntry->Target->ChannelSummary == PowerChannelSummary) {
  373. //
  374. // the supplied device object already points to the supplied
  375. // channel, so just say we're done.
  376. //
  377. ExFreePool(pSourceEntry);
  378. ExFreePool(pTargetEntry);
  379. return STATUS_SUCCESS;
  380. }
  381. }
  382. //
  383. // we're not already in the list, so use the source and target entries
  384. // we created above
  385. //
  386. pSourceEntry->Signature = POP_PNSC_TAG;
  387. pSourceEntry->Target = pTargetEntry;
  388. pSourceEntry->Dope = pdope;
  389. InsertHeadList(pSourceHead, &(pSourceEntry->List));
  390. pTargetEntry->Signature = POP_PNTG_TAG;
  391. pTargetEntry->Source = pSourceEntry;
  392. pTargetEntry->ChannelSummary = PowerChannelSummary;
  393. pdope = CONTAINING_RECORD(PowerChannelSummary, DEVICE_OBJECT_POWER_EXTENSION, PowerChannelSummary);
  394. InsertHeadList(&(pdope->NotifyTargetList), &(pTargetEntry->List));
  395. //
  396. // adjust the counts in the PowerChannelSummary
  397. //
  398. PowerChannelSummary->TotalCount++;
  399. if (PopGetDoDevicePowerState(DeviceObject->DeviceObjectExtension) == PowerDeviceD0) {
  400. PowerChannelSummary->D0Count++;
  401. }
  402. //
  403. // at this point the one PDO we know about refers to the channel
  404. // so we now look for things it depends on...
  405. //
  406. PopUnlockDopeGlobal(OldIrql);
  407. PopFindPowerDependencies(DeviceObject, PowerChannelSummary, RecursionThrottle);
  408. return STATUS_SUCCESS;
  409. }
  410. NTSTATUS
  411. PopCompleteFindIrp(
  412. IN PDEVICE_OBJECT DeviceObject,
  413. IN PIRP Irp,
  414. IN PVOID Context
  415. )
  416. {
  417. UNREFERENCED_PARAMETER (DeviceObject);
  418. UNREFERENCED_PARAMETER (Irp);
  419. KeSetEvent((PKEVENT)Context, 0, FALSE);
  420. return STATUS_MORE_PROCESSING_REQUIRED;
  421. }
  422. NTSTATUS
  423. PopFindPowerDependencies(
  424. PDEVICE_OBJECT DeviceObject,
  425. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  426. ULONG RecursionThrottle
  427. )
  428. /*++
  429. Routine Description:
  430. Get the power relations for device object, step through them
  431. looking for pdos. call PopBuildPowerChannel to add PDOs to
  432. channel inclusion list. recurse into non-pdos looking for pdos.
  433. Arguments:
  434. DeviceObject - supplies a PDO
  435. PowerChannel - the power channel structure to add to the PDO's run list
  436. RecursionThrottle - number of times we're recursed into this routine,
  437. punt if it exceeds threshold
  438. Return Value:
  439. Standard NTSTATUS values, including:
  440. STATUS_SUCCESS
  441. STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
  442. --*/
  443. {
  444. PDEVICE_RELATIONS pdr;
  445. KEVENT findevent;
  446. ULONG i;
  447. PIRP irp;
  448. PIO_STACK_LOCATION irpsp;
  449. PDEVICE_OBJECT childDeviceObject;
  450. NTSTATUS status;
  451. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  452. if (RecursionThrottle > POP_RECURSION_LIMIT) {
  453. ASSERT(0);
  454. return STATUS_STACK_OVERFLOW;
  455. }
  456. //
  457. // allocate and fill an irp to send to the device object
  458. //
  459. irp = IoAllocateIrp(
  460. (CCHAR)(DeviceObject->StackSize),
  461. TRUE
  462. );
  463. if (!irp) {
  464. return STATUS_INSUFFICIENT_RESOURCES;
  465. }
  466. irpsp = IoGetNextIrpStackLocation(irp);
  467. irpsp->MajorFunction = IRP_MJ_PNP;
  468. irpsp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
  469. irpsp->Parameters.QueryDeviceRelations.Type = PowerRelations;
  470. irpsp->DeviceObject = DeviceObject;
  471. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  472. irp->IoStatus.Information = 0;
  473. IoSetCompletionRoutine(
  474. irp,
  475. PopCompleteFindIrp,
  476. (PVOID)(&findevent),
  477. TRUE,
  478. TRUE,
  479. TRUE
  480. );
  481. KeInitializeEvent(&findevent, SynchronizationEvent, FALSE);
  482. KeResetEvent(&findevent);
  483. IoCallDriver(DeviceObject, irp);
  484. KeWaitForSingleObject(
  485. &findevent,
  486. Executive,
  487. KernelMode,
  488. FALSE,
  489. NULL
  490. );
  491. //
  492. // we have in hand the completed irp, if it worked, it will list
  493. // device objects that the subject device object has a power relation with
  494. //
  495. if (!NT_SUCCESS(irp->IoStatus.Status)) {
  496. return irp->IoStatus.Status;
  497. }
  498. pdr = (PDEVICE_RELATIONS)(irp->IoStatus.Information);
  499. IoFreeIrp(irp);
  500. if (!pdr) {
  501. return STATUS_SUCCESS;
  502. }
  503. if (pdr->Count == 0) {
  504. ExFreePool(pdr);
  505. return STATUS_SUCCESS;
  506. }
  507. //
  508. // walk the pdr, for each entry, either add it as a reference and recurse down
  509. // (if it's a pdo) or skip the add and simply recurse down (for a !pdo)
  510. //
  511. RecursionThrottle++;
  512. status = STATUS_SUCCESS;
  513. for (i = 0; i < pdr->Count; i++) {
  514. childDeviceObject = pdr->Objects[i];
  515. if (IS_DO_PDO(childDeviceObject)) {
  516. status = PopBuildPowerChannel(
  517. childDeviceObject,
  518. PowerChannelSummary,
  519. RecursionThrottle
  520. );
  521. } else {
  522. status = PopFindPowerDependencies(
  523. childDeviceObject,
  524. PowerChannelSummary,
  525. RecursionThrottle
  526. );
  527. }
  528. if (!NT_SUCCESS(status)) {
  529. goto Exit;
  530. }
  531. }
  532. Exit:
  533. //
  534. // regardless of how far we got before we hit any errors,
  535. // we must deref the device objects in the list and free the list itself
  536. //
  537. for (i = 0; i < pdr->Count; i++) {
  538. ObDereferenceObject(pdr->Objects[i]);
  539. }
  540. ExFreePool(pdr);
  541. return status;
  542. }
  543. VOID
  544. PopStateChangeNotify(
  545. PDEVICE_OBJECT DeviceObject,
  546. ULONG NotificationType
  547. )
  548. /*++
  549. Routine Description:
  550. Called by PoSetPowerState to execute notifications.
  551. Arguments:
  552. Dope - the dope for the dev obj that expereinced the change
  553. NotificationType - what happened
  554. Return Value:
  555. --*/
  556. {
  557. PPOWER_CHANNEL_SUMMARY pchannel;
  558. PDEVICE_OBJECT_POWER_EXTENSION Dope;
  559. PLIST_ENTRY pSourceHead;
  560. PPOWER_NOTIFY_SOURCE pSourceEntry;
  561. PPOWER_NOTIFY_TARGET pTargetEntry;
  562. PLIST_ENTRY plink;
  563. KIRQL oldIrql;
  564. KIRQL oldIrql2;
  565. oldIrql = KeGetCurrentIrql();
  566. if (oldIrql != PASSIVE_LEVEL) {
  567. //
  568. // caller had BETTER be doing a Power up, and we will use
  569. // the DopeGlobal local to protect access
  570. //
  571. PopLockDopeGlobal(&oldIrql2);
  572. } else {
  573. //
  574. // caller could be going up or down, we can just grab the resource
  575. //
  576. oldIrql2 = PASSIVE_LEVEL;
  577. ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
  578. }
  579. Dope = DeviceObject->DeviceObjectExtension->Dope;
  580. ASSERT((Dope));
  581. //
  582. // run the notify source structures hanging off the dope
  583. //
  584. pSourceHead = &(Dope->NotifySourceList);
  585. for (plink = pSourceHead->Flink;
  586. plink != pSourceHead;
  587. plink = plink->Flink)
  588. {
  589. pSourceEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
  590. ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
  591. pTargetEntry = pSourceEntry->Target;
  592. ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
  593. pchannel = pTargetEntry->ChannelSummary;
  594. if (NotificationType & PO_NOTIFY_D0) {
  595. //
  596. // going to D0
  597. //
  598. pchannel->D0Count++;
  599. if (pchannel->D0Count == pchannel->TotalCount) {
  600. PopPresentNotify(DeviceObject, pchannel, NotificationType);
  601. }
  602. } else if (NotificationType & PO_NOTIFY_TRANSITIONING_FROM_D0) {
  603. //
  604. // dropping from D0
  605. //
  606. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  607. pchannel->D0Count--;
  608. if (pchannel->D0Count == (pchannel->TotalCount - 1)) {
  609. PopPresentNotify(DeviceObject, pchannel, NotificationType);
  610. }
  611. } else if (NotificationType & PO_NOTIFY_INVALID) {
  612. PopPresentNotify(DeviceObject, pchannel, NotificationType);
  613. }
  614. }
  615. if (oldIrql != PASSIVE_LEVEL) {
  616. PopUnlockDopeGlobal(oldIrql2);
  617. } else {
  618. ExReleaseResourceLite(&PopNotifyLock);
  619. }
  620. return;
  621. }
  622. VOID
  623. PopPresentNotify(
  624. PDEVICE_OBJECT DeviceObject,
  625. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  626. ULONG NotificationType
  627. )
  628. /*++
  629. Routine Description:
  630. Run the device object's list of notify nodes, and call the handler
  631. each one refers to.
  632. Arguments:
  633. DeviceObject - device object that is the source of the notification,
  634. is NOT necessarily the device object that the power
  635. channel applies to
  636. PowerChannelSummary - base of list of notification blocks to call through
  637. NotificationType - what sort of event occurred
  638. Return Value:
  639. --*/
  640. {
  641. PLIST_ENTRY plisthead;
  642. PLIST_ENTRY plist;
  643. PPOWER_NOTIFY_BLOCK pnb;
  644. plisthead = &(PowerChannelSummary->NotifyList);
  645. for (plist = plisthead->Flink; plist != plisthead;) {
  646. pnb = CONTAINING_RECORD(plist, POWER_NOTIFY_BLOCK, NotifyList);
  647. ASSERT(pnb->Invalidated == FALSE);
  648. if ( (NotificationType & PO_NOTIFY_INVALID) ||
  649. (pnb->NotificationType & NotificationType) )
  650. {
  651. (pnb->NotificationFunction)(
  652. DeviceObject,
  653. pnb->NotificationContext,
  654. NotificationType,
  655. 0
  656. );
  657. }
  658. if (NotificationType & PO_NOTIFY_INVALID) {
  659. //
  660. // this pnb is no longer valid, so take it off the list.
  661. // right now this is all we do.
  662. // N.B. caller is holding the right locks...
  663. //
  664. plist = plist->Flink;
  665. RemoveEntryList(&(pnb->NotifyList));
  666. InitializeListHead(&(pnb->NotifyList));
  667. pnb->Invalidated = TRUE;
  668. PopInvalidNotifyBlockCount += 1;
  669. } else {
  670. plist = plist->Flink;
  671. }
  672. }
  673. return;
  674. }
  675. VOID
  676. PopRunDownSourceTargetList(
  677. PDEVICE_OBJECT DeviceObject
  678. )
  679. /*++
  680. Routine Description:
  681. This routine runs the source and target lists for the notify
  682. network hanging off a particular device object. It knocks down
  683. these entries and their mates, and sends invalidates for notify
  684. blocks as needed.
  685. The caller is expected to be holding PopNotifyLock and the
  686. PopDopeGlobalLock.
  687. Arguments:
  688. DeviceObject - supplies the address of the device object to
  689. be cut out of the notify network.
  690. D0Count - 1 if the D0Count in target channel summaries is to
  691. be decremented (run down devobj is in d0) else 0.
  692. Return Value:
  693. --*/
  694. {
  695. PDEVICE_OBJECT_POWER_EXTENSION Dope;
  696. PDEVOBJ_EXTENSION Doe;
  697. PLIST_ENTRY pListHead;
  698. PLIST_ENTRY plink;
  699. PPOWER_NOTIFY_SOURCE pSourceEntry;
  700. PPOWER_NOTIFY_TARGET pTargetEntry;
  701. PPOWER_CHANNEL_SUMMARY targetchannel;
  702. ULONG D0Count;
  703. KIRQL OldIrql;
  704. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  705. Doe = DeviceObject->DeviceObjectExtension;
  706. Dope = DeviceObject->DeviceObjectExtension->Dope;
  707. PopLockIrpSerialList(&OldIrql);
  708. if (PopGetDoSystemPowerState(Doe) == PowerDeviceD0) {
  709. D0Count = 1;
  710. } else {
  711. D0Count = 0;
  712. }
  713. PopUnlockIrpSerialList(OldIrql);
  714. if (!Dope) {
  715. return;
  716. }
  717. //
  718. // run all of the source nodes for the device object
  719. //
  720. pListHead = &(Dope->NotifySourceList);
  721. for (plink = pListHead->Flink; plink != pListHead; ) {
  722. pSourceEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
  723. ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
  724. pTargetEntry = pSourceEntry->Target;
  725. ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
  726. //
  727. // free the target node
  728. //
  729. targetchannel = pTargetEntry->ChannelSummary;
  730. RemoveEntryList(&(pTargetEntry->List));
  731. pTargetEntry->Signature = POP_NONO;
  732. ExFreePool(pTargetEntry);
  733. targetchannel->TotalCount--;
  734. targetchannel->D0Count -= D0Count;
  735. //
  736. // have PopPresentNotify call anybody listening, and remove
  737. // notify blocks for us
  738. //
  739. PopPresentNotify(DeviceObject, targetchannel, PO_NOTIFY_INVALID);
  740. //
  741. // knock down the source entry
  742. //
  743. plink = plink->Flink;
  744. RemoveEntryList(&(pSourceEntry->List));
  745. pSourceEntry->Signature = POP_NONO;
  746. ExFreePool(pSourceEntry);
  747. }
  748. //
  749. // run the target list and shoot down the targets and their source mates
  750. //
  751. pListHead = &(Dope->NotifyTargetList);
  752. for (plink = pListHead->Flink; plink != pListHead; ) {
  753. pTargetEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_TARGET, List);
  754. ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
  755. pSourceEntry = pTargetEntry->Source;
  756. ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
  757. //
  758. // free the source node on the other end
  759. //
  760. RemoveEntryList(&(pSourceEntry->List));
  761. pSourceEntry->Signature = POP_NONO;
  762. ExFreePool(pSourceEntry);
  763. //
  764. // free this target node
  765. //
  766. plink = plink->Flink;
  767. RemoveEntryList(&(pTargetEntry->List));
  768. pTargetEntry->Signature = POP_NONO;
  769. ExFreePool(pTargetEntry);
  770. }
  771. //
  772. // since we ran our own target list, and emptied it, we should
  773. // also have shot down our own notify list. So this devobj's
  774. // channel summary should be totally clean now.
  775. //
  776. Dope->PowerChannelSummary.TotalCount = 0;
  777. Dope->PowerChannelSummary.D0Count = 0;
  778. ASSERT(Dope->PowerChannelSummary.NotifyList.Flink == &(Dope->PowerChannelSummary.NotifyList));
  779. return;
  780. }