1 /*-- 2 3 Copyright (C) Microsoft Corporation. All rights reserved. 4 5 Module Name: 6 7 pnppower.c 8 9 Abstract: 10 11 Functions to handle PnP and Power IRPs. 12 13 Environment: 14 15 kernel mode only 16 17 Notes: 18 optical devices do not need to issue SPIN UP when power up. 19 The device itself should SPIN UP to process commands. 20 21 22 Revision History: 23 24 --*/ 25 26 27 #include "ntddk.h" 28 #include "wdfcore.h" 29 30 #include "cdrom.h" 31 #include "ioctl.h" 32 #include "scratch.h" 33 #include "mmc.h" 34 35 #ifdef DEBUG_USE_WPP 36 #include "pnppower.tmh" 37 #endif 38 39 40 NTSTATUS 41 DeviceScratchSyncCache( 42 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 43 ); 44 45 NTSTATUS 46 DeviceScratchPreventMediaRemoval( 47 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 48 _In_ BOOLEAN Prevent 49 ); 50 51 NTSTATUS 52 RequestIssueShutdownFlush( 53 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 54 _In_ PIRP Irp 55 ); 56 57 IO_COMPLETION_ROUTINE RequestProcessPowerIrpCompletion; 58 59 EVT_WDF_REQUEST_COMPLETION_ROUTINE RequestUnlockQueueCompletion; 60 61 #ifdef ALLOC_PRAGMA 62 63 #pragma alloc_text(PAGE, DevicePowerSettingCallback) 64 65 #endif 66 67 #pragma warning(push) 68 #pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression 69 70 #pragma warning(disable:28118) // Dispatch routines for IRP_MJ_SHUTDOWN, IRP_MJ_FLUSH_BUFFERS occur at PASSIVE_LEVEL. 71 // WDF defines EVT_WDFDEVICE_WDM_IRP_PREPROCESS with _IRQL_requires_max_(DISPATCH_LEVEL), 72 // triggering a false positive for this warning. 73 74 NTSTATUS 75 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 76 RequestProcessShutdownFlush( 77 WDFDEVICE Device, 78 PIRP Irp 79 ) 80 /*++ 81 82 Routine Description: 83 84 process IRP: IRP_MJ_SHUTDOWN, IRP_MJ_FLUSH_BUFFERS 85 86 Arguments: 87 88 Device - device object 89 Irp - the irp 90 91 Return Value: 92 93 NTSTATUS 94 95 --*/ 96 { 97 NTSTATUS status = STATUS_SUCCESS; 98 PIO_STACK_LOCATION currentStack = NULL; 99 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); 100 101 //add trace info 102 103 // acquire the shutdown/flush lock 104 WdfWaitLockAcquire(deviceExtension->ShutdownFlushWaitLock, NULL); 105 106 currentStack = IoGetCurrentIrpStackLocation(Irp); 107 108 // finish all current requests 109 WdfIoQueueStopSynchronously(deviceExtension->SerialIOQueue); 110 111 // sync cache 112 if (NT_SUCCESS(status)) 113 { 114 // safe to use scratch srb to send the request. 115 status = DeviceScratchSyncCache(deviceExtension); 116 } 117 118 // For SHUTDOWN, allow media removal. 119 if (NT_SUCCESS(status)) 120 { 121 if (currentStack->MajorFunction == IRP_MJ_SHUTDOWN) 122 { 123 // safe to use scratch srb to send the request. 124 status = DeviceScratchPreventMediaRemoval(deviceExtension, FALSE); 125 } 126 } 127 128 // Use original IRP, send SRB_FUNCTION_SHUTDOWN or SRB_FUNCTION_FLUSH (no retry) 129 if (NT_SUCCESS(status)) 130 { 131 status = RequestIssueShutdownFlush(deviceExtension, Irp); 132 } 133 134 // restart queue to allow processing further requests. 135 WdfIoQueueStart(deviceExtension->SerialIOQueue); 136 137 // release the shutdown/flush lock 138 WdfWaitLockRelease(deviceExtension->ShutdownFlushWaitLock); 139 140 // 6. complete the irp 141 Irp->IoStatus.Status = status; 142 IoCompleteRequest(Irp, 0); 143 144 return status; 145 } 146 147 NTSTATUS 148 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 149 RequestProcessSetPower( 150 WDFDEVICE Device, 151 PIRP Irp 152 ) 153 /*++ 154 155 Routine Description: 156 157 process IRP: IRP_MJ_POWER 158 159 Arguments: 160 161 Device - device object 162 Irp - the irp 163 164 Return Value: 165 166 NTSTATUS 167 168 --*/ 169 { 170 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); 171 PIO_STACK_LOCATION currentStack; 172 NTSTATUS status; 173 BOOLEAN IrpMarkedPending = FALSE; 174 175 currentStack = IoGetCurrentIrpStackLocation(Irp); 176 177 if ((currentStack->Parameters.Power.Type == DevicePowerState) && 178 (currentStack->Parameters.Power.State.DeviceState != PowerDeviceD0)) 179 { 180 // We need to unlock the device queue in D3 postprocessing. 181 IoCopyCurrentIrpStackLocationToNext(Irp); 182 IoSetCompletionRoutine(Irp, 183 RequestProcessPowerIrpCompletion, 184 deviceExtension, 185 TRUE, 186 TRUE, 187 TRUE); 188 189 // Mark the Irp pending as we'll defer the I/O completion. 190 IoMarkIrpPending(Irp); 191 IrpMarkedPending = TRUE; 192 } 193 else { 194 195 IoSkipCurrentIrpStackLocation(Irp); 196 } 197 198 #pragma warning(push) 199 #pragma warning(disable: 28193) // OACR will complain that the status variable is not examined. 200 201 // 202 // Deliver the IRP back to the framework. 203 // 204 205 status = WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp); 206 207 if (IrpMarkedPending) 208 { 209 UNREFERENCED_PARAMETER(status); 210 return STATUS_PENDING; 211 } 212 213 #pragma warning(pop) 214 215 return status; 216 } 217 218 // use scratch SRB to issue SYNC CACHE command. 219 NTSTATUS 220 DeviceScratchSyncCache( 221 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 222 ) 223 /*++ 224 225 Routine Description: 226 227 use scratch buffer to send SYNC CACHE command 228 229 Arguments: 230 231 DeviceExtension - device context 232 233 Return Value: 234 235 NTSTATUS 236 237 --*/ 238 { 239 NTSTATUS status = STATUS_SUCCESS; 240 ULONG transferSize = 0; 241 CDB cdb; 242 243 ScratchBuffer_BeginUse(DeviceExtension); 244 245 RtlZeroMemory(&cdb, sizeof(CDB)); 246 // Set up the CDB 247 cdb.SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE; 248 //srb->QueueTag = SP_UNTAGGED; 249 //srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; 250 251 status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, NULL, transferSize, FALSE, &cdb, 10, TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, 4)); 252 253 ScratchBuffer_EndUse(DeviceExtension); 254 255 return status; 256 } 257 258 NTSTATUS 259 DeviceScratchPreventMediaRemoval( 260 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 261 _In_ BOOLEAN Prevent 262 ) 263 /*++ 264 265 Routine Description: 266 267 use scratch SRB to issue ALLOW/PREVENT MEDIA REMOVAL command. 268 269 Arguments: 270 271 DeviceExtension - device context 272 Prevent - TRUE (prevent media removal); FALSE (allow media removal) 273 274 Return Value: 275 276 NTSTATUS 277 278 --*/ 279 { 280 NTSTATUS status = STATUS_SUCCESS; 281 ULONG transferSize = 0; 282 CDB cdb; 283 284 ScratchBuffer_BeginUse(DeviceExtension); 285 286 RtlZeroMemory(&cdb, sizeof(CDB)); 287 // Set up the CDB 288 cdb.MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; 289 cdb.MEDIA_REMOVAL.Prevent = Prevent; 290 //srb->QueueTag = SP_UNTAGGED; 291 //srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; 292 293 status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, FALSE, &cdb, 6); 294 295 ScratchBuffer_EndUse(DeviceExtension); 296 297 return status; 298 } 299 300 NTSTATUS 301 RequestIssueShutdownFlush( 302 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 303 _In_ PIRP Irp 304 ) 305 /*++ 306 307 Routine Description: 308 309 issue SRB function Flush/Shutdown command. 310 311 Arguments: 312 313 DeviceExtension - device context 314 Irp - the irp 315 316 Return Value: 317 318 NTSTATUS 319 320 --*/ 321 { 322 NTSTATUS status = STATUS_SUCCESS; 323 PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb; 324 PIO_STACK_LOCATION currentStack = NULL; 325 326 ULONG transferSize = 0; 327 BOOLEAN shouldRetry = TRUE; 328 ULONG timesAlreadyRetried = 0; 329 LONGLONG retryIn100nsUnits = 0; 330 331 332 currentStack = IoGetCurrentIrpStackLocation(Irp); 333 334 335 ScratchBuffer_BeginUse(DeviceExtension); 336 337 // no retry needed. 338 { 339 ScratchBuffer_SetupSrb(DeviceExtension, NULL, transferSize, FALSE); 340 341 // Set up the SRB/CDB 342 srb->QueueTag = SP_UNTAGGED; 343 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; 344 srb->TimeOutValue = TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, 4); 345 srb->CdbLength = 0; 346 347 if (currentStack->MajorFunction == IRP_MJ_SHUTDOWN) 348 { 349 srb->Function = SRB_FUNCTION_SHUTDOWN; 350 } 351 else 352 { 353 srb->Function = SRB_FUNCTION_FLUSH; 354 } 355 356 ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL); 357 358 shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension, 359 timesAlreadyRetried, 360 &status, 361 &retryIn100nsUnits); 362 UNREFERENCED_PARAMETER(shouldRetry); //defensive coding, avoid PREFAST warning. 363 UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning. 364 365 // retrieve the real status from the request. 366 status = WdfRequestGetStatus(DeviceExtension->ScratchContext.ScratchRequest); 367 } 368 369 ScratchBuffer_EndUse(DeviceExtension); 370 371 372 return status; 373 } 374 375 VOID 376 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 377 RequestUnlockQueueCompletion ( 378 _In_ WDFREQUEST Request, 379 _In_ WDFIOTARGET Target, 380 _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, 381 _In_ WDFCONTEXT Context 382 ) 383 { 384 PIRP Irp = Context; 385 WDFDEVICE device = WdfIoTargetGetDevice(Target); 386 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device); 387 388 UNREFERENCED_PARAMETER(Request); 389 UNREFERENCED_PARAMETER(Params); 390 391 deviceExtension->PowerContext.Options.LockQueue = FALSE; 392 393 PowerContextEndUse(deviceExtension); 394 395 // Complete the original power irp 396 IoCompleteRequest( Irp, IO_NO_INCREMENT ); 397 } 398 399 NTSTATUS 400 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 401 RequestProcessPowerIrpCompletion( 402 _In_ PDEVICE_OBJECT DeviceObject, 403 _In_ PIRP Irp, 404 _In_reads_opt_(_Inexpressible_("varies")) PVOID Context 405 ) 406 /*++ 407 408 Routine Description: 409 410 Free the Irp. 411 412 Arguments: 413 414 DeviceObject - device that the completion routine fires on. 415 416 Irp - The irp to be completed. 417 418 Context - IRP context 419 420 Return Value: 421 NTSTATUS 422 423 --*/ 424 { 425 PCDROM_DEVICE_EXTENSION deviceExtension = Context; 426 PIO_STACK_LOCATION currentStack; 427 428 UNREFERENCED_PARAMETER(DeviceObject); 429 430 if (Irp->PendingReturned) 431 { 432 IoMarkIrpPending( Irp ); 433 } 434 435 currentStack = IoGetCurrentIrpStackLocation(Irp); 436 437 NT_ASSERT(currentStack->Parameters.Power.Type == DevicePowerState); 438 NT_ASSERT(currentStack->Parameters.Power.State.DeviceState != PowerDeviceD0); 439 440 _Analysis_assume_(deviceExtension != NULL); 441 442 deviceExtension->PowerContext.PowerChangeState.PowerDown++; 443 444 // Step 5. UNLOCK QUEUE 445 if (deviceExtension->PowerContext.Options.LockQueue) 446 { 447 (VOID)DeviceSendPowerDownProcessRequest(deviceExtension, 448 RequestUnlockQueueCompletion, 449 Irp); 450 451 // Let the completion routine complete the Irp 452 return STATUS_MORE_PROCESSING_REQUIRED; 453 } 454 455 // Release the power context if it wasn't already done as part of D0Exit handling 456 if (deviceExtension->PowerContext.InUse) 457 { 458 PowerContextEndUse(deviceExtension); 459 } 460 461 return STATUS_CONTINUE_COMPLETION; 462 } 463 464 465 _IRQL_requires_max_(DISPATCH_LEVEL) 466 NTSTATUS 467 PowerContextReuseRequest( 468 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 469 ) 470 /*++ 471 472 Routine Description: 473 474 reset fields for the request. 475 476 Arguments: 477 478 DeviceExtension - device context 479 480 Return Value: 481 482 NTSTATUS 483 484 --*/ 485 { 486 NTSTATUS status = STATUS_SUCCESS; 487 WDF_REQUEST_REUSE_PARAMS reuseParams; 488 PIRP irp = NULL; 489 490 RtlZeroMemory(&(DeviceExtension->PowerContext.SenseData), sizeof(DeviceExtension->PowerContext.SenseData)); 491 RtlZeroMemory(&(DeviceExtension->PowerContext.Srb), sizeof(DeviceExtension->PowerContext.Srb)); 492 493 irp = WdfRequestWdmGetIrp(DeviceExtension->PowerContext.PowerRequest); 494 495 // Re-use the previously created PowerRequest object and format it 496 WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED); 497 status = WdfRequestReuse(DeviceExtension->PowerContext.PowerRequest, &reuseParams); 498 if (NT_SUCCESS(status)) 499 { 500 // This request was preformated during initialization so this call should never fail. 501 status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, 502 DeviceExtension->PowerContext.PowerRequest, 503 IOCTL_SCSI_EXECUTE_IN, 504 NULL, NULL, 505 NULL, NULL, 506 NULL, NULL); 507 508 if (!NT_SUCCESS(status)) 509 { 510 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 511 "PowerContextReuseRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n", 512 status)); 513 } 514 } 515 516 // Do some basic initialization of the PowerRequest, the rest will be done by the caller 517 // of this function 518 if (NT_SUCCESS(status)) 519 { 520 PIO_STACK_LOCATION nextStack = NULL; 521 522 nextStack = IoGetNextIrpStackLocation(irp); 523 524 nextStack->MajorFunction = IRP_MJ_SCSI; 525 nextStack->Parameters.Scsi.Srb = &(DeviceExtension->PowerContext.Srb); 526 527 DeviceExtension->PowerContext.Srb.Length = sizeof(SCSI_REQUEST_BLOCK); 528 DeviceExtension->PowerContext.Srb.OriginalRequest = irp; 529 530 DeviceExtension->PowerContext.Srb.SenseInfoBuffer = &(DeviceExtension->PowerContext.SenseData); 531 DeviceExtension->PowerContext.Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE; 532 } 533 534 return status; 535 } 536 537 _IRQL_requires_max_(DISPATCH_LEVEL) 538 NTSTATUS 539 PowerContextBeginUse( 540 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 541 ) 542 /*++ 543 544 Routine Description: 545 546 initialize fields in power context 547 548 Arguments: 549 550 DeviceExtension - device context 551 552 Return Value: 553 554 NTSTATUS 555 556 --*/ 557 { 558 NTSTATUS status = STATUS_SUCCESS; 559 560 NT_ASSERT(!DeviceExtension->PowerContext.InUse); 561 562 DeviceExtension->PowerContext.InUse = TRUE; 563 DeviceExtension->PowerContext.RetryCount = MAXIMUM_RETRIES; 564 DeviceExtension->PowerContext.RetryIntervalIn100ns = 0; 565 566 KeQueryTickCount(&DeviceExtension->PowerContext.StartTime); 567 568 RtlZeroMemory(&(DeviceExtension->PowerContext.Options), sizeof(DeviceExtension->PowerContext.Options)); 569 RtlZeroMemory(&(DeviceExtension->PowerContext.PowerChangeState), sizeof(DeviceExtension->PowerContext.PowerChangeState)); 570 571 status = PowerContextReuseRequest(DeviceExtension); 572 573 RequestClearSendTime(DeviceExtension->PowerContext.PowerRequest); 574 575 return status; 576 } 577 578 _IRQL_requires_max_(DISPATCH_LEVEL) 579 NTSTATUS 580 PowerContextEndUse( 581 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 582 ) 583 /*++ 584 585 Routine Description: 586 587 inidate that power context using is finished. 588 589 Arguments: 590 591 DeviceExtension - device context 592 593 Return Value: 594 595 NTSTATUS 596 597 --*/ 598 { 599 NT_ASSERT(DeviceExtension->PowerContext.InUse); 600 601 DeviceExtension->PowerContext.InUse = FALSE; 602 603 KeQueryTickCount(&DeviceExtension->PowerContext.CompleteTime); 604 605 return STATUS_SUCCESS; 606 } 607 608 609 _Function_class_(POWER_SETTING_CALLBACK) 610 _IRQL_requires_same_ 611 NTSTATUS 612 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 613 DevicePowerSettingCallback( 614 _In_ LPCGUID SettingGuid, 615 _In_reads_bytes_(ValueLength) PVOID Value, 616 _In_ ULONG ValueLength, 617 _Inout_opt_ PVOID Context 618 ) 619 /*++ 620 Description: 621 622 This function is the callback for power setting notifications (registered 623 when ClasspGetD3IdleTimeout() is called for the first time). 624 625 Currently, this function is used to get the disk idle timeout value from 626 the system power settings. 627 628 This function is guaranteed to be called at PASSIVE_LEVEL. 629 630 Arguments: 631 632 SettingGuid - The power setting GUID. 633 Value - Pointer to the power setting value. 634 ValueLength - Size of the Value buffer. 635 Context - The FDO's device extension. 636 637 Return Value: 638 639 STATUS_SUCCESS 640 641 --*/ 642 { 643 PCDROM_DEVICE_EXTENSION DeviceExtension = Context; 644 MONITOR_DISPLAY_STATE DisplayState; 645 646 PAGED_CODE(); 647 648 if (IsEqualGUID(SettingGuid, &GUID_CONSOLE_DISPLAY_STATE)) { 649 650 if ((ValueLength == sizeof(ULONG)) && (Value != NULL)) { 651 652 DisplayState = *((PULONG)Value); 653 654 _Analysis_assume_(DeviceExtension != NULL); 655 656 // 657 // Power setting callbacks are asynchronous so make sure the device 658 // is completely initialized before taking any actions. 659 // 660 if (DeviceExtension->IsInitialized) { 661 662 // 663 // If monitor is off, change media change requests to not keep device active. 664 // This allows the devices to go to sleep if there are no other active requests. 665 // 666 667 if (DisplayState == PowerMonitorOff) { 668 669 // 670 // Mark the device inactive so that it can enter a low power state. 671 // 672 673 DeviceMarkActive(DeviceExtension, FALSE, TRUE); 674 SET_FLAG(DeviceExtension->MediaChangeDetectionInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); 675 } 676 else 677 { 678 CLEAR_FLAG(DeviceExtension->MediaChangeDetectionInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); 679 DeviceMarkActive(DeviceExtension, TRUE, TRUE); 680 } 681 } 682 } 683 } 684 685 return STATUS_SUCCESS; 686 } 687 688 #pragma warning(pop) // un-sets any local warning changes 689 690