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 */
RequestProcessShutdownFlush(WDFDEVICE Device,PIRP Irp)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 */
RequestProcessSetPower(WDFDEVICE Device,PIRP Irp)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
DeviceScratchSyncCache(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension)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
DeviceScratchPreventMediaRemoval(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ BOOLEAN Prevent)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
RequestIssueShutdownFlush(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ PIRP Irp)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 */
RequestUnlockQueueCompletion(_In_ WDFREQUEST Request,_In_ WDFIOTARGET Target,_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,_In_ WDFCONTEXT Context)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
_IRQL_requires_max_(DISPATCH_LEVEL)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
_IRQL_requires_max_(DISPATCH_LEVEL)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
_IRQL_requires_max_(DISPATCH_LEVEL)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
_Function_class_(POWER_SETTING_CALLBACK)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