Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

973 lines
26 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. KIRQL OldIrql;
  102. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  103. //
  104. // return error for nonsense parameters, or DeviceObject not PDO
  105. //
  106. if ( (NotificationFunction == NULL) ||
  107. (NotificationType == 0) ||
  108. (NotificationHandle == NULL) ||
  109. (DeviceState == NULL) ||
  110. (DeviceObject == NULL) )
  111. {
  112. return STATUS_INVALID_PARAMETER;
  113. }
  114. if ( ! (IS_DO_PDO(DeviceObject)) ) {
  115. return STATUS_INVALID_PARAMETER;
  116. }
  117. //
  118. // acquire the notification channel lock, since we will be
  119. // changing channel structures
  120. //
  121. ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
  122. //
  123. // if the channel isn't already in place, create it
  124. //
  125. if (!PopGetDope(DeviceObject)) {
  126. ExReleaseResourceLite(&PopNotifyLock);
  127. return STATUS_INSUFFICIENT_RESOURCES;
  128. }
  129. pchannel = &(DeviceObject->DeviceObjectExtension->Dope->PowerChannelSummary);
  130. if (pchannel->Signature == 0) {
  131. //
  132. // we do NOT already have a channel, bug GetDope has
  133. // inited the notify list and set the signature and owner for us
  134. //
  135. if (!NT_SUCCESS(status = PopBuildPowerChannel(DeviceObject, pchannel, 0))) {
  136. ExReleaseResourceLite(&PopNotifyLock);
  137. return status;
  138. }
  139. pchannel->Signature = (ULONG)POP_PNCS_TAG;
  140. }
  141. //
  142. // since we are here, pchannel points to a filled in power channel for
  143. // the request PDO, so we just add this notification instance to it
  144. // and we are done
  145. //
  146. status = PopEnterNotification(
  147. pchannel,
  148. DeviceObject,
  149. NotificationFunction,
  150. NotificationContext,
  151. NotificationType,
  152. DeviceState,
  153. NotificationHandle
  154. );
  155. ExReleaseResourceLite(&PopNotifyLock);
  156. return status;
  157. }
  158. NTKERNELAPI
  159. NTSTATUS
  160. PoCancelDeviceNotify (
  161. IN PVOID NotificationHandle
  162. )
  163. /*++
  164. Routine Description:
  165. Check that NotificationHandle points to a notify block and that
  166. it makes sense to cancel it. Decrement ref count. If new ref count
  167. is 0, blast the entry, cut it from the list, and free its memory.
  168. Arguments:
  169. NotificationHandle - reference to the notification list entry of interest
  170. Return Value:
  171. --*/
  172. {
  173. PPOWER_NOTIFY_BLOCK pnb;
  174. KIRQL OldIrql;
  175. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  176. pnb = (PPOWER_NOTIFY_BLOCK)NotificationHandle;
  177. ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
  178. PopLockDopeGlobal(&OldIrql);
  179. //
  180. // check for blatant errors
  181. //
  182. if ( (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, oldIrql2;
  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. KeBugCheckEx(INTERNAL_POWER_ERROR, 2, 1, (ULONG_PTR)DeviceObject, (ULONG_PTR)PowerChannelSummary);
  331. }
  332. if (RecursionThrottle > POP_RECURSION_LIMIT) {
  333. ASSERT(0);
  334. return STATUS_STACK_OVERFLOW;
  335. }
  336. if (!PopGetDope(DeviceObject)) {
  337. return STATUS_INSUFFICIENT_RESOURCES;
  338. }
  339. //
  340. // allocate entries in case we need them
  341. //
  342. pSourceEntry =
  343. ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_NOTIFY_SOURCE), POP_PNSC_TAG);
  344. pTargetEntry =
  345. ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_NOTIFY_TARGET), POP_PNTG_TAG);
  346. if ((!pSourceEntry) || (!pTargetEntry)) {
  347. if (pSourceEntry) ExFreePool(pSourceEntry);
  348. if (pTargetEntry) ExFreePool(pTargetEntry);
  349. return STATUS_INSUFFICIENT_RESOURCES;
  350. }
  351. //
  352. // run the source list
  353. //
  354. PopLockDopeGlobal(&OldIrql);
  355. pdope = DeviceObject->DeviceObjectExtension->Dope;
  356. pSourceHead = &(pdope->NotifySourceList);
  357. for (plink = pSourceHead->Flink;
  358. plink != pSourceHead;
  359. plink = plink->Flink)
  360. {
  361. pEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
  362. if (pEntry->Target->ChannelSummary == PowerChannelSummary) {
  363. //
  364. // the supplied device object already points to the supplied
  365. // channel, so just say we're done.
  366. //
  367. ExFreePool(pSourceEntry);
  368. ExFreePool(pTargetEntry);
  369. return STATUS_SUCCESS;
  370. }
  371. }
  372. //
  373. // we're not already in the list, so use the source and target entries
  374. // we created above
  375. //
  376. pSourceEntry->Signature = POP_PNSC_TAG;
  377. pSourceEntry->Target = pTargetEntry;
  378. pSourceEntry->Dope = pdope;
  379. InsertHeadList(pSourceHead, &(pSourceEntry->List));
  380. pTargetEntry->Signature = POP_PNTG_TAG;
  381. pTargetEntry->Source = pSourceEntry;
  382. pTargetEntry->ChannelSummary = PowerChannelSummary;
  383. pdope = CONTAINING_RECORD(PowerChannelSummary, DEVICE_OBJECT_POWER_EXTENSION, PowerChannelSummary);
  384. InsertHeadList(&(pdope->NotifyTargetList), &(pTargetEntry->List));
  385. //
  386. // adjust the counts in the PowerChannelSummary
  387. //
  388. PowerChannelSummary->TotalCount++;
  389. if (PopGetDoDevicePowerState(DeviceObject->DeviceObjectExtension) == PowerDeviceD0) {
  390. PowerChannelSummary->D0Count++;
  391. }
  392. //
  393. // at this point the one PDO we know about refers to the channel
  394. // so we now look for things it depends on...
  395. //
  396. PopUnlockDopeGlobal(OldIrql);
  397. PopFindPowerDependencies(DeviceObject, PowerChannelSummary, RecursionThrottle);
  398. return STATUS_SUCCESS;
  399. }
  400. NTSTATUS
  401. PopCompleteFindIrp(
  402. IN PDEVICE_OBJECT DeviceObject,
  403. IN PIRP Irp,
  404. IN PVOID Context
  405. )
  406. {
  407. KeSetEvent((PKEVENT)Context, 0, FALSE);
  408. return STATUS_MORE_PROCESSING_REQUIRED;
  409. }
  410. NTSTATUS
  411. PopFindPowerDependencies(
  412. PDEVICE_OBJECT DeviceObject,
  413. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  414. ULONG RecursionThrottle
  415. )
  416. /*++
  417. Routine Description:
  418. Get the power relations for device object, step through them
  419. looking for pdos. call PopBuildPowerChannel to add PDOs to
  420. channel inclusion list. recurse into non-pdos looking for pdos.
  421. Arguments:
  422. DeviceObject - supplies a PDO
  423. PowerChannel - the power channel structure to add to the PDO's run list
  424. RecursionThrottle - number of times we're recursed into this routine,
  425. punt if it exceeds threshold
  426. Return Value:
  427. Standard NTSTATUS values, including:
  428. STATUS_SUCCESS
  429. STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
  430. --*/
  431. {
  432. PDEVICE_RELATIONS pdr;
  433. KEVENT findevent;
  434. ULONG i;
  435. PIRP irp;
  436. PIO_STACK_LOCATION irpsp;
  437. PDEVICE_OBJECT childDeviceObject;
  438. NTSTATUS status;
  439. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  440. if (RecursionThrottle > POP_RECURSION_LIMIT) {
  441. ASSERT(0);
  442. return STATUS_STACK_OVERFLOW;
  443. }
  444. //
  445. // allocate and fill an irp to send to the device object
  446. //
  447. irp = IoAllocateIrp(
  448. (CCHAR)(DeviceObject->StackSize),
  449. TRUE
  450. );
  451. if (!irp) {
  452. return STATUS_INSUFFICIENT_RESOURCES;
  453. }
  454. irpsp = IoGetNextIrpStackLocation(irp);
  455. irpsp->MajorFunction = IRP_MJ_PNP;
  456. irpsp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
  457. irpsp->Parameters.QueryDeviceRelations.Type = PowerRelations;
  458. irpsp->DeviceObject = DeviceObject;
  459. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  460. irp->IoStatus.Information = 0;
  461. IoSetCompletionRoutine(
  462. irp,
  463. PopCompleteFindIrp,
  464. (PVOID)(&findevent),
  465. TRUE,
  466. TRUE,
  467. TRUE
  468. );
  469. KeInitializeEvent(&findevent, SynchronizationEvent, FALSE);
  470. KeResetEvent(&findevent);
  471. IoCallDriver(DeviceObject, irp);
  472. KeWaitForSingleObject(
  473. &findevent,
  474. Executive,
  475. KernelMode,
  476. FALSE,
  477. NULL
  478. );
  479. //
  480. // we have in hand the completed irp, if it worked, it will list
  481. // device objects that the subject device object has a power relation with
  482. //
  483. if (!NT_SUCCESS(irp->IoStatus.Status)) {
  484. return irp->IoStatus.Status;
  485. }
  486. pdr = (PDEVICE_RELATIONS)(irp->IoStatus.Information);
  487. IoFreeIrp(irp);
  488. if (!pdr) {
  489. return STATUS_SUCCESS;
  490. }
  491. if (pdr->Count == 0) {
  492. ExFreePool(pdr);
  493. return STATUS_SUCCESS;
  494. }
  495. //
  496. // walk the pdr, for each entry, either add it as a reference and recurse down
  497. // (if it's a pdo) or skip the add and simply recurse down (for a !pdo)
  498. //
  499. RecursionThrottle++;
  500. for (i = 0; i < pdr->Count; i++) {
  501. childDeviceObject = pdr->Objects[i];
  502. if (IS_DO_PDO(childDeviceObject)) {
  503. status = PopBuildPowerChannel(
  504. childDeviceObject,
  505. PowerChannelSummary,
  506. RecursionThrottle
  507. );
  508. } else {
  509. status = PopFindPowerDependencies(
  510. childDeviceObject,
  511. PowerChannelSummary,
  512. RecursionThrottle
  513. );
  514. }
  515. if (!NT_SUCCESS(status)) {
  516. goto Exit;
  517. }
  518. }
  519. Exit:
  520. //
  521. // regardless of how far we got before we hit any errors,
  522. // we must deref the device objects in the list and free the list itself
  523. //
  524. for (i = 0; i < pdr->Count; i++) {
  525. ObDereferenceObject(pdr->Objects[i]);
  526. }
  527. ExFreePool(pdr);
  528. return status;
  529. }
  530. VOID
  531. PopStateChangeNotify(
  532. PDEVICE_OBJECT DeviceObject,
  533. ULONG NotificationType
  534. )
  535. /*++
  536. Routine Description:
  537. Called by PoSetPowerState to execute notifications.
  538. Arguments:
  539. Dope - the dope for the dev obj that expereinced the change
  540. NotificationType - what happened
  541. Return Value:
  542. --*/
  543. {
  544. PPOWER_CHANNEL_SUMMARY pchannel;
  545. PDEVICE_OBJECT_POWER_EXTENSION Dope;
  546. PLIST_ENTRY pSourceHead;
  547. PPOWER_NOTIFY_SOURCE pSourceEntry;
  548. PPOWER_NOTIFY_TARGET pTargetEntry;
  549. PLIST_ENTRY plink;
  550. KIRQL oldIrql;
  551. KIRQL oldIrql2;
  552. oldIrql = KeGetCurrentIrql();
  553. if (oldIrql != PASSIVE_LEVEL) {
  554. //
  555. // caller had BETTER be doing a Power up, and we will use
  556. // the DopeGlobal local to protect access
  557. //
  558. PopLockDopeGlobal(&oldIrql2);
  559. } else {
  560. //
  561. // caller could be going up or down, we can just grab the resource
  562. //
  563. ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
  564. }
  565. Dope = DeviceObject->DeviceObjectExtension->Dope;
  566. ASSERT((Dope));
  567. //
  568. // run the notify source structures hanging off the dope
  569. //
  570. pSourceHead = &(Dope->NotifySourceList);
  571. for (plink = pSourceHead->Flink;
  572. plink != pSourceHead;
  573. plink = plink->Flink)
  574. {
  575. pSourceEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
  576. ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
  577. pTargetEntry = pSourceEntry->Target;
  578. ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
  579. pchannel = pTargetEntry->ChannelSummary;
  580. if (NotificationType & PO_NOTIFY_D0) {
  581. //
  582. // going to D0
  583. //
  584. pchannel->D0Count++;
  585. if (pchannel->D0Count == pchannel->TotalCount) {
  586. PopPresentNotify(DeviceObject, pchannel, NotificationType);
  587. }
  588. } else if (NotificationType & PO_NOTIFY_TRANSITIONING_FROM_D0) {
  589. //
  590. // dropping from D0
  591. //
  592. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  593. pchannel->D0Count--;
  594. if (pchannel->D0Count == (pchannel->TotalCount - 1)) {
  595. PopPresentNotify(DeviceObject, pchannel, NotificationType);
  596. }
  597. } else if (NotificationType & PO_NOTIFY_INVALID) {
  598. PopPresentNotify(DeviceObject, pchannel, NotificationType);
  599. }
  600. }
  601. if (oldIrql != PASSIVE_LEVEL) {
  602. PopUnlockDopeGlobal(oldIrql2);
  603. } else {
  604. ExReleaseResourceLite(&PopNotifyLock);
  605. }
  606. return;
  607. }
  608. VOID
  609. PopPresentNotify(
  610. PDEVICE_OBJECT DeviceObject,
  611. PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
  612. ULONG NotificationType
  613. )
  614. /*++
  615. Routine Description:
  616. Run the device object's list of notify nodes, and call the handler
  617. each one refers to.
  618. Arguments:
  619. DeviceObject - device object that is the source of the notification,
  620. is NOT necessarily the device object that the power
  621. channel applies to
  622. PowerChannelSummary - base of list of notification blocks to call through
  623. NotificationType - what sort of event occurred
  624. Return Value:
  625. --*/
  626. {
  627. PLIST_ENTRY plisthead;
  628. PLIST_ENTRY plist;
  629. PPOWER_NOTIFY_BLOCK pnb;
  630. plisthead = &(PowerChannelSummary->NotifyList);
  631. for (plist = plisthead->Flink; plist != plisthead;) {
  632. pnb = CONTAINING_RECORD(plist, POWER_NOTIFY_BLOCK, NotifyList);
  633. ASSERT(pnb->Invalidated == FALSE);
  634. if ( (NotificationType & PO_NOTIFY_INVALID) ||
  635. (pnb->NotificationType & NotificationType) )
  636. {
  637. (pnb->NotificationFunction)(
  638. DeviceObject,
  639. pnb->NotificationContext,
  640. NotificationType,
  641. 0
  642. );
  643. }
  644. if (NotificationType & PO_NOTIFY_INVALID) {
  645. //
  646. // this pnb is no longer valid, so take it off the list.
  647. // right now this is all we do.
  648. // N.B. caller is holding the right locks...
  649. //
  650. plist = plist->Flink;
  651. RemoveEntryList(&(pnb->NotifyList));
  652. InitializeListHead(&(pnb->NotifyList));
  653. pnb->Invalidated = TRUE;
  654. PopInvalidNotifyBlockCount += 1;
  655. } else {
  656. plist = plist->Flink;
  657. }
  658. }
  659. return;
  660. }
  661. VOID
  662. PopRunDownSourceTargetList(
  663. PDEVICE_OBJECT DeviceObject
  664. )
  665. /*++
  666. Routine Description:
  667. This routine runs the source and target lists for the notify
  668. network hanging off a particular device object. It knocks down
  669. these entries and their mates, and sends invalidates for notify
  670. blocks as needed.
  671. The caller is expected to be holding PopNotifyLock and the
  672. PopDopeGlobalLock.
  673. Arguments:
  674. DeviceObject - supplies the address of the device object to
  675. be cut out of the notify network.
  676. D0Count - 1 if the D0Count in target channel summaries is to
  677. be decremented (run down devobj is in d0) else 0.
  678. Return Value:
  679. --*/
  680. {
  681. PDEVICE_OBJECT_POWER_EXTENSION Dope;
  682. PDEVOBJ_EXTENSION Doe;
  683. PLIST_ENTRY pListHead;
  684. PLIST_ENTRY plink;
  685. PPOWER_NOTIFY_SOURCE pSourceEntry;
  686. PPOWER_NOTIFY_TARGET pTargetEntry;
  687. PPOWER_CHANNEL_SUMMARY targetchannel;
  688. ULONG D0Count;
  689. KIRQL OldIrql;
  690. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  691. Doe = DeviceObject->DeviceObjectExtension;
  692. Dope = DeviceObject->DeviceObjectExtension->Dope;
  693. PopLockIrpSerialList(&OldIrql);
  694. if (PopGetDoSystemPowerState(Doe) == PowerDeviceD0) {
  695. D0Count = 1;
  696. } else {
  697. D0Count = 0;
  698. }
  699. PopUnlockIrpSerialList(OldIrql);
  700. if (!Dope) {
  701. return;
  702. }
  703. //
  704. // run all of the source nodes for the device object
  705. //
  706. pListHead = &(Dope->NotifySourceList);
  707. for (plink = pListHead->Flink; plink != pListHead; ) {
  708. pSourceEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
  709. ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
  710. pTargetEntry = pSourceEntry->Target;
  711. ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
  712. //
  713. // free the target node
  714. //
  715. targetchannel = pTargetEntry->ChannelSummary;
  716. RemoveEntryList(&(pTargetEntry->List));
  717. pTargetEntry->Signature = POP_NONO;
  718. ExFreePool(pTargetEntry);
  719. targetchannel->TotalCount--;
  720. targetchannel->D0Count -= D0Count;
  721. //
  722. // have PopPresentNotify call anybody listening, and remove
  723. // notify blocks for us
  724. //
  725. PopPresentNotify(DeviceObject, targetchannel, PO_NOTIFY_INVALID);
  726. //
  727. // knock down the source entry
  728. //
  729. plink = plink->Flink;
  730. RemoveEntryList(&(pSourceEntry->List));
  731. pSourceEntry->Signature = POP_NONO;
  732. ExFreePool(pSourceEntry);
  733. }
  734. //
  735. // run the target list and shoot down the targets and their source mates
  736. //
  737. pListHead = &(Dope->NotifyTargetList);
  738. for (plink = pListHead->Flink; plink != pListHead; ) {
  739. pTargetEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_TARGET, List);
  740. ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
  741. pSourceEntry = pTargetEntry->Source;
  742. ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
  743. //
  744. // free the source node on the other end
  745. //
  746. RemoveEntryList(&(pSourceEntry->List));
  747. pSourceEntry->Signature = POP_NONO;
  748. ExFreePool(pSourceEntry);
  749. //
  750. // free this target node
  751. //
  752. plink = plink->Flink;
  753. RemoveEntryList(&(pTargetEntry->List));
  754. pTargetEntry->Signature = POP_NONO;
  755. ExFreePool(pTargetEntry);
  756. }
  757. //
  758. // since we ran our own target list, and emptied it, we should
  759. // also have shot down our own notify list. So this devobj's
  760. // channel summary should be totally clean now.
  761. //
  762. Dope->PowerChannelSummary.TotalCount = 0;
  763. Dope->PowerChannelSummary.D0Count = 0;
  764. ASSERT(Dope->PowerChannelSummary.NotifyList.Flink == &(Dope->PowerChannelSummary.NotifyList));
  765. return;
  766. }