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.

1462 lines
39 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. PpProfile.c
  5. Abstract:
  6. Kernel-mode Plug and Play Manager Docking and Hardware Profile Support
  7. Routines.
  8. Author:
  9. Adrian J. Oney (AdriaO) June 1998
  10. Kenneth D. Ray (kenray) June 1998
  11. Revision History:
  12. --*/
  13. #include "pnpmgrp.h"
  14. //
  15. // ISSUE-2000/07/24-AdriaO - Header mess
  16. // We should not be including private headers from CM.
  17. //
  18. #undef ExAllocatePool
  19. #undef ExAllocatePoolWithQuota
  20. #include "..\config\cmp.h"
  21. #include "piprofile.h"
  22. #pragma hdrstop
  23. #ifdef ALLOC_PRAGMA
  24. #pragma alloc_text(INIT, PpProfileInit)
  25. #pragma alloc_text(PAGE, PpProfileBeginHardwareProfileTransition)
  26. #pragma alloc_text(PAGE, PpProfileIncludeInHardwareProfileTransition)
  27. #pragma alloc_text(PAGE, PpProfileQueryHardwareProfileChange)
  28. #pragma alloc_text(PAGE, PpProfileCommitTransitioningDock)
  29. #pragma alloc_text(PAGE, PpProfileCancelTransitioningDock)
  30. #pragma alloc_text(PAGE, PpProfileCancelHardwareProfileTransition)
  31. #pragma alloc_text(PAGE, PpProfileMarkAllTransitioningDocksEjected)
  32. #pragma alloc_text(PAGE, PpProfileProcessDockDeviceCapability)
  33. #pragma alloc_text(PAGE, PiProfileSendHardwareProfileCommit)
  34. #pragma alloc_text(PAGE, PiProfileSendHardwareProfileCancel)
  35. #pragma alloc_text(PAGE, PiProfileConvertFakeDockToRealDock)
  36. #pragma alloc_text(PAGE, PiProfileRetrievePreferredCallback)
  37. #pragma alloc_text(PAGE, PpProfileRetrievePreferredDockToEject)
  38. #pragma alloc_text(PAGE, PiProfileUpdateDeviceTree)
  39. #pragma alloc_text(PAGE, PiProfileUpdateDeviceTreeWorker)
  40. #pragma alloc_text(PAGE, PiProfileUpdateDeviceTreeCallback)
  41. #endif
  42. //
  43. // List of current dock devices, and the number of dockdevices.
  44. // Must hold PiProfileDeviceListLock to change these values.
  45. //
  46. LIST_ENTRY PiProfileDeviceListHead;
  47. ULONG PiProfileDeviceCount;
  48. KGUARDED_MUTEX PiProfileDeviceListLock;
  49. KSEMAPHORE PiProfileChangeSemaphore;
  50. BOOLEAN PiProfileChangeCancelRequired;
  51. LONG PiProfileDevicesInTransition;
  52. VOID
  53. PpProfileInit(
  54. VOID
  55. )
  56. /*++
  57. Routine Description:
  58. This routine initializes docking support for Win2K.
  59. Arguments:
  60. None.
  61. Return Value:
  62. Nope.
  63. --*/
  64. {
  65. //
  66. // Initialize the list of dock devices, and its lock.
  67. //
  68. InitializeListHead(&PiProfileDeviceListHead);
  69. KeInitializeGuardedMutex(&PiProfileDeviceListLock);
  70. PiProfileDeviceCount = 0;
  71. KeInitializeSemaphore(&PiProfileChangeSemaphore, 1, 1);
  72. }
  73. VOID
  74. PpProfileBeginHardwareProfileTransition(
  75. IN BOOLEAN SubsumeExistingDeparture
  76. )
  77. /*++
  78. Routine Description:
  79. This routine must be called before any dock devnodes can be marked for
  80. transition (ie arriving or departing). After calling this function,
  81. PpProfileIncludeInHardwareProfileTransition should be called for each dock
  82. that is appearing or disappearing.
  83. Functionally, this code acquires the profile change semaphore. Future
  84. changes in the life of the added dock devnodes cause it to be released.
  85. Arguments:
  86. SubsumeExistingDeparture - Set if we are ejecting the parent of a
  87. device that is still in the process of
  88. ejecting...
  89. Return Value:
  90. None.
  91. --*/
  92. {
  93. NTSTATUS status;
  94. if (SubsumeExistingDeparture) {
  95. //
  96. // We will already have queried in this case. Also, enumeration is
  97. // locked right now, so the appropriate devices found cannot disappear.
  98. // Assert everything is consistant.
  99. //
  100. ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  101. ASSERT(PiProfileDevicesInTransition != 0);
  102. return;
  103. }
  104. //
  105. // Take the profile change semaphore. We do this whenever a dock is
  106. // in our list, even if no query is going to occur.
  107. //
  108. status = KeWaitForSingleObject(
  109. &PiProfileChangeSemaphore,
  110. Executive,
  111. KernelMode,
  112. FALSE,
  113. NULL
  114. );
  115. ASSERT(status == STATUS_SUCCESS);
  116. }
  117. VOID
  118. PpProfileIncludeInHardwareProfileTransition(
  119. IN PDEVICE_NODE DeviceNode,
  120. IN PROFILE_STATUS ChangeInPresence
  121. )
  122. /*++
  123. Routine Description:
  124. This routine is called to mark a dock as "in transition", ie it is either
  125. disappearing or appearing, the results of which determine our final
  126. hardware profile state. After all the docks that are transitioning have
  127. been passed into this function, PiProfileQueryHardwareProfileChange is
  128. called.
  129. Arguments:
  130. DeviceNode - The dock devnode that is appearing or disappearing
  131. ChangeInPresence - Either DOCK_DEPARTING or DOCK_ARRIVING
  132. Return Value:
  133. Nope.
  134. --*/
  135. {
  136. PWCHAR deviceSerialNumber;
  137. PDEVICE_OBJECT deviceObject;
  138. NTSTATUS status;
  139. //
  140. // Verify we are under semaphore, we aren't marking the dock twice, and
  141. // our parameters are sensable.
  142. //
  143. ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  144. ASSERT(DeviceNode->DockInfo.DockStatus == DOCK_QUIESCENT);
  145. ASSERT((ChangeInPresence == DOCK_DEPARTING)||
  146. (ChangeInPresence == DOCK_ARRIVING));
  147. if (ChangeInPresence == DOCK_ARRIVING) {
  148. //
  149. // First, ensure this dock is a member of the dock list.
  150. //
  151. // ADRIAO N.B. 07/09/2000 -
  152. // We should move this into IopProcessNewDeviceNode, or perhaps
  153. // PipStartPhaseN.
  154. //
  155. if (IsListEmpty(&DeviceNode->DockInfo.ListEntry)) {
  156. //
  157. // Acquire the lock on the list of dock devices
  158. //
  159. KeAcquireGuardedMutex(&PiProfileDeviceListLock);
  160. //
  161. // Add this element to the head of the list
  162. //
  163. InsertHeadList(&PiProfileDeviceListHead,
  164. &DeviceNode->DockInfo.ListEntry);
  165. PiProfileDeviceCount++;
  166. //
  167. // Release the lock on the list of dock devices
  168. //
  169. KeReleaseGuardedMutex(&PiProfileDeviceListLock);
  170. }
  171. //
  172. // Retrieve the Serial Number from this dock device. We do this just
  173. // to test the BIOS today. Later we will be acquiring the information
  174. // to determine the profile we are *about* to enter.
  175. //
  176. deviceObject = DeviceNode->PhysicalDeviceObject;
  177. status = PpQuerySerialNumber(
  178. DeviceNode,
  179. &deviceSerialNumber
  180. );
  181. if (NT_SUCCESS(status) && (deviceSerialNumber != NULL)) {
  182. ExFreePool(deviceSerialNumber);
  183. }
  184. } else {
  185. //
  186. // DOCK_DEPARTING case, we must be a member of the dock list...
  187. //
  188. ASSERT(!IsListEmpty(&DeviceNode->DockInfo.ListEntry));
  189. }
  190. InterlockedIncrement(&PiProfileDevicesInTransition);
  191. DeviceNode->DockInfo.DockStatus = ChangeInPresence;
  192. }
  193. NTSTATUS
  194. PpProfileQueryHardwareProfileChange(
  195. IN BOOLEAN SubsumingExistingDeparture,
  196. IN PROFILE_NOTIFICATION_TIME InPnpEvent,
  197. OUT PPNP_VETO_TYPE VetoType,
  198. OUT PUNICODE_STRING VetoName OPTIONAL
  199. )
  200. /*++
  201. Routine Description:
  202. This function queries drivers to see if it is OK to exit the current
  203. hardware profile and enter next one (as determined by which docks have
  204. been marked). One of two functions should be used subsequently to this
  205. call:
  206. PpProfileCommitTransitioningDock
  207. (call when a dock has been successfully started or has disappeared)
  208. PpProfileCancelHardwareProfileTransition
  209. (call to abort a transition, say if a dock failed to start or a
  210. query returned failure for eject)
  211. Arguments:
  212. InPnpEvent - This argument indicates whether an operation is being done
  213. within the context of another PnpEvent or not. If not, we
  214. will queue such an event and block on it. If so, we cannot
  215. queue&block (we'd deadlock), so we do the query manually.
  216. VetoType - If this function returns false, this parameter will describe
  217. who failed the query profile change. The below optional
  218. parameter will contain the name of said vetoer.
  219. VetoName - This optional parameter will get the name of the vetoer (ie
  220. devinst, service name, application name, etc). If VetoName
  221. is supplied, the caller must free the buffer returned.
  222. Return Value:
  223. NTSTATUS.
  224. --*/
  225. {
  226. NTSTATUS status;
  227. BOOLEAN arrivingDockFound;
  228. PLIST_ENTRY listEntry;
  229. PDEVICE_NODE devNode;
  230. ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  231. //
  232. // Acquire the lock on the list of dock devices and determine whether any
  233. // dock devnodes are arriving.
  234. //
  235. KeAcquireGuardedMutex(&PiProfileDeviceListLock);
  236. ASSERT(PiProfileDevicesInTransition);
  237. arrivingDockFound = FALSE;
  238. for (listEntry = PiProfileDeviceListHead.Flink;
  239. listEntry != &(PiProfileDeviceListHead);
  240. listEntry = listEntry->Flink ) {
  241. devNode = CONTAINING_RECORD(listEntry,
  242. DEVICE_NODE,
  243. DockInfo.ListEntry);
  244. ASSERT((devNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE)&&
  245. (devNode->DockInfo.DockStatus != DOCK_EJECTIRP_COMPLETED));
  246. if (devNode->DockInfo.DockStatus == DOCK_ARRIVING) {
  247. arrivingDockFound = TRUE;
  248. }
  249. }
  250. //
  251. // Release the lock on the list of dock devices
  252. //
  253. KeReleaseGuardedMutex(&PiProfileDeviceListLock);
  254. if (SubsumingExistingDeparture) {
  255. ASSERT(PiProfileChangeCancelRequired);
  256. //
  257. // We're nesting. Work off the last query, and don't requery.
  258. //
  259. return STATUS_SUCCESS;
  260. }
  261. if (arrivingDockFound) {
  262. //
  263. // We currently don't actually query for hardware profile change on a
  264. // dock event as the user may have the lid closed. If we ever find a
  265. // piece of hardware that needs to be updated *prior* to actually
  266. // switching over, we will have to remove this bit of code.
  267. //
  268. PiProfileChangeCancelRequired = FALSE;
  269. return STATUS_SUCCESS;
  270. }
  271. IopDbgPrint((IOP_TRACE_LEVEL,
  272. "NTOSKRNL: Sending HW profile change [query]\n"));
  273. status = IopRequestHwProfileChangeNotification(
  274. (LPGUID) &GUID_HWPROFILE_QUERY_CHANGE,
  275. InPnpEvent,
  276. VetoType,
  277. VetoName
  278. );
  279. if (NT_SUCCESS(status)) {
  280. PiProfileChangeCancelRequired = TRUE;
  281. } else {
  282. PiProfileChangeCancelRequired = FALSE;
  283. }
  284. return status;
  285. }
  286. VOID
  287. PpProfileCommitTransitioningDock(
  288. IN PDEVICE_NODE DeviceNode,
  289. IN PROFILE_STATUS ChangeInPresence
  290. )
  291. /*++
  292. Routine Description:
  293. This routine finalized the state the specified device in the list of
  294. current dock devices and requests a Hardware Profile change.
  295. Arguments:
  296. DeviceNode - The dock devnode that has finished being started or removed.
  297. ChangeInPresence - Either DOCK_DEPARTING or DOCK_ARRIVING
  298. Return Value:
  299. None.
  300. --*/
  301. {
  302. NTSTATUS status;
  303. PDEVICE_OBJECT deviceObject;
  304. PWCHAR deviceSerialNumber;
  305. BOOLEAN profileChanged;
  306. LONG remainingDockCount;
  307. //
  308. // If we are commiting a dock, the transition list should not be empty.
  309. // all dock devices present, the list should not be empty.
  310. //
  311. ASSERT(!IsListEmpty(&DeviceNode->DockInfo.ListEntry));
  312. ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  313. if (ChangeInPresence == DOCK_DEPARTING) {
  314. ASSERT((DeviceNode->DockInfo.DockStatus == DOCK_DEPARTING) ||
  315. (DeviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED));
  316. //
  317. // Free up the serial number
  318. //
  319. if (DeviceNode->DockInfo.SerialNumber != NULL) {
  320. ExFreePool(DeviceNode->DockInfo.SerialNumber);
  321. DeviceNode->DockInfo.SerialNumber = NULL;
  322. }
  323. //
  324. // Acquire the lock on the list of dock devices
  325. //
  326. KeAcquireGuardedMutex(&PiProfileDeviceListLock);
  327. //
  328. // Remove the current devnode from the list of docks
  329. //
  330. RemoveEntryList(&DeviceNode->DockInfo.ListEntry);
  331. InitializeListHead(&DeviceNode->DockInfo.ListEntry);
  332. PiProfileDeviceCount--;
  333. //
  334. // Release the lock on the list of dock devices
  335. //
  336. KeReleaseGuardedMutex(&PiProfileDeviceListLock);
  337. } else {
  338. ASSERT(DeviceNode->DockInfo.DockStatus == DOCK_ARRIVING);
  339. //
  340. // We only add one dock at a time. So this should have been the last!
  341. //
  342. ASSERT(PiProfileDevicesInTransition == 1);
  343. //
  344. // Retrieve the Serial Number from this dock device if we don't already
  345. // have it.
  346. //
  347. if (DeviceNode->DockInfo.SerialNumber == NULL) {
  348. deviceObject = DeviceNode->PhysicalDeviceObject;
  349. status = PpQuerySerialNumber(
  350. DeviceNode,
  351. &deviceSerialNumber);
  352. DeviceNode->DockInfo.SerialNumber = deviceSerialNumber;
  353. }
  354. }
  355. DeviceNode->DockInfo.DockStatus = DOCK_QUIESCENT;
  356. remainingDockCount = InterlockedDecrement(&PiProfileDevicesInTransition);
  357. ASSERT(remainingDockCount >= 0);
  358. if (remainingDockCount) {
  359. return;
  360. }
  361. profileChanged = FALSE;
  362. if ((ChangeInPresence == DOCK_ARRIVING) &&
  363. (DeviceNode->DockInfo.SerialNumber == NULL)) {
  364. //
  365. // Couldn't get Serial Number for this dock device, or serial number
  366. // was NULL. We can make this check here as only one dock at a time
  367. // can currently arrive.
  368. //
  369. status = STATUS_UNSUCCESSFUL;
  370. goto BroadcastAndLeave;
  371. }
  372. //
  373. // Update the current Hardware Profile now that the transition list has
  374. // been emptied. This routine does two things for us:
  375. // 1) It determines whether the profile actually changed and updates
  376. // the global flag IopProfileChangeOccured appropriately.
  377. // 2) If the profile changed, this routine updates the registry.
  378. //
  379. status = PiProfileUpdateHardwareProfile(&profileChanged);
  380. if (!NT_SUCCESS(status)) {
  381. IopDbgPrint((IOP_TRACE_LEVEL,
  382. "PiProfileUpdateHardwareProfile failed with status == %lx\n", status));
  383. }
  384. BroadcastAndLeave:
  385. //
  386. // Clean up
  387. //
  388. if (NT_SUCCESS(status) && profileChanged) {
  389. PiProfileSendHardwareProfileCommit();
  390. PiProfileUpdateDeviceTree();
  391. } else if (PiProfileChangeCancelRequired) {
  392. PiProfileSendHardwareProfileCancel();
  393. }
  394. KeReleaseSemaphore(
  395. &PiProfileChangeSemaphore,
  396. IO_NO_INCREMENT,
  397. 1,
  398. FALSE
  399. );
  400. return;
  401. }
  402. VOID
  403. PpProfileCancelTransitioningDock(
  404. IN PDEVICE_NODE DeviceNode,
  405. IN PROFILE_STATUS ChangeInPresence
  406. )
  407. /*++
  408. Routine Description:
  409. This routine is called when a dock that was marked to disappear didn't (ie,
  410. after the eject, the dock device still enumerated). We remove it from the
  411. transition list and complete/cancel the HW profile change as appropriate.
  412. See PpProfileMarkAllTransitioningDocksEjected.
  413. Arguments:
  414. DeviceNode - The dock devnode that either didn't start or didn't disappear.
  415. ChangeInPresence - Either DOCK_DEPARTING or DOCK_ARRIVING
  416. N.B. - Currently only DOCK_DEPARTING is supported.
  417. Return Value:
  418. None.
  419. --*/
  420. {
  421. NTSTATUS status;
  422. BOOLEAN profileChanged;
  423. LONG remainingDockCount;
  424. #if !DBG
  425. UNREFERENCED_PARAMETER (ChangeInPresence);
  426. #endif
  427. ASSERT(ChangeInPresence == DOCK_DEPARTING);
  428. //
  429. // Acquire the lock on the list of dock devices
  430. //
  431. KeAcquireGuardedMutex(&PiProfileDeviceListLock);
  432. //
  433. // Since we are about to remove this dock device from the list of
  434. // all dock devices present, the list should not be empty.
  435. //
  436. ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  437. ASSERT(DeviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED);
  438. ASSERT(!IsListEmpty(&DeviceNode->DockInfo.ListEntry));
  439. DeviceNode->DockInfo.DockStatus = DOCK_QUIESCENT;
  440. remainingDockCount = InterlockedDecrement(&PiProfileDevicesInTransition);
  441. ASSERT(remainingDockCount >= 0);
  442. //
  443. // Release the lock on the list of dock devices
  444. //
  445. KeReleaseGuardedMutex(&PiProfileDeviceListLock);
  446. if (remainingDockCount) {
  447. return;
  448. }
  449. //
  450. // Update the current Hardware Profile after removing this device.
  451. //
  452. status = PiProfileUpdateHardwareProfile(&profileChanged);
  453. if (!NT_SUCCESS(status)) {
  454. //
  455. // So we're there physically, but not mentally? Too bad, where broadcasting
  456. // change either way.
  457. //
  458. IopDbgPrint((IOP_TRACE_LEVEL,
  459. "PiProfileUpdateHardwareProfile failed with status == %lx\n", status));
  460. ASSERT(NT_SUCCESS(status));
  461. }
  462. if (NT_SUCCESS(status) && profileChanged) {
  463. PiProfileSendHardwareProfileCommit();
  464. PiProfileUpdateDeviceTree();
  465. } else {
  466. ASSERT(PiProfileChangeCancelRequired);
  467. PiProfileSendHardwareProfileCancel();
  468. }
  469. KeReleaseSemaphore(
  470. &PiProfileChangeSemaphore,
  471. IO_NO_INCREMENT,
  472. 1,
  473. FALSE
  474. );
  475. return;
  476. }
  477. VOID
  478. PpProfileCancelHardwareProfileTransition(
  479. VOID
  480. )
  481. /*++
  482. Routine Description:
  483. This routine unmarks any marked devnodes (ie, sets them to no change,
  484. appearing or disappearing), and sends the CancelQueryProfileChange as
  485. appropriate. Once called, other profile changes can occur.
  486. Arguments:
  487. None.
  488. Return Value:
  489. Nodda.
  490. --*/
  491. {
  492. PLIST_ENTRY listEntry;
  493. PDEVICE_NODE devNode;
  494. ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  495. //
  496. // Acquire the lock on the list of dock devices
  497. //
  498. KeAcquireGuardedMutex(&PiProfileDeviceListLock);
  499. for (listEntry = PiProfileDeviceListHead.Flink;
  500. listEntry != &(PiProfileDeviceListHead);
  501. listEntry = listEntry->Flink ) {
  502. devNode = CONTAINING_RECORD(listEntry,
  503. DEVICE_NODE,
  504. DockInfo.ListEntry);
  505. ASSERT((devNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE)&&
  506. (devNode->DockInfo.DockStatus != DOCK_EJECTIRP_COMPLETED));
  507. if (devNode->DockInfo.DockStatus != DOCK_QUIESCENT) {
  508. InterlockedDecrement(&PiProfileDevicesInTransition);
  509. devNode->DockInfo.DockStatus = DOCK_QUIESCENT;
  510. }
  511. }
  512. ASSERT(!PiProfileDevicesInTransition);
  513. //
  514. // Release the lock on the list of dock devices
  515. //
  516. KeReleaseGuardedMutex(&PiProfileDeviceListLock);
  517. if (PiProfileChangeCancelRequired) {
  518. PiProfileSendHardwareProfileCancel();
  519. }
  520. KeReleaseSemaphore(
  521. &PiProfileChangeSemaphore,
  522. IO_NO_INCREMENT,
  523. 1,
  524. FALSE
  525. );
  526. }
  527. VOID
  528. PpProfileMarkAllTransitioningDocksEjected(
  529. VOID
  530. )
  531. /*++
  532. Routine Description:
  533. This routine moves any departing devnodes to the ejected state. If any
  534. subsequent enumeration lists the device as present, we know the eject
  535. failed and we appropriately cancel that piece of the profile change.
  536. PpProfileCancelTransitioningDock can only be called after this function
  537. is called.
  538. Arguments:
  539. None.
  540. Return Value:
  541. Nodda.
  542. --*/
  543. {
  544. PLIST_ENTRY listEntry;
  545. PDEVICE_NODE devNode;
  546. //
  547. // The semaphore might not be signalled if the dock was resurrected before
  548. // the eject completed. This can happen in warm undock scenarios where the
  549. // machine is resumed inside the dock instead of being detached.
  550. //
  551. //ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  552. //
  553. // Acquire the lock on the list of dock devices
  554. //
  555. KeAcquireGuardedMutex(&PiProfileDeviceListLock);
  556. for (listEntry = PiProfileDeviceListHead.Flink;
  557. listEntry != &(PiProfileDeviceListHead);
  558. listEntry = listEntry->Flink ) {
  559. devNode = CONTAINING_RECORD(listEntry,
  560. DEVICE_NODE,
  561. DockInfo.ListEntry);
  562. ASSERT((devNode->DockInfo.DockStatus == DOCK_QUIESCENT)||
  563. (devNode->DockInfo.DockStatus == DOCK_DEPARTING));
  564. if (devNode->DockInfo.DockStatus != DOCK_QUIESCENT) {
  565. devNode->DockInfo.DockStatus = DOCK_EJECTIRP_COMPLETED;
  566. }
  567. }
  568. //
  569. // Release the lock on the list of dock devices
  570. //
  571. KeReleaseGuardedMutex(&PiProfileDeviceListLock);
  572. }
  573. VOID
  574. PiProfileSendHardwareProfileCommit(
  575. VOID
  576. )
  577. /*++
  578. Routine Description:
  579. This routine (internal to ppdock.c) simply sends the change complete message.
  580. We do not wait for this, as it is asynchronous...
  581. Arguments:
  582. None.
  583. Return Value:
  584. Nodda.
  585. --*/
  586. {
  587. ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  588. IopDbgPrint((IOP_TRACE_LEVEL,
  589. "NTOSKRNL: Sending HW profile change [commit]\n"));
  590. IopRequestHwProfileChangeNotification(
  591. (LPGUID) &GUID_HWPROFILE_CHANGE_COMPLETE,
  592. PROFILE_PERHAPS_IN_PNPEVENT,
  593. NULL,
  594. NULL
  595. );
  596. }
  597. VOID
  598. PiProfileSendHardwareProfileCancel(
  599. VOID
  600. )
  601. /*++
  602. Routine Description:
  603. This routine (internal to ppdock.c) simply sends the cancel.
  604. Arguments:
  605. None.
  606. Return Value:
  607. Nodda.
  608. --*/
  609. {
  610. ASSERT_SEMA_NOT_SIGNALLED(&PiProfileChangeSemaphore);
  611. IopDbgPrint((IOP_TRACE_LEVEL,
  612. "NTOSKRNL: Sending HW profile change [cancel]\n"));
  613. IopRequestHwProfileChangeNotification(
  614. (LPGUID) &GUID_HWPROFILE_CHANGE_CANCELLED,
  615. PROFILE_PERHAPS_IN_PNPEVENT,
  616. NULL,
  617. NULL
  618. );
  619. }
  620. NTSTATUS
  621. PiProfileUpdateHardwareProfile(
  622. OUT BOOLEAN *ProfileChanged
  623. )
  624. /*++
  625. Routine Description:
  626. This routine scans the list of current dock devices, builds a list of serial
  627. numbers from those devices, and calls for the Hardware Profile to be
  628. changed, based on that list.
  629. Arguments:
  630. ProfileChanged - Supplies a variable to receive TRUE if the current hardware
  631. profile changes as a result of calling this routine.
  632. Return Value:
  633. NTSTATUS code.
  634. --*/
  635. {
  636. NTSTATUS status = STATUS_SUCCESS;
  637. PLIST_ENTRY listEntry;
  638. PDEVICE_NODE devNode;
  639. PWCHAR *profileSerialNumbers, *p;
  640. HANDLE hProfileKey=NULL;
  641. ULONG len, numProfiles;
  642. HANDLE hCurrent, hIDConfigDB;
  643. UNICODE_STRING unicodeName;
  644. //
  645. // Acquire the lock on the list of dock devices
  646. //
  647. KeAcquireGuardedMutex(&PiProfileDeviceListLock);
  648. //
  649. // Update the flag for Ejectable Docks (flag is the count of docks)
  650. //
  651. PiWstrToUnicodeString(&unicodeName, CM_HARDWARE_PROFILE_STR_DATABASE);
  652. if(NT_SUCCESS(IopOpenRegistryKeyEx(&hIDConfigDB,
  653. NULL,
  654. &unicodeName,
  655. KEY_READ) )) {
  656. PiWstrToUnicodeString(&unicodeName, CM_HARDWARE_PROFILE_STR_CURRENT_DOCK_INFO);
  657. if(NT_SUCCESS(IopOpenRegistryKeyEx(&hCurrent,
  658. hIDConfigDB,
  659. &unicodeName,
  660. KEY_READ | KEY_WRITE) )) {
  661. PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_EJECTABLE_DOCKS);
  662. ZwSetValueKey(hCurrent,
  663. &unicodeName,
  664. 0,
  665. REG_DWORD,
  666. &PiProfileDeviceCount,
  667. sizeof(PiProfileDeviceCount));
  668. ZwClose(hCurrent);
  669. }
  670. ZwClose(hIDConfigDB);
  671. }
  672. if (PiProfileDeviceCount == 0) {
  673. //
  674. // if there are no dock devices, the list should
  675. // contain a single null entry, in addition to the null
  676. // termination.
  677. //
  678. numProfiles = 1;
  679. ASSERT(IsListEmpty(&PiProfileDeviceListHead));
  680. } else {
  681. numProfiles = PiProfileDeviceCount;
  682. ASSERT(!IsListEmpty(&PiProfileDeviceListHead));
  683. }
  684. //
  685. // Allocate space for a null-terminated list of SerialNumber lists.
  686. //
  687. len = (numProfiles+1)*sizeof(PWCHAR);
  688. profileSerialNumbers = ExAllocatePool(NonPagedPool, len);
  689. if (profileSerialNumbers) {
  690. p = profileSerialNumbers;
  691. //
  692. // Create the list of Serial Numbers
  693. //
  694. for (listEntry = PiProfileDeviceListHead.Flink;
  695. listEntry != &(PiProfileDeviceListHead);
  696. listEntry = listEntry->Flink ) {
  697. devNode = CONTAINING_RECORD(listEntry,
  698. DEVICE_NODE,
  699. DockInfo.ListEntry);
  700. ASSERT(devNode->DockInfo.DockStatus == DOCK_QUIESCENT);
  701. if (devNode->DockInfo.SerialNumber) {
  702. *p = devNode->DockInfo.SerialNumber;
  703. p++;
  704. }
  705. }
  706. KeReleaseGuardedMutex(&PiProfileDeviceListLock);
  707. if (p == profileSerialNumbers) {
  708. //
  709. // Set a single list entry to NULL if we look to be in an "undocked"
  710. // profile
  711. //
  712. *p = NULL;
  713. p++;
  714. }
  715. //
  716. // Null-terminate the list
  717. //
  718. *p = NULL;
  719. numProfiles = (ULONG)(p - profileSerialNumbers);
  720. //
  721. // Change the current Hardware Profile based on the new Dock State
  722. // and perform notification that the Hardware Profile has changed
  723. //
  724. status = IopExecuteHardwareProfileChange(HardwareProfileBusTypeACPI,
  725. profileSerialNumbers,
  726. numProfiles,
  727. &hProfileKey,
  728. ProfileChanged);
  729. if (hProfileKey) {
  730. ZwClose(hProfileKey);
  731. }
  732. ExFreePool (profileSerialNumbers);
  733. } else {
  734. KeReleaseGuardedMutex(&PiProfileDeviceListLock);
  735. status = STATUS_INSUFFICIENT_RESOURCES;
  736. }
  737. return status;
  738. }
  739. PDEVICE_OBJECT
  740. PpProfileRetrievePreferredDockToEject(
  741. VOID
  742. )
  743. /*++
  744. Routine Description:
  745. This routine is called to retrieve the dock that should be ejected via
  746. start menu UI.
  747. Arguments:
  748. None.
  749. Return Value:
  750. Dock device object if one exists.
  751. ++*/
  752. {
  753. BEST_DOCK_TO_EJECT bestDock;
  754. //
  755. // Search for the Dock Nodes
  756. //
  757. bestDock.PhysicalDeviceObject = NULL;
  758. PipForAllDeviceNodes(PiProfileRetrievePreferredCallback, (PVOID)&bestDock);
  759. return bestDock.PhysicalDeviceObject;
  760. }
  761. PDEVICE_NODE
  762. PiProfileConvertFakeDockToRealDock(
  763. IN PDEVICE_NODE FakeDockDevnode
  764. )
  765. /*++
  766. Routine Description:
  767. Given a docking Physical Device Object for a fake dock, walk its ejection
  768. relations to find out the corresponding real dock node.
  769. Arguments:
  770. FakeDockDevnode - Fake Dock node
  771. Returns
  772. Real Dock (PDO referenced once), NULL if none.
  773. --*/
  774. {
  775. ULONG i;
  776. NTSTATUS status;
  777. PDEVICE_OBJECT devobj;
  778. PDEVICE_NODE devnode, realDock;
  779. PDEVICE_RELATIONS ejectRelations = NULL;
  780. IO_STACK_LOCATION irpSp;
  781. //
  782. // Obtain the list of ejection relations.
  783. //
  784. RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
  785. irpSp.MajorFunction = IRP_MJ_PNP;
  786. irpSp.MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
  787. irpSp.Parameters.QueryDeviceRelations.Type = EjectionRelations;
  788. status = IopSynchronousCall(
  789. FakeDockDevnode->PhysicalDeviceObject,
  790. &irpSp,
  791. (PULONG_PTR) &ejectRelations
  792. );
  793. if ((!NT_SUCCESS(status)) || (ejectRelations == NULL)) {
  794. return NULL;
  795. }
  796. //
  797. // Walk the eject relations looking for the depth.
  798. //
  799. realDock = NULL;
  800. for(i = 0; i < ejectRelations->Count; i++) {
  801. devobj = ejectRelations->Objects[i];
  802. //
  803. // The last ejection relation is the one that points to the
  804. // underlying physically enumerated device.
  805. //
  806. if (i == ejectRelations->Count-1) {
  807. devnode = (PDEVICE_NODE) devobj->DeviceObjectExtension->DeviceNode;
  808. //
  809. // The devnode might be NULL if:
  810. // 1) A driver make a mistake
  811. // 2) We got back an ejection relation on a newly created PDO
  812. // that hasn't made it's way back up to the OS (we don't
  813. // raise the tree lock to BlockReads while an enumeration
  814. // IRP is outstanding...)
  815. //
  816. if (devnode) {
  817. realDock = devnode;
  818. ObReferenceObject(devobj);
  819. }
  820. }
  821. ObDereferenceObject(devobj);
  822. }
  823. ExFreePool(ejectRelations);
  824. return realDock;
  825. }
  826. NTSTATUS
  827. PiProfileRetrievePreferredCallback(
  828. IN PDEVICE_NODE DeviceNode,
  829. IN PVOID Context
  830. )
  831. /*++
  832. Routine Description:
  833. Scan the list of device nodes for docks, and keep the one with the most
  834. attractive depth (ie, the one eject PC should select.)
  835. Arguments:
  836. DeviceNode - Possible docking station DevNode.
  837. Context - Pointer to the BEST_DOCK_TO_EJECT structure to fill in. The
  838. PhysicalDeviceObject pointer in this structure should be
  839. preinited to NULL. The located docking station PDO will be
  840. referenced.
  841. Returns:
  842. NTSTATUS (Unsuccessful status's stop the enumeration of devnodes)
  843. --*/
  844. {
  845. RTL_QUERY_REGISTRY_TABLE queryTable[2];
  846. PBEST_DOCK_TO_EJECT pBestDock;
  847. PDEVICE_NODE realDock, curDock;
  848. NTSTATUS status;
  849. ULONG dockDepth = 0;
  850. HANDLE hDeviceKey;
  851. //
  852. // Cast the context appropriately.
  853. //
  854. pBestDock = (PBEST_DOCK_TO_EJECT) Context;
  855. //
  856. // If it's not a dock device, we will ignore it...
  857. //
  858. if (!IopDeviceNodeFlagsToCapabilities(DeviceNode)->DockDevice) {
  859. //
  860. // Continue enumerating.
  861. //
  862. return STATUS_SUCCESS;
  863. }
  864. //
  865. // First get the corresponding real dock that goes with the fake dock
  866. // created by ACPI.
  867. //
  868. realDock = PiProfileConvertFakeDockToRealDock(DeviceNode);
  869. //
  870. // Search for overrides. Examine the real dock first, then the fake
  871. //
  872. curDock = realDock ? realDock : DeviceNode;
  873. while(1) {
  874. //
  875. // Examine the devnode for a specified ejection priority.
  876. //
  877. status = IoOpenDeviceRegistryKey(
  878. curDock->PhysicalDeviceObject,
  879. PLUGPLAY_REGKEY_DEVICE,
  880. KEY_READ,
  881. &hDeviceKey
  882. );
  883. if (NT_SUCCESS(status)) {
  884. RtlZeroMemory(queryTable, sizeof(queryTable));
  885. dockDepth = 0;
  886. queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
  887. queryTable[0].Name = (PWSTR) REGSTR_VAL_EJECT_PRIORITY;
  888. queryTable[0].EntryContext = &dockDepth;
  889. queryTable[0].DefaultType = REG_NONE;
  890. queryTable[0].DefaultData = NULL;
  891. queryTable[0].DefaultLength = 0;
  892. status = RtlQueryRegistryValues(
  893. RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
  894. hDeviceKey,
  895. queryTable,
  896. NULL,
  897. NULL
  898. );
  899. if (NT_SUCCESS(status)) {
  900. //
  901. // Promote manually specified priorities over inferred ones.
  902. // Note that we _add_ in 0x80000000 rather than _or_ it in.
  903. // This lets us wrap in case we ever need to specify a priority
  904. // lower than what's inferred.
  905. //
  906. dockDepth += 0x80000000;
  907. }
  908. ZwClose(hDeviceKey);
  909. }
  910. if (NT_SUCCESS(status) || (curDock == DeviceNode)) {
  911. break;
  912. }
  913. curDock = DeviceNode;
  914. }
  915. if (!NT_SUCCESS(status)) {
  916. //
  917. // If we can find no eject preference order, use the depth of the
  918. // dock devnode.
  919. //
  920. dockDepth = realDock ? realDock->Level : DeviceNode->Level;
  921. }
  922. if (realDock) {
  923. ObDereferenceObject(realDock->PhysicalDeviceObject);
  924. }
  925. //
  926. // The best dock is selected as the dock with the deepest ejected device.
  927. //
  928. if ((pBestDock->PhysicalDeviceObject == NULL) ||
  929. (dockDepth > pBestDock->Depth)) {
  930. if (pBestDock->PhysicalDeviceObject) {
  931. ObDereferenceObject(pBestDock->PhysicalDeviceObject);
  932. }
  933. pBestDock->PhysicalDeviceObject = DeviceNode->PhysicalDeviceObject;
  934. pBestDock->Depth = dockDepth;
  935. ObReferenceObject(pBestDock->PhysicalDeviceObject);
  936. }
  937. //
  938. // Continue enumerating.
  939. //
  940. return STATUS_SUCCESS;
  941. }
  942. NTSTATUS
  943. PiProfileUpdateDeviceTree(
  944. VOID
  945. )
  946. /*++
  947. Routine Description:
  948. This function is called after the system has transitioned into a new
  949. hardware profile. The thread from which it is called may be holding an
  950. enumeration lock. Calling this function does two tasks:
  951. 1) If a disabled devnode in the tree should be enabled in this new hardware
  952. profile state, it will be started.
  953. 2) If an enabled devnode in the tree should be disabled in this new hardware
  954. profile state, it will be (surprise) removed.
  955. ADRIAO N.B. 02/19/1999 -
  956. Why surprise remove? There are four cases to be handled:
  957. a) Dock disappearing, need to enable device in new profile
  958. b) Dock appearing, need to enable device in new profile
  959. c) Dock disappearing, need to disable device in new profile
  960. d) Dock appearing, need to disable device in new profile
  961. a) and b) are trivial. c) involves treating the appropriate devices as
  962. if they were in the removal relation lists for the dock. d) is another
  963. matter altogether as we need to query-remove/remove devices before
  964. starting another. NT5's PnP state machine cannot handle this, so for
  965. this release we cleanup rather hastily after the profile change.
  966. Parameters:
  967. NONE.
  968. Return Value:
  969. NTSTATUS.
  970. --*/
  971. {
  972. PWORK_QUEUE_ITEM workQueueItem;
  973. PAGED_CODE();
  974. workQueueItem = (PWORK_QUEUE_ITEM) ExAllocatePool(
  975. NonPagedPool,
  976. sizeof(WORK_QUEUE_ITEM)
  977. );
  978. if (workQueueItem) {
  979. //
  980. // Queue this up so we can walk the tree outside of the enumeration lock.
  981. //
  982. ExInitializeWorkItem(
  983. workQueueItem,
  984. PiProfileUpdateDeviceTreeWorker,
  985. workQueueItem
  986. );
  987. ExQueueWorkItem(
  988. workQueueItem,
  989. CriticalWorkQueue
  990. );
  991. return STATUS_SUCCESS;
  992. } else {
  993. return STATUS_INSUFFICIENT_RESOURCES;
  994. }
  995. }
  996. VOID
  997. PiProfileUpdateDeviceTreeWorker(
  998. IN PVOID Context
  999. )
  1000. /*++
  1001. Routine Description:
  1002. This function is called on a work thread by PiProfileUpdateDeviceTree
  1003. when the system has transitioned to a new hardware profile.
  1004. Parameters:
  1005. NONE.
  1006. Return Value:
  1007. NONE.
  1008. --*/
  1009. {
  1010. PAGED_CODE();
  1011. PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
  1012. PipForAllDeviceNodes(PiProfileUpdateDeviceTreeCallback, NULL);
  1013. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  1014. ExFreePool(Context);
  1015. }
  1016. NTSTATUS
  1017. PiProfileUpdateDeviceTreeCallback(
  1018. IN PDEVICE_NODE DeviceNode,
  1019. IN PVOID Context
  1020. )
  1021. /*++
  1022. Routine Description:
  1023. This function is called for each devnode after the system has transitioned
  1024. hardware profile states.
  1025. Parameters:
  1026. NONE.
  1027. Return Value:
  1028. NONE.
  1029. --*/
  1030. {
  1031. PDEVICE_NODE parentDevNode;
  1032. UNREFERENCED_PARAMETER( Context );
  1033. PAGED_CODE();
  1034. if (DeviceNode->State == DeviceNodeStarted) {
  1035. //
  1036. // Calling this function will disable the device if it is appropriate
  1037. // to do so.
  1038. //
  1039. if (!IopIsDeviceInstanceEnabled(NULL, &DeviceNode->InstancePath, FALSE)) {
  1040. PipRequestDeviceRemoval(DeviceNode, FALSE, CM_PROB_DISABLED);
  1041. }
  1042. } else if (((DeviceNode->State == DeviceNodeInitialized) ||
  1043. (DeviceNode->State == DeviceNodeRemoved)) &&
  1044. PipIsDevNodeProblem(DeviceNode, CM_PROB_DISABLED)) {
  1045. //
  1046. // We might be turning on the device. So we will clear the problem
  1047. // flags iff the device problem was CM_PROB_DISABLED. We must clear the
  1048. // problem code or otherwise IopIsDeviceInstanceEnabled will ignore us.
  1049. //
  1050. PipClearDevNodeProblem(DeviceNode);
  1051. //
  1052. // Make sure the device stays down iff appropriate.
  1053. //
  1054. if (IopIsDeviceInstanceEnabled(NULL, &DeviceNode->InstancePath, FALSE)) {
  1055. //
  1056. // This device should come back online. Bring it out of the
  1057. // removed state and queue up an enumeration at the parent level
  1058. // to resurrect him.
  1059. //
  1060. IopRestartDeviceNode(DeviceNode);
  1061. parentDevNode = DeviceNode->Parent;
  1062. IoInvalidateDeviceRelations(
  1063. parentDevNode->PhysicalDeviceObject,
  1064. BusRelations
  1065. );
  1066. } else {
  1067. //
  1068. // Restore the problem code.
  1069. //
  1070. PipSetDevNodeProblem(DeviceNode, CM_PROB_DISABLED);
  1071. }
  1072. } else {
  1073. ASSERT((!PipIsDevNodeProblem(DeviceNode, CM_PROB_DISABLED)) ||
  1074. ((DeviceNode->State == DeviceNodeAwaitingQueuedRemoval) ||
  1075. (DeviceNode->State == DeviceNodeAwaitingQueuedDeletion)));
  1076. }
  1077. return STATUS_SUCCESS;
  1078. }
  1079. VOID
  1080. PpProfileProcessDockDeviceCapability(
  1081. IN PDEVICE_NODE DeviceNode,
  1082. IN PDEVICE_CAPABILITIES Capabilities
  1083. )
  1084. {
  1085. PAGED_CODE();
  1086. if (Capabilities->DockDevice) {
  1087. if (DeviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED) {
  1088. ASSERT(DeviceNode->DockInfo.DockStatus != DOCK_EJECTIRP_COMPLETED);
  1089. PpProfileCancelTransitioningDock(DeviceNode, DOCK_DEPARTING);
  1090. }
  1091. DeviceNode->DockInfo.DockStatus = DOCK_QUIESCENT;
  1092. } else {
  1093. DeviceNode->DockInfo.DockStatus = DOCK_NOTDOCKDEVICE;
  1094. }
  1095. }