xref: /reactos/drivers/storage/class/cdrom/zpodd.c (revision 3088717b)
1 /*++
2 
3 Copyright (C) Microsoft Corporation. All rights reserved.
4 
5 Module Name:
6 
7     zpodd.c
8 
9 Abstract:
10 
11     Code for Zero Power ODD support.
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "ntddk.h"
25 #include "ntddstor.h"
26 #include "wdmguid.h"
27 #include "cdrom.h"
28 #include "mmc.h"
29 #include "ioctl.h"
30 #include "scratch.h"
31 
32 #ifdef DEBUG_USE_WPP
33 #include "zpodd.tmh"
34 #endif
35 
36 _IRQL_requires_max_(PASSIVE_LEVEL)
37 ULONG
38 DeviceGetZPODDEnabledFromRegistry();
39 
40 _IRQL_requires_max_(PASSIVE_LEVEL)
41 NTSTATUS
42 DeviceQueryD3ColdInterface(
43     _In_    PCDROM_DEVICE_EXTENSION     DeviceExtension,
44     _Out_   PD3COLD_SUPPORT_INTERFACE   D3ColdInterface
45     );
46 
47 _IRQL_requires_max_(PASSIVE_LEVEL)
48 NTSTATUS
49 DeviceSendEnableIdlePowerIoctl(
50     _In_    PCDROM_DEVICE_EXTENSION     DeviceExtension,
51     _In_    BOOLEAN                     WakeCapable,
52     _In_    BOOLEAN                     Enable,
53     _In_    ULONG                       D3IdleTimeout
54     );
55 
56 #if ALLOC_PRAGMA
57 
58 #pragma alloc_text(PAGE, DeviceInitializeZPODD)
59 #pragma alloc_text(PAGE, DeviceGetZPODDEnabledFromRegistry)
60 #pragma alloc_text(PAGE, DeviceQueryD3ColdInterface)
61 #pragma alloc_text(PAGE, DeviceSendEnableIdlePowerIoctl)
62 #pragma alloc_text(PAGE, DeviceReleaseZPODDResources)
63 #pragma alloc_text(PAGE, DeviceZPODDIsInHomePosition)
64 #pragma alloc_text(PAGE, DeviceMarkActive)
65 
66 #endif
67 
68 #pragma warning(push)
69 #pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
70 #pragma warning(disable:26000) // read overflow reported because of pointer type conversion
71 
_IRQL_requires_max_(PASSIVE_LEVEL)72 _IRQL_requires_max_(PASSIVE_LEVEL)
73 NTSTATUS
74 DeviceInitializeZPODD(
75     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
76     )
77 /*++
78 
79 Routine Description:
80 
81     This routine initialize the contents of ZPODD structure.
82 
83 Arguments:
84 
85     DeviceExtension - the device extension
86 
87 Return Value:
88     NTSTATUS
89 
90 --*/
91 {
92     NTSTATUS                            status = STATUS_SUCCESS;
93     NTSTATUS                            tempStatus = STATUS_SUCCESS;
94     PZERO_POWER_ODD_INFO                zpoddInfo = NULL;
95     PFEATURE_DATA_REMOVABLE_MEDIUM      removableMediumHeader = NULL;
96     ULONG                               ZPODDEnabledInRegistry = 0;
97     PD3COLD_SUPPORT_INTERFACE           d3ColdInterface = NULL;
98     DEVICE_WAKE_DEPTH                   deepestWakeableDstate = DeviceWakeDepthNotWakeable;
99     BOOLEAN                             inHomePosition = FALSE;
100 
101     PAGED_CODE();
102 
103     if (DeviceExtension->ZeroPowerODDInfo != NULL)
104     {
105         //
106         // Already initialized.
107         //
108 
109         goto Cleanup;
110     }
111 
112     ZPODDEnabledInRegistry = DeviceGetZPODDEnabledFromRegistry();
113 
114     if (ZPODDEnabledInRegistry == 0)
115     {
116         //
117         // User has explicitly disabled Zero Power ODD.
118         //
119 
120         status = STATUS_NOT_SUPPORTED;
121 
122         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
123                     "DeviceInitializeZPODD: ZPODD not enabled due to registry settings.\n"
124                     ));
125 
126         goto Cleanup;
127     }
128 
129     zpoddInfo = ExAllocatePoolWithTag(NonPagedPoolNx,
130                                       sizeof(ZERO_POWER_ODD_INFO),
131                                       CDROM_TAG_ZERO_POWER_ODD);
132 
133     if (zpoddInfo == NULL)
134     {
135         status = STATUS_INSUFFICIENT_RESOURCES;
136 
137         goto Cleanup;
138     }
139 
140     RtlZeroMemory(zpoddInfo, sizeof (ZERO_POWER_ODD_INFO));
141 
142     //
143     // Check the system for the following prerequisites:
144     //
145     // 1. SATA: Device Attention line
146     // 2. SATA: Asynchronous Notification
147     // 3. ODD:  LoChange / MediaRemoval
148     // 4. ACPI: Wake capable
149     //
150     // Only drawer and slot loading types have well defined behaviors in the spec, so only these two
151     // types are supported.
152     //
153 
154     //
155     // Check for DA & AN
156     //
157 
158     if ((DeviceExtension->PowerDescriptor == NULL) ||
159         (DeviceExtension->PowerDescriptor->DeviceAttentionSupported == FALSE) ||
160         (DeviceExtension->PowerDescriptor->AsynchronousNotificationSupported == FALSE))
161     {
162         status = STATUS_NOT_SUPPORTED;
163 
164         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
165                     "DeviceInitializeZPODD: ZPODD not enabled due to SATA features not present.\n"
166                     ));
167 
168         goto Cleanup;
169     }
170 
171     //
172     // Check for LoChange / MediaRemoval
173     //
174 
175     removableMediumHeader = (PFEATURE_DATA_REMOVABLE_MEDIUM)
176                             DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
177                                                   DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
178                                                   FeatureRemovableMedium);
179 
180     if ((removableMediumHeader == NULL) ||
181         (!((removableMediumHeader->LoadingMechanism == LOADING_MECHANISM_TRAY) && (removableMediumHeader->Load == 0) &&     // Drawer ...
182          (removableMediumHeader->DBML != FALSE)) &&                                                                         // requires LoChange/NotBusy
183         !((removableMediumHeader->LoadingMechanism == LOADING_MECHANISM_CADDY) && (removableMediumHeader->Load == 0) &&     // Slot ...
184          (DeviceExtension->MediaChangeDetectionInfo->Gesn.Supported != FALSE))))                                            // requires MediaRemoval
185     {
186         status = STATUS_NOT_SUPPORTED;
187 
188         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
189                     "DeviceInitializeZPODD: ZPODD not enabled due to ODD features not present.\n"
190                     ));
191 
192         goto Cleanup;
193     }
194 
195     zpoddInfo->LoadingMechanism = removableMediumHeader->LoadingMechanism;
196     zpoddInfo->Load = removableMediumHeader->Load;
197 
198     //
199     // Check for ACPI
200     //
201 
202     status = DeviceQueryD3ColdInterface(DeviceExtension, &zpoddInfo->D3ColdInterface);
203 
204     if (!NT_SUCCESS(status))
205     {
206         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
207                     "DeviceInitializeZPODD: Query D3Cold support interface failed.\n"
208                     ));
209 
210         goto Cleanup;
211     }
212 
213     //
214     // If the platform supports Zero Power ODD, the following conditions must be met:
215     //
216     // 1. The deepest wakeable D-state for the device is D3Cold;
217     // 2. The platform supports D3Cold for the device.
218     //
219 
220     d3ColdInterface = &zpoddInfo->D3ColdInterface;
221 
222     status = d3ColdInterface->GetIdleWakeInfo(d3ColdInterface->Context,
223                                               PowerSystemWorking,
224                                               &deepestWakeableDstate);
225 
226     if (!NT_SUCCESS(status))
227     {
228         goto Cleanup;
229     }
230 
231     //
232     // DeviceExtension->PowerDescriptor->D3ColdSupported is retrieved from lower layer.
233     // It has more accurate supportive information than just uses d3ColdInterface->GetD3ColdCapability
234     //
235     if ((deepestWakeableDstate != DeviceWakeDepthD3cold) ||
236         (DeviceExtension->PowerDescriptor->D3ColdSupported == FALSE))
237     {
238         status = STATUS_NOT_SUPPORTED;
239 
240         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
241                     "DeviceInitializeZPODD: ZPODD not enabled due to ACPI support not present.\n"
242                     ));
243 
244         goto Cleanup;
245     }
246 
247     //
248     // The system meets all requirements. Go ahead and enable ZPODD.
249     //
250 
251     //
252     // Register with the runtime power framework.
253     // Note that no un-registration is needed during tear-down.
254     // D3Cold will be enabled (success case of following call) or disabled by port driver during processing Enable Idle Power IOCTL.
255     //
256 
257     status = DeviceSendEnableIdlePowerIoctl(DeviceExtension, TRUE, TRUE, DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS);
258 
259     if (!NT_SUCCESS(status))
260     {
261         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
262                     "DeviceInitializeZPODD: ZPODD not enabled due to runtime power framework.\n"
263                     ));
264 
265         goto Cleanup;
266     }
267 
268     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
269                 "DeviceInitializeZPODD: ZPODD is enabled.\n"
270                 ));
271 
272     DeviceExtension->ZeroPowerODDInfo = zpoddInfo;
273 
274     //
275     // If device is not in home position, then we should take an active reference here
276     // to prevent it from being powered off.
277     //
278 
279     inHomePosition = DeviceZPODDIsInHomePosition(DeviceExtension);
280 
281     if (inHomePosition == FALSE)
282     {
283         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
284                    "DeviceInitializeZPODD: not ready to power off, device marked as active\n"));
285 
286         DeviceMarkActive(DeviceExtension, TRUE, FALSE);
287     }
288     else
289     {
290         //
291         // cache get configuration response.
292         // failing is not critical, so we don't want to check for status here.
293         //
294 
295         if (zpoddInfo->GetConfigurationBuffer == NULL)
296         {
297             tempStatus = DeviceGetConfigurationWithAlloc(DeviceExtension->Device,
298                                                          &zpoddInfo->GetConfigurationBuffer,
299                                                          &zpoddInfo->GetConfigurationBufferSize,
300                                                          FeatureProfileList,
301                                                          SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
302 
303             UNREFERENCED_PARAMETER(tempStatus); // Avoid PREFAST warning.
304         }
305     }
306 
307 Cleanup:
308 
309     if (!NT_SUCCESS(status))
310     {
311         //
312         // We register always even in non-ZPODD case, per request from storport.
313         //
314 
315         tempStatus = DeviceSendEnableIdlePowerIoctl(DeviceExtension, FALSE, FALSE, DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS);
316 
317         if (NT_SUCCESS(tempStatus))
318         {
319             //
320             // Mark the device active; this reference will never be released unless the system enters a
321             // low power state.
322             //
323 
324             DeviceMarkActive(DeviceExtension, TRUE, FALSE);
325         }
326 
327         FREE_POOL(zpoddInfo);
328     }
329 
330     //
331     // If Zero Power ODD is not supported, we should not block the device init sequence.
332     //
333 
334     return STATUS_SUCCESS;
335 }
336 
_IRQL_requires_max_(PASSIVE_LEVEL)337 _IRQL_requires_max_(PASSIVE_LEVEL)
338 ULONG
339 DeviceGetZPODDEnabledFromRegistry()
340 /*++
341 
342 Routine Description:
343 
344     Get the ZeroPowerODDEnabled value from registry, which dictates if Zero Power ODD
345     should be enabled or not. If the value is not in registry, by default Zero
346     Power ODD is enabled.
347 
348 Arguments:
349 
350     None
351 
352 Return Value:
353 
354     ULONG
355 
356 --*/
357 {
358     NTSTATUS    status = STATUS_SUCCESS;
359     WDFKEY      registryKey = NULL;
360     ULONG       ZPODDEnabled = 0;
361 
362     DECLARE_CONST_UNICODE_STRING(registryValueName, L"ZeroPowerODDEnabled");
363 
364     PAGED_CODE();
365 
366     //
367     // open the Parameters key under the service key.
368     //
369 
370     status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
371                                                 KEY_READ,
372                                                 WDF_NO_OBJECT_ATTRIBUTES,
373                                                 &registryKey);
374 
375     if (NT_SUCCESS(status))
376     {
377         status = WdfRegistryQueryULong(registryKey,
378                                        &registryValueName,
379                                        &ZPODDEnabled);
380 
381         WdfRegistryClose(registryKey);
382     }
383 
384     if (!NT_SUCCESS(status))
385     {
386         //
387         // By default, Zero Power ODD is enabled
388         //
389 
390         ZPODDEnabled = 1;
391     }
392 
393     return ZPODDEnabled;
394 }
395 
_IRQL_requires_max_(PASSIVE_LEVEL)396 _IRQL_requires_max_(PASSIVE_LEVEL)
397 NTSTATUS
398 DeviceQueryD3ColdInterface(
399     _In_    PCDROM_DEVICE_EXTENSION     DeviceExtension,
400     _Out_   PD3COLD_SUPPORT_INTERFACE   D3ColdInterface
401     )
402 /*++
403 
404 Routine Description:
405 
406     Queries ACPI for the D3Cold support interface.
407 
408 Arguments:
409 
410     DeviceExtension - the device extension
411     D3ColdInterface - output buffer receiving the interface
412 
413 Return Value:
414 
415     NTSTATUS
416 
417 --*/
418 {
419     PIRP                irp = NULL;
420     KEVENT              event;
421     NTSTATUS            status = STATUS_SUCCESS;
422     PDEVICE_OBJECT      targetDevice = NULL;
423     IO_STATUS_BLOCK     ioStatus = {0};
424     PIO_STACK_LOCATION  irpStack = NULL;
425 
426     PAGED_CODE();
427 
428     RtlZeroMemory(D3ColdInterface, sizeof(D3COLD_SUPPORT_INTERFACE));
429 
430     //
431     // Query D3COLD support interface synchronously
432     //
433 
434     KeInitializeEvent(&event, NotificationEvent, FALSE);
435 
436     targetDevice = IoGetAttachedDeviceReference(DeviceExtension->DeviceObject);
437 
438     irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
439                                        targetDevice,
440                                        NULL,
441                                        0,
442                                        0,
443                                        &event,
444                                        &ioStatus);
445 
446     if (irp == NULL)
447     {
448         status = STATUS_INSUFFICIENT_RESOURCES;
449 
450         goto Cleanup;
451     }
452 
453     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
454     irp->IoStatus.Information = 0;
455 
456     irpStack = IoGetNextIrpStackLocation(irp);
457     irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
458     irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_D3COLD_SUPPORT_INTERFACE;
459     irpStack->Parameters.QueryInterface.Size = sizeof (D3COLD_SUPPORT_INTERFACE);
460     irpStack->Parameters.QueryInterface.Version = D3COLD_SUPPORT_INTERFACE_VERSION;
461     irpStack->Parameters.QueryInterface.Interface = (PINTERFACE) D3ColdInterface;
462     irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
463 
464     status = IoCallDriver(targetDevice, irp);
465 
466     if (status == STATUS_PENDING)
467     {
468         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
469 
470         status = ioStatus.Status;
471     }
472 
473     if (!NT_SUCCESS(status))
474     {
475         goto Cleanup;
476     }
477 
478     NT_ASSERT(D3ColdInterface->SetD3ColdSupport != NULL);
479     NT_ASSERT(D3ColdInterface->GetIdleWakeInfo != NULL);
480     NT_ASSERT(D3ColdInterface->GetD3ColdCapability != NULL);
481 
482 Cleanup:
483 
484     ObDereferenceObject(targetDevice);
485 
486     return status;
487 }
488 
_IRQL_requires_max_(PASSIVE_LEVEL)489 _IRQL_requires_max_(PASSIVE_LEVEL)
490 NTSTATUS
491 DeviceSendEnableIdlePowerIoctl(
492     _In_    PCDROM_DEVICE_EXTENSION     DeviceExtension,
493     _In_    BOOLEAN                     WakeCapable,
494     _In_    BOOLEAN                     Enable,
495     _In_    ULONG                       D3IdleTimeout
496     )
497 /*++
498 
499 Routine Description:
500 
501     Enables idle power support.
502 
503 Arguments:
504 
505     DeviceExtension - the device extension
506     WakeCapable - whether the device is wake capable
507     Enable - enable / disable idle power management
508 
509 Return Value:
510 
511     NTSTATUS
512 
513 --*/
514 {
515     NTSTATUS            status = STATUS_SUCCESS;
516     STORAGE_IDLE_POWER  idlePower = {0};
517     IO_STATUS_BLOCK     ioStatus = {0};
518     PIRP                irp = NULL;
519     KEVENT              event;
520 
521     PAGED_CODE();
522 
523     idlePower.Version = 1;
524     idlePower.Size = sizeof (STORAGE_IDLE_POWER);
525     idlePower.WakeCapableHint = WakeCapable;
526     idlePower.D3ColdSupported = Enable;
527     idlePower.D3IdleTimeout = D3IdleTimeout;
528 
529     KeInitializeEvent(&event, NotificationEvent, FALSE);
530 
531     irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_ENABLE_IDLE_POWER,
532                                         DeviceExtension->LowerPdo,
533                                         &idlePower,
534                                         sizeof(STORAGE_IDLE_POWER),
535                                         NULL,
536                                         0,
537                                         FALSE,
538                                         &event,
539                                         &ioStatus);
540 
541     if (irp == NULL)
542     {
543         status = STATUS_INSUFFICIENT_RESOURCES;
544     }
545     else
546     {
547         //
548         // Send the synchronous request to port driver.
549         //
550 
551         status = IoCallDriver(DeviceExtension->LowerPdo, irp);
552 
553         if (status == STATUS_PENDING)
554         {
555             KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
556 
557             status = ioStatus.Status;
558         }
559     }
560 
561     TracePrint((TRACE_LEVEL_INFORMATION,
562                 TRACE_FLAG_POWER,
563                 "DeviceSendEnableIdlePowerIoctl: Port driver returned status (%x) for FDO (%p)\n"
564                 "\tD3ColdSupported: %u\n"
565                 "\tD3IdleTimeout: %u (ms)",
566                 status,
567                 DeviceExtension->DeviceObject,
568                 Enable,
569                 DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS));
570 
571     return status;
572 }
573 
_IRQL_requires_max_(APC_LEVEL)574 _IRQL_requires_max_(APC_LEVEL)
575 VOID
576 DeviceReleaseZPODDResources(
577     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
578     )
579 /*++
580 
581 Routine Description:
582 
583     This routine will cleanup any resources allocated for ZPODD.
584 
585 Arguments:
586 
587     DeviceExtension - the device context
588 
589 Return Value:
590     None.
591 
592 --*/
593 {
594     PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
595 
596     PAGED_CODE()
597 
598     if (zpoddInfo != NULL)
599     {
600         FREE_POOL(zpoddInfo->GetConfigurationBuffer);
601         FREE_POOL(zpoddInfo);
602     }
603 
604     return;
605 }
606 
607 NTSTATUS
DeviceZPODDGetPowerupReason(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_Out_ PSTORAGE_IDLE_POWERUP_REASON PowerupReason)608 DeviceZPODDGetPowerupReason(
609     _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
610     _Out_   PSTORAGE_IDLE_POWERUP_REASON PowerupReason
611     )
612 /*++
613 
614 Routine Description:
615 
616     This routine queries the port driver for what caused the power up.
617 
618 Arguments:
619 
620     DeviceExtension - device extension.
621     PowerupReason - what caused the power up.
622 
623 Return Value:
624     NTSTATUS
625 
626 --*/
627 {
628     NTSTATUS                status = STATUS_SUCCESS;
629     PIRP                    irp = NULL;
630     IO_STATUS_BLOCK         ioStatus = {0};
631     KEVENT                  event;
632 
633     RtlZeroMemory(PowerupReason, sizeof (STORAGE_IDLE_POWERUP_REASON));
634 
635     PowerupReason->Size = sizeof (STORAGE_IDLE_POWERUP_REASON);
636     PowerupReason->Version = STORAGE_IDLE_POWERUP_REASON_VERSION_V1;
637 
638     //
639     // Setup a synchronous irp.
640     //
641 
642     KeInitializeEvent(&event, NotificationEvent, FALSE);
643 
644     irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_IDLE_POWERUP_REASON,
645                                         DeviceExtension->LowerPdo,
646                                         PowerupReason,
647                                         sizeof (STORAGE_IDLE_POWERUP_REASON),
648                                         PowerupReason,
649                                         sizeof (STORAGE_IDLE_POWERUP_REASON),
650                                         FALSE,
651                                         &event,
652                                         &ioStatus);
653 
654     if (irp == NULL)
655     {
656         status = STATUS_INSUFFICIENT_RESOURCES;
657     }
658     else
659     {
660         //
661         // Send the synchronous request to port driver.
662         //
663 
664         status = IoCallDriver(DeviceExtension->LowerPdo, irp);
665 
666         if (status == STATUS_PENDING)
667         {
668             KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
669 
670             status = ioStatus.Status;
671         }
672     }
673 
674     return status;
675 }
676 
_IRQL_requires_max_(PASSIVE_LEVEL)677 _IRQL_requires_max_(PASSIVE_LEVEL)
678 BOOLEAN
679 DeviceZPODDIsInHomePosition(
680     _In_    PCDROM_DEVICE_EXTENSION DeviceExtension
681     )
682 /*++
683 
684 Routine Description:
685 
686     Checks to see if the device is ready to be powered off.
687     Requirements are: 1. tray closed 2. no media present.
688 
689 Arguments:
690 
691     DeviceExtension - device extension.
692 
693 Return Value:
694     BOOLEAN
695 
696 --*/
697 {
698     NTSTATUS             status = STATUS_SUCCESS;
699     PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
700     SCSI_REQUEST_BLOCK   srb = {0};
701     PCDB                 cdb = (PCDB) srb.Cdb;
702     BOOLEAN              inHomePosition = FALSE;
703 
704     PAGED_CODE();
705 
706     if (zpoddInfo != NULL)
707     {
708         //
709         // Clear sense data.
710         //
711 
712         zpoddInfo->SenseKey = 0;
713         zpoddInfo->AdditionalSenseCode = 0;
714         zpoddInfo->AdditionalSenseCodeQualifier = 0;
715 
716         //
717         // Send a Test Unit Ready to check media & tray status.
718         //
719 
720         RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
721 
722         srb.CdbLength = 6;
723         cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
724 
725         srb.TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT;
726 
727         status = DeviceSendSrbSynchronously(DeviceExtension->Device,
728                                             &srb,
729                                             NULL,
730                                             0,
731                                             FALSE,
732                                             NULL);
733 
734 #ifdef __REACTOS__
735         if (!NT_SUCCESS(status))
736         {
737             return FALSE;
738         }
739 #endif
740 
741         //
742         // At this time, sense data, if available, is already copied into zpoddInfo.
743         //
744         // We don't check for status because we expect it to fail in case there is no media in device.
745         //
746         // Should enter Zero Power state if:
747         //
748         // Drawer:  02/3A/01
749         // Slot:    02/3A/xx
750         //
751 
752         if (((zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) && (zpoddInfo->Load == 0) && // Drawer
753              (zpoddInfo->SenseKey                       == SCSI_SENSE_NOT_READY) &&
754              (zpoddInfo->AdditionalSenseCode            == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) &&
755              (zpoddInfo->AdditionalSenseCodeQualifier   == 0x01)) ||
756             ((zpoddInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) && (zpoddInfo->Load == 0) && // Slot
757              (zpoddInfo->SenseKey                       == SCSI_SENSE_NOT_READY) &&
758              (zpoddInfo->AdditionalSenseCode            == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)))
759         {
760             inHomePosition = TRUE;
761         }
762     }
763 
764     return inHomePosition;
765 }
766 
_IRQL_requires_max_(PASSIVE_LEVEL)767 _IRQL_requires_max_(PASSIVE_LEVEL)
768 VOID
769 DeviceMarkActive(
770     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
771     _In_ BOOLEAN                 IsActive,
772     _In_ BOOLEAN                 SetIdleTimeout
773     )
774 /*++
775 
776 Routine Description:
777 
778     This routine will mark the device as active / idle.
779 
780 Arguments:
781 
782     DeviceExtension - the device context
783     IsActive - if the device should be marked as active
784 
785 Return Value:
786     None.
787 
788 --*/
789 {
790     NTSTATUS                status = STATUS_SUCCESS;
791     PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
792 
793     PAGED_CODE()
794 
795     if (DeviceExtension->IsActive != IsActive)
796     {
797         if ((IsActive == FALSE) && (zpoddInfo != NULL))
798         {
799             // cache get configuration response.
800             // failing is not critical, so we don't want to check for status here.
801             if (zpoddInfo->GetConfigurationBuffer == NULL)
802             {
803                 (VOID)DeviceGetConfigurationWithAlloc(DeviceExtension->Device,
804                                                       &zpoddInfo->GetConfigurationBuffer,
805                                                       &zpoddInfo->GetConfigurationBufferSize,
806                                                       FeatureProfileList,
807                                                       SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
808             }
809         }
810 
811         if (SetIdleTimeout)
812         {
813             status = DeviceSendEnableIdlePowerIoctl(DeviceExtension,
814                                                     FALSE,
815                                                     FALSE,
816                                                     IsActive ? DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS : DELAY_TIME_TO_ENTER_AOAC_IDLE_POWER_IN_MS);
817         }
818 
819         if (NT_SUCCESS(status))
820         {
821             DeviceSendIoctlAsynchronously(DeviceExtension,
822                                           IsActive ? IOCTL_STORAGE_POWER_ACTIVE : IOCTL_STORAGE_POWER_IDLE,
823                                           DeviceExtension->LowerPdo);
824         }
825 
826         DeviceExtension->IsActive = IsActive;
827     }
828 }
829 
830 #pragma warning(pop) // un-sets any local warning changes
831 
832 
833