summaryrefslogtreecommitdiff
path: root/storage/class/cdrom/src/zpodd.c
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-04-29 09:48:49 -0700
committerDave Wilson <[email protected]>2015-04-29 10:47:29 -0700
commitd40232638faaab19de557e70a3ee938da7a35295 (patch)
tree14e61c973745949af186b7940e3a53d25322f5de /storage/class/cdrom/src/zpodd.c
parent02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff)
samples update for //build
Diffstat (limited to 'storage/class/cdrom/src/zpodd.c')
-rw-r--r--storage/class/cdrom/src/zpodd.c825
1 files changed, 825 insertions, 0 deletions
diff --git a/storage/class/cdrom/src/zpodd.c b/storage/class/cdrom/src/zpodd.c
new file mode 100644
index 00000000..59434896
--- /dev/null
+++ b/storage/class/cdrom/src/zpodd.c
@@ -0,0 +1,825 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ zpodd.c
+
+Abstract:
+
+ Code for Zero Power ODD support.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "wdmguid.h"
+#include "cdrom.h"
+#include "mmc.h"
+#include "ioctl.h"
+#include "scratch.h"
+
+#ifdef DEBUG_USE_WPP
+#include "zpodd.tmh"
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+ULONG
+DeviceGetZPODDEnabledFromRegistry();
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceQueryD3ColdInterface(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PD3COLD_SUPPORT_INTERFACE D3ColdInterface
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceSendEnableIdlePowerIoctl(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _In_ BOOLEAN WakeCapable,
+ _In_ BOOLEAN Enable,
+ _In_ ULONG D3IdleTimeout
+ );
+
+#if ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DeviceInitializeZPODD)
+#pragma alloc_text(PAGE, DeviceGetZPODDEnabledFromRegistry)
+#pragma alloc_text(PAGE, DeviceQueryD3ColdInterface)
+#pragma alloc_text(PAGE, DeviceSendEnableIdlePowerIoctl)
+#pragma alloc_text(PAGE, DeviceReleaseZPODDResources)
+#pragma alloc_text(PAGE, DeviceZPODDIsInHomePosition)
+#pragma alloc_text(PAGE, DeviceMarkActive)
+
+#endif
+
+#pragma warning(push)
+#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
+#pragma warning(disable:26000) // read overflow reported because of pointer type conversion
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeZPODD(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+ )
+/*++
+
+Routine Description:
+
+ This routine initialize the contents of ZPODD structure.
+
+Arguments:
+
+ DeviceExtension - the device extension
+
+Return Value:
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ NTSTATUS tempStatus = STATUS_SUCCESS;
+ PZERO_POWER_ODD_INFO zpoddInfo = NULL;
+ PFEATURE_DATA_REMOVABLE_MEDIUM removableMediumHeader = NULL;
+ ULONG ZPODDEnabledInRegistry = 0;
+ PD3COLD_SUPPORT_INTERFACE d3ColdInterface = NULL;
+ DEVICE_WAKE_DEPTH deepestWakeableDstate = DeviceWakeDepthNotWakeable;
+ BOOLEAN inHomePosition = FALSE;
+
+ PAGED_CODE();
+
+ if (DeviceExtension->ZeroPowerODDInfo != NULL)
+ {
+ //
+ // Already initialized.
+ //
+
+ goto Cleanup;
+ }
+
+ ZPODDEnabledInRegistry = DeviceGetZPODDEnabledFromRegistry();
+
+ if (ZPODDEnabledInRegistry == 0)
+ {
+ //
+ // User has explicitly disabled Zero Power ODD.
+ //
+
+ status = STATUS_NOT_SUPPORTED;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceInitializeZPODD: ZPODD not enabled due to registry settings.\n"
+ ));
+
+ goto Cleanup;
+ }
+
+ zpoddInfo = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(ZERO_POWER_ODD_INFO),
+ CDROM_TAG_ZERO_POWER_ODD);
+
+ if (zpoddInfo == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ goto Cleanup;
+ }
+
+ RtlZeroMemory(zpoddInfo, sizeof (ZERO_POWER_ODD_INFO));
+
+ //
+ // Check the system for the following prerequisites:
+ //
+ // 1. SATA: Device Attention line
+ // 2. SATA: Asynchronous Notification
+ // 3. ODD: LoChange / MediaRemoval
+ // 4. ACPI: Wake capable
+ //
+ // Only drawer and slot loading types have well defined behaviors in the spec, so only these two
+ // types are supported.
+ //
+
+ //
+ // Check for DA & AN
+ //
+
+ if ((DeviceExtension->PowerDescriptor == NULL) ||
+ (DeviceExtension->PowerDescriptor->DeviceAttentionSupported == FALSE) ||
+ (DeviceExtension->PowerDescriptor->AsynchronousNotificationSupported == FALSE))
+ {
+ status = STATUS_NOT_SUPPORTED;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceInitializeZPODD: ZPODD not enabled due to SATA features not present.\n"
+ ));
+
+ goto Cleanup;
+ }
+
+ //
+ // Check for LoChange / MediaRemoval
+ //
+
+ removableMediumHeader = (PFEATURE_DATA_REMOVABLE_MEDIUM)
+ DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
+ DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
+ FeatureRemovableMedium);
+
+ if ((removableMediumHeader == NULL) ||
+ !((removableMediumHeader->LoadingMechanism == LOADING_MECHANISM_TRAY) && (removableMediumHeader->Load == 0) && // Drawer ...
+ (removableMediumHeader->DBML != FALSE)) && // requires LoChange/NotBusy
+ !((removableMediumHeader->LoadingMechanism == LOADING_MECHANISM_CADDY) && (removableMediumHeader->Load == 0) && // Slot ...
+ (DeviceExtension->MediaChangeDetectionInfo->Gesn.Supported != FALSE))) // requires MediaRemoval
+ {
+ status = STATUS_NOT_SUPPORTED;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceInitializeZPODD: ZPODD not enabled due to ODD features not present.\n"
+ ));
+
+ goto Cleanup;
+ }
+
+ zpoddInfo->LoadingMechanism = removableMediumHeader->LoadingMechanism;
+ zpoddInfo->Load = removableMediumHeader->Load;
+
+ //
+ // Check for ACPI
+ //
+
+ status = DeviceQueryD3ColdInterface(DeviceExtension, &zpoddInfo->D3ColdInterface);
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceInitializeZPODD: Query D3Cold support interface failed.\n"
+ ));
+
+ goto Cleanup;
+ }
+
+ //
+ // If the platform supports Zero Power ODD, the following conditions must be met:
+ //
+ // 1. The deepest wakeable D-state for the device is D3Cold;
+ // 2. The platform supports D3Cold for the device.
+ //
+
+ d3ColdInterface = &zpoddInfo->D3ColdInterface;
+
+ status = d3ColdInterface->GetIdleWakeInfo(d3ColdInterface->Context,
+ PowerSystemWorking,
+ &deepestWakeableDstate);
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Cleanup;
+ }
+
+ //
+ // DeviceExtension->PowerDescriptor->D3ColdSupported is retrieved from lower layer.
+ // It has more accurate supportive information than just uses d3ColdInterface->GetD3ColdCapability
+ //
+ if ((deepestWakeableDstate != DeviceWakeDepthD3cold) ||
+ (DeviceExtension->PowerDescriptor->D3ColdSupported == FALSE))
+ {
+ status = STATUS_NOT_SUPPORTED;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceInitializeZPODD: ZPODD not enabled due to ACPI support not present.\n"
+ ));
+
+ goto Cleanup;
+ }
+
+ //
+ // The system meets all requirements. Go ahead and enable ZPODD.
+ //
+
+ //
+ // Register with the runtime power framework.
+ // Note that no un-registration is needed during tear-down.
+ // D3Cold will be enabled (success case of following call) or disabled by port driver during processing Enable Idle Power IOCTL.
+ //
+
+ status = DeviceSendEnableIdlePowerIoctl(DeviceExtension, TRUE, TRUE, DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS);
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceInitializeZPODD: ZPODD not enabled due to runtime power framework.\n"
+ ));
+
+ goto Cleanup;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceInitializeZPODD: ZPODD is enabled.\n"
+ ));
+
+ DeviceExtension->ZeroPowerODDInfo = zpoddInfo;
+
+ //
+ // If device is not in home position, then we should take an active reference here
+ // to prevent it from being powered off.
+ //
+
+ inHomePosition = DeviceZPODDIsInHomePosition(DeviceExtension);
+
+ if (inHomePosition == FALSE)
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceInitializeZPODD: not ready to power off, device marked as active\n"));
+
+ DeviceMarkActive(DeviceExtension, TRUE, FALSE);
+ }
+ else
+ {
+ //
+ // cache get configuration response.
+ // failing is not critical, so we don't want to check for status here.
+ //
+
+ if (zpoddInfo->GetConfigurationBuffer == NULL)
+ {
+ tempStatus = DeviceGetConfigurationWithAlloc(DeviceExtension->Device,
+ &zpoddInfo->GetConfigurationBuffer,
+ &zpoddInfo->GetConfigurationBufferSize,
+ FeatureProfileList,
+ SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+
+ UNREFERENCED_PARAMETER(tempStatus); // Avoid PREFAST warning.
+ }
+ }
+
+Cleanup:
+
+ if (!NT_SUCCESS(status))
+ {
+ //
+ // We register always even in non-ZPODD case, per request from storport.
+ //
+
+ tempStatus = DeviceSendEnableIdlePowerIoctl(DeviceExtension, FALSE, FALSE, DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS);
+
+ if (NT_SUCCESS(tempStatus))
+ {
+ //
+ // Mark the device active; this reference will never be released unless the system enters a
+ // low power state.
+ //
+
+ DeviceMarkActive(DeviceExtension, TRUE, FALSE);
+ }
+
+ FREE_POOL(zpoddInfo);
+ }
+
+ //
+ // If Zero Power ODD is not supported, we should not block the device init sequence.
+ //
+
+ return STATUS_SUCCESS;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+ULONG
+DeviceGetZPODDEnabledFromRegistry()
+/*++
+
+Routine Description:
+
+ Get the ZeroPowerODDEnabled value from registry, which dictates if Zero Power ODD
+ should be enabled or not. If the value is not in registry, by default Zero
+ Power ODD is enabled.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ ULONG
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ WDFKEY registryKey = NULL;
+ ULONG ZPODDEnabled = 0;
+
+ DECLARE_CONST_UNICODE_STRING(registryValueName, L"ZeroPowerODDEnabled");
+
+ PAGED_CODE();
+
+ //
+ // open the Parameters key under the service key.
+ //
+
+ status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &registryKey);
+
+ if (NT_SUCCESS(status))
+ {
+ status = WdfRegistryQueryULong(registryKey,
+ &registryValueName,
+ &ZPODDEnabled);
+
+ WdfRegistryClose(registryKey);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ //
+ // By default, Zero Power ODD is enabled
+ //
+
+ ZPODDEnabled = 1;
+ }
+
+ return ZPODDEnabled;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceQueryD3ColdInterface(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PD3COLD_SUPPORT_INTERFACE D3ColdInterface
+ )
+/*++
+
+Routine Description:
+
+ Queries ACPI for the D3Cold support interface.
+
+Arguments:
+
+ DeviceExtension - the device extension
+ D3ColdInterface - output buffer receiving the interface
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PIRP irp = NULL;
+ KEVENT event;
+ NTSTATUS status = STATUS_SUCCESS;
+ PDEVICE_OBJECT targetDevice = NULL;
+ IO_STATUS_BLOCK ioStatus = {0};
+ PIO_STACK_LOCATION irpStack = NULL;
+
+ PAGED_CODE();
+
+ RtlZeroMemory(D3ColdInterface, sizeof(D3COLD_SUPPORT_INTERFACE));
+
+ //
+ // Query D3COLD support interface synchronously
+ //
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ targetDevice = IoGetAttachedDeviceReference(DeviceExtension->DeviceObject);
+
+ irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+ targetDevice,
+ NULL,
+ 0,
+ 0,
+ &event,
+ &ioStatus);
+
+ if (irp == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ goto Cleanup;
+ }
+
+ irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+ irp->IoStatus.Information = 0;
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+ irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
+ irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_D3COLD_SUPPORT_INTERFACE;
+ irpStack->Parameters.QueryInterface.Size = sizeof (D3COLD_SUPPORT_INTERFACE);
+ irpStack->Parameters.QueryInterface.Version = D3COLD_SUPPORT_INTERFACE_VERSION;
+ irpStack->Parameters.QueryInterface.Interface = (PINTERFACE) D3ColdInterface;
+ irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
+
+ status = IoCallDriver(targetDevice, irp);
+
+ if (status == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+ status = ioStatus.Status;
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Cleanup;
+ }
+
+ NT_ASSERT(D3ColdInterface->SetD3ColdSupport != NULL);
+ NT_ASSERT(D3ColdInterface->GetIdleWakeInfo != NULL);
+ NT_ASSERT(D3ColdInterface->GetD3ColdCapability != NULL);
+
+Cleanup:
+
+ ObDereferenceObject(targetDevice);
+
+ return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceSendEnableIdlePowerIoctl(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _In_ BOOLEAN WakeCapable,
+ _In_ BOOLEAN Enable,
+ _In_ ULONG D3IdleTimeout
+ )
+/*++
+
+Routine Description:
+
+ Enables idle power support.
+
+Arguments:
+
+ DeviceExtension - the device extension
+ WakeCapable - whether the device is wake capable
+ Enable - enable / disable idle power management
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ STORAGE_IDLE_POWER idlePower = {0};
+ IO_STATUS_BLOCK ioStatus = {0};
+ PIRP irp = NULL;
+ KEVENT event;
+
+ PAGED_CODE();
+
+ idlePower.Version = 1;
+ idlePower.Size = sizeof (STORAGE_IDLE_POWER);
+ idlePower.WakeCapableHint = WakeCapable;
+ idlePower.D3ColdSupported = Enable;
+ idlePower.D3IdleTimeout = D3IdleTimeout;
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_ENABLE_IDLE_POWER,
+ DeviceExtension->LowerPdo,
+ &idlePower,
+ sizeof(STORAGE_IDLE_POWER),
+ NULL,
+ 0,
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ else
+ {
+ //
+ // Send the synchronous request to port driver.
+ //
+
+ status = IoCallDriver(DeviceExtension->LowerPdo, irp);
+
+ if (status == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+ status = ioStatus.Status;
+ }
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_POWER,
+ "DeviceSendEnableIdlePowerIoctl: Port driver returned status (%x) for FDO (%p)\n"
+ "\tD3ColdSupported: %u\n"
+ "\tD3IdleTimeout: %u (ms)",
+ status,
+ DeviceExtension->DeviceObject,
+ Enable,
+ DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS));
+
+ return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceReleaseZPODDResources(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+ )
+/*++
+
+Routine Description:
+
+ This routine will cleanup any resources allocated for ZPODD.
+
+Arguments:
+
+ DeviceExtension - the device context
+
+Return Value:
+ None.
+
+--*/
+{
+ PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+ PAGED_CODE()
+
+ if (zpoddInfo != NULL)
+ {
+ FREE_POOL(zpoddInfo->GetConfigurationBuffer);
+ FREE_POOL(zpoddInfo);
+ }
+
+ return;
+}
+
+NTSTATUS
+DeviceZPODDGetPowerupReason(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PSTORAGE_IDLE_POWERUP_REASON PowerupReason
+ )
+/*++
+
+Routine Description:
+
+ This routine queries the port driver for what caused the power up.
+
+Arguments:
+
+ DeviceExtension - device extension.
+ PowerupReason - what caused the power up.
+
+Return Value:
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIRP irp = NULL;
+ IO_STATUS_BLOCK ioStatus = {0};
+ KEVENT event;
+
+ RtlZeroMemory(PowerupReason, sizeof (STORAGE_IDLE_POWERUP_REASON));
+
+ PowerupReason->Size = sizeof (STORAGE_IDLE_POWERUP_REASON);
+ PowerupReason->Version = STORAGE_IDLE_POWERUP_REASON_VERSION_V1;
+
+ //
+ // Setup a synchronous irp.
+ //
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_IDLE_POWERUP_REASON,
+ DeviceExtension->LowerPdo,
+ PowerupReason,
+ sizeof (STORAGE_IDLE_POWERUP_REASON),
+ PowerupReason,
+ sizeof (STORAGE_IDLE_POWERUP_REASON),
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ else
+ {
+ //
+ // Send the synchronous request to port driver.
+ //
+
+ status = IoCallDriver(DeviceExtension->LowerPdo, irp);
+
+ if (status == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+ status = ioStatus.Status;
+ }
+ }
+
+ return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+DeviceZPODDIsInHomePosition(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+ )
+/*++
+
+Routine Description:
+
+ Checks to see if the device is ready to be powered off.
+ Requirements are: 1. tray closed 2. no media present.
+
+Arguments:
+
+ DeviceExtension - device extension.
+
+Return Value:
+ BOOLEAN
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+ SCSI_REQUEST_BLOCK srb = {0};
+ PCDB cdb = (PCDB) srb.Cdb;
+ BOOLEAN inHomePosition = FALSE;
+
+ PAGED_CODE();
+
+ if (zpoddInfo != NULL)
+ {
+ //
+ // Clear sense data.
+ //
+
+ zpoddInfo->SenseKey = 0;
+ zpoddInfo->AdditionalSenseCode = 0;
+ zpoddInfo->AdditionalSenseCodeQualifier = 0;
+
+ //
+ // Send a Test Unit Ready to check media & tray status.
+ //
+
+ RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ srb.CdbLength = 6;
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+ srb.TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT;
+
+ status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+ &srb,
+ NULL,
+ 0,
+ FALSE,
+ NULL);
+
+ //
+ // At this time, sense data, if available, is already copied into zpoddInfo.
+ //
+ // We don't check for status because we expect it to fail in case there is no media in device.
+ //
+ // Should enter Zero Power state if:
+ //
+ // Drawer: 02/3A/01
+ // Slot: 02/3A/xx
+ //
+
+ if (((zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) && (zpoddInfo->Load == 0) && // Drawer
+ (zpoddInfo->SenseKey == SCSI_SENSE_NOT_READY) &&
+ (zpoddInfo->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) &&
+ (zpoddInfo->AdditionalSenseCodeQualifier == 0x01)) ||
+ ((zpoddInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) && (zpoddInfo->Load == 0) && // Slot
+ (zpoddInfo->SenseKey == SCSI_SENSE_NOT_READY) &&
+ (zpoddInfo->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)))
+ {
+ inHomePosition = TRUE;
+ }
+ }
+
+ return inHomePosition;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceMarkActive(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _In_ BOOLEAN IsActive,
+ _In_ BOOLEAN SetIdleTimeout
+ )
+/*++
+
+Routine Description:
+
+ This routine will mark the device as active / idle.
+
+Arguments:
+
+ DeviceExtension - the device context
+ IsActive - if the device should be marked as active
+
+Return Value:
+ None.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+ PAGED_CODE()
+
+ if (DeviceExtension->IsActive != IsActive)
+ {
+ if ((IsActive == FALSE) && (zpoddInfo != NULL))
+ {
+ // cache get configuration response.
+ // failing is not critical, so we don't want to check for status here.
+ if (zpoddInfo->GetConfigurationBuffer == NULL)
+ {
+ (VOID)DeviceGetConfigurationWithAlloc(DeviceExtension->Device,
+ &zpoddInfo->GetConfigurationBuffer,
+ &zpoddInfo->GetConfigurationBufferSize,
+ FeatureProfileList,
+ SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+ }
+ }
+
+ if (SetIdleTimeout)
+ {
+ status = DeviceSendEnableIdlePowerIoctl(DeviceExtension,
+ FALSE,
+ FALSE,
+ IsActive ? DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS : DELAY_TIME_TO_ENTER_AOAC_IDLE_POWER_IN_MS);
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ DeviceSendIoctlAsynchronously(DeviceExtension,
+ IsActive ? IOCTL_STORAGE_POWER_ACTIVE : IOCTL_STORAGE_POWER_IDLE,
+ DeviceExtension->LowerPdo);
+ }
+
+ DeviceExtension->IsActive = IsActive;
+ }
+}
+
+#pragma warning(pop) // un-sets any local warning changes
+
+