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.

319 lines
8.2 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. idle.c
  5. Abstract:
  6. This module implements the power management idle timing code for
  7. device objects
  8. Author:
  9. Bryan Willman (bryanwi) 7-Nov-96
  10. Revision History:
  11. --*/
  12. #include "pop.h"
  13. NTKERNELAPI
  14. PULONG
  15. PoRegisterDeviceForIdleDetection (
  16. IN PDEVICE_OBJECT DeviceObject,
  17. IN ULONG ConservationIdleTime,
  18. IN ULONG PerformanceIdleTime,
  19. IN DEVICE_POWER_STATE State
  20. )
  21. /*++
  22. Routine Description:
  23. A device driver calls this routine to either:
  24. a. Create and initialize a new idle detection block
  25. b. Reset values in an existing idle detection block
  26. If the device object has an idle detection block, it is
  27. filled in with new values.
  28. Otherwise, an idle detect block is created and linked to the device
  29. object.
  30. Arguments:
  31. DeviceObject - Device object which wants idle detection, set_power
  32. IRPs will be sent here
  33. ConservationIdleTime - timeout for system in "conserve mode"
  34. PerformanceIdleTime - timeout for system in "performance mode"
  35. Type - Type of set_power sent (for set_power irp)
  36. State - what state to go to (for set_power irp)
  37. Return Value:
  38. NULL - if an attempt to create a new idle block failed
  39. non-NULL - if an idle block was created, or if an existing one was reset
  40. --*/
  41. {
  42. PDEVICE_OBJECT_POWER_EXTENSION pdope;
  43. KIRQL OldIrql;
  44. NTSTATUS Status;
  45. ULONG DeviceType, OldDeviceType;
  46. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  47. //
  48. // deal with the case where idle detection is being turned off
  49. //
  50. if ((ConservationIdleTime == 0) && (PerformanceIdleTime == 0)) {
  51. PopLockDopeGlobal(&OldIrql);
  52. pdope = DeviceObject->DeviceObjectExtension->Dope;
  53. if (pdope == NULL) {
  54. //
  55. // cannot be linked into the chain, so must already be off,
  56. // so we're done
  57. //
  58. } else {
  59. //
  60. // there is a pdope, so we may be on the idle list
  61. //
  62. if ((pdope->IdleList.Flink == &(pdope->IdleList)) &&
  63. (pdope->IdleList.Blink == &(pdope->IdleList)))
  64. {
  65. //
  66. // we're off the queue already, so we're done
  67. //
  68. } else {
  69. //
  70. // a dope vector exists and is on the idle scan list,
  71. // so we must delist ourselves
  72. //
  73. RemoveEntryList(&(pdope->IdleList));
  74. OldDeviceType = pdope->DeviceType | ES_CONTINUOUS;
  75. pdope->DeviceType = 0;
  76. PopApplyAttributeState (ES_CONTINUOUS, OldDeviceType);
  77. pdope->ConservationIdleTime = 0L;
  78. pdope->PerformanceIdleTime = 0L;
  79. pdope->State = PowerDeviceUnspecified;
  80. pdope->IdleCount = 0;
  81. InitializeListHead(&(pdope->IdleList));
  82. }
  83. }
  84. PopUnlockDopeGlobal(OldIrql);
  85. return NULL;
  86. }
  87. //
  88. // Set DeviceType if this is an idle registration by type
  89. //
  90. DeviceType = 0;
  91. if (ConservationIdleTime == (ULONG) -1 &&
  92. PerformanceIdleTime == (ULONG) -1) {
  93. switch (DeviceObject->DeviceType) {
  94. case FILE_DEVICE_DISK:
  95. case FILE_DEVICE_MASS_STORAGE:
  96. DeviceType = POP_DISK_SPINDOWN | ES_CONTINUOUS;
  97. break;
  98. default:
  99. //
  100. // Unsupported type
  101. //
  102. return NULL;
  103. }
  104. }
  105. //
  106. // now, the case where it's being turned on
  107. //
  108. pdope = PopGetDope(DeviceObject);
  109. if (pdope == NULL) {
  110. //
  111. // we didn't have a DOPE structure and couldn't allocate one, fail
  112. //
  113. return (PVOID)NULL;
  114. }
  115. //
  116. // May be a newly allocated Dope, or an existing one.
  117. // In either case, update values.
  118. // Enqueue if not already in queue
  119. //
  120. PopLockDopeGlobal(&OldIrql);
  121. OldDeviceType = pdope->DeviceType | ES_CONTINUOUS;
  122. pdope->ConservationIdleTime = ConservationIdleTime;
  123. pdope->PerformanceIdleTime = PerformanceIdleTime;
  124. pdope->State = State;
  125. pdope->IdleCount = 0;
  126. pdope->DeviceType = (UCHAR) DeviceType;
  127. if ((pdope->IdleList.Flink == &(pdope->IdleList)) &&
  128. (pdope->IdleList.Blink == &(pdope->IdleList)))
  129. {
  130. //
  131. // we're off the queue, and must be enqueued
  132. //
  133. InsertTailList(&PopIdleDetectList, &(pdope->IdleList));
  134. }
  135. PopUnlockDopeGlobal(OldIrql);
  136. PopApplyAttributeState(DeviceType, OldDeviceType);
  137. PopCheckForWork(TRUE);
  138. return &(pdope->IdleCount); // success
  139. }
  140. VOID
  141. PopScanIdleList(
  142. IN PKDPC Dpc,
  143. IN PVOID DeferredContext,
  144. IN PVOID SystemArgument1,
  145. IN PVOID SystemArgument2
  146. )
  147. /*++
  148. Routine Description:
  149. Called by the PopIdleScanTimer at PopIdleScanTimeInseconds interval,
  150. this routine runs the list of Idle Blocks, finding any that meet the
  151. trip conditions, and sends commands to the appropriate device objects
  152. to change state.
  153. The timer that calls this DPC is setup in poinit.c.
  154. Arguments:
  155. Standard DPC arguments, all are ignored.
  156. Return Value:
  157. None.
  158. --*/
  159. {
  160. KIRQL OldIrql;
  161. PLIST_ENTRY link;
  162. ULONG idlelimit;
  163. PDEVICE_OBJECT_POWER_EXTENSION pblock;
  164. POWER_STATE PowerState;
  165. PULONG pIdleCount;
  166. ULONG oldCount;
  167. PopLockDopeGlobal(&OldIrql);
  168. link = PopIdleDetectList.Flink;
  169. while (link != &PopIdleDetectList) {
  170. pblock = CONTAINING_RECORD(link, DEVICE_OBJECT_POWER_EXTENSION, IdleList);
  171. pIdleCount = &(pblock->IdleCount);
  172. oldCount = InterlockedIncrement(pIdleCount);
  173. switch (pblock->DeviceType) {
  174. case 0:
  175. idlelimit = pblock->PerformanceIdleTime;
  176. if (PopIdleDetectionMode == PO_IDLE_CONSERVATION) {
  177. idlelimit = pblock->ConservationIdleTime;
  178. }
  179. break;
  180. case POP_DISK_SPINDOWN:
  181. idlelimit = PopPolicy->SpindownTimeout;
  182. break;
  183. default:
  184. PopInternalAddToDumpFile( NULL, 0, pblock->DeviceObject, NULL, NULL, pblock );
  185. KeBugCheckEx( INTERNAL_POWER_ERROR,
  186. 0x200,
  187. POP_IDLE,
  188. (ULONG_PTR)pblock->DeviceObject,
  189. (ULONG_PTR)pblock );
  190. }
  191. if ((idlelimit > 0) && ((oldCount+1) == idlelimit)) {
  192. PowerState.DeviceState = pblock->State;
  193. PoRequestPowerIrp (
  194. pblock->DeviceObject,
  195. IRP_MN_SET_POWER,
  196. PowerState,
  197. NULL,
  198. NULL,
  199. NULL
  200. );
  201. }
  202. link = link->Flink;
  203. }
  204. PopUnlockDopeGlobal(OldIrql);
  205. return;
  206. }
  207. PDEVICE_OBJECT_POWER_EXTENSION
  208. PopGetDope (
  209. PDEVICE_OBJECT DeviceObject
  210. )
  211. {
  212. PDEVOBJ_EXTENSION Doe;
  213. PDEVICE_OBJECT_POWER_EXTENSION Dope;
  214. KIRQL OldIrql;
  215. Doe = (PDEVOBJ_EXTENSION) DeviceObject->DeviceObjectExtension;
  216. if (!Doe->Dope) {
  217. PopLockDopeGlobal(&OldIrql);
  218. if (!Doe->Dope) {
  219. Dope = (PDEVICE_OBJECT_POWER_EXTENSION)
  220. ExAllocatePoolWithTag(
  221. NonPagedPool,
  222. sizeof(DEVICE_OBJECT_POWER_EXTENSION),
  223. POP_DOPE_TAG
  224. );
  225. if (Dope) {
  226. RtlZeroMemory (Dope, sizeof(DEVICE_OBJECT_POWER_EXTENSION));
  227. Dope->DeviceObject = DeviceObject;
  228. Dope->State = PowerDeviceUnspecified;
  229. InitializeListHead(&(Dope->IdleList));
  230. InitializeListHead(&(Dope->NotifySourceList));
  231. InitializeListHead(&(Dope->NotifyTargetList));
  232. // force the signature to 0 so buildpowerchannel gets called
  233. Dope->PowerChannelSummary.Signature = (ULONG)0;
  234. InitializeListHead(&(Dope->PowerChannelSummary.NotifyList));
  235. Doe->Dope = Dope;
  236. }
  237. }
  238. PopUnlockDopeGlobal(OldIrql);
  239. }
  240. return Doe->Dope;
  241. }