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.

609 lines
16 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. pnppower.c
  5. Abstract:
  6. This file contains the routines that integrate PnP and Power
  7. Author:
  8. Adrian J. Oney (AdriaO) 01-19-1999
  9. Revision History:
  10. Modified for nt kernel.
  11. --*/
  12. #include "pnpmgrp.h"
  13. //
  14. // Internal References
  15. //
  16. PWCHAR
  17. IopCaptureObjectName (
  18. IN PVOID Object
  19. );
  20. VOID
  21. IopFreePoDeviceNotifyListHead (
  22. PLIST_ENTRY ListHead
  23. );
  24. #ifdef ALLOC_PRAGMA
  25. #pragma alloc_text(PAGE, IopWarmEjectDevice)
  26. #pragma alloc_text(PAGELK, IoBuildPoDeviceNotifyList)
  27. #pragma alloc_text(PAGELK, IoFreePoDeviceNotifyList)
  28. #pragma alloc_text(PAGELK, IopFreePoDeviceNotifyListHead)
  29. #pragma alloc_text(PAGELK, IoGetPoNotifyParent)
  30. #pragma alloc_text(PAGELK, IoMovePoNotifyChildren)
  31. #pragma alloc_text(PAGELK, IopCaptureObjectName)
  32. #endif
  33. NTSTATUS
  34. IoBuildPoDeviceNotifyList (
  35. IN OUT PPO_DEVICE_NOTIFY_ORDER Order
  36. )
  37. {
  38. PLIST_ENTRY link;
  39. PPO_DEVICE_NOTIFY notify, parentnotify;
  40. PDEVICE_NODE node;
  41. PDEVICE_NODE parent;
  42. ULONG noLists, listIndex;
  43. PLIST_ENTRY notifyLists;
  44. LONG maxLevel, level;
  45. UCHAR orderLevel;
  46. PDEVICE_OBJECT nonPaged;
  47. PDEVICE_OBJECT current;
  48. PDEVICE_OBJECT next;
  49. LIST_ENTRY RebaseList;
  50. ULONG i;
  51. //
  52. // Block PnP operations like rebalance.
  53. //
  54. PiLockDeviceActionQueue();
  55. RtlZeroMemory(Order, sizeof (*Order));
  56. Order->DevNodeSequence = IoDeviceNodeTreeSequence;
  57. for (i=0; i <= PO_ORDER_MAXIMUM; i++) {
  58. KeInitializeEvent(&Order->OrderLevel[i].LevelReady,
  59. NotificationEvent,
  60. FALSE);
  61. InitializeListHead(&Order->OrderLevel[i].WaitSleep);
  62. InitializeListHead(&Order->OrderLevel[i].ReadySleep);
  63. InitializeListHead(&Order->OrderLevel[i].Pending);
  64. InitializeListHead(&Order->OrderLevel[i].Complete);
  65. InitializeListHead(&Order->OrderLevel[i].ReadyS0);
  66. InitializeListHead(&Order->OrderLevel[i].WaitS0);
  67. }
  68. InitializeListHead(&RebaseList);
  69. //
  70. // Allocate notification structures for all nodes, and determine
  71. // maximum depth.
  72. //
  73. level = -1;
  74. node = IopRootDeviceNode;
  75. while (node->Child) {
  76. node = node->Child;
  77. level += 1;
  78. }
  79. //
  80. // ADRIAO 01/12/1999 N.B. -
  81. //
  82. // Note that we include devices without the started flag. However, two
  83. // things prevent us from excluding devices that aren't started:
  84. // 1) We must be able to send power messages to a device we are warm
  85. // undocking.
  86. // 2) Many devices may not be started, that is no guarentee they are in D3!
  87. // For example, they could easily have a boot config, and PNP still
  88. // relies heavily on BIOS boot configs to keep us from placing hardware
  89. // ontop of other devices with boot configs we haven't found or started
  90. // yet!
  91. //
  92. maxLevel = level;
  93. while (node != IopRootDeviceNode) {
  94. notify = ExAllocatePoolWithTag (
  95. NonPagedPool,
  96. sizeof(PO_DEVICE_NOTIFY),
  97. IOP_DPWR_TAG
  98. );
  99. if (!notify) {
  100. PiUnlockDeviceActionQueue();
  101. return STATUS_INSUFFICIENT_RESOURCES;
  102. }
  103. RtlZeroMemory (notify, sizeof(PO_DEVICE_NOTIFY));
  104. ASSERT(node->Notify == NULL) ;
  105. node->Notify = notify;
  106. notify->Node = node;
  107. notify->DeviceObject = node->PhysicalDeviceObject;
  108. notify->TargetDevice = IoGetAttachedDevice(node->PhysicalDeviceObject);
  109. notify->DriverName = IopCaptureObjectName(notify->TargetDevice->DriverObject);
  110. notify->DeviceName = IopCaptureObjectName(notify->TargetDevice);
  111. ObReferenceObject (notify->DeviceObject);
  112. ObReferenceObject (notify->TargetDevice);
  113. orderLevel = 0;
  114. if (notify->TargetDevice->DeviceType != FILE_DEVICE_SCREEN &&
  115. notify->TargetDevice->DeviceType != FILE_DEVICE_VIDEO) {
  116. orderLevel |= PO_ORDER_NOT_VIDEO;
  117. }
  118. if (notify->TargetDevice->Flags & DO_POWER_PAGABLE) {
  119. orderLevel |= PO_ORDER_PAGABLE;
  120. }
  121. //
  122. // If this is a level 0 node it's in the root. Look for
  123. // non-bus stuff in the root as those guys need to be re-based
  124. // below everything else.
  125. //
  126. notify->OrderLevel = orderLevel;
  127. //
  128. // If the node is root-enumerated, put it on the rebase list so
  129. // we can mark all its children later.
  130. // If the node is a leaf node it is ready to receive Sx irps.
  131. // If it has children, it must wait for its children to complete their Sx irps.
  132. //
  133. //
  134. if ((level == 0) &&
  135. (node->InterfaceType != Internal) &&
  136. !(node->Flags & DNF_HAL_NODE)) {
  137. InsertHeadList(&RebaseList, &notify->Link);
  138. } else {
  139. ++Order->OrderLevel[orderLevel].DeviceCount;
  140. if (node->Child == NULL) {
  141. InsertHeadList(&Order->OrderLevel[orderLevel].ReadySleep, &notify->Link);
  142. } else {
  143. InsertHeadList(&Order->OrderLevel[orderLevel].WaitSleep, &notify->Link);
  144. }
  145. }
  146. //
  147. // Next node
  148. //
  149. if (node->Sibling) {
  150. node = node->Sibling;
  151. while (node->Child) {
  152. node = node->Child;
  153. level += 1;
  154. if (level > maxLevel) {
  155. maxLevel = level;
  156. }
  157. }
  158. } else {
  159. node = node->Parent;
  160. level -= 1;
  161. }
  162. }
  163. //
  164. // Rebase anything on the rebase list to be after the normal pnp stuff
  165. //
  166. while (!IsListEmpty(&RebaseList)) {
  167. link = RemoveHeadList(&RebaseList);
  168. notify = CONTAINING_RECORD (link, PO_DEVICE_NOTIFY, Link);
  169. //
  170. // Rebase this node
  171. //
  172. node = notify->Node;
  173. notify->OrderLevel |= PO_ORDER_ROOT_ENUM;
  174. ++Order->OrderLevel[notify->OrderLevel].DeviceCount;
  175. if (node->Child == NULL) {
  176. InsertHeadList(&Order->OrderLevel[notify->OrderLevel].ReadySleep, &notify->Link);
  177. } else {
  178. InsertHeadList(&Order->OrderLevel[notify->OrderLevel].WaitSleep, &notify->Link);
  179. }
  180. //
  181. // Now rebase all the node's children
  182. //
  183. parent = node;
  184. while (node->Child) {
  185. node = node->Child;
  186. }
  187. while (node != parent) {
  188. notify = node->Notify;
  189. if (notify) {
  190. RemoveEntryList(&notify->Link);
  191. --Order->OrderLevel[notify->OrderLevel].DeviceCount;
  192. notify->OrderLevel |= PO_ORDER_ROOT_ENUM;
  193. ++Order->OrderLevel[notify->OrderLevel].DeviceCount;
  194. if (node->Child == NULL) {
  195. InsertHeadList(&Order->OrderLevel[notify->OrderLevel].ReadySleep, &notify->Link);
  196. } else {
  197. InsertHeadList(&Order->OrderLevel[notify->OrderLevel].WaitSleep, &notify->Link);
  198. }
  199. }
  200. // next node
  201. if (node->Sibling) {
  202. node = node->Sibling;
  203. while (node->Child) {
  204. node = node->Child;
  205. }
  206. } else {
  207. node = node->Parent;
  208. }
  209. }
  210. }
  211. //
  212. // make one more pass through all the notify devices in order to count
  213. // the children of each parent. It would be nice if the PNP engine kept
  214. // track of the number of children in the devnode, but until that is done,
  215. // we need this second pass.
  216. //
  217. // Also make sure that each node's parent is an order level >= its children.
  218. //
  219. node = IopRootDeviceNode;
  220. while (node->Child) {
  221. node = node->Child;
  222. }
  223. while (node != IopRootDeviceNode) {
  224. if (node->Parent != IopRootDeviceNode) {
  225. parentnotify = node->Parent->Notify;
  226. parentnotify->ChildCount++;
  227. parentnotify->ActiveChild++;
  228. if (parentnotify->OrderLevel > node->Notify->OrderLevel) {
  229. //
  230. // The parent is a higher order level than its child. Move the
  231. // parent down to the same order as its child
  232. //
  233. RemoveEntryList(&parentnotify->Link);
  234. --Order->OrderLevel[parentnotify->OrderLevel].DeviceCount;
  235. parentnotify->OrderLevel = node->Notify->OrderLevel;
  236. ++Order->OrderLevel[parentnotify->OrderLevel].DeviceCount;
  237. InsertHeadList(&Order->OrderLevel[parentnotify->OrderLevel].WaitSleep, &parentnotify->Link);
  238. }
  239. }
  240. //
  241. // Next node
  242. //
  243. if (node->Sibling) {
  244. node = node->Sibling;
  245. while (node->Child) {
  246. node = node->Child;
  247. }
  248. } else {
  249. node = node->Parent;
  250. }
  251. }
  252. Order->WarmEjectPdoPointer = &IopWarmEjectPdo;
  253. //
  254. // The engine lock is release when the notify list is freed
  255. //
  256. return STATUS_SUCCESS;
  257. }
  258. PVOID
  259. IoGetPoNotifyParent(
  260. IN PPO_DEVICE_NOTIFY Notify
  261. )
  262. /*++
  263. Routine Description:
  264. Returns the notify structure of the specified device's parent.
  265. Arguments:
  266. Notify - Supplies the child device
  267. Return Value:
  268. Parent's notify structure if present
  269. NULL if there is no parent
  270. --*/
  271. {
  272. PDEVICE_NODE Node;
  273. Node = Notify->Node;
  274. if (Node->Parent != IopRootDeviceNode) {
  275. return(Node->Parent->Notify);
  276. } else {
  277. return(NULL);
  278. }
  279. }
  280. VOID
  281. IoMovePoNotifyChildren(
  282. IN PPO_DEVICE_NOTIFY Notify,
  283. IN PPO_DEVICE_NOTIFY_ORDER Order
  284. )
  285. /*++
  286. Routine Description:
  287. Removes any children of the supplied device that are at the
  288. same orderlevel as the supplied parent and reinserts them
  289. on the ReadyS0 list.
  290. Arguments:
  291. Notify - Supplies the device notify structure
  292. Orderr - Supplies the device notification order structure
  293. Return Value:
  294. None
  295. --*/
  296. {
  297. PDEVICE_NODE Node;
  298. PDEVICE_NODE Child;
  299. PPO_DEVICE_NOTIFY ChildNotify;
  300. PPO_NOTIFY_ORDER_LEVEL Level;
  301. Node = Notify->Node;
  302. Child = Node->Child;
  303. while (Child) {
  304. ChildNotify = Child->Notify;
  305. if (ChildNotify->OrderLevel == Notify->OrderLevel) {
  306. RemoveEntryList(&ChildNotify->Link);
  307. Level = &Order->OrderLevel[ChildNotify->OrderLevel];
  308. InsertTailList(&Level->ReadyS0, &ChildNotify->Link);
  309. }
  310. Child = Child->Sibling;
  311. }
  312. }
  313. VOID
  314. IopFreePoDeviceNotifyListHead (
  315. PLIST_ENTRY ListHead
  316. )
  317. {
  318. PLIST_ENTRY Link;
  319. PPO_DEVICE_NOTIFY Notify;
  320. PDEVICE_NODE Node;
  321. while (!IsListEmpty(ListHead)) {
  322. Link = RemoveHeadList(ListHead);
  323. Notify = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link);
  324. Node = (PDEVICE_NODE) Notify->Node;
  325. Node->Notify = NULL;
  326. ObDereferenceObject (Notify->DeviceObject);
  327. ObDereferenceObject (Notify->TargetDevice);
  328. if (Notify->DeviceName) {
  329. ExFreePool (Notify->DeviceName);
  330. }
  331. if (Notify->DriverName) {
  332. ExFreePool (Notify->DriverName);
  333. }
  334. ExFreePool(Notify);
  335. }
  336. }
  337. VOID
  338. IoFreePoDeviceNotifyList (
  339. IN OUT PPO_DEVICE_NOTIFY_ORDER Order
  340. )
  341. {
  342. ULONG i;
  343. PLIST_ENTRY ListHead;
  344. PLIST_ENTRY Link;
  345. PPO_DEVICE_NOTIFY Notify;
  346. if (Order->DevNodeSequence) {
  347. Order->DevNodeSequence = 0;
  348. PiUnlockDeviceActionQueue();
  349. }
  350. //
  351. // Free the resources from the notify list
  352. //
  353. for (i=0; i <= PO_ORDER_MAXIMUM; i++) {
  354. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].WaitSleep);
  355. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].ReadySleep);
  356. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].Pending);
  357. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].Complete);
  358. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].ReadyS0);
  359. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].WaitS0);
  360. }
  361. }
  362. PWCHAR
  363. IopCaptureObjectName (
  364. IN PVOID Object
  365. )
  366. {
  367. NTSTATUS Status;
  368. UCHAR Buffer[512];
  369. POBJECT_NAME_INFORMATION ObName;
  370. ULONG len;
  371. PWCHAR Name;
  372. ObName = (POBJECT_NAME_INFORMATION) Buffer;
  373. Status = ObQueryNameString (
  374. Object,
  375. ObName,
  376. sizeof (Buffer),
  377. &len
  378. );
  379. Name = NULL;
  380. if (NT_SUCCESS(Status) && ObName->Name.Buffer) {
  381. Name = ExAllocatePoolWithTag (
  382. NonPagedPool,
  383. ObName->Name.Length + sizeof(WCHAR),
  384. IOP_DPWR_TAG
  385. );
  386. if (Name) {
  387. memcpy (Name, ObName->Name.Buffer, ObName->Name.Length);
  388. Name[ObName->Name.Length/sizeof(WCHAR)] = 0;
  389. }
  390. }
  391. return Name;
  392. }
  393. NTSTATUS
  394. IopWarmEjectDevice(
  395. IN PDEVICE_OBJECT DeviceToEject,
  396. IN SYSTEM_POWER_STATE LightestSleepState
  397. )
  398. /*++
  399. Routine Description:
  400. This function is invoked to initiate a warm eject. The eject progresses
  401. from S1 to the passed in lightest sleep state.
  402. Arguments:
  403. DeviceToEject - The device to eject
  404. LightestSleepState - The lightest S state (at least S1) that the device
  405. may be ejected in. This might be S4 if we are truely
  406. low on power.
  407. Return Value:
  408. NTSTATUS value.
  409. --*/
  410. {
  411. NTSTATUS status;
  412. PAGED_CODE();
  413. //
  414. // Acquire the warm eject device lock. A warm eject requires we enter a
  415. // specific S-state, and two different devices may have conflicting options.
  416. // Therefore only one is allowed to occur at once.
  417. //
  418. status = KeWaitForSingleObject(
  419. &IopWarmEjectLock,
  420. Executive,
  421. KernelMode,
  422. FALSE,
  423. NULL
  424. );
  425. ASSERT(status == STATUS_SUCCESS) ;
  426. //
  427. // Acquire engine lock. We are not allowed to set or clear this field
  428. // unless we are under this lock.
  429. //
  430. PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
  431. //
  432. // Set the current Pdo to eject.
  433. //
  434. ASSERT(IopWarmEjectPdo == NULL);
  435. IopWarmEjectPdo = DeviceToEject;
  436. //
  437. // Release the engine lock.
  438. //
  439. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  440. //
  441. // Attempt to invalidate Po's cached notification list. This should cause
  442. // IoBuildPoDeviceNotifyList to be called at which time it will in theory
  443. // pickup the above placed warm eject Pdo.
  444. //
  445. // ADRIAO NOTE 01/07/1999 -
  446. // Actually, this whole IoDeviceNodeTreeSequence stuff isn't neccessary.
  447. // PnP will make no changes to the tree while the device tree lock is owned,
  448. // and it's owned for the duration of a power notification.
  449. //
  450. IoDeviceNodeTreeSequence++;
  451. //
  452. // Sleep...
  453. //
  454. status = NtInitiatePowerAction(
  455. PowerActionWarmEject,
  456. LightestSleepState,
  457. POWER_ACTION_QUERY_ALLOWED |
  458. POWER_ACTION_UI_ALLOWED,
  459. FALSE // Asynchronous == FALSE
  460. );
  461. //
  462. // Acquire the engine lock. We are not allowed to set or clear this field
  463. // unless we are under this lock.
  464. //
  465. PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
  466. //
  467. // Clear the current PDO to eject, and see if the Pdo was actually picked
  468. // up.
  469. //
  470. if (IopWarmEjectPdo) {
  471. if (NT_SUCCESS(status)) {
  472. //
  473. // If our device wasn't picked up, the return of
  474. // NtInitiatePowerAction should *not* be successful!
  475. //
  476. ASSERT(0);
  477. status = STATUS_UNSUCCESSFUL;
  478. }
  479. IopWarmEjectPdo = NULL;
  480. }
  481. //
  482. // Release the engine lock.
  483. //
  484. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  485. //
  486. // Release the warm eject device lock
  487. //
  488. KeSetEvent(
  489. &IopWarmEjectLock,
  490. IO_NO_INCREMENT,
  491. FALSE
  492. );
  493. return status;
  494. }