xref: /reactos/drivers/storage/class/cdrom/pnppower.c (revision 05c39d8d)
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