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.

1437 lines
37 KiB

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