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.

2051 lines
59 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. fdopower.c
  5. Abstract:
  6. This module contains code to handle
  7. IRP_MJ_POWER dispatches for PCMCIA controllers
  8. Contains support routines for pc-card power management
  9. Authors:
  10. Ravisankar Pudipeddi (ravisp) May 30, 1997
  11. Neil Sandlin (neilsa) June 1, 1999
  12. Environment:
  13. Kernel mode only
  14. Notes:
  15. Revision History:
  16. Neil Sandlin (neilsa) April 16, 1999
  17. - split setpower into device and system, fixed synchronization
  18. --*/
  19. #include "pch.h"
  20. //
  21. // Internal References
  22. //
  23. NTSTATUS
  24. PcmciaFdoWaitWake(
  25. IN PDEVICE_OBJECT Fdo,
  26. IN PIRP Irp
  27. );
  28. NTSTATUS
  29. PcmciaFdoWaitWakeIoCompletion(
  30. IN PDEVICE_OBJECT Fdo,
  31. IN PIRP Irp,
  32. IN PVOID Context
  33. );
  34. NTSTATUS
  35. PcmciaFdoSaveControllerContext(
  36. IN PFDO_EXTENSION FdoExtension
  37. );
  38. NTSTATUS
  39. PcmciaFdoRestoreControllerContext(
  40. IN PFDO_EXTENSION FdoExtension
  41. );
  42. NTSTATUS
  43. PcmciaFdoSaveSocketContext(
  44. IN PSOCKET Socket
  45. );
  46. NTSTATUS
  47. PcmciaFdoRestoreSocketContext(
  48. IN PSOCKET Socket
  49. );
  50. NTSTATUS
  51. PcmciaSetFdoPowerState(
  52. IN PDEVICE_OBJECT Fdo,
  53. IN OUT PIRP Irp
  54. );
  55. NTSTATUS
  56. PcmciaSetFdoSystemPowerState(
  57. IN PDEVICE_OBJECT Fdo,
  58. IN OUT PIRP Irp
  59. );
  60. VOID
  61. PcmciaFdoSystemPowerDeviceIrpComplete(
  62. IN PDEVICE_OBJECT Fdo,
  63. IN UCHAR MinorFunction,
  64. IN POWER_STATE PowerState,
  65. IN PVOID Context,
  66. IN PIO_STATUS_BLOCK IoStatus
  67. );
  68. NTSTATUS
  69. PcmciaSetFdoDevicePowerState(
  70. IN PDEVICE_OBJECT Fdo,
  71. IN OUT PIRP Irp
  72. );
  73. NTSTATUS
  74. PcmciaFdoPowerWorker (
  75. IN PVOID Context,
  76. IN NTSTATUS Status
  77. );
  78. NTSTATUS
  79. PcmciaFdoDevicePowerCompletion(
  80. IN PDEVICE_OBJECT Fdo,
  81. IN PIRP Irp,
  82. IN PVOID Context
  83. );
  84. //
  85. //
  86. //
  87. NTSTATUS
  88. PcmciaFdoPowerDispatch(
  89. IN PDEVICE_OBJECT Fdo,
  90. IN PIRP Irp
  91. )
  92. /*++
  93. Routine Description:
  94. This routine handles power requests
  95. for the PDOs.
  96. Arguments:
  97. Pdo - pointer to the physical device object
  98. Irp - pointer to the io request packet
  99. Return Value:
  100. status
  101. --*/
  102. {
  103. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  104. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  105. NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
  106. switch (irpStack->MinorFunction) {
  107. case IRP_MN_SET_POWER: {
  108. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x --> IRP_MN_SET_POWER\n", Fdo, Irp));
  109. DebugPrint((PCMCIA_DEBUG_POWER, " (%s%x context %x)\n",
  110. (irpStack->Parameters.Power.Type == SystemPowerState) ?
  111. "S":
  112. ((irpStack->Parameters.Power.Type == DevicePowerState) ?
  113. "D" :
  114. "Unknown"),
  115. irpStack->Parameters.Power.State,
  116. irpStack->Parameters.Power.SystemContext
  117. ));
  118. status = PcmciaSetFdoPowerState(Fdo, Irp);
  119. break;
  120. }
  121. case IRP_MN_QUERY_POWER: {
  122. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x --> IRP_MN_QUERY_POWER\n", Fdo, Irp));
  123. DebugPrint((PCMCIA_DEBUG_POWER, " (%s%x context %x)\n",
  124. (irpStack->Parameters.Power.Type == SystemPowerState) ?
  125. "S":
  126. ((irpStack->Parameters.Power.Type == DevicePowerState) ?
  127. "D" :
  128. "Unknown"),
  129. irpStack->Parameters.Power.State,
  130. irpStack->Parameters.Power.SystemContext
  131. ));
  132. //
  133. // Let the pdo handle it
  134. //
  135. PoStartNextPowerIrp(Irp);
  136. IoSkipCurrentIrpStackLocation(Irp);
  137. status = PoCallDriver(fdoExtension->LowerDevice, Irp);
  138. break;
  139. }
  140. case IRP_MN_WAIT_WAKE: {
  141. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x --> IRP_MN_WAIT_WAKE\n", Fdo, Irp));
  142. status = PcmciaFdoWaitWake(Fdo, Irp);
  143. break;
  144. }
  145. default: {
  146. DebugPrint((PCMCIA_DEBUG_POWER, "FdoPowerDispatch: Unhandled Irp %x received for 0x%08x\n",
  147. Irp,
  148. Fdo));
  149. PoStartNextPowerIrp(Irp);
  150. IoSkipCurrentIrpStackLocation(Irp);
  151. status = PoCallDriver(fdoExtension->LowerDevice, Irp);
  152. break;
  153. }
  154. }
  155. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x <-- %08x\n", Fdo, Irp, status));
  156. return status;
  157. }
  158. /**************************************************************************
  159. WAKE ROUTINES
  160. **************************************************************************/
  161. NTSTATUS
  162. PcmciaFdoWaitWake(
  163. IN PDEVICE_OBJECT Fdo,
  164. IN PIRP Irp
  165. )
  166. /*++
  167. Routine Description
  168. Handles WAIT_WAKE for the given pcmcia controller
  169. Arguments
  170. Pdo - Pointer to the functional device object for the pcmcia controller
  171. Irp - The IRP_MN_WAIT_WAKE Irp
  172. Return Value
  173. STATUS_PENDING - Wait wake is pending
  174. STATUS_SUCCESS - Wake is already asserted, wait wake IRP is completed
  175. in this case
  176. Any other status - Error
  177. --*/
  178. {
  179. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  180. WAKESTATE oldWakeState;
  181. //
  182. // Record the wait wake Irp..
  183. //
  184. fdoExtension->WaitWakeIrp = Irp;
  185. oldWakeState = InterlockedCompareExchange(&fdoExtension->WaitWakeState,
  186. WAKESTATE_ARMED, WAKESTATE_WAITING);
  187. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %x irp %x WaitWake: prevState %s\n",
  188. Fdo, Irp, WAKESTATE_STRING(oldWakeState)));
  189. if (oldWakeState == WAKESTATE_WAITING_CANCELLED) {
  190. fdoExtension->WaitWakeState = WAKESTATE_COMPLETING;
  191. Irp->IoStatus.Status = STATUS_CANCELLED;
  192. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  193. return STATUS_CANCELLED;
  194. }
  195. IoMarkIrpPending(Irp);
  196. IoCopyCurrentIrpStackLocationToNext (Irp);
  197. //
  198. // Set our completion routine in the Irp..
  199. //
  200. IoSetCompletionRoutine(Irp,
  201. PcmciaFdoWaitWakeIoCompletion,
  202. Fdo,
  203. TRUE,
  204. TRUE,
  205. TRUE);
  206. //
  207. // now pass this down to the lower driver..
  208. //
  209. PoCallDriver(fdoExtension->LowerDevice, Irp);
  210. return STATUS_PENDING;
  211. }
  212. NTSTATUS
  213. PcmciaFdoWaitWakeIoCompletion(
  214. IN PDEVICE_OBJECT Fdo,
  215. IN PIRP Irp,
  216. IN PVOID Context
  217. )
  218. /*++
  219. Routine Description:
  220. Completion routine for the IRP_MN_WAIT_WAKE request for this
  221. pcmcia controller. This is called when the WAIT_WAKE IRP is
  222. completed by the lower driver (PCI/ACPI) indicating either that
  223. 1. PCMCIA controller asserted wake
  224. 2. WAIT_WAKE was cancelled
  225. 3. Lower driver returned an error for some reason
  226. Arguments:
  227. Fdo - Pointer to Functional device object for the pcmcia controller
  228. Irp - Pointer to the IRP for the power request (IRP_MN_WAIT_WAKE)
  229. Context - Not used
  230. Return Value:
  231. STATUS_SUCCESS - WAIT_WAKE was completed with success
  232. Any other status - Wake could be not be accomplished.
  233. --*/
  234. {
  235. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  236. PPDO_EXTENSION pdoExtension;
  237. PDEVICE_OBJECT pdo;
  238. WAKESTATE oldWakeState;
  239. UNREFERENCED_PARAMETER(Context);
  240. oldWakeState = InterlockedExchange(&fdoExtension->WaitWakeState, WAKESTATE_COMPLETING);
  241. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %x irp %x WW IoComp: prev=%s\n",
  242. Fdo, Irp, WAKESTATE_STRING(oldWakeState)));
  243. if (oldWakeState != WAKESTATE_ARMED) {
  244. ASSERT(oldWakeState == WAKESTATE_ARMING_CANCELLED);
  245. return STATUS_MORE_PROCESSING_REQUIRED;
  246. }
  247. if (IsFdoFlagSet(fdoExtension, PCMCIA_FDO_WAKE_BY_CD)) {
  248. POWER_STATE powerState;
  249. ResetFdoFlag(fdoExtension, PCMCIA_FDO_WAKE_BY_CD);
  250. PoStartNextPowerIrp(Irp);
  251. powerState.DeviceState = PowerDeviceD0;
  252. PoRequestPowerIrp(fdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL);
  253. } else {
  254. // NOTE:
  255. // At this point we do NOT know how to distinguish which function
  256. // in a multifunction device has asserted wake.
  257. // So we go through the entire list of PDOs hanging off this FDO
  258. // and complete all the outstanding WAIT_WAKE Irps for every PDO that
  259. // that's waiting. We leave it up to the FDO for the device to figure
  260. // if it asserted wake
  261. //
  262. for (pdo = fdoExtension->PdoList; pdo != NULL ; pdo = pdoExtension->NextPdoInFdoChain) {
  263. pdoExtension = pdo->DeviceExtension;
  264. if (IsDeviceLogicallyRemoved(pdoExtension) ||
  265. IsDevicePhysicallyRemoved(pdoExtension)) {
  266. //
  267. // This pdo is about to be removed ..
  268. // skip it
  269. //
  270. continue;
  271. }
  272. if (pdoExtension->WaitWakeIrp != NULL) {
  273. PIRP finishedIrp;
  274. //
  275. // Ah.. this is a possible candidate to have asserted the wake
  276. //
  277. //
  278. // Make sure this IRP will not be completed again or cancelled
  279. //
  280. finishedIrp = pdoExtension->WaitWakeIrp;
  281. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %x WW IoComp: irp %08x for pdo %08x\n",
  282. Fdo, finishedIrp, pdo));
  283. IoSetCancelRoutine(finishedIrp, NULL);
  284. //
  285. // Propagate parent's status to child
  286. //
  287. PoStartNextPowerIrp(finishedIrp);
  288. finishedIrp->IoStatus.Status = Irp->IoStatus.Status;
  289. //
  290. // Since we didn't pass this IRP down, call our own completion routine
  291. //
  292. PcmciaPdoWaitWakeCompletion(pdo, finishedIrp, pdoExtension);
  293. IoCompleteRequest(finishedIrp, IO_NO_INCREMENT);
  294. }
  295. }
  296. PoStartNextPowerIrp(Irp);
  297. }
  298. return Irp->IoStatus.Status;
  299. }
  300. VOID
  301. PcmciaFdoWaitWakePoCompletion(
  302. IN PDEVICE_OBJECT Fdo,
  303. IN UCHAR MinorFunction,
  304. IN POWER_STATE PowerState,
  305. IN PVOID Context,
  306. IN PIO_STATUS_BLOCK IoStatus
  307. )
  308. /*++
  309. Routine Description
  310. This routine is called on completion of a D irp generated by an S irp.
  311. Parameters
  312. DeviceObject - Pointer to the Fdo for the PCMCIA controller
  313. MinorFunction - Minor function of the IRP_MJ_POWER request
  314. PowerState - Power state requested
  315. Context - Context passed in to the completion routine
  316. IoStatus - Pointer to the status block which will contain
  317. the returned status
  318. Return Value
  319. Status
  320. --*/
  321. {
  322. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  323. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %x irp %x WaitWakePoCompletion: prevState %s\n",
  324. Fdo, fdoExtension->WaitWakeIrp,
  325. WAKESTATE_STRING(fdoExtension->WaitWakeState)));
  326. ASSERT (fdoExtension->WaitWakeIrp);
  327. fdoExtension->WaitWakeIrp = NULL;
  328. ASSERT (fdoExtension->WaitWakeState == WAKESTATE_COMPLETING);
  329. fdoExtension->WaitWakeState = WAKESTATE_DISARMED;
  330. }
  331. NTSTATUS
  332. PcmciaFdoArmForWake(
  333. PFDO_EXTENSION FdoExtension
  334. )
  335. /*++
  336. Routine Description:
  337. This routine is called to enable the controller for wake. It is called by the Pdo
  338. wake routines when a wake-enabled controller gets a wait-wake irp, and also by
  339. the idle routine to arm for wake from D3 by card insertion.
  340. Arguments:
  341. FdoExtension - device extension of the controller
  342. Return Value:
  343. status
  344. --*/
  345. {
  346. NTSTATUS status = STATUS_PENDING;
  347. PIO_STACK_LOCATION irpStack;
  348. PIRP irp;
  349. LONG oldWakeState;
  350. POWER_STATE powerState;
  351. oldWakeState = InterlockedCompareExchange(&FdoExtension->WaitWakeState,
  352. WAKESTATE_WAITING, WAKESTATE_DISARMED);
  353. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %x ArmForWake: prevState %s\n",
  354. FdoExtension->DeviceObject, WAKESTATE_STRING(oldWakeState)));
  355. if ((oldWakeState == WAKESTATE_ARMED) || (oldWakeState == WAKESTATE_WAITING)) {
  356. return STATUS_SUCCESS;
  357. }
  358. if (oldWakeState != WAKESTATE_DISARMED) {
  359. return STATUS_UNSUCCESSFUL;
  360. }
  361. powerState.SystemState = FdoExtension->DeviceCapabilities.SystemWake;
  362. status = PoRequestPowerIrp(FdoExtension->DeviceObject,
  363. IRP_MN_WAIT_WAKE,
  364. powerState,
  365. PcmciaFdoWaitWakePoCompletion,
  366. NULL,
  367. NULL);
  368. if (!NT_SUCCESS(status)) {
  369. FdoExtension->WaitWakeState = WAKESTATE_DISARMED;
  370. DebugPrint((PCMCIA_DEBUG_POWER, "WaitWake to FDO, expecting STATUS_PENDING, got %08X\n", status));
  371. }
  372. return status;
  373. }
  374. NTSTATUS
  375. PcmciaFdoDisarmWake(
  376. PFDO_EXTENSION FdoExtension
  377. )
  378. /*++
  379. Routine Description:
  380. This routine is called to disable the controller for wake.
  381. Arguments:
  382. FdoExtension - device extension of the controller
  383. Return Value:
  384. status
  385. --*/
  386. {
  387. WAKESTATE oldWakeState;
  388. oldWakeState = InterlockedCompareExchange(&FdoExtension->WaitWakeState,
  389. WAKESTATE_WAITING_CANCELLED, WAKESTATE_WAITING);
  390. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %x DisarmWake: prevState %s\n",
  391. FdoExtension->DeviceObject, WAKESTATE_STRING(oldWakeState)));
  392. if (oldWakeState != WAKESTATE_WAITING) {
  393. oldWakeState = InterlockedCompareExchange(&FdoExtension->WaitWakeState,
  394. WAKESTATE_ARMING_CANCELLED, WAKESTATE_ARMED);
  395. if (oldWakeState != WAKESTATE_ARMED) {
  396. return STATUS_UNSUCCESSFUL;
  397. }
  398. }
  399. if (oldWakeState == WAKESTATE_ARMED) {
  400. IoCancelIrp(FdoExtension->WaitWakeIrp);
  401. //
  402. // Now that we've cancelled the IRP, try to give back ownership
  403. // to the completion routine by restoring the WAKESTATE_ARMED state
  404. //
  405. oldWakeState = InterlockedCompareExchange(&FdoExtension->WaitWakeState,
  406. WAKESTATE_ARMED, WAKESTATE_ARMING_CANCELLED);
  407. if (oldWakeState == WAKESTATE_COMPLETING) {
  408. //
  409. // We didn't give control back of the IRP in time, we we own it now
  410. //
  411. IoCompleteRequest(FdoExtension->WaitWakeIrp, IO_NO_INCREMENT);
  412. }
  413. }
  414. return STATUS_SUCCESS;
  415. }
  416. NTSTATUS
  417. PcmciaFdoCheckForIdle(
  418. IN PFDO_EXTENSION FdoExtension
  419. )
  420. {
  421. POWER_STATE powerState;
  422. NTSTATUS status;
  423. PSOCKET socket;
  424. if (!(PcmciaPowerPolicy & PCMCIA_PP_D3_ON_IDLE)) {
  425. return STATUS_SUCCESS;
  426. }
  427. //
  428. // Make sure all sockets are empty
  429. //
  430. for (socket = FdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
  431. if (IsCardInSocket(socket)) {
  432. return STATUS_UNSUCCESSFUL;
  433. }
  434. }
  435. //
  436. // Arm for wakeup
  437. //
  438. status = PcmciaFdoArmForWake(FdoExtension);
  439. if (!NT_SUCCESS(status)) {
  440. return status;
  441. }
  442. SetFdoFlag(FdoExtension, PCMCIA_FDO_WAKE_BY_CD);
  443. powerState.DeviceState = PowerDeviceD3;
  444. PoRequestPowerIrp(FdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL);
  445. return STATUS_SUCCESS;
  446. }
  447. /**************************************************************************
  448. POWER ROUTINES
  449. **************************************************************************/
  450. NTSTATUS
  451. PcmciaSetFdoPowerState(
  452. IN PDEVICE_OBJECT Fdo,
  453. IN OUT PIRP Irp
  454. )
  455. /*++
  456. Routine Description
  457. Dispatches the IRP based on whether a system power state
  458. or device power state transition is requested
  459. Arguments
  460. DeviceObject - Pointer to the functional device object for the pcmcia controller
  461. Irp - Pointer to the Irp for the power dispatch
  462. Return value
  463. status
  464. --*/
  465. {
  466. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  467. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  468. NTSTATUS status;
  469. if (irpStack->Parameters.Power.Type == DevicePowerState) {
  470. status = PcmciaSetFdoDevicePowerState(Fdo, Irp);
  471. } else if (irpStack->Parameters.Power.Type == SystemPowerState) {
  472. status = PcmciaSetFdoSystemPowerState(Fdo, Irp);
  473. } else {
  474. status = Irp->IoStatus.Status;
  475. PoStartNextPowerIrp (Irp);
  476. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  477. }
  478. return status;
  479. }
  480. NTSTATUS
  481. PcmciaSetFdoSystemPowerState(
  482. IN PDEVICE_OBJECT Fdo,
  483. IN OUT PIRP Irp
  484. )
  485. /*++
  486. Routine Description
  487. Handles system power state IRPs for the pccard controller.
  488. Arguments
  489. DeviceObject - Pointer to the functional device object for the pcmcia controller
  490. Irp - Pointer to the Irp for the power dispatch
  491. Return value
  492. status
  493. --*/
  494. {
  495. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  496. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  497. SYSTEM_POWER_STATE newSystemState = irpStack->Parameters.Power.State.SystemState;
  498. NTSTATUS status = STATUS_SUCCESS;
  499. POWER_STATE powerState;
  500. ASSERT(irpStack->Parameters.Power.Type == SystemPowerState);
  501. //
  502. // Find the device power state corresponding to this system state
  503. //
  504. if (newSystemState >= PowerSystemHibernate) {
  505. //
  506. // Turn device off beyond hibernate..
  507. //
  508. powerState.DeviceState = PowerDeviceD3;
  509. } else {
  510. //
  511. // Switch to the appropriate device power state
  512. //
  513. powerState.DeviceState = fdoExtension->DeviceCapabilities.DeviceState[newSystemState];
  514. if (powerState.DeviceState == PowerDeviceUnspecified) {
  515. //
  516. // Capabilities not obtained?
  517. // do the best we can
  518. //
  519. // Working --> D0
  520. // otherwise power it off
  521. //
  522. if (newSystemState == PowerSystemWorking) {
  523. powerState.DeviceState = PowerDeviceD0;
  524. } else {
  525. powerState.DeviceState = PowerDeviceD3;
  526. }
  527. }
  528. // NOTE: HACKHACK:
  529. //
  530. // This hack is available to work around a BIOS bug. The way that WOL is supposed to work
  531. // is that, after the device causes the wake, then the BIOS should run a method which
  532. // issues a "notify(,0x2)", and thus prompting ACPI to complete the wait-wake IRP. If the
  533. // W/W IRP is completed, then this allows the device state to be cleared before repowering
  534. // the device.
  535. //
  536. // If the device state is not cleared, then we get an interrupt storm. This happens because
  537. // when PCI.SYS switches the device to D0, then the PME# which was asserted to wake the system
  538. // is still firing, which becomes a cardbus STSCHG interrupt, which asserts the PCI IRQ. But
  539. // the act of switching the device to D0 has cleared the socket register BAR, so now the ISR
  540. // can't clear the interrupt.
  541. //
  542. // The risk of forcing the device to D0 while going to standby is that the machine may be
  543. // designed such that cardbus bridge may not function. So this should only be applied when
  544. // we know that it will work.
  545. //
  546. if ((PcmciaPowerPolicy & PCMCIA_PP_WAKE_FROM_D0) &&
  547. (powerState.DeviceState != PowerDeviceD0) && (fdoExtension->WaitWakeState != WAKESTATE_DISARMED) &&
  548. (newSystemState < PowerSystemHibernate)) {
  549. powerState.DeviceState = PowerDeviceD0; // force D0
  550. }
  551. }
  552. //
  553. // Transitioned to system state
  554. //
  555. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x transition S state %d => %d, sending D%d\n",
  556. Fdo, Irp, fdoExtension->SystemPowerState-1, newSystemState-1, powerState.DeviceState-1));
  557. fdoExtension->SystemPowerState = newSystemState;
  558. //
  559. // Send a D IRP to the cardbus controller stack if necessary
  560. //
  561. if ((powerState.DeviceState > PowerDeviceUnspecified) &&
  562. (powerState.DeviceState != fdoExtension->DevicePowerState)) {
  563. if (powerState.DeviceState == PowerDeviceD0) {
  564. //
  565. // Powering up, optimize by letting the S irp complete immediately
  566. //
  567. PoRequestPowerIrp(fdoExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL);
  568. PoSetPowerState (Fdo, SystemPowerState, irpStack->Parameters.Power.State);
  569. //
  570. // Send the S IRP to the pdo
  571. //
  572. PoStartNextPowerIrp (Irp);
  573. IoSkipCurrentIrpStackLocation(Irp);
  574. status = PoCallDriver(fdoExtension->LowerDevice, Irp);
  575. } else {
  576. IoMarkIrpPending(Irp);
  577. status = PoRequestPowerIrp(fdoExtension->DeviceObject,
  578. IRP_MN_SET_POWER,
  579. powerState,
  580. PcmciaFdoSystemPowerDeviceIrpComplete,
  581. Irp,
  582. NULL
  583. );
  584. if (status != STATUS_PENDING) {
  585. //
  586. // Probably low memory failure
  587. //
  588. ASSERT( !NT_SUCCESS(status) );
  589. Irp->IoStatus.Status = status;
  590. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  591. //
  592. // We've already marked the IRP pending, so we must return STATUS_PENDING
  593. // (ie fail it asynchronously)
  594. //
  595. status = STATUS_PENDING;
  596. }
  597. }
  598. } else {
  599. PoSetPowerState (Fdo, SystemPowerState, irpStack->Parameters.Power.State);
  600. //
  601. // Send the S IRP to the pdo
  602. //
  603. PoStartNextPowerIrp (Irp);
  604. IoSkipCurrentIrpStackLocation(Irp);
  605. status = PoCallDriver(fdoExtension->LowerDevice, Irp);
  606. }
  607. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x <-- %08x\n", Fdo, Irp, status));
  608. return status;
  609. }
  610. VOID
  611. PcmciaFdoSystemPowerDeviceIrpComplete(
  612. IN PDEVICE_OBJECT Fdo,
  613. IN UCHAR MinorFunction,
  614. IN POWER_STATE PowerState,
  615. IN PVOID Context,
  616. IN PIO_STATUS_BLOCK IoStatus
  617. )
  618. /*++
  619. Routine Description
  620. This routine is called on completion of a D irp generated by an S irp.
  621. Parameters
  622. DeviceObject - Pointer to the Fdo for the PCMCIA controller
  623. MinorFunction - Minor function of the IRP_MJ_POWER request
  624. PowerState - Power state requested
  625. Context - Context passed in to the completion routine
  626. IoStatus - Pointer to the status block which will contain
  627. the returned status
  628. Return Value
  629. Status
  630. --*/
  631. {
  632. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  633. PIRP Irp = Context;
  634. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  635. ASSERT(NT_SUCCESS(IoStatus->Status));
  636. PoSetPowerState (Fdo, SystemPowerState, irpStack->Parameters.Power.State);
  637. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x request for D%d complete, passing S irp down\n",
  638. Fdo, Irp, PowerState.DeviceState-1));
  639. //
  640. // Send the S IRP to the pdo
  641. //
  642. PoStartNextPowerIrp (Irp);
  643. IoSkipCurrentIrpStackLocation(Irp);
  644. PoCallDriver(fdoExtension->LowerDevice, Irp);
  645. }
  646. NTSTATUS
  647. PcmciaSetFdoDevicePowerState (
  648. IN PDEVICE_OBJECT Fdo,
  649. IN OUT PIRP Irp
  650. )
  651. /*++
  652. Routine Description
  653. Handles device power state IRPs for the pccard controller.
  654. Arguments
  655. DeviceObject - Pointer to the functional device object for the pcmcia controller
  656. Irp - Pointer to the Irp for the power dispatch
  657. Return value
  658. status
  659. --*/
  660. {
  661. NTSTATUS status;
  662. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  663. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  664. if ((fdoExtension->PendingPowerIrp != NULL) || (fdoExtension->PowerWorkerState != FPW_Stopped)) {
  665. //
  666. // oops. We already have a pending irp.
  667. //
  668. ASSERT(fdoExtension->PendingPowerIrp == NULL);
  669. status = STATUS_DEVICE_BUSY;
  670. Irp->IoStatus.Status = status;
  671. PoStartNextPowerIrp (Irp);
  672. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  673. } else {
  674. fdoExtension->PendingPowerIrp = Irp;
  675. if (irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0) {
  676. fdoExtension->PowerWorkerState = FPW_BeginPowerDown;
  677. } else {
  678. fdoExtension->PowerWorkerState = FPW_BeginPowerUp;
  679. }
  680. status = PcmciaFdoPowerWorker(Fdo, STATUS_SUCCESS);
  681. }
  682. return status;
  683. }
  684. VOID
  685. MoveToNextFdoPowerWorkerState(
  686. PFDO_EXTENSION fdoExtension,
  687. LONG increment
  688. )
  689. /*++
  690. Routine Description
  691. This routine controls the sequencing of FDO power worker.
  692. Initially, the state must be set to one of two states, namely BeginPowerDown
  693. or BeginPowerUp. From there, this routine defines the list of states to follow.
  694. The parameter "increment" is normally the value '1'. Other values are used
  695. to modify the normal sequence. For example, '-1' backs the engine up 1 step.
  696. Use FPW_END_SEQUENCE to skip to the end of the sequence.
  697. Arguments
  698. Return Value
  699. status
  700. --*/
  701. {
  702. //
  703. // NOTE!: code in power worker dependent on the following state sequences
  704. // in PowerUpSequence remaining adjacent:
  705. //
  706. // FPW_PowerUpSocket : FPW_PowerUpSocketVerify : FPW_PowerUpComplete
  707. //
  708. // FPW_IrpComplete : FPW_Stopped
  709. //
  710. static FDO_POWER_WORKER_STATE PowerUpSequence[] = {
  711. FPW_SendIrpDown,
  712. FPW_PowerUp,
  713. FPW_PowerUpSocket,
  714. FPW_PowerUpSocket2,
  715. FPW_PowerUpSocketVerify,
  716. FPW_PowerUpSocketComplete,
  717. FPW_PowerUpComplete,
  718. FPW_CompleteIrp,
  719. FPW_Stopped
  720. };
  721. static FDO_POWER_WORKER_STATE PowerDownSequence[] = {
  722. FPW_PowerDown,
  723. FPW_PowerDownSocket,
  724. FPW_PowerDownComplete,
  725. FPW_SendIrpDown,
  726. FPW_CompleteIrp,
  727. FPW_Stopped
  728. };
  729. static FDO_POWER_WORKER_STATE NoOpSequence[] = {
  730. FPW_SendIrpDown,
  731. FPW_CompleteIrp,
  732. FPW_Stopped
  733. };
  734. if (fdoExtension->PowerWorkerState == FPW_BeginPowerDown) {
  735. //
  736. // Initialize sequence and phase
  737. //
  738. fdoExtension->PowerWorkerPhase = (UCHAR) -1;
  739. if (fdoExtension->DevicePowerState == PowerDeviceD0) {
  740. fdoExtension->PowerWorkerSequence = PowerDownSequence;
  741. fdoExtension->PowerWorkerMaxPhase = sizeof(PowerDownSequence)/sizeof(FDO_POWER_WORKER_STATE) - 1;
  742. } else {
  743. fdoExtension->PowerWorkerSequence = NoOpSequence;
  744. fdoExtension->PowerWorkerMaxPhase = sizeof(NoOpSequence)/sizeof(FDO_POWER_WORKER_STATE) - 1;
  745. }
  746. } else if (fdoExtension->PowerWorkerState == FPW_BeginPowerUp) {
  747. //
  748. // Initialize sequence and phase
  749. //
  750. fdoExtension->PowerWorkerPhase = (UCHAR) -1;
  751. if (fdoExtension->DevicePowerState > PowerDeviceD0) {
  752. fdoExtension->PowerWorkerSequence = PowerUpSequence;
  753. fdoExtension->PowerWorkerMaxPhase = sizeof(PowerUpSequence)/sizeof(FDO_POWER_WORKER_STATE) - 1;
  754. } else {
  755. fdoExtension->PowerWorkerSequence = NoOpSequence;
  756. fdoExtension->PowerWorkerMaxPhase = sizeof(NoOpSequence)/sizeof(FDO_POWER_WORKER_STATE) - 1;
  757. }
  758. }
  759. //
  760. // Increment the phase, but not past the end of the sequence
  761. //
  762. if (fdoExtension->PowerWorkerState != FPW_Stopped) {
  763. if (increment == FPW_END_SEQUENCE) {
  764. fdoExtension->PowerWorkerPhase = fdoExtension->PowerWorkerMaxPhase;
  765. } else {
  766. fdoExtension->PowerWorkerPhase += (UCHAR)increment;
  767. if (fdoExtension->PowerWorkerPhase > fdoExtension->PowerWorkerMaxPhase) {
  768. fdoExtension->PowerWorkerPhase = fdoExtension->PowerWorkerMaxPhase;
  769. }
  770. }
  771. //
  772. // The next state is pointed to by the current phase
  773. //
  774. fdoExtension->PowerWorkerState =
  775. fdoExtension->PowerWorkerSequence[ fdoExtension->PowerWorkerPhase ];
  776. }
  777. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker next state : %s\n", fdoExtension->DeviceObject,
  778. FDO_POWER_WORKER_STRING(fdoExtension->PowerWorkerState)));
  779. }
  780. NTSTATUS
  781. PcmciaFdoPowerWorker (
  782. IN PVOID Context,
  783. IN NTSTATUS Status
  784. )
  785. /*++
  786. Routine Description
  787. This routine handles sequencing of the device power state change for the
  788. ppcard controller.
  789. Arguments
  790. DeviceObject - Pointer to the functional device object for the pcmcia controller
  791. Status - status from previous operation
  792. Return value
  793. status
  794. --*/
  795. {
  796. PDEVICE_OBJECT Fdo = Context;
  797. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  798. PIRP Irp = fdoExtension->PendingPowerIrp;
  799. PIO_STACK_LOCATION irpStack;
  800. NTSTATUS status = Status;
  801. PDEVICE_OBJECT pdo;
  802. PPDO_EXTENSION pdoExtension;
  803. PSOCKET socket;
  804. BOOLEAN cardInSocket;
  805. BOOLEAN deviceChange;
  806. ULONG DelayTime = 0;
  807. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker - %s\n", Fdo,
  808. FDO_POWER_WORKER_STRING(fdoExtension->PowerWorkerState)));
  809. switch(fdoExtension->PowerWorkerState) {
  810. //-------------------------------------------------------------------------
  811. // POWER DOWN STATES
  812. //-------------------------------------------------------------------------
  813. case FPW_BeginPowerDown:
  814. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  815. break;
  816. case FPW_PowerDown:
  817. //
  818. // Controller being powered down
  819. //
  820. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x preparing for powerdown\n", Fdo, Irp));
  821. //
  822. // Getting out of D0
  823. //
  824. if (fdoExtension->Flags & PCMCIA_USE_POLLED_CSC) {
  825. //
  826. // Cancel the poll timer
  827. //
  828. KeCancelTimer(&fdoExtension->PollTimer);
  829. }
  830. //
  831. // Save necessary controller registers
  832. //
  833. PcmciaFdoSaveControllerContext(fdoExtension);
  834. fdoExtension->PendingPowerSocket = fdoExtension->SocketList;
  835. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  836. break;
  837. case FPW_PowerDownSocket:
  838. if ((socket = fdoExtension->PendingPowerSocket) == NULL) {
  839. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  840. break;
  841. }
  842. //
  843. // Ready to turn off the socket
  844. //
  845. PcmciaFdoSaveSocketContext(socket);
  846. //
  847. // Clear card detect unless we intend to wake using it
  848. //
  849. if (IsSocketFlagSet(socket, SOCKET_ENABLED_FOR_CARD_DETECT) && !IsFdoFlagSet(fdoExtension, PCMCIA_FDO_WAKE_BY_CD)) {
  850. (*(socket->SocketFnPtr->PCBEnableDisableCardDetectEvent))(socket, FALSE);
  851. }
  852. //
  853. // Cardbus cards need socket power all the time to allow PCI.SYS to read config space, even
  854. // if the device is logically removed. So instead of turning off socket power when the
  855. // children go to D3, we turn it off here when the parent goes to D3.
  856. // For R2 cards, the power may already be off, since the socket power does follow the
  857. // children. So this step would typically be superfluous.
  858. //
  859. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x skt %08x power worker SystemState=S%d\n",
  860. Fdo, socket, fdoExtension->SystemPowerState-1));
  861. switch(fdoExtension->SystemPowerState) {
  862. case PowerSystemWorking:
  863. //
  864. // The system is still running, we must be powering down because the socket is idle
  865. //
  866. ASSERT(!IsCardInSocket(socket));
  867. status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
  868. break;
  869. case PowerSystemSleeping1:
  870. case PowerSystemSleeping2:
  871. case PowerSystemSleeping3:
  872. //
  873. // If the device is armed for wakeup, we need to leave socket power on
  874. //
  875. if (fdoExtension->WaitWakeState == WAKESTATE_DISARMED) {
  876. status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
  877. }
  878. break;
  879. case PowerSystemHibernate:
  880. status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
  881. break;
  882. case PowerSystemShutdown:
  883. //
  884. // Doing a shutdown - check to see if we need to leave socket power on since NDIS
  885. // and SCSIPORT leaves their devices in D0. This can be a problem since many machines
  886. // will hang in the BIOS if devices are left powered up.
  887. //
  888. if (!IsDeviceFlagSet(fdoExtension, PCMCIA_FDO_DISABLE_AUTO_POWEROFF)) {
  889. status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
  890. }
  891. break;
  892. default:
  893. ASSERT(FALSE);
  894. }
  895. //
  896. // if success, then recurse below
  897. // if pending, then release_socket_power will call us back
  898. // Prepare to go to next socket
  899. //
  900. fdoExtension->PendingPowerSocket = fdoExtension->PendingPowerSocket->NextSocket;
  901. if (fdoExtension->PendingPowerSocket == NULL) {
  902. //
  903. // Done, ready to move on
  904. //
  905. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  906. }
  907. break;
  908. case FPW_PowerDownComplete:
  909. irpStack = IoGetCurrentIrpStackLocation(Irp);
  910. fdoExtension->DevicePowerState = irpStack->Parameters.Power.State.DeviceState;
  911. PoSetPowerState (Fdo, DevicePowerState, irpStack->Parameters.Power.State);
  912. fdoExtension->Flags |= PCMCIA_FDO_OFFLINE;
  913. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  914. break;
  915. //-------------------------------------------------------------------------
  916. // POWER UP STATES
  917. //-------------------------------------------------------------------------
  918. case FPW_BeginPowerUp:
  919. //
  920. // Back in D0. Restore the minimal context so we can access the registers
  921. //
  922. PcmciaFdoRestoreControllerContext(fdoExtension);
  923. //
  924. // Delay for a while after restoring PCI config space to allow the controller
  925. // to settle. The Panasonic Toughbook with at TI-1251B seemed to need this delay
  926. // before the cardbus state register showed the right value.
  927. //
  928. DelayTime = 8192;
  929. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  930. break;
  931. case FPW_PowerUp:
  932. //
  933. // Should be ready to touch the registers again
  934. //
  935. fdoExtension->Flags &= ~PCMCIA_FDO_OFFLINE;
  936. //
  937. // Get registers to a known state
  938. //
  939. PcmciaInitializeController(Fdo);
  940. if (!ValidateController(fdoExtension)) {
  941. status = STATUS_DEVICE_NOT_READY;
  942. //
  943. // Fast forward the sequence skipping to complete the irp
  944. //
  945. MoveToNextFdoPowerWorkerState(fdoExtension, FPW_END_SEQUENCE); // moves to state: Stopped
  946. MoveToNextFdoPowerWorkerState(fdoExtension, -1); // moves to state: CompleteIrp
  947. break;
  948. }
  949. //
  950. // We just transitioned into D0
  951. // Set socket flags to current state
  952. //
  953. for (socket = fdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
  954. if (fdoExtension->PcmciaInterruptObject) {
  955. //
  956. // this should clear any pending interrupts in the socket event register
  957. //
  958. ((*(socket->SocketFnPtr->PCBDetectCardChanged))(socket));
  959. }
  960. //
  961. // Some cardbus cards (NEC based 1394 cards) are not quiet when they power up,
  962. // avoid an interrupt storm here by setting ISA irq routing
  963. //
  964. if (IsCardBusCardInSocket(socket)) {
  965. USHORT word;
  966. GetPciConfigSpace(fdoExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
  967. word |= BCTRL_IRQROUTING_ENABLE;
  968. SetPciConfigSpace(fdoExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
  969. }
  970. }
  971. fdoExtension->PendingPowerSocket = fdoExtension->SocketList;
  972. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  973. break;
  974. case FPW_PowerUpSocket:
  975. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  976. if ((socket = fdoExtension->PendingPowerSocket) == NULL) {
  977. break;
  978. }
  979. //
  980. // Make sure socket is really off first before powering up
  981. //
  982. status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
  983. break;
  984. case FPW_PowerUpSocket2:
  985. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  986. if ((socket = fdoExtension->PendingPowerSocket) == NULL) {
  987. break;
  988. }
  989. //
  990. // We now decide if the socket should be turned on. We really want to turn
  991. // at this point to make sure the device hasn't been swapped while the
  992. // controller was off. If status_change is already set on the socket flags,
  993. // then we anyway will power it on during enumeration, so don't bother now.
  994. //
  995. if (!IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE) && IsCardInSocket(socket)) {
  996. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker - PowerON socket %08x\n", Fdo, socket));
  997. status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWERON);
  998. }
  999. break;
  1000. case FPW_PowerUpSocketVerify:
  1001. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  1002. if ((socket = fdoExtension->PendingPowerSocket) == NULL) {
  1003. break;
  1004. }
  1005. //
  1006. // verify the same card is still inserted.
  1007. //
  1008. if (!IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE) &&
  1009. IsCardInSocket(socket) &&
  1010. IsSocketFlagSet(socket, SOCKET_CARD_POWERED_UP)) {
  1011. PcmciaVerifyCardInSocket(socket);
  1012. }
  1013. //
  1014. // Now we decide whether or not to turn the power back off.
  1015. //
  1016. if (Is16BitCardInSocket(socket)) {
  1017. if (IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE) ||
  1018. (socket->PowerRequests == 0)) {
  1019. status = PcmciaSetSocketPower(socket, PcmciaFdoPowerWorker, Fdo, PCMCIA_POWEROFF);
  1020. }
  1021. }
  1022. break;
  1023. case FPW_PowerUpSocketComplete:
  1024. if (fdoExtension->PendingPowerSocket == NULL) {
  1025. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  1026. break;
  1027. }
  1028. //
  1029. // go to next socket, if any
  1030. //
  1031. fdoExtension->PendingPowerSocket = fdoExtension->PendingPowerSocket->NextSocket;
  1032. if (fdoExtension->PendingPowerSocket != NULL) {
  1033. //
  1034. // Back up sequence to FPW_PowerUpSocket
  1035. //
  1036. MoveToNextFdoPowerWorkerState(fdoExtension, -2);
  1037. break;
  1038. }
  1039. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  1040. break;
  1041. case FPW_PowerUpComplete:
  1042. irpStack = IoGetCurrentIrpStackLocation(Irp);
  1043. deviceChange = FALSE;
  1044. for (socket = fdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
  1045. if (IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE)) {
  1046. deviceChange = TRUE;
  1047. }
  1048. PcmciaFdoRestoreSocketContext(socket);
  1049. if (CardBus(socket)) {
  1050. CBEnableDeviceInterruptRouting(socket);
  1051. }
  1052. if (IsSocketFlagSet(socket, SOCKET_ENABLED_FOR_CARD_DETECT)) {
  1053. (*(socket->SocketFnPtr->PCBEnableDisableCardDetectEvent))(socket, TRUE);
  1054. }
  1055. }
  1056. fdoExtension->DevicePowerState = irpStack->Parameters.Power.State.DeviceState;
  1057. PoSetPowerState (Fdo, DevicePowerState, irpStack->Parameters.Power.State);
  1058. if (deviceChange) {
  1059. //
  1060. // Make sure i/o arbiter is not hanging on the devnode
  1061. //
  1062. if (CardBusExtension(fdoExtension)) {
  1063. IoInvalidateDeviceState(fdoExtension->Pdo);
  1064. }
  1065. //
  1066. // Device state changed..
  1067. //
  1068. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker - Invalidating Device Relations!\n", Fdo));
  1069. IoInvalidateDeviceRelations(fdoExtension->Pdo, BusRelations);
  1070. }
  1071. //
  1072. // Getting back to D0, set the poll timer on
  1073. //
  1074. if (fdoExtension->Flags & PCMCIA_USE_POLLED_CSC) {
  1075. LARGE_INTEGER dueTime;
  1076. //
  1077. // Set first fire to twice the peroidic interval - just
  1078. //
  1079. dueTime.QuadPart = -PCMCIA_CSC_POLL_INTERVAL * 1000 * 10 * 2;
  1080. KeSetTimerEx(&(fdoExtension->PollTimer),
  1081. dueTime,
  1082. PCMCIA_CSC_POLL_INTERVAL,
  1083. &fdoExtension->TimerDpc
  1084. );
  1085. }
  1086. PCMCIA_ACQUIRE_DEVICE_LOCK(fdoExtension);
  1087. if (!IsListEmpty(&fdoExtension->PdoPowerRetryList)) {
  1088. PLIST_ENTRY NextEntry;
  1089. PIRP pdoIrp;
  1090. NextEntry = RemoveHeadList(&fdoExtension->PdoPowerRetryList);
  1091. PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
  1092. pdoIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.DriverContext[0]);
  1093. KeInsertQueueDpc(&fdoExtension->PdoPowerRetryDpc, pdoIrp, NULL);
  1094. } else {
  1095. PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
  1096. }
  1097. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  1098. break;
  1099. //-------------------------------------------------------------------------
  1100. // IRP HANDLING STATES
  1101. //-------------------------------------------------------------------------
  1102. case FPW_SendIrpDown:
  1103. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x sending irp down to PDO\n", Fdo, Irp));
  1104. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  1105. //
  1106. // Send the IRP to the pdo
  1107. //
  1108. IoMarkIrpPending(Irp);
  1109. IoCopyCurrentIrpStackLocationToNext (Irp);
  1110. IoSetCompletionRoutine(Irp,
  1111. PcmciaFdoDevicePowerCompletion,
  1112. NULL,
  1113. TRUE,
  1114. TRUE,
  1115. TRUE);
  1116. status = PoCallDriver(fdoExtension->LowerDevice, Irp);
  1117. if (NT_SUCCESS(status)) {
  1118. status = STATUS_PENDING;
  1119. }
  1120. break;
  1121. case FPW_CompleteIrp:
  1122. MoveToNextFdoPowerWorkerState(fdoExtension, 1);
  1123. if (Irp) {
  1124. fdoExtension->PendingPowerIrp = NULL;
  1125. Irp->IoStatus.Status = status;
  1126. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x comp %08x\n", fdoExtension->DeviceObject, Irp, Irp->IoStatus.Status));
  1127. PoStartNextPowerIrp(Irp);
  1128. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  1129. }
  1130. fdoExtension->PowerWorkerState = FPW_Stopped;
  1131. break;
  1132. case FPW_Stopped:
  1133. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker final exit %08x\n", Fdo, status));
  1134. if (!NT_SUCCESS(status)) {
  1135. for (socket = fdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
  1136. SetSocketFlag(socket, SOCKET_CARD_STATUS_CHANGE);
  1137. }
  1138. }
  1139. return status;
  1140. default:
  1141. ASSERT(FALSE);
  1142. }
  1143. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker status %08x\n", Fdo, status));
  1144. if (status == STATUS_PENDING) {
  1145. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker exit (pending)\n", Fdo));
  1146. //
  1147. // Current action calls us back
  1148. //
  1149. if ((Irp=fdoExtension->PendingPowerIrp)!=NULL) {
  1150. IoMarkIrpPending(Irp);
  1151. }
  1152. return status;
  1153. }
  1154. //
  1155. // Not done yet. Recurse or call timer
  1156. //
  1157. if (DelayTime) {
  1158. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x power worker delay type %s, %d usec\n", Fdo,
  1159. (KeGetCurrentIrql() < DISPATCH_LEVEL) ? "Wait" : "Timer",
  1160. DelayTime));
  1161. if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
  1162. PcmciaWait(DelayTime);
  1163. } else {
  1164. LARGE_INTEGER dueTime;
  1165. //
  1166. // Running on a DPC, kick of a kernel timer
  1167. //
  1168. dueTime.QuadPart = -((LONG) DelayTime*10);
  1169. KeSetTimer(&fdoExtension->PowerTimer, dueTime, &fdoExtension->PowerDpc);
  1170. //
  1171. // We will reenter on timer dpc
  1172. //
  1173. if ((Irp=fdoExtension->PendingPowerIrp)!=NULL) {
  1174. IoMarkIrpPending(Irp);
  1175. }
  1176. return STATUS_PENDING;
  1177. }
  1178. }
  1179. //
  1180. // recurse
  1181. //
  1182. return (PcmciaFdoPowerWorker(Fdo, status));
  1183. }
  1184. NTSTATUS
  1185. PcmciaFdoDevicePowerCompletion(
  1186. IN PDEVICE_OBJECT Fdo,
  1187. IN PIRP Irp,
  1188. IN PVOID Context
  1189. )
  1190. /*++
  1191. Routine Description
  1192. Completion routine for the power IRP sent down to the PDO for the
  1193. pcmcia controller. If we are getting out of a working system state,
  1194. requests a power IRP to put the device in appropriate device power state.
  1195. Also makes sure that we stop poking device registers when the controller
  1196. is powered down and reenables that if necessary when it powers up
  1197. Parameters
  1198. DeviceObject - Pointer to FDO for the controller
  1199. Irp - Pointer to the IRP for the power request
  1200. Context - Pointer to the FDO_POWER_CONTEXT which is
  1201. filled in when the IRP is passed down
  1202. Return Value
  1203. Status
  1204. --*/
  1205. {
  1206. PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
  1207. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  1208. ULONG timerInterval;
  1209. NTSTATUS status;
  1210. LARGE_INTEGER dueTime;
  1211. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x DevicePowerCompletion PDO status %08x\n", Fdo, Irp, Irp->IoStatus.Status));
  1212. if ((NT_SUCCESS(Irp->IoStatus.Status))) {
  1213. if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) {
  1214. timerInterval = ControllerPowerUpDelay;
  1215. } else {
  1216. //
  1217. // powering down
  1218. // stall to avoid hardware problem on ThinkPads where power
  1219. // to the device is left on after the system in general powers off
  1220. //
  1221. timerInterval = 20000;
  1222. }
  1223. //
  1224. // Do the rest in our timer routine
  1225. //
  1226. dueTime.QuadPart = -((LONG) timerInterval*10);
  1227. KeSetTimer(&fdoExtension->PowerTimer, dueTime, &fdoExtension->PowerDpc);
  1228. status = STATUS_MORE_PROCESSING_REQUIRED;
  1229. } else {
  1230. DebugPrint ((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x power irp failed by pdo %08x\n", Fdo, Irp, fdoExtension->LowerDevice));
  1231. PoStartNextPowerIrp (Irp);
  1232. status = Irp->IoStatus.Status;
  1233. //
  1234. // This irp is now complete
  1235. //
  1236. fdoExtension->PendingPowerIrp = NULL;
  1237. MoveToNextFdoPowerWorkerState(fdoExtension, FPW_END_SEQUENCE); // moves to state: Stopped
  1238. PcmciaFdoPowerWorker(Fdo, status);
  1239. }
  1240. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x DevicePowerCompletion <-- %08x\n", Fdo, Irp, status));
  1241. return status;
  1242. }
  1243. VOID
  1244. PcmciaFdoPowerWorkerDpc(
  1245. IN PKDPC Dpc,
  1246. IN PVOID Context,
  1247. IN PVOID SystemArgument1,
  1248. IN PVOID SystemArgument2
  1249. )
  1250. /*++
  1251. Routine Description
  1252. This routine is called a short time after the controller power state
  1253. is changed in order to give the hardware a chance to stabilize. It
  1254. is called in the context of a device power request.
  1255. Parameters
  1256. same as KDPC (Context is fdoExtension)
  1257. Return Value
  1258. none
  1259. --*/
  1260. {
  1261. PFDO_EXTENSION fdoExtension = Context;
  1262. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x PowerWorkerDpc\n", fdoExtension->DeviceObject, fdoExtension->PendingPowerIrp));
  1263. //
  1264. // Fdo power worker will complete the irp
  1265. //
  1266. PcmciaFdoPowerWorker(fdoExtension->DeviceObject, STATUS_SUCCESS);
  1267. }
  1268. VOID
  1269. PcmciaFdoRetryPdoPowerRequest(
  1270. IN PKDPC Dpc,
  1271. IN PVOID Context,
  1272. IN PVOID SystemArgument1,
  1273. IN PVOID SystemArgument2
  1274. )
  1275. /*++
  1276. Routine Description
  1277. This routine is called to finish off any PDO power irps that may have
  1278. been queued.
  1279. Parameters
  1280. same as KDPC (Context is fdoExtension)
  1281. Return Value
  1282. none
  1283. --*/
  1284. {
  1285. PFDO_EXTENSION fdoExtension = Context;
  1286. PIRP Irp = SystemArgument1;
  1287. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  1288. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x irp %08x FdoRetryPdoPowerRequest\n", fdoExtension->DeviceObject, Irp));
  1289. PcmciaSetPdoDevicePowerState(irpStack->DeviceObject, Irp);
  1290. while(TRUE) {
  1291. PLIST_ENTRY NextEntry;
  1292. PCMCIA_ACQUIRE_DEVICE_LOCK(fdoExtension);
  1293. if (IsListEmpty(&fdoExtension->PdoPowerRetryList)) {
  1294. PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
  1295. break;
  1296. }
  1297. NextEntry = RemoveHeadList(&fdoExtension->PdoPowerRetryList);
  1298. PCMCIA_RELEASE_DEVICE_LOCK(fdoExtension);
  1299. Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.DriverContext[0]);
  1300. irpStack = IoGetCurrentIrpStackLocation(Irp);
  1301. PcmciaSetPdoDevicePowerState(irpStack->DeviceObject, Irp);
  1302. }
  1303. }
  1304. NTSTATUS
  1305. PcmciaFdoSaveControllerContext(
  1306. IN PFDO_EXTENSION FdoExtension
  1307. )
  1308. /*++
  1309. Routine Description:
  1310. Saves the state of the necessary PCI config registers
  1311. in the device extension of the cardbus controller
  1312. Arguments:
  1313. FdoExtension - Pointer to device extension for the FDO of the
  1314. cardbus controller
  1315. Return Value:
  1316. Status
  1317. --*/
  1318. {
  1319. ULONG index, offset, count;
  1320. PULONG alignedBuffer;
  1321. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x save reg context\n", FdoExtension->DeviceObject));
  1322. if (!FdoExtension->PciContext.BufferLength) {
  1323. // nothing to save
  1324. return STATUS_SUCCESS;
  1325. }
  1326. if (!ValidateController(FdoExtension)) {
  1327. return STATUS_DEVICE_NOT_READY;
  1328. }
  1329. SetDeviceFlag(FdoExtension, PCMCIA_FDO_CONTEXT_SAVED);
  1330. if (FdoExtension->PciContextBuffer == NULL) {
  1331. FdoExtension->PciContextBuffer = ExAllocatePool(NonPagedPool, FdoExtension->PciContext.BufferLength);
  1332. if (FdoExtension->PciContextBuffer == NULL) {
  1333. return STATUS_INSUFFICIENT_RESOURCES;
  1334. }
  1335. }
  1336. alignedBuffer = ExAllocatePool(NonPagedPool, FdoExtension->PciContext.MaxLen);
  1337. if (alignedBuffer == NULL) {
  1338. return STATUS_INSUFFICIENT_RESOURCES;
  1339. }
  1340. if (CardBusExtension(FdoExtension)) {
  1341. //
  1342. // Save PCI context
  1343. //
  1344. for (index = 0, offset = 0; index < FdoExtension->PciContext.RangeCount; index++) {
  1345. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x saving PCI context, offset %x length %x\n",
  1346. FdoExtension->DeviceObject,
  1347. FdoExtension->PciContext.Range[index].wOffset,
  1348. FdoExtension->PciContext.Range[index].wLen));
  1349. ASSERT(FdoExtension->PciContext.Range[index].wLen <= FdoExtension->PciContext.MaxLen);
  1350. GetPciConfigSpace(FdoExtension,
  1351. (ULONG) FdoExtension->PciContext.Range[index].wOffset,
  1352. alignedBuffer,
  1353. FdoExtension->PciContext.Range[index].wLen);
  1354. RtlCopyMemory(&FdoExtension->PciContextBuffer[offset], alignedBuffer, FdoExtension->PciContext.Range[index].wLen);
  1355. offset += FdoExtension->PciContext.Range[index].wLen;
  1356. }
  1357. }
  1358. ExFreePool(alignedBuffer);
  1359. return STATUS_SUCCESS;
  1360. }
  1361. NTSTATUS
  1362. PcmciaFdoSaveSocketContext(
  1363. IN PSOCKET Socket
  1364. )
  1365. /*++
  1366. Routine Description:
  1367. Saves the state of the necessary socket registers (CB, EXCA)
  1368. Arguments:
  1369. Socket - Pointer to socket data structure
  1370. Return Value:
  1371. Status
  1372. --*/
  1373. {
  1374. PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
  1375. ULONG index, offset, count;
  1376. if (CardBusExtension(fdoExtension) && fdoExtension->CardbusContext.BufferLength) {
  1377. //
  1378. // Save Cardbus context
  1379. //
  1380. if (Socket->CardbusContextBuffer == NULL) {
  1381. Socket->CardbusContextBuffer = ExAllocatePool(NonPagedPool, fdoExtension->CardbusContext.BufferLength);
  1382. if (Socket->CardbusContextBuffer == NULL) {
  1383. return STATUS_INSUFFICIENT_RESOURCES;
  1384. }
  1385. }
  1386. for (index = 0, offset = 0; index < fdoExtension->CardbusContext.RangeCount; index++) {
  1387. PULONG pBuffer = (PULONG) &Socket->CardbusContextBuffer[offset];
  1388. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x saving Cardbus context, offset %x length %x\n",
  1389. fdoExtension->DeviceObject,
  1390. fdoExtension->CardbusContext.Range[index].wOffset,
  1391. fdoExtension->CardbusContext.Range[index].wLen));
  1392. for (count = 0; count < (fdoExtension->CardbusContext.Range[index].wLen/sizeof(ULONG)) ; count++) {
  1393. *pBuffer++ = CBReadSocketRegister(Socket,
  1394. (UCHAR) (fdoExtension->CardbusContext.Range[index].wOffset + count*sizeof(ULONG)));
  1395. }
  1396. offset += fdoExtension->CardbusContext.Range[index].wLen;
  1397. }
  1398. }
  1399. //
  1400. // Save Exca context
  1401. //
  1402. if (fdoExtension->ExcaContext.BufferLength) {
  1403. if (Socket->ExcaContextBuffer == NULL) {
  1404. Socket->ExcaContextBuffer = ExAllocatePool(NonPagedPool, fdoExtension->ExcaContext.BufferLength);
  1405. if (Socket->ExcaContextBuffer == NULL) {
  1406. return STATUS_INSUFFICIENT_RESOURCES;
  1407. }
  1408. }
  1409. for (index = 0, offset = 0; index < fdoExtension->ExcaContext.RangeCount; index++) {
  1410. PUCHAR pBuffer = &Socket->ExcaContextBuffer[offset];
  1411. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x saving Exca context, offset %x length %x\n",
  1412. fdoExtension->DeviceObject,
  1413. fdoExtension->ExcaContext.Range[index].wOffset,
  1414. fdoExtension->ExcaContext.Range[index].wLen));
  1415. for (count = 0; count < fdoExtension->ExcaContext.Range[index].wLen; count++) {
  1416. *pBuffer++ = PcicReadSocket(Socket,
  1417. fdoExtension->ExcaContext.Range[index].wOffset + count);
  1418. }
  1419. offset += fdoExtension->ExcaContext.Range[index].wLen;
  1420. }
  1421. }
  1422. return STATUS_SUCCESS;
  1423. }
  1424. NTSTATUS
  1425. PcmciaFdoRestoreControllerContext(
  1426. IN PFDO_EXTENSION FdoExtension
  1427. )
  1428. /*++
  1429. Routine Description:
  1430. Restores the state of the necessary PCI config registers
  1431. from the device extension of the cardbus controller
  1432. Arguments:
  1433. FdoExtension - Pointer to device extension for the FDO of the
  1434. cardbus controller
  1435. Return Value:
  1436. Status
  1437. --*/
  1438. {
  1439. ULONG index, offset, count;
  1440. PULONG alignedBuffer;
  1441. if (!CardBusExtension(FdoExtension)) {
  1442. return STATUS_SUCCESS;
  1443. }
  1444. //
  1445. // Make sure we don't restore stale or uninitialized data
  1446. //
  1447. if (!IsDeviceFlagSet(FdoExtension, PCMCIA_FDO_CONTEXT_SAVED)) {
  1448. ASSERT(IsDeviceFlagSet(FdoExtension, PCMCIA_FDO_CONTEXT_SAVED));
  1449. return STATUS_UNSUCCESSFUL;
  1450. }
  1451. ResetDeviceFlag(FdoExtension, PCMCIA_FDO_CONTEXT_SAVED);
  1452. if (FdoExtension->PciContextBuffer == NULL) {
  1453. // nothing to restore... strange since our flag was set
  1454. ASSERT(FALSE);
  1455. return STATUS_SUCCESS;
  1456. }
  1457. alignedBuffer = ExAllocatePool(NonPagedPool, FdoExtension->PciContext.MaxLen);
  1458. if (alignedBuffer == NULL) {
  1459. return STATUS_INSUFFICIENT_RESOURCES;
  1460. }
  1461. DebugPrint((PCMCIA_DEBUG_POWER,
  1462. "fdo %08x restore reg context\n", FdoExtension->DeviceObject));
  1463. //
  1464. // Restore PCI context
  1465. //
  1466. for (index = 0, offset = 0; index < FdoExtension->PciContext.RangeCount; index++) {
  1467. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x restoring PCI context, offset %x length %x\n",
  1468. FdoExtension->DeviceObject,
  1469. FdoExtension->PciContext.Range[index].wOffset,
  1470. FdoExtension->PciContext.Range[index].wLen));
  1471. ASSERT(FdoExtension->PciContext.Range[index].wLen <= FdoExtension->PciContext.MaxLen);
  1472. RtlCopyMemory(alignedBuffer, &FdoExtension->PciContextBuffer[offset], FdoExtension->PciContext.Range[index].wLen);
  1473. SetPciConfigSpace(FdoExtension,
  1474. (ULONG) FdoExtension->PciContext.Range[index].wOffset,
  1475. alignedBuffer,
  1476. FdoExtension->PciContext.Range[index].wLen);
  1477. offset += FdoExtension->PciContext.Range[index].wLen;
  1478. //
  1479. // hang on resume on NEC NX laptop (w/ Ricoh devid 0x475) is avoided with a stall here.
  1480. // The hang occurs if CBRST is turned OFF. I'm unclear about the reason for it, this
  1481. // is just an empirically derived hack.
  1482. //
  1483. PcmciaWait(1);
  1484. }
  1485. ExFreePool(alignedBuffer);
  1486. return STATUS_SUCCESS;
  1487. }
  1488. NTSTATUS
  1489. PcmciaFdoRestoreSocketContext(
  1490. IN PSOCKET Socket
  1491. )
  1492. /*++
  1493. Routine Description:
  1494. Restores the state of the necessary socket registers (CB, EXCA)
  1495. Arguments:
  1496. Socket - Pointer to socket data structure
  1497. Return Value:
  1498. Status
  1499. --*/
  1500. {
  1501. PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
  1502. ULONG index, offset, count;
  1503. if (CardBusExtension(fdoExtension) && (Socket->CardbusContextBuffer != NULL)) {
  1504. //
  1505. // Restore Cardbus context
  1506. //
  1507. for (index = 0, offset = 0; index < fdoExtension->CardbusContext.RangeCount; index++) {
  1508. PULONG pBuffer = (PULONG) &Socket->CardbusContextBuffer[offset];
  1509. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x restoring Cardbus context offset %x length %x\n",
  1510. fdoExtension->DeviceObject,
  1511. fdoExtension->CardbusContext.Range[index].wOffset,
  1512. fdoExtension->CardbusContext.Range[index].wLen));
  1513. for (count = 0; count < (fdoExtension->CardbusContext.Range[index].wLen/sizeof(ULONG)) ; count++) {
  1514. CBWriteSocketRegister(Socket,
  1515. (UCHAR) (fdoExtension->CardbusContext.Range[index].wOffset + count*sizeof(ULONG)),
  1516. *pBuffer++);
  1517. }
  1518. offset += fdoExtension->CardbusContext.Range[index].wLen;
  1519. }
  1520. }
  1521. //
  1522. // Restore Exca context
  1523. //
  1524. if (Socket->ExcaContextBuffer != NULL) {
  1525. for (index = 0, offset = 0; index < fdoExtension->ExcaContext.RangeCount; index++) {
  1526. PUCHAR pBuffer = &Socket->ExcaContextBuffer[offset];
  1527. DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x Restoring Exca context, offset %x length %x\n",
  1528. fdoExtension->DeviceObject,
  1529. fdoExtension->ExcaContext.Range[index].wOffset,
  1530. fdoExtension->ExcaContext.Range[index].wLen));
  1531. for (count = 0; count < fdoExtension->ExcaContext.Range[index].wLen; count++) {
  1532. PcicWriteSocket(Socket,
  1533. fdoExtension->ExcaContext.Range[index].wOffset + count,
  1534. *pBuffer++);
  1535. }
  1536. offset += fdoExtension->ExcaContext.Range[index].wLen;
  1537. }
  1538. }
  1539. return STATUS_SUCCESS;
  1540. }