Sample Code

Windows Driver Samples/ StorAhci StorPort Miniport Driver/ C++/ src/ entrypts.c/

/*++

Copyright (C) Microsoft Corporation, 2009

Module Name:

    entrypts.c

Abstract:

    This file contains function of entry points to the AHCI miniport.


Notes:

Revision History:

--*/

#if _MSC_VER >= 1200
#pragma warning(push)
#endif

#pragma warning(disable:4152)   // nonstandard extension, function/data pointer conversion in expression
#pragma warning(disable:4214) // bit field types other than int
#pragma warning(disable:4201) // nameless struct/union


#include "generic.h"


// storahci.sys global variables
PVOID   g_AdapterExtension[4] = {0};
UCHAR   g_AdapterExtensionIndex = 0;

//
// Boot device telemetry default initialization values, override by the registry table
//

ULONG
AhciPublicGPLogTableAddresses[TC_PUBLIC_DEVICEDUMP_CONTENT_GPLOG_MAX] =
    {
        //0x01,     // Summary SMART error log. Accessed by SMART command only
        //0x02,     // Comprehensive SMART error log. Accessed by SMART command only
        0x04,       // Device statistics page
        0x08,       // Power conditions
        0x0d,       // LPS Mis-alignment
        0x10,       // NCQ command error log
        0x11,       // SATA PHY event counters log
        0
    };

ULONG
AhciGPLogPageIntoPrivate =  IDE_GP_LOG_CURRENT_DEVICE_INTERNAL_STATUS;

//

ULONG
DriverEntry(
    _In_ PVOID Argument1,    //IN PDRIVER_OBJECT  DriverObject,
    _In_ PVOID Argument2     //IN PUNICODE_STRING  RegistryPath
    )

/*++

Routine Description:

    Initial entry point for miniport driver.

Arguments:

    Driver Object

Return Value:

    Status from StorPortInitialize()

--*/

{
    ULONG status;
    HW_INITIALIZATION_DATA hwInitializationData = {0};

    DebugPrint((1, "\tSTORAHCI, Storport AHCI Miniport Driver.\n"));

    hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);

    // required miniport entry point routines.
    hwInitializationData.HwInitialize = AhciHwInitialize;
    hwInitializationData.HwStartIo = AhciHwStartIo;
    hwInitializationData.HwInterrupt = AhciHwInterrupt;
    hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
    hwInitializationData.HwResetBus = AhciHwResetBus;
    hwInitializationData.HwAdapterControl = AhciHwAdapterControl;
    hwInitializationData.HwBuildIo = AhciHwBuildIo;
    hwInitializationData.HwTracingEnabled = AhciHwTracingEnabled;
    hwInitializationData.HwUnitControl = AhciHwUnitControl;

    // Specifiy adapter specific information.
    hwInitializationData.AutoRequestSense = TRUE;
    hwInitializationData.NeedPhysicalAddresses = TRUE;
    hwInitializationData.NumberOfAccessRanges = NUM_ACCESS_RANGES;
    hwInitializationData.AdapterInterfaceType = PCIBus;
    hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
    hwInitializationData.TaggedQueuing = TRUE;
    hwInitializationData.MultipleRequestPerLu = TRUE;
    hwInitializationData.FeatureSupport |= STOR_FEATURE_ATA_PASS_THROUGH;                       // indicating this miniport driver supports ATA PASS-THROUGH(16) command.
    hwInitializationData.FeatureSupport |= STOR_FEATURE_FULL_PNP_DEVICE_CAPABILITIES;           // indicating this miniport driver supplies values for all PnP Device Capability fields.
    hwInitializationData.FeatureSupport |= STOR_FEATURE_DUMP_POINTERS;                          // indicating this miniport support the dump pointers SRBs
    hwInitializationData.FeatureSupport |= STOR_FEATURE_DUMP_RESUME_CAPABLE;                    // indicating this miniport driver supports resume capability for dump stack.
    hwInitializationData.FeatureSupport |= STOR_FEATURE_DEVICE_NAME_NO_SUFFIX;                  // indicating the miniport driver prefers device friendly name without suffix: "SCSI <type> Device"
    hwInitializationData.FeatureSupport |= STOR_FEATURE_DEVICE_DESCRIPTOR_FROM_ATA_INFO_VPD;    // indicating that port driver forms STORAGE_DEVICE_DESCRIPTOR from ATA Information VPD page rather than INQUIRY data
    hwInitializationData.FeatureSupport |= STOR_FEATURE_EXTRA_IO_INFORMATION;                   // Indicating that miniport driver wants SRBEX_DATA_IO_INFO in a SRBEX if available

    // Set required extension sizes.
    hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);

    // NOTE: Command Table (1st field in AHCI_SRB_EXTENSION structure) must align to 128 bytes as physical limitation.
    // StorPort does not have interface allowing miniport requiring this.
    // Adding 128 in SrbExtensionSize so that we can use the part starting from right alignment.
    hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION) + 128 ; // SrbExtension contains AHCI_SRB_EXTENSION

    //
    // Support SrbEx by default.
    //
    hwInitializationData.SrbTypeFlags = SRB_TYPE_FLAG_STORAGE_REQUEST_BLOCK;

    // call StorPort to register HW init data
    status = StorPortInitialize(Argument1,
                                Argument2,
                                &hwInitializationData,
                                NULL);

    return status;

} // end DriverEntry()

BOOLEAN
AllocateResourcesForAdapter(
    _In_ PAHCI_ADAPTER_EXTENSION         AdapterExtension,
    _In_ PPORT_CONFIGURATION_INFORMATION ConfigInfo,
    _In_range_(1, AHCI_MAX_PORT_COUNT) ULONG PortCount
    )
/*
    Internal function to allocate required memory for Ports

    In AHCI, the controller structures are both the command header list and the received FIS buffer.
        The mechanism for requesting all of this memory is StorPortGetUncachedExtension. This function returns page-aligned address.
        NOTE! StorPortGetUncachedExtension can only be called while in FindAdapter routine.
        NOTE! In order to perform crashdump/hibernate the UncachedExtensionSize should not be larger than 30K.

    Alignment requirement:
    -   Command List Base Addresses: must be 1K aligned, and the Command list is (sizeof (AHCI_COMMAND_HEADER) * cap.NCS), which is some multiple of 32 bytes in length.
    -   The FIS Base Address: must be 256 aligned, and the FIS Receive buffer is sizeof (AHCI_RECEIVED_FIS), 256 bytes in length.
        The Command Table: must be 128 aligned, and is sizeof(AHCI_COMMAND_TABLE), 1280 bytes in length thanks to some padding in the AHCI_COMMAND_TABLE structure.
                           Commmand Table is per request, resides in AHCI_SRB_EXTENSION for each request.

        Size of nonCachedExtensionSize for every channel should round up to KiloBytes, so that the pointer for next channel is starting at 1K boundary.

        the Command Header is variable and must be padded so the Received FIS is on a 256 boundry.
        Therefore the number of Command Headers must be 256/32 = 8. Round cap.NCS to the next multiple of 8
*/
{
    ULONG paddedNCS = 0;
    ULONG paddedSrbExtensionSize = 0;
    ULONG nonCachedExtensionSize = 0;
    PVOID portsChannelExtension = NULL;
    PCHAR portsUncachedExtension = NULL;
    ULONG i = 0;
    ULONG j = 0;

    NT_ASSERT(PortCount >= 1 && PortCount <= AHCI_MAX_PORT_COUNT);

    // 2.1 allocate nonCachedExtension for Hardware access

    // Allocate Identify Data buffer, CommandList, Receive FIS, SRB Extension for Local SRB, INQUIRY buffer
    // paddedNCS is (CAP.NCS+1) rounds up to the next multiple of 8.
    paddedNCS = ((AdapterExtension->CAP.NCS) / 8 + 1) * 8;

    // SrbExtension needs to align to 128 bytes, pad the size to be multiple of 128 bytes
    paddedSrbExtensionSize = ((sizeof(AHCI_SRB_EXTENSION) - 1) / 128 + 1) * 128;

    // size per Port
    nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * paddedNCS +      // align to 1K, padded to multiple of 256 bytes. (nocachedextension is page aligned)
                             sizeof(AHCI_RECEIVED_FIS) +                    // align to 256 bytes.
                             paddedSrbExtensionSize * 2 +                   // align to 128 bytes. Local.SrbExtension and SenseSrbExtension
                             sizeof(IDENTIFY_DEVICE_DATA) +                 // 512 bytes
                             ATA_BLOCK_SIZE +                               // ReadLogExtPageData --- 512 bytes
                             INQUIRYDATABUFFERSIZE;                         // Inquiry Data
    // round up to KiloBytes if it's not dump mode. this makes sure that NonCachedExtension for next port can align to 1K.
    if (!IsDumpMode(AdapterExtension)) {
        nonCachedExtensionSize = ((nonCachedExtensionSize - 1) / 0x400 + 1) * 0x400;
        // total size for all ports
        nonCachedExtensionSize *= PortCount;
    } else {
        // dump mode, address returned from StorPortGetUncachedExtension() is not guaranteed align with 1K.
        // adding 1K into length so that we can start from 1K alignment safely.
        //       NOTE: StorPortAllocatePool is not supported in dump stack, so we allocate ChannelExtension from UnCachedExtension as work around.
        nonCachedExtensionSize += 0x400 + sizeof(AHCI_CHANNEL_EXTENSION);
    }

    AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension, ConfigInfo, nonCachedExtensionSize);

    if (AdapterExtension->NonCachedExtension == NULL) {
       // we cannot continue if cannot get nonCachedMemory for Channels.
        return FALSE;
    }

    AhciZeroMemory((PCHAR)AdapterExtension->NonCachedExtension, nonCachedExtensionSize);

    // 2.2 allocate resources for Ports which need AHCI_CHANNEL_EXTENSION for each of them.
    if (!IsDumpMode(AdapterExtension)) {
        ULONG status = STOR_STATUS_SUCCESS;
        // allocate pool and zero the content
        status = StorPortAllocatePool(AdapterExtension,
                                      PortCount * sizeof(AHCI_CHANNEL_EXTENSION),
                                      AHCI_POOL_TAG,
                                      (PVOID*)&portsChannelExtension);

        if ((status != STOR_STATUS_SUCCESS) || (portsChannelExtension == NULL)) {
           // we cannot continue if cannot get memory for ChannelExtension.
            return FALSE;
        }
        AhciZeroMemory((PCHAR)portsChannelExtension, PortCount * sizeof(AHCI_CHANNEL_EXTENSION));
        //get the starting pointer
        portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension;
    } else {
        ULONG_PTR left = 0;
        // get channelExtension
        portsChannelExtension = (PCHAR)AdapterExtension->NonCachedExtension + nonCachedExtensionSize - sizeof(AHCI_CHANNEL_EXTENSION);

        //get the starting pointer; align the starting location to 1K.
        left = ((ULONG_PTR)AdapterExtension->NonCachedExtension) % 1024;

        if (left > 0) {
            portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension + 1024 - left;
        } else {
            portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension;
        }
    }

    // reset nonCachedExtensionSize to be the size for one Port, it works for dump case also as PortCount is '1'.
    nonCachedExtensionSize /= PortCount;

    // assign allocated memory and uncachedExension into ChannelExtensions
    for (i = 0; i <= AdapterExtension->HighestPort; i++) {
        if ( (AdapterExtension->PortImplemented & (1 << i)) != 0 ) {
            // this port is implemented, allocate and initialize extension for the port
            AdapterExtension->PortExtension[i] = (PAHCI_CHANNEL_EXTENSION)((PCHAR)portsChannelExtension + sizeof(AHCI_CHANNEL_EXTENSION) * j);
            AdapterExtension->PortExtension[i]->AdapterExtension = AdapterExtension;
            AdapterExtension->PortExtension[i]->PortNumber = i;
            // set ChannelExtension fields that use NonCachedExtension
            AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension + nonCachedExtensionSize * j);
            AdapterExtension->PortExtension[i]->ReceivedFIS = (PAHCI_RECEIVED_FIS)((PCHAR)AdapterExtension->PortExtension[i]->CommandList + sizeof(AHCI_COMMAND_HEADER) * paddedNCS);
            AdapterExtension->PortExtension[i]->Local.SrbExtension = (PAHCI_SRB_EXTENSION)((PCHAR)AdapterExtension->PortExtension[i]->ReceivedFIS + sizeof(AHCI_RECEIVED_FIS));
            AdapterExtension->PortExtension[i]->Sense.SrbExtension = (PAHCI_SRB_EXTENSION)((PCHAR)AdapterExtension->PortExtension[i]->Local.SrbExtension + paddedSrbExtensionSize);
            AdapterExtension->PortExtension[i]->DeviceExtension[0].IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)((PCHAR)AdapterExtension->PortExtension[i]->Sense.SrbExtension + paddedSrbExtensionSize);
            AdapterExtension->PortExtension[i]->DeviceExtension[0].ReadLogExtPageData = (PUSHORT)((PCHAR)AdapterExtension->PortExtension[i]->DeviceExtension[0].IdentifyDeviceData + sizeof(IDENTIFY_DEVICE_DATA));
            AdapterExtension->PortExtension[i]->DeviceExtension[0].InquiryData = (PUCHAR)((PCHAR)AdapterExtension->PortExtension[i]->DeviceExtension[0].ReadLogExtPageData + ATA_BLOCK_SIZE);
            //
            j++;
        }
    }

    return TRUE;
}


ULONG AhciHwFindAdapter(
    _In_ PVOID AdapterExtension,
    _In_ PVOID HwContext,
    _In_ PVOID BusInformation,
    _In_z_ PCHAR ArgumentString,
    _Inout_ PPORT_CONFIGURATION_INFORMATION ConfigInfo,
    _In_ PBOOLEAN Reserved3
    )
/*++
    This function is called by the Storport driver indirectly when handling an IRP_MJ_PnP, IRP_MN_START_DEVICE.
    The adapter is being started. This function is called at PASSIVE LEVEL.
It assumes:
    Resources for the AHCI controller exist in a single Memory Mapped range.
    No Channels have been initialized or started; no IO is outstanding.
Called by:
    Storport

It performs:
    (overview)
    1. Check the working mode and retrieve adapter vendor/model info.
    2. Initialize ChannelExtension with Identifying memory mapped IO resources, Version and CAP.
    3. Enabling the AHCI interface as per AHCI 1.1 section 10.1.2 (part one)
    4. Initializing the IDE_CONTROLLER_CONFIGURATION structure with data from the AHCI Interface
    (details)
    1.1 Get dump mode
    1.2 Gather Vendor,Device,Revision IDs from PCI
    2.1 Initialize adapterExtension with AHCI abar
    2.2 Initialize adapterExtension with version & cap values
    3.1 Turn on AE, reset the controller if AE is already set
        AHCI 1.1 Section 10.1.2 - 1.
        "Indicate that system software is AHCI aware by setting GHC.AE to �1�."
    3.2 Determine which ports are implemented by the HBA
        AHCI 1.1 Section 10.1.2 - 2.
        "Determine which ports are implemented by the HBA, by reading the PI register. This bitmap value will aid software in determining how many ports are available and which port registers need to be initialized."
    3.3 get biggest port number
    3.4 Initializing the rest of PORT_CONFIGURATION_INFORMATION
    3.5 Register Power Setting Change Notification Guids
    4.1 Turn on IE, pending interrupts will be cleared when port starts
        This has to be done after 3.2 because we need to know the number of channels before we check each PxIS.
        Verify that none of the PxIS registers are loaded, but take no action
        Note: Due to the multi-tiered nature of the AHCI HBA�s interrupt architecture, system software must always ensure that the PxIS (clear this first) and IS.IPS (clear this second) registers are cleared to �0� before programming the PxIE and GHC.IE registers. This will prevent any residual bits set in these registers from causing an interrupt to be asserted.
        However, the interrupt handler hasn't been hooked up by StorPort yet, so no interrupts will be handled by software until that happens.
    4.2 Allocate resources for both DMA use and all Channel/Port extensions.
    4.3 Initialize ports and start them. Dump stack will do this when receiving the INQUIRY command

Affected Variables/Registers:
    AdapterExtension->ABAR_Address
    AdapterExtension->CAP
    AdapterExtension->CAP2
    AdapterExtension->Version

    GHC.AE, GHC.IE, GHC.HR
    IS and all fields in the HBA�s register memory space except PxFB/PxFBU/PxCLB/PxCLBU that are not HwInit

Return Values:
    The miniport driver returns TRUE if it successfully exectute the whole function.
    Any errors causes the driver to return FALSE and prevents the driver from loading.

Note:
    HwStorFindAdapter must set the MaximumTransferLength and NumberOfPhysicalBreaks fields in the ConfigInfo structure.

    Other than these fields, the PORT_CONFIGURATION_INFORMATION (Storport) structure will always fully specify all adapter resources
    that are required to start the adapter.

--*/
{
    ULONG storStatus = STOR_STATUS_UNSUCCESSFUL;
    PAHCI_ADAPTER_EXTENSION adapterExtension = NULL;
    PAHCI_MEMORY_REGISTERS  abar = NULL;
    //Used to find the number of channels implemented
    UCHAR                   i = 0;
    ULONG                   piMask = 0;
    UCHAR                   numberOfHighestPort = 0;
    ULONG                   portCount = 0;
    //Used to enable the AHCI interface
    AHCI_Global_HBA_CONTROL ghc = {0};
    //guids
    GUID                    powerSettingChangeGuids[2] = {0};

    PAHCI_DUMP_CONTEXT      dumpContext = (PAHCI_DUMP_CONTEXT)ConfigInfo->MiniportDumpData;

    UNREFERENCED_PARAMETER(HwContext);
    UNREFERENCED_PARAMETER(BusInformation);
    UNREFERENCED_PARAMETER(ArgumentString);
    UNREFERENCED_PARAMETER(Reserved3);

    adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;

    // "StoppedState == 0" indicates it's the normal HwFindAdapter call;
    // "StoppedState == 1" indicates the adapter is restarting.
    if (adapterExtension->StateFlags.StoppedState == 0) {
        // use for debugging, assume no more than 4 AHCI adapters in system
        g_AdapterExtension[g_AdapterExtensionIndex++] = AdapterExtension;
        g_AdapterExtensionIndex %= 4;
    }

    //adapterExtension->AdapterNumber = ConfigInfo->SlotNumber;
    adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
    adapterExtension->SlotNumber = ConfigInfo->SlotNumber;

  //1.1 Get dump mode
    adapterExtension->DumpMode = ConfigInfo->DumpMode;

    if (IsDumpMode(adapterExtension)) {
        if (dumpContext != NULL) {
            // In dump/hibernation mode, need to mark ConfigInfo->MiniportDumpData and any embedded memory buffer(s) in MiniportDumpData
            StorPortMarkDumpMemory(AdapterExtension, dumpContext, sizeof(AHCI_DUMP_CONTEXT), 0);
        } else {
            NT_ASSERT(FALSE);
            return SP_RETURN_ERROR;
        }
    }

  //1.2 Gather Vendor,Device,Revision IDs from PCI
    if (!IsDumpMode(adapterExtension)) {
        ULONG pcicfgLen = 0;
        UCHAR pcicfgBuffer[0x30] = {0};

        pcicfgLen = StorPortGetBusData(adapterExtension,
                                       PCIConfiguration,
                                       ConfigInfo->SystemIoBusNumber,
                                       (ULONG)ConfigInfo->SlotNumber,
                                       (PVOID)pcicfgBuffer,
                                       (ULONG)0x30);
        if (pcicfgLen == 0x30) {
            PPCI_COMMON_CONFIG pciConfigData = (PPCI_COMMON_CONFIG)pcicfgBuffer;
            adapterExtension->VendorID = pciConfigData->VendorID;
            adapterExtension->DeviceID = pciConfigData->DeviceID;
            adapterExtension->RevisionID = pciConfigData->RevisionID;
            // on PCI bus, AHCI Base Address is BAR5. Bits 0-3 defined for other usages, not part of address value.
            adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
        } else {
            NT_ASSERT(FALSE);
            return SP_RETURN_ERROR;
        }
    } else {
        adapterExtension->VendorID = dumpContext->VendorID;
        adapterExtension->DeviceID = dumpContext->DeviceID;
        adapterExtension->RevisionID = dumpContext->RevisionID;
        adapterExtension->AhciBaseAddress = dumpContext->AhciBaseAddress;

        adapterExtension->RegistryFlags = dumpContext->AdapterRegistryFlags;
        adapterExtension->LogFlags = dumpContext->LogFlags;
    }

  //2.1 Initialize adapterExtension with AHCI abar
    abar = GetABARAddress(adapterExtension, ConfigInfo);

  //2.1.1 If abar is still NULL after all of that, malformed resources.  We aren't going to get very far.
    if (abar == NULL) {
        return SP_RETURN_ERROR;
    } else {
        adapterExtension->ABAR_Address = abar;
    }

  //2.2 Initialize adapterExtension with version & cap values
    adapterExtension->Version.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->VS.AsUlong);
    adapterExtension->CAP.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->CAP.AsUlong);
    adapterExtension->CAP2.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2.AsUlong);

  //3.1 Turn on AE (AHCI 1.1 Section 10.1.2 - 1)
    ghc.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->GHC.AsUlong);
    if (ghc.AE == 1) {
        if (!AhciAdapterReset(adapterExtension)) {
            return SP_RETURN_ERROR;
        }
    } //AE is 0. Either through power up or reset we are now pretty sure the controller is in 5.2.2.1    H:Init
    ghc.AsUlong = 0;
    ghc.AE = 1;
    StorPortWriteRegisterUlong(adapterExtension, &abar->GHC.AsUlong, ghc.AsUlong);

    adapterExtension->IS = &abar->IS;

  //3.2 Determine which ports are implemented by the HBA (AHCI 1.1 Section 10.1.2 - 2)
    adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);

    //
    // AHCI specification requires that at least one bit is set in PI register.
    // In other words, at least one port must be implemented.
    //
    if (adapterExtension->PortImplemented == 0) {
        return SP_RETURN_ERROR;
    }

  //3.3 Get biggest port number value and implemented port count.
    //3.3.1 get biggest port number value
    numberOfHighestPort = AHCI_MAX_PORT_COUNT;
    //Check from highest bit to lowest bit for the first highest bit set
    for (piMask = (ULONG)(1 << (AHCI_MAX_PORT_COUNT - 1)); piMask != 0; piMask = (ULONG)(piMask >> 1)){
        numberOfHighestPort--;
        if ( (adapterExtension->PortImplemented & piMask) != 0) {
            break;
        }
    } //numberOfHighestPort now holds the correct value

    //3.3.2 get implemented port count
    if (!IsDumpMode(adapterExtension)) {
        for (i = 0; i <= numberOfHighestPort; i++) {
            if ( (adapterExtension->PortImplemented & (1 << i)) != 0 ) {
                portCount++;
            }
        }
    } else {
        // in dump environment, only use the desired port.
        portCount = 1;
    }

    //
    // AHCI specification requires that implemented port count (number of bit set in PI register)
    // is less than or equal to CAP.NP + 1
    //
    // Currently StorAHCI does not utilize CAP.NP. It remains useful to identify platform BIOSes
    // which may violate the specification.
    //
    NT_ASSERT(portCount > 0 && portCount <= (adapterExtension->CAP.NP + 1));

  //3.4 Initializing the rest of PORT_CONFIGURATION_INFORMATION
    ConfigInfo->MaximumTransferLength = AHCI_MAX_TRANSFER_LENGTH;
    ConfigInfo->NumberOfPhysicalBreaks = 0x20;
    ConfigInfo->AlignmentMask = 1;              // ATA devices need WORD alignment
    ConfigInfo->ScatterGather = TRUE;
    ConfigInfo->ResetTargetSupported = TRUE;
    ConfigInfo->NumberOfBuses = (UCHAR)(numberOfHighestPort + 1);
    ConfigInfo->MaximumNumberOfTargets = AHCI_MAX_DEVICE;
    ConfigInfo->MaximumNumberOfLogicalUnits = 1 /*AHCI_MAX_LUN*/;   //NOTE: only supports 1 for now. there is a legacy ATAPI device that may have 2 luns.
    // set driver to run in full duplex mode
    ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
    ConfigInfo->BusResetHoldTime = 0;       // StorAHCI wait RESET to be completed by itself, no need for port driver to wait.
    ConfigInfo->MaxNumberOfIO = portCount * adapterExtension->CAP.NCS;

    if (adapterExtension->CAP.S64A) {
        ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_SUPPORTED;
        // increase size of SrbExtension to accomodate 64-bit move commands
        // and 1 extra Scripts instruction (turn off 64-bit mode)
        ConfigInfo->SrbExtensionSize += (ULONG)((ConfigInfo->NumberOfPhysicalBreaks + 2) * 4);
    }

    ConfigInfo->FeatureSupport |= STOR_ADAPTER_FEATURE_STOP_UNIT_DURING_POWER_DOWN;

  // 3.4.2 update PortImplemented, HighestPort and portCount if necessary
    if (!IsDumpMode(adapterExtension)) {
        adapterExtension->HighestPort = numberOfHighestPort;
    } else {

        if (dumpContext->DumpPortNumber < AHCI_MAX_PORT_COUNT) {
            adapterExtension->PortImplemented = 1 << dumpContext->DumpPortNumber;
            adapterExtension->HighestPort = dumpContext->DumpPortNumber;
        } else {
            NT_ASSERT(FALSE);
            return SP_RETURN_ERROR;
        }
    }

  //3.5 Register Power Setting Change Notification Guids
    if (!IsDumpMode(adapterExtension)) {
        /* 0b2d69d7-a2a1-449c-9680-f91c70521c60 -DIPM/HIPM */
        powerSettingChangeGuids[0].Data1 = 0x0b2d69d7;
        powerSettingChangeGuids[0].Data2 = 0xa2a1;
        powerSettingChangeGuids[0].Data3 = 0x449c;
        powerSettingChangeGuids[0].Data4[0] = 0x96;
        powerSettingChangeGuids[0].Data4[1] = 0x80;
        powerSettingChangeGuids[0].Data4[2] = 0xf9;
        powerSettingChangeGuids[0].Data4[3] = 0x1c;
        powerSettingChangeGuids[0].Data4[4] = 0x70;
        powerSettingChangeGuids[0].Data4[5] = 0x52;
        powerSettingChangeGuids[0].Data4[6] = 0x1c;
        powerSettingChangeGuids[0].Data4[7] = 0x60;

        /* DAB60367-53FE-4fbc-825E-521D069D2456 -Adaptive */
        powerSettingChangeGuids[1].Data1 = 0xDAB60367;
        powerSettingChangeGuids[1].Data2 = 0x53FE;
        powerSettingChangeGuids[1].Data3 = 0x4fbc;
        powerSettingChangeGuids[1].Data4[0] = 0x82;
        powerSettingChangeGuids[1].Data4[1] = 0x5E;
        powerSettingChangeGuids[1].Data4[2] = 0x52;
        powerSettingChangeGuids[1].Data4[3] = 0x1D;
        powerSettingChangeGuids[1].Data4[4] = 0x06;
        powerSettingChangeGuids[1].Data4[5] = 0x9D;
        powerSettingChangeGuids[1].Data4[6] = 0x24;
        powerSettingChangeGuids[1].Data4[7] = 0x56;

        StorPortSetPowerSettingNotificationGuids(AdapterExtension, 2, powerSettingChangeGuids);
    }

  //4.1 Turn on IE, pending interrupts will be cleared when port starts
    adapterExtension->LastInterruptedPort = (ULONG)(-1);
    ghc.IE = 1;
    StorPortWriteRegisterUlong(adapterExtension, &abar->GHC.AsUlong, ghc.AsUlong);


  //4.2 allocate resources, implemented port information needs to be ready before calling the following routine.
    if (adapterExtension->StateFlags.StoppedState == 0) {
        if (!AllocateResourcesForAdapter(adapterExtension, ConfigInfo, portCount)) {
            return SP_RETURN_ERROR;
        }
    }

    //
    // Initialize timers.
    //
    // Notes:
    //
    // 1. If we are in dump mode, do not initialize timers. Interrupt is disabled.
    //
    // 2. This initialization has to be done before trying to getting ports into running state (i.e. by
    //    calling AhciAdapterRunAllPorts() later below in this function). The port start process
    //    utilizes StartPortTimer (Note: In dump mode the port start process will be using polling).
    //
    // 3. Currently WorkerTimer is used only for PartialToSlumber during interrupt servicing (not applicable in dump mode)
    //
    if (!IsDumpMode(adapterExtension))
    {
        for (i = 0; i <= adapterExtension->HighestPort; i++) {

            if (adapterExtension->PortExtension[i] != NULL) {

                if (adapterExtension->PortExtension[i]->StartPortTimer == NULL) {

                    storStatus = StorPortInitializeTimer(AdapterExtension, &adapterExtension->PortExtension[i]->StartPortTimer);

                    if (storStatus != STOR_STATUS_SUCCESS) {
                        NT_ASSERT(FALSE);
                        return SP_RETURN_ERROR;
                    }
                }

                if (adapterExtension->PortExtension[i]->WorkerTimer == NULL) {

                    storStatus = StorPortInitializeTimer(AdapterExtension, &adapterExtension->PortExtension[i]->WorkerTimer);

                    if (storStatus != STOR_STATUS_SUCCESS) {
                        NT_ASSERT(FALSE);
                        return SP_RETURN_ERROR;
                    }
                }

            }
        }
    }

    // reset "StoppedState". NOTE: This field should not be referenced anymore in this function after following line.
    adapterExtension->StateFlags.StoppedState = 0;

  // 4.3 initialize ports and start them.
    //4.3.1 initialize all AHCI ports
    for (i = 0; i <= adapterExtension->HighestPort; i++) {
        if (adapterExtension->PortExtension[i] != NULL) {
            // in case of PortInitialize fails, ChannelExtension->StateFlags.Initialized will remain as 'FALSE'. there will be not attempt to start the Port.
            AhciPortInitialize(adapterExtension->PortExtension[i]);
        }
    }

    if (IsDumpMode(adapterExtension)) {

        //
        // In dump mode copy registry flags and telemetry configuration from the dump context. If telemetry extends to more than one
        // device the logic should cover migration of per device settings, initially we are limited to a single boot device
        //
        ULONG   Index;

#pragma warning (suppress: 6385) // dumpContext->DumpPortNumber is guaranteed to be in-bound earlier
        if (adapterExtension->PortExtension[dumpContext->DumpPortNumber] != NULL) {

            adapterExtension->PortExtension[dumpContext->DumpPortNumber]->RegistryFlags = dumpContext->PortRegistryFlags;

            //
            // If the table in dump context is empty (not filled) - keep static data intact
            //
            if (dumpContext->PublicGPLogTableAddresses[0]) {
                for(Index=0;
                    Index < sizeof(AhciPublicGPLogTableAddresses) / sizeof(AhciPublicGPLogTableAddresses[0] );
                    Index++) {
                    AhciPublicGPLogTableAddresses[Index] = dumpContext->PublicGPLogTableAddresses[Index];
                }

                AhciGPLogPageIntoPrivate = dumpContext->PrivateGPLogPageAddress;
            }

            StorPortMoveMemory((PVOID)&adapterExtension->PortExtension[dumpContext->DumpPortNumber]->DeviceExtension->HybridInfo,
                               (PVOID)&dumpContext->HybridInfo,
                               sizeof(GP_LOG_HYBRID_INFORMATION_HEADER));
        }
    }

    //4.3.2 async process to get all ports into running state
    AhciAdapterRunAllPorts(adapterExtension);

    return SP_RETURN_FOUND;
}

BOOLEAN
AhciHwInitialize (
    _In_ PVOID AdapterExtension
    )
{
    ULONG                       status = STOR_STATUS_SUCCESS;
    PERF_CONFIGURATION_DATA     perfConfigData = {0};

    StorPortEnablePassiveInitialization(AdapterExtension, AhciHwPassiveInitialize);

    //
    // Query perf optimization information
    //
    perfConfigData.Version = STOR_PERF_VERSION;
    perfConfigData.Size = sizeof(PERF_CONFIGURATION_DATA);

    status = StorPortInitializePerfOpts(AdapterExtension, TRUE, &perfConfigData);

    //
    // Turn on DPC Redirection if it's supported.
    //
    if ((status == STOR_STATUS_SUCCESS) &&
        ((perfConfigData.Flags & STOR_PERF_DPC_REDIRECTION) != 0)) {

        AhciZeroMemory((PCHAR)&perfConfigData, sizeof(PERF_CONFIGURATION_DATA));
        perfConfigData.Version = STOR_PERF_VERSION;
        perfConfigData.Size = sizeof(PERF_CONFIGURATION_DATA);

        perfConfigData.Flags = STOR_PERF_DPC_REDIRECTION;

        status = StorPortInitializePerfOpts(AdapterExtension, FALSE, &perfConfigData);

        NT_ASSERT(status == STOR_STATUS_SUCCESS);
    }

    return TRUE;
}

BOOLEAN
AhciHwPassiveInitialize (
    _In_ PVOID AdapterExtension
    )
{
    UCHAR i;
    ULONG status = STOR_STATUS_SUCCESS;
    BOOLEAN enableD3Cold = FALSE;

    PAHCI_ADAPTER_EXTENSION adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;

    // 1. initialize DPC for IO completion
    for (i = 0; i <= adapterExtension->HighestPort; i++) {
        if (adapterExtension->PortExtension[i] != NULL) {
            StorPortInitializeDpc(AdapterExtension, &adapterExtension->PortExtension[i]->CompletionDpc, AhciPortSrbCompletionDpcRoutine);
            StorPortInitializeDpc(AdapterExtension, &adapterExtension->PortExtension[i]->BusChangeDpc, AhciPortBusChangeDpcRoutine);
        }
    }

    // 2. check if ACPI supports turning off power on link
    AhciAdapterEvaluateDSMMethod(adapterExtension);


    // 3. allocate STOR_POFX_DEVICE data structure for adapter, initialize the structure and register for runtime power management.
    status = StorPortAllocatePool(AdapterExtension,
                                  sizeof(STOR_POFX_DEVICE_V2),
                                  AHCI_POOL_TAG,
                                  (PVOID*)&adapterExtension->PoFxDevice);

    if (status != STOR_STATUS_SUCCESS) {
        goto Exit;
    }

    AhciZeroMemory((PCHAR)adapterExtension->PoFxDevice, sizeof(STOR_POFX_DEVICE_V2));

    adapterExtension->PoFxDevice->Version = STOR_POFX_DEVICE_VERSION_V2;
    adapterExtension->PoFxDevice->Size = STOR_POFX_DEVICE_V2_SIZE;
    adapterExtension->PoFxDevice->ComponentCount = 1;

    if (IsD3ColdAllowed(adapterExtension)) {
        adapterExtension->PoFxDevice->Flags = STOR_POFX_DEVICE_FLAG_ENABLE_D3_COLD;
    }

    // indicate dump miniport can't bring adapter to active
    adapterExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_NO_DUMP_ACTIVE;

    adapterExtension->PoFxDevice->Components[0].Version = STOR_POFX_COMPONENT_VERSION_V1;
    adapterExtension->PoFxDevice->Components[0].Size = STOR_POFX_COMPONENT_SIZE;
    adapterExtension->PoFxDevice->Components[0].FStateCount = 1;
    adapterExtension->PoFxDevice->Components[0].DeepestWakeableFState = 0;
    adapterExtension->PoFxDevice->Components[0].Id = STORPORT_POFX_ADAPTER_GUID;

    adapterExtension->PoFxDevice->Components[0].FStates[0].Version = STOR_POFX_COMPONENT_IDLE_STATE_VERSION_V1;
    adapterExtension->PoFxDevice->Components[0].FStates[0].Size = STOR_POFX_COMPONENT_IDLE_STATE_SIZE;
    adapterExtension->PoFxDevice->Components[0].FStates[0].TransitionLatency = 0;
    adapterExtension->PoFxDevice->Components[0].FStates[0].ResidencyRequirement = 0;
    adapterExtension->PoFxDevice->Components[0].FStates[0].NominalPower = STOR_POFX_UNKNOWN_POWER;

    // registry runtime power management for Adapter
    status = StorPortInitializePoFxPower(AdapterExtension, NULL, (PSTOR_POFX_DEVICE)adapterExtension->PoFxDevice, &enableD3Cold);

    if (status != STOR_STATUS_SUCCESS) {
        StorPortFreePool(AdapterExtension, adapterExtension->PoFxDevice);
        adapterExtension->PoFxDevice = NULL;
        adapterExtension->StateFlags.PoFxEnabled = FALSE;
        adapterExtension->StateFlags.PoFxActive = FALSE;

        goto Exit;
    }

    // register success
    adapterExtension->StateFlags.PoFxEnabled = TRUE;
    adapterExtension->StateFlags.PoFxActive = TRUE;


Exit:
    return TRUE;
}

VOID
AhciAdapterPrepareForBusReScan(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    )
/*++

Routine Description:

    This function do preparation work before StorAHCI can process any device enumeration commands

Arguments:

    AdapterExtension - Pointer to the device extension for adapter.

Return Value:

    None

--*/
{
    // 1. If a port/device is powered off, power it on.
    if (AdapterExtension->StateFlags.SupportsAcpiDSM == 1) {
        // ACPI method is implemented, invoke ACPI method to power on all ports and devices
        AhciPortAcpiDSMControl(AdapterExtension, (ULONG)-1, FALSE);
    }


    return;
}

__inline
VOID
AdapterStop (
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
)
/*++

Routine Description:

    This function stops all the ports of the adapter.

Arguments:

    AdapterExtension - Pointer to the device extension for adapter.

    Note: StartIo spin lock must be held before this function is invoked.

Return Value:

    None

--*/
{
    ULONG   i;

    for (i = 0; i <= AdapterExtension->HighestPort; i++) {
        if (AdapterExtension->PortExtension[i] != NULL) {
            AhciPortStop(AdapterExtension->PortExtension[i]);
        }
    }

    // clear ABAR
    AdapterExtension->ABAR_Address = 0;

    AdapterExtension->StateFlags.StoppedState = 1;

    AdapterReleaseActiveReference(AdapterExtension);

    // clear this bit indicating the work has been finished
    AdapterExtension->PoFxPendingWork.AdapterStop = 0;
}

VOID
AhciAdapterStop (
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
)
/*
    Adapter is being stopped.
*/
{
    // stop all ports.
    ULONG   adapterStopInProcess;
    BOOLEAN adapterIdle = FALSE;

    if (AdapterExtension->StateFlags.StoppedState == 1) {
        // nothing to do if the adapter is already stopped.
        return;
    }

    adapterStopInProcess = InterlockedBitTestAndSet((LONG*)&AdapterExtension->PoFxPendingWork, 0);  //AdapterStop is at bit 0

    if (adapterStopInProcess == 1) {
        // adapter Stop is pending in another process.
        return;
    }

    AdapterAcquireActiveReference(AdapterExtension, &adapterIdle);

    if (!adapterIdle) {
        ULONG   adapterStopPending;
        adapterStopPending = InterlockedBitTestAndReset((LONG*)&AdapterExtension->PoFxPendingWork, 0);  //AdapterStop is at bit 0

        // adapter was in active state, perform AdapterStop.
        // if adapter was in idle state, this work will be done in PoFxActive callback
        if (adapterStopPending == 1) {
            // Reference will be released in AdapterStop() function
            AdapterStop(AdapterExtension);
        }
    }

    return;
}

VOID
AhciAdapterRemoval (
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
)
/*
    Release resources allocated for the adapter and its managed ports/devices
*/
{
    ULONG   i;
    PVOID   bufferToFree = NULL;

    if (IsDumpMode(AdapterExtension)) {
        return;
    }

    if (AdapterExtension->PoFxDevice != NULL) {
        StorPortFreePool(AdapterExtension, AdapterExtension->PoFxDevice);
        AdapterExtension->PoFxDevice = NULL;
        AdapterExtension->StateFlags.PoFxEnabled = FALSE;
        AdapterExtension->StateFlags.PoFxActive = FALSE;
    }

    // release portsChannelExtension. the whole buffer is sliced for Ports, only need to free the first ChannelExtension.
    for (i = 0; i <= AdapterExtension->HighestPort; i++) {
        if (AdapterExtension->PortExtension[i] != NULL) {
            if (bufferToFree == NULL) {
                bufferToFree = AdapterExtension->PortExtension[i];
            }

            if (AdapterExtension->PortExtension[i]->StartPortTimer != NULL) {
                StorPortFreeTimer(AdapterExtension, AdapterExtension->PortExtension[i]->StartPortTimer);
                AdapterExtension->PortExtension[i]->StartPortTimer = NULL;
            }
            if (AdapterExtension->PortExtension[i]->WorkerTimer != NULL) {
                StorPortFreeTimer(AdapterExtension, AdapterExtension->PortExtension[i]->WorkerTimer);
                AdapterExtension->PortExtension[i]->WorkerTimer = NULL;
            }

            if (AdapterExtension->PortExtension[i]->DeviceInitCommands.CommandTaskFile != NULL) {
                StorPortFreePool(AdapterExtension, (PVOID)AdapterExtension->PortExtension[i]->DeviceInitCommands.CommandTaskFile);
                AdapterExtension->PortExtension[i]->DeviceInitCommands.CommandTaskFile = NULL;
            }

            if (AdapterExtension->PortExtension[i]->PoFxDevice != NULL) {
                StorPortFreePool(AdapterExtension, AdapterExtension->PortExtension[i]->PoFxDevice);
                AdapterExtension->PortExtension[i]->PoFxDevice = NULL;
                AdapterExtension->PortExtension[i]->StateFlags.PoFxEnabled = FALSE;
                AdapterExtension->PortExtension[i]->StateFlags.PoFxActive = FALSE;
            }


            AdapterExtension->PortExtension[i] = NULL;
        }
    }

    if (bufferToFree != NULL) {
        StorPortFreePool(AdapterExtension, bufferToFree);
    }

    return;
}


SCSI_ADAPTER_CONTROL_STATUS
AhciHwAdapterControl (
    _In_ PVOID AdapterExtension,
    _In_ SCSI_ADAPTER_CONTROL_TYPE ControlType,
    _In_ PVOID Parameters
    )
/*++

Routine Description:

    This is a generic routine to allow for special adapter control activities.
    It maianly handles power change notifications for the adapter.

Arguments:

    AdapterExtension - Pointer to the device extension for adapter.

    ControlType - Specifies the type of call being made through this routine.

    Parameters - Pointer to parameters needed for this control type (optional).

Return Value:

    SCSI_ADAPTER_CONTROL_STATUS - currently either:
        ScsiAdapterControlSuccess (= 0)
        ScsiAdapterControlUnsuccessful (= 1)

--*/

{
    PAHCI_ADAPTER_EXTENSION adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
    PSCSI_SUPPORTED_CONTROL_TYPE_LIST controlTypeList;
    SCSI_ADAPTER_CONTROL_STATUS status = ScsiAdapterControlSuccess;

    switch (ControlType) {
        // determine which control types (routines) are supported
        case ScsiQuerySupportedControlTypes:
            // get pointer to control type list
            controlTypeList = (PSCSI_SUPPORTED_CONTROL_TYPE_LIST)Parameters;

            // Report StopAdapter and RestartAdapter are supported.
            if (ScsiQuerySupportedControlTypes < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiQuerySupportedControlTypes] = TRUE;
            }
            if (ScsiStopAdapter < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiStopAdapter] = TRUE;
            }
            if (ScsiRestartAdapter < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiRestartAdapter] = TRUE;
            }
            if (ScsiPowerSettingNotification < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiPowerSettingNotification] = TRUE;
            }
            if (ScsiAdapterPoFxPowerActive < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiAdapterPoFxPowerActive] = TRUE;
            }
            if (ScsiAdapterPower < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiAdapterPower] = TRUE;
            }
            if (ScsiAdapterPrepareForBusReScan < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiAdapterPrepareForBusReScan] = TRUE;
            }
            if (ScsiAdapterSystemPowerHints < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiAdapterSystemPowerHints] = TRUE;
            }
            break;

        // ScsiStopAdapter maybe called with PnP or Power activities.
        // StorAHCI supports PnP Srb and ScsiAdapterPower thus does nothing for ScsiStopAdapter.
        // NOTE: ScsiStopAdapter is called after PnP call -StorRemoveDevice or StorSurpriseRemoval,
        // thus resource release should be done here.
        case ScsiStopAdapter:
            if (adapterExtension->StateFlags.Removed == 1) {
                AhciAdapterRemoval(adapterExtension);
            }
            break;

        // ScsiRestartAdapter is called during adapter powering up.
        // StorAHCI supports PnP Srb and ScsiAdapterPower thus does nothing for ScsiRestartAdapter. return value is ScsiAdapterControlSuccess.
        case ScsiRestartAdapter:
            break;

        // ScsiPowerSettingNotification is called when Storport receives notification from Power Manager when power setting is changed.
        case ScsiPowerSettingNotification:
            AhciAdapterPowerSettingNotification(adapterExtension, (PSTOR_POWER_SETTING_INFO)Parameters);
            break;

        case ScsiAdapterPoFxPowerActive: {
            PSTOR_POFX_ACTIVE_CONTEXT activeContext = (PSTOR_POFX_ACTIVE_CONTEXT)Parameters;

            if (AdapterPoFxEnabled(adapterExtension)) {
                adapterExtension->StateFlags.PoFxActive = activeContext->Active ? 1 : 0;

                if (activeContext->Active) {
                    ULONG   adapterStopPending;
                    adapterStopPending = InterlockedBitTestAndReset((LONG*)&adapterExtension->PoFxPendingWork, 0);  //AdapterStop is at bit 0

                    if (adapterStopPending == 1) {
                        //perform pending Adapter Stop work
                        STOR_LOCK_HANDLE lockhandle = {StartIoLock, 0};

                        NT_ASSERT(adapterExtension->StateFlags.StoppedState == 0);

                        // Storport does not acquire any spinlock before calling 
                        // miniport's HwAdapterControl routine. Need to acquire
                        // StartIo spin lock before calling AdapterStop as this is 
                        // required by AdapterStop.
                        StorPortAcquireSpinLock(AdapterExtension, StartIoLock, NULL, &lockhandle);
                        AdapterStop(adapterExtension);
                        StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
                    }
                }
            }
            break;
        }

        case ScsiAdapterPower: {
            PSTOR_ADAPTER_CONTROL_POWER adapterPower = (PSTOR_ADAPTER_CONTROL_POWER)Parameters;

            NT_ASSERT(adapterPower != NULL);

            StorPortDebugPrint(3, "StorAHCI - LPM: Adapter - %s\n", adapterPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3");

            if (adapterPower->PowerState == StorPowerDeviceD0) {
                AhciAdapterPowerUp(adapterExtension);                  //power up
            } else if (adapterPower->PowerState == StorPowerDeviceD3) {
                AhciAdapterPowerDown(adapterExtension);                //power down
            } else {
                // AHCI adapter does not support D1 or D2, assert but do not fail the call
                NT_ASSERT(FALSE);
            }
            break;
        }

        case ScsiAdapterPrepareForBusReScan:
            AhciAdapterPrepareForBusReScan(adapterExtension);
            break;

        case ScsiAdapterSystemPowerHints: {
            PSTOR_SYSTEM_POWER_HINTS powerHints = (PSTOR_SYSTEM_POWER_HINTS)Parameters;            

            if (powerHints->Size >= sizeof(STOR_SYSTEM_POWER_HINTS)) {

                StorPortDebugPrint(3, "StorAHCI - LPM: Adapter - System Power Hint - State: %u - Latency: %u ms \n", powerHints->SystemPower, powerHints->ResumeLatencyMSec);

                adapterExtension->SystemPowerHintState = powerHints->SystemPower;
                adapterExtension->SystemPowerResumeLatencyMSec = powerHints->ResumeLatencyMSec;


                StorPortEtwEvent2(AdapterExtension,
                                  NULL,
                                  AhciEtwEventSystemPowerHint,
                                  L"Sys Power Hint",
                                  STORPORT_ETW_EVENT_KEYWORD_POWER,
                                  StorportEtwLevelInformational,
                                  StorportEtwEventOpcodeInfo,
                                  NULL,
                                  L"State",
                                  powerHints->SystemPower,
                                  L"Latency (ms)",
                                  powerHints->ResumeLatencyMSec);
 
            } else {
                status = ScsiAdapterControlUnsuccessful;
            }
            break;
        }

        default:
            status = ScsiAdapterControlUnsuccessful;
            break;

    } // end of switch

    return status;

} // AhciHwAdapterControl

BOOLEAN
AhciHwResetBus (
    _In_ PVOID AdapterExtension,
    _In_ ULONG PathId
    )
/*
    Used to Reset the PathId/Port/Channel
*/
{
    BOOLEAN status = FALSE;
    STOR_LOCK_HANDLE lockhandle = {0};
    PAHCI_ADAPTER_EXTENSION adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;

    if ( IsPortValid(adapterExtension, PathId) ) {
        StorPortAcquireSpinLock(adapterExtension, InterruptLock, NULL, &lockhandle);
        status = AhciPortReset(adapterExtension->PortExtension[PathId], TRUE);
        StorPortReleaseSpinLock(adapterExtension, &lockhandle);
        adapterExtension->PortExtension[PathId]->DeviceExtension[0].IoRecord.PortDriverResetCount++;
    }

    return status;
}

BOOLEAN
AhciHwBuildIo (
    _In_ PVOID AdapterExtension,
    _In_ PSCSI_REQUEST_BLOCK Srb
    )
{
    PAHCI_ADAPTER_EXTENSION adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
    PAHCI_CHANNEL_EXTENSION channelExtension = NULL;
    UCHAR                   pathId = SrbGetPathId(Srb);
    PVOID                   srbSenseBuffer = NULL;
    UCHAR                   srbSenseBufferLength = 0;

    //
    // Make sure the incoming Srb with the expected type
    //
    NT_ASSERT(Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK);

    // SrbExtension is not Null-ed by Storport, so do it here.
    AhciZeroMemory((PCHAR)GetSrbExtension((PSTORAGE_REQUEST_BLOCK)Srb), sizeof(AHCI_SRB_EXTENSION));

    RequestGetSrbScsiData((PSTORAGE_REQUEST_BLOCK)Srb, NULL, NULL, &srbSenseBuffer, &srbSenseBufferLength);

    if ((srbSenseBuffer != NULL) && (srbSenseBufferLength > 0)) {
        AhciZeroMemory((PCHAR)srbSenseBuffer, srbSenseBufferLength);
    }

    channelExtension = adapterExtension->PortExtension[pathId];

    if ( IsPortValid(adapterExtension, pathId) &&
         (SrbGetSrbFunction(Srb) == SRB_FUNCTION_EXECUTE_SCSI) &&
         (channelExtension->StateFlags.PowerDown == TRUE)) {
        AhciPortPowerUp(channelExtension);
    }

    return TRUE;
}

BOOLEAN
AhciHwStartIo (
    _In_ PVOID AdapterExtension,
    _In_ PSCSI_REQUEST_BLOCK Srb
    )
/*
    1. Process Adapter request
    2. Bail out if it�s adapter request
    3. Validate Port Number, if not valid, bail out.
    4. Process Device/Port request

*/
{
    STOR_LOCK_HANDLE lockhandle = {0};
    PAHCI_ADAPTER_EXTENSION adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
    ULONG function = SrbGetSrbFunction(Srb);
    UCHAR pathId = SrbGetPathId(Srb);
    BOOLEAN adapterRequest = FALSE;
    BOOLEAN processIO = FALSE;

  //1 Work on Adapter requests
    switch (function) {
        case SRB_FUNCTION_PNP: {
            PSRBEX_DATA_PNP pnpData = (PSRBEX_DATA_PNP)SrbGetSrbExDataByType((PSTORAGE_REQUEST_BLOCK)Srb, SrbExDataTypePnP);

            NT_ASSERT(pnpData != NULL);

            if ( (pnpData->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST ) != 0 ) {
                // only process PnP request for adapter here.
                adapterRequest = TRUE;

                if ( (pnpData->PnPAction == StorRemoveDevice) || (pnpData->PnPAction == StorSurpriseRemoval) ) {
                    // the adapter is going to be removed, mark the state and release all resources allocated later in AdapterControl - ScsiStopAdapter.
                    adapterExtension->StateFlags.Removed = 1;
                    Srb->SrbStatus = SRB_STATUS_SUCCESS;
                } else if (pnpData->PnPAction == StorStopDevice) {
                    AhciAdapterStop(adapterExtension);
                    Srb->SrbStatus = SRB_STATUS_SUCCESS;
                } else {
                    Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
                }
                //complete all Adapter PnP request
                StorPortNotification(RequestComplete, AdapterExtension, Srb);
            }
            break;
        }
        case SRB_FUNCTION_POWER: {
            PSRBEX_DATA_POWER powerData = (PSRBEX_DATA_POWER)SrbGetSrbExDataByType((PSTORAGE_REQUEST_BLOCK)Srb, SrbExDataTypePower);

            NT_ASSERT(powerData != NULL);

            if ((powerData->SrbPowerFlags & SRB_POWER_FLAGS_ADAPTER_REQUEST) != 0) {
                // only process Power request for adapter here.
                adapterRequest = TRUE;

                // StorAHCI supports ScsiAdapterPower, thus Storport should not send adapter power Srb to this miniport.
                // Complete the request as SUCCESS anyway.
                NT_ASSERT(FALSE);
                Srb->SrbStatus = SRB_STATUS_SUCCESS;
                StorPortNotification(RequestComplete, AdapterExtension, Srb);
            }
            break;
        }

        case SRB_FUNCTION_FLUSH:  {
            adapterRequest = TRUE;
            //FLUSH Adapter cache. No cache, complete the request.
            Srb->SrbStatus = SRB_STATUS_SUCCESS;
            StorPortNotification(RequestComplete, AdapterExtension, Srb);
            break;
        }

    }

    // 1.1 if the request is for adapter, it has been processed. Return from here.
    if (adapterRequest) {
        return TRUE;
    }

    // 2.1 All requests reach here should be for devices
    if ( !IsPortValid(adapterExtension, pathId) ) {
        Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
        StorPortNotification(RequestComplete, AdapterExtension, Srb);
        return TRUE;
    }

     // 2.2 work on device reqeusts
    switch (function) {
        case SRB_FUNCTION_RESET_BUS:    // this one may come from class driver, not port driver. same as AhciHwResetBus
        case SRB_FUNCTION_RESET_DEVICE:
        case SRB_FUNCTION_RESET_LOGICAL_UNIT: {
            // these reset requests target to Port
            StorPortAcquireSpinLock(adapterExtension, InterruptLock, NULL, &lockhandle);
            Srb->SrbStatus = AhciPortReset(adapterExtension->PortExtension[pathId], TRUE) ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR;
            StorPortNotification(RequestComplete, AdapterExtension, Srb);
            StorPortReleaseSpinLock(adapterExtension, &lockhandle);
            adapterExtension->PortExtension[pathId]->DeviceExtension[0].IoRecord.PortDriverResetCount++;
            break;
        }

        case SRB_FUNCTION_PNP: {
            PSRBEX_DATA_PNP pnpData = (PSRBEX_DATA_PNP)SrbGetSrbExDataByType((PSTORAGE_REQUEST_BLOCK)Srb, SrbExDataTypePnP);

            NT_ASSERT(pnpData != NULL);

            if ( ((pnpData->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) == 0) &&
                 (pnpData->PnPAction == StorQueryCapabilities) &&
                 (SrbGetDataTransferLength(Srb) >= sizeof(STOR_DEVICE_CAPABILITIES_EX)) ) {
                // process StorQueryCapabilities request for device, not adapter.
                // fill in fields of STOR_DEVICE_CAPABILITIES_EX
                PSTOR_DEVICE_CAPABILITIES_EX storCapabilities = (PSTOR_DEVICE_CAPABILITIES_EX)SrbGetDataBuffer(Srb);
                ULONG portProperties = adapterExtension->PortExtension[pathId]->PortProperties;

                storCapabilities->Removable = (portProperties & PORT_PROPERTIES_EXTERNAL_PORT) ? 1 : 0;
                storCapabilities->DeviceD1 = 0;
                storCapabilities->DeviceD2 = 0;
                storCapabilities->LockSupported = 0;
                storCapabilities->EjectSupported = 0;
                storCapabilities->DockDevice = 0;
                storCapabilities->UniqueID = 0;         //no uniqueID, let PnP generates one
                storCapabilities->SilentInstall = 1;
                storCapabilities->SurpriseRemovalOK = 0;
                storCapabilities->NoDisplayInUI = 0;

                Srb->SrbStatus = SRB_STATUS_SUCCESS;
                StorPortNotification(RequestComplete, AdapterExtension, Srb);

            } else {
                Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
                StorPortNotification(RequestComplete, AdapterExtension, Srb);
            }
            break;
        }

        case SRB_FUNCTION_SHUTDOWN: {
            //This request is for the device to flush device data from adapter cache.
            //The miniport driver must hold on to the shutdown request until no data remains in the HBA's internal cache for the target logical unit and,
            //then, complete the shutdown request.
            PATA_DEVICE_PARAMETERS  deviceParameters = &adapterExtension->PortExtension[pathId]->DeviceExtension->DeviceParameters;
            BOOLEAN                 sendStandby = (IsDumpHiberMode(adapterExtension) || IsDumpCrashMode(adapterExtension));

            deviceParameters->StateFlags.SystemPoweringDown = TRUE;
            if (adapterExtension->PortExtension[pathId]->DeviceExtension->SupportedCommands.SetDateAndTime == 0x1) {
                IssueSetDateAndTimeCommand(adapterExtension->PortExtension[pathId], Srb, sendStandby);
                processIO = TRUE;
            } else if (sendStandby) {
                //in dump mode, this is the last Srb sent after SYNC CACHE, spin down the disk
                PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)Srb);
                srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND;
                SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_STANDBY_IMMEDIATE);
                processIO = TRUE;
            } else {
                Srb->SrbStatus = SRB_STATUS_SUCCESS;
                StorPortNotification(RequestComplete, AdapterExtension, Srb);
            }

            break;
        }

        case SRB_FUNCTION_IO_CONTROL: {
                PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)Srb);

                IOCTLtoATA(adapterExtension->PortExtension[pathId], (PSTORAGE_REQUEST_BLOCK)Srb);
                if (srbExtension->AtaFunction != 0) {
                    if ( ( srbExtension->Sgl == NULL ) && ( IsDataTransferNeeded((PSTORAGE_REQUEST_BLOCK)Srb) )  ) {
                        srbExtension->Sgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(adapterExtension, Srb);
                    }
                    processIO = TRUE;
                } else {
                    // complete Srb if no command should be sent to device.
                    NT_ASSERT(Srb->SrbStatus != SRB_STATUS_PENDING);
                    StorPortNotification(RequestComplete, AdapterExtension, Srb);
                }
                break;
            }

        case SRB_FUNCTION_EXECUTE_SCSI: {
                PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)Srb);

                SCSItoATA(adapterExtension->PortExtension[pathId], (PSTORAGE_REQUEST_BLOCK)Srb);
                if (srbExtension->AtaFunction != 0) {
                    if ( ( srbExtension->Sgl == NULL ) && ( IsDataTransferNeeded((PSTORAGE_REQUEST_BLOCK)Srb) )  ) {
                        srbExtension->Sgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(adapterExtension, Srb);
                    }
                    processIO = TRUE;
                } else {
                    // complete Srb if no command should be sent to device.
                    NT_ASSERT(Srb->SrbStatus != SRB_STATUS_PENDING);
                    StorPortNotification(RequestComplete, AdapterExtension, Srb);
                }
                break;
            }

        case SRB_FUNCTION_DUMP_POINTERS: {
            ULONG status = STOR_STATUS_SUCCESS;
            PAHCI_DUMP_CONTEXT dumpContext = NULL;
            PMINIPORT_DUMP_POINTERS dumpPointers = (PMINIPORT_DUMP_POINTERS)SrbGetDataBuffer(Srb);

            if ( (dumpPointers == NULL) ||
                 (SrbGetDataTransferLength(Srb) < RTL_SIZEOF_THROUGH_FIELD(MINIPORT_DUMP_POINTERS, MiniportPrivateDumpData)) ) {
                Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
            } else {

                // allocate pool and zero the content
                status = StorPortAllocatePool(AdapterExtension,
                                              sizeof(AHCI_DUMP_CONTEXT),
                                              AHCI_POOL_TAG,
                                              (PVOID*)&dumpContext);

                if ((status != STOR_STATUS_SUCCESS) || (dumpContext == NULL)) {
                   // we cannot continue if cannot get memory for ChannelExtension.
                    Srb->SrbStatus = SRB_STATUS_ERROR;
                } else {
                    dumpPointers->Version = DUMP_MINIPORT_VERSION_1;
                    dumpPointers->Size = sizeof(MINIPORT_DUMP_POINTERS);

                    dumpPointers->MiniportPrivateDumpData = (PVOID)dumpContext;

                    AhciZeroMemory((PCHAR)dumpContext, sizeof(AHCI_DUMP_CONTEXT));
                    dumpContext->VendorID = adapterExtension->VendorID;
                    dumpContext->DeviceID = adapterExtension->DeviceID;
                    dumpContext->RevisionID = adapterExtension->RevisionID;
                    dumpContext->AhciBaseAddress = adapterExtension->AhciBaseAddress;
                    dumpContext->LogFlags = adapterExtension->LogFlags;
                    dumpContext->AdapterRegistryFlags = adapterExtension->RegistryFlags;
                    dumpContext->DumpPortNumber = adapterExtension->PortExtension[pathId]->PortNumber;
                    dumpContext->PortRegistryFlags = adapterExtension->PortExtension[pathId]->RegistryFlags;

                    //
                    // Fill telemetry collection context - shared with diskdump.sys stack
                    //
                    //dumpContext->MaxDumpLevelEnabled = 1;
                    //dumpContext->MaxDeviceDumpSize = 100;

                    AhciZeroMemory((PCHAR)dumpContext->PublicGPLogTableAddresses,sizeof(dumpContext->PublicGPLogTableAddresses));
                    dumpContext->PrivateGPLogPageAddress = 0;

                    //
                    // Override from the registry settings if passed by the storport miniport
                    // If private address is not set we will use T13 defined one . Consider always trying default one and resort to registry if it is not supported
                    //
                    if (dumpContext->PrivateGPLogPageAddress == 0) {
                        dumpContext->PrivateGPLogPageAddress = IDE_GP_LOG_CURRENT_DEVICE_INTERNAL_STATUS;
                    }

                    //
                    // Preserve Hybrid Disk Information.
                    //
                    if (IsDeviceHybridInfoSupported(adapterExtension->PortExtension[pathId])) {
                        StorPortMoveMemory((PVOID)&dumpContext->HybridInfo,
                                           (PVOID)&adapterExtension->PortExtension[pathId]->DeviceExtension->HybridInfo,
                                           sizeof(GP_LOG_HYBRID_INFORMATION_HEADER));
                    }

                    Srb->SrbStatus = SRB_STATUS_SUCCESS;
                }
            }
            StorPortNotification(RequestComplete, AdapterExtension, Srb);
            break;
        }

        case SRB_FUNCTION_FREE_DUMP_POINTERS: {
            ULONG status = STOR_STATUS_SUCCESS;
            PMINIPORT_DUMP_POINTERS dumpPointers = (PMINIPORT_DUMP_POINTERS)SrbGetDataBuffer(Srb);

            if ( (dumpPointers == NULL) ||
                 (SrbGetDataTransferLength(Srb) < RTL_SIZEOF_THROUGH_FIELD(MINIPORT_DUMP_POINTERS, MiniportPrivateDumpData)) ) {
                Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
            } else {
                status = StorPortFreePool(AdapterExtension, dumpPointers->MiniportPrivateDumpData);
                Srb->SrbStatus = (status == STOR_STATUS_SUCCESS) ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR;
            }
            StorPortNotification(RequestComplete, AdapterExtension, Srb);
            break;
        }

        default: {
            // for unsupported SRB: complete with status: SRB_STATUS_INVALID_REQUEST
            Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
            StorPortNotification(RequestComplete, AdapterExtension, Srb);
            break;
        }

    } //end of switch (function)

    if (processIO) {
        // get active reference for port/device and adapter if this request
        // isn't part of D3 processing and isn't a SCSI command.
        // Storport already make sure that the Unit in active state before a SCSI command is sent to miniport.
        if ((function != SRB_FUNCTION_EXECUTE_SCSI) && (SrbGetSrbFlags(Srb) & SRB_FLAGS_D3_PROCESSING) == 0) {
            // for incoming request, port driver should make sure it's active.
            PortAcquireActiveReference(adapterExtension->PortExtension[pathId], (PSTORAGE_REQUEST_BLOCK)Srb, NULL);
        }

        StorPortAcquireSpinLock(adapterExtension, InterruptLock, NULL, &lockhandle);
        AddQueue(adapterExtension->PortExtension[pathId], &adapterExtension->PortExtension[pathId]->SrbQueue, (PSTORAGE_REQUEST_BLOCK)Srb, 0xDEADBEEF, 0x11);
        AhciGetNextIos(adapterExtension->PortExtension[pathId], TRUE);
        StorPortReleaseSpinLock(adapterExtension, &lockhandle);
    }

    return TRUE;
}

VOID
AhciGetNextIos (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN AtDIRQL
    )
/*
    get Srb from queue and program it to adapter.

    assumption: internal request doesn't call this routine. Otherwise, the check of available slot should be changed.
*/
{
    PSTORAGE_REQUEST_BLOCK Srb;
    BOOLEAN keepFilling;
    ULONG i, commandSlotMask, allocated;

  //1.0 Check if command processing should happen.  If not, this function will be called again when it is ready.
    if (ChannelExtension->StartState.ChannelNextStartState != StartComplete) {
        //We could be waiting ... but it is possible there is no device.  If we know there is no device, then fail all commands.
        if (ChannelExtension->StartState.ChannelNextStartState == StartFailed) {
            // complete all rquests still in queue
            AhciPortFailAllIos(ChannelExtension, SRB_STATUS_NO_DEVICE, AtDIRQL);
        }
        return;
    }

    if (ChannelExtension->StateFlags.PowerDown == TRUE) {
        //We should wait for device to power up.
        return;
    }

  //1.1 Initialize Variables
    commandSlotMask = 0;

    //If there is a command in the Srb Queue ...
    if (ChannelExtension->SrbQueue.Head != NULL) {
        keepFilling = TRUE;
        //get a mask of all slots expect slot 0, which is reserved for internal use
        for (i = 1; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) {
            commandSlotMask |= ( 1 << i );
        }
    } else {
        keepFilling = FALSE;
    }

    while (keepFilling) {
        keepFilling = FALSE;
        allocated = GetOccupiedSlots(ChannelExtension);

        if ((~allocated & commandSlotMask) != 0) {
            //there is empty slot. get the next IO
            Srb = RemoveQueue(ChannelExtension, &ChannelExtension->SrbQueue, 0xDEADC0DE, 0x1F);
            if (Srb != NULL) {
                NT_ASSERT(SrbGetPathId(Srb) == ChannelExtension->PortNumber);
                keepFilling = TRUE;

              // get a Srb, try to find an empty slot, fill command table and command header, put Srb into IO slices.
                AhciProcessIo(ChannelExtension, Srb, AtDIRQL);

            } else {  //No more SRBs.  Finish.
                keepFilling = FALSE;
            }
        }
    }

  //Check to see if IO is ready to be programmed to adapter
    ActivateQueue(ChannelExtension, AtDIRQL);
    return;
}


BOOLEAN
AhciHwInterrupt (
    _In_ PVOID AdapterExtension
    )
{
/*++
AtaHwInterrupt is the interrupt handler.
If the miniport driver requires a large amount of time to process the interrupt it must defer processing to a worker routine.
This routine must attemp one clear the interrupt on the HBA before it returns TRUE.

It assumes:
    The following StorPort routines shall not be called from the AhciHwInterrupt routine � StorPortCompleteAllRequests and StorPortDeviceBusy.
    The miniport could however request for a worker routine and make the calls in the worker routine.

Called by:
    external

It performs:
    (overview)
    1. Prepare for handling the interrupt
    2. Handle interrupts on this port
    3. Clear port interrupt
    4. Complete outstanding commands
    5. Handle error processing if necessary
    (details)
    1.1 Verify the interrupt is for this adapter
    1.2 round robin for selecting the port to process in case of multi-ports have interrupt pending.
    1.3 Initialize Variables
        AHCI 1.1 Section 5.5.3 - 1.
        "Software determines the cause of the interrupt by reading the PxIS register.  It is possible for multiple bits to be set"
    2.1 Understand interrupts on this channel
        AHCI 1.1 Section 5.5.3 - 2.
        "Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt."
        2.1.1 Handle Fatal Errors
            Clear the PxIS Fatal Error bits
            And prep for error handling
        2.1.3 Handle non Native HotPlug Events
            Clear the PxSERR bits to clear PxIS
            2.1.3.1 Handle Serial ATA Errors (Hotplug insertion or unsolicited COMINIT)
            2.1.3.2 Handle Serial ATA Errors (Hotplug removal or PHY Power Management event)
            2.1.3.3 Handle Serial ATA Errors (everything else)
        2.1.4 Handle Datalength Mismatch Error
            Clear the PxIS Overflow bit bits
            And prep for dataLengthMisMatch handling
        2.1.15 Handle NonFatal Errors
    2.2 Handle Normal Command Completion
    3.1 Clear channel interrupt
        AHCI 1.1 Section 5.5.3 - 3.
        "Software clears the interrupt bit in IS.IPS corresponding to the port."
    4.1 Complete outstanding commands
        AHCI 1.1 Section 5.5.3 - 4.
        "If executing non-queued commands, software reads the PxCI register, and compares the current value to the list of commands previously issued by software that are still outstanding.  If executing native queued commands, software reads the PxSACT register and compares the current value to the list of commands previously issued by software.  Software completes with success any outstanding command whose corresponding bit has been cleared in the respective register. PxCI and PxSACT are volatile registers;
        software should only use their values to determine commands that have completed, not to determine which commands have previously been issued."
        4.1.1 Determine if there was an ATA error on the command completed. Error register is only valid if bit0 of Status is 1
        4.1.2 Check to see if the right amount of data transfered
            set channelExtension->Slot[i]->DataTransferLength = the amount transfered
        4.1.3 Otherwise, command completed successfully
    5.1 Handle error processing
        Until the error recovery can be completed, don't let any more IO's come through
        AHCI 1.1 Section 5.5.3 - 5.
        "If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2)."
        AHCI 1.1 Section 6.2.2.1 Non-Queued Error Recovery (this may take a while, better queue a DPC)
        Complete further processing in the worker routine and enable interrupts on the channel
    6.1 Partial to Slumber auto transit

Affected Variables/Registers:

Return Values:
    AtaHwInterrrupt returns TRUE if the interrupt is handled.
    If the adapter, channel/port did not generate the interrupt the routine should return FALSE as soon as possible.

--*/
    PAHCI_ADAPTER_EXTENSION adapterExtension;
    PAHCI_CHANNEL_EXTENSION channelExtension;
    //Interrupt handling structures
    AHCI_INTERRUPT_STATUS   pxis;
    AHCI_INTERRUPT_STATUS   pxisMask;
    AHCI_SERIAL_ATA_STATUS  ssts;
    AHCI_SERIAL_ATA_ERROR   serr;
    AHCI_SERIAL_ATA_ERROR   serrMask;
    AHCI_COMMAND            cmd;
    ULONG                   ci;
    ULONG                   sact;
    ULONG                   outstanding;
    ULONG                   is;
    ULONG                   interruptPorts;
    ULONG                   i;
    ULONG                   storStatus;
    ULONGLONG               asyncNotifyFlags;

    adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;

    is = StorPortReadRegisterUlong(AdapterExtension, adapterExtension->IS);
    interruptPorts = (is & adapterExtension->PortImplemented);

  //1.1 Verify the interrupt is for this adapter
    if (interruptPorts == 0) {
        // interrupt is not for this adapter
        return FALSE;
    }

  //1.2 round robin for selecting the port to process in case of multi-ports have interrupt pending.
    i = (adapterExtension->LastInterruptedPort + 1) % (adapterExtension->HighestPort + 1);
    do {
        if ( ((interruptPorts & (1 << i)) != 0) && IsPortStartCapable(adapterExtension->PortExtension[i]) ) {
            break;
        }
        i = (i + 1) % (adapterExtension->HighestPort + 1);

    } while (i != adapterExtension->LastInterruptedPort);

    if ( (i == adapterExtension->LastInterruptedPort) &&
         (((interruptPorts & (1 << i)) == 0) || !IsPortStartCapable(adapterExtension->PortExtension[i])) ) {
        // interrupt is not for this adapter
        return FALSE;
    }

    adapterExtension->LastInterruptedPort = i;
    channelExtension = adapterExtension->PortExtension[i];

    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(channelExtension, 0x00000005);//AhciHwInterrupt Enter
    }

  //1.3 Initialize Variables
    sact = 0;
    cmd.AsUlong = 0;
    ssts.AsUlong = 0;
    pxisMask.AsUlong = serrMask.AsUlong = 0;

    pxis.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->IS.AsUlong);
    serr.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->SERR.AsUlong);

  //2.1 Understand interrupts on this channel
    //2.1.1 Handle Fatal Errors: Interface Fatal Error Status || Host Bus Data Error Status || Host Bus Fatal Error Status || Task File Error Status
    if (pxis.IFS || pxis.HBDS || pxis.HBFS || pxis.TFES) {
        pxisMask.AsUlong = 0;
        pxisMask.IFS = pxisMask.HBDS = pxisMask.HBFS = pxisMask.TFES = 1;
        StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->IS.AsUlong, pxisMask.AsUlong);

      //call the correct error handling based on current hw queue workload type
        sact = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->SACT);


        if(sact != 0) {
          //5.1 NCQ, Handle error processing
            channelExtension->StateFlags.CallAhciReset = 1;


          //Give NCQ one chance
            if (channelExtension->StateFlags.NCQ_Succeeded == 0) {
                channelExtension->StateFlags.NCQ_Activated = 0;
            }
        } else {
            //5.1 Non-NCQ, Handle error processing
            channelExtension->StateFlags.CallAhciNonQueuedErrorRecovery = 1;
        }
    }

  //2.1.2 Handle non Native HotPlug Events
    // Cold Port Detect Status
    if (pxis.CPDS) {
        pxisMask.AsUlong = 0;
        pxisMask.CPDS = 1;
        StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->IS.AsUlong, pxisMask.AsUlong);
      // Handle bus rescan processing processing
        channelExtension->StateFlags.CallAhciReportBusChange = 1;
    }

    if (pxis.DMPS || pxis.PCS) {
        cmd.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->CMD.AsUlong);

        // Device Mechanical Presence Status
        if (pxis.DMPS) {
            pxisMask.AsUlong = 0;
            pxisMask.DMPS = 1;
            StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->IS.AsUlong, pxisMask.AsUlong);
            // Mechanical Presence Switch Attached to Port
            if (cmd.MPSP) {
              // Handle bus rescan processing processing
                channelExtension->StateFlags.CallAhciReportBusChange = 1;
            }
        }

        //2.1.3.1 Handle Serial ATA Errors (Hotplug insertion or unsolicited COMINIT)
        // Port Connect Change Status
        if (pxis.PCS) {
            //When PxSERR.DIAG.X is set to one this bit indicates a COMINIT signal was received.  This bit is reflected in the PxIS.PCS bit.
            serrMask.DIAG.X = 1;
            StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->SERR.AsUlong, serrMask.AsUlong);
            // PCS = 1 could be an unsolicited COMINIT on an already detected drive. See AHCI 6.2.2.3    Recovery of Unsolicited COMINIT
            if (!IgnoreHotPlug(channelExtension) && (cmd.ST == 0) ) {
              // Handle bus rescan processing processing
                channelExtension->StateFlags.CallAhciReportBusChange = 1;
            }
        }
    }

    //2.1.3.2 Handle Serial ATA Errors (Hotplug removal or PHY Power Management event)
    // PhyRdy Change Status
    if (pxis.PRCS) {
        //Hot plug removals are detected via the PxIS.PRCS bit that directly reflects the PxSERR.DIAG.N bit.
        //Note that PxSERR.DIAG.N is also set to �1� on insertions and during interface power management entry/exit.
        serrMask.DIAG.N = 1;
        StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->SERR.AsUlong, serrMask.AsUlong);

        ssts.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->SSTS.AsUlong);

        if (!IgnoreHotPlug(channelExtension)) {
            //If a ZPODD drive has already been found and it is a ZPODD system
            if ( (adapterExtension->StateFlags.SupportsAcpiDSM == 1) &&
                 IsAtapiDevice(&channelExtension->DeviceExtension->DeviceParameters) &&
                 (channelExtension->DeviceExtension[0].IdentifyPacketData->SerialAtaCapabilities.SlimlineDeviceAttention) ) {

                if (ssts.DET == 0) {
                  // ... (*) and there is no presence on the wire ...
                    // ... try to stop the port.
                    if ( P_NotRunning(channelExtension, channelExtension->Px) ) {
                        // If that succeeds, complete all outstanding commands.  CI is now cleared.
                        // This is precautionary as there shall be no IO when D3 occured, but the miniport may always create its own commands.
                        channelExtension->SlotManager.CommandsToComplete = GetOccupiedSlots(channelExtension);
                        channelExtension->SlotManager.CommandsIssued = 0;
                        channelExtension->SlotManager.NCQueueSlice = 0;
                        channelExtension->SlotManager.NormalQueueSlice = 0;
                        channelExtension->SlotManager.SingleIoSlice = 0;
                        channelExtension->SlotManager.HighPriorityAttribute = 0;
                    } else {
                        NT_ASSERT(FALSE);     // Looks like a hardware issue, will recover in P_Running_StartAttempt() when ZPODD is powered on again.
                    }
                } else if (ssts.DET == 3) {
                  // ... (*) and there is presence on the wire ...
                    // ... try to get the channel started.
                    P_Running_StartAttempt(channelExtension, TRUE);
                }
            } else if ( (ssts.DET == 0) && (ssts.IPM == 0) ) {
                // Handle bus rescan processing processing
                channelExtension->StateFlags.CallAhciReportBusChange = 1;
            }
        }
    }

    //2.1.3.3 Handle other Serial ATA Errors (everything else)
    if (serr.AsUlong > 0) {
        StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->SERR.AsUlong, (ULONG)~0);
    }

    //2.1.4 Handle Datalength Mismatch Error
    // Overflow Status
    if (pxis.OFS) {
        pxisMask.AsUlong = 0;
        pxisMask.OFS = 1;
        StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->IS.AsUlong, pxisMask.AsUlong);
      //5.1 Handle error processing
        // AHCI 6.1.5 COMRESET is required by software to clean up from this serious error
        channelExtension->StateFlags.CallAhciReset = 1;
    }

    //2.1.5 Handle NonFatal Errors
    // Interface Non-fatal Error Status
    if (pxis.INFS) {
        pxisMask.AsUlong = 0;
        pxisMask.INFS = 1;
        StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->IS.AsUlong, pxisMask.AsUlong);
    }

    //2.1.6 Handle Asynchronous Notification, only ATAPI device supports this feature.
    // Set Device Bits Interrupt
    if ( (pxis.SDBS == 1) && (channelExtension->ReceivedFIS->SetDeviceBitsFis.N) &&
         IsAtapiDevice(&channelExtension->DeviceExtension->DeviceParameters) &&
         IsDeviceSupportsAN(channelExtension->DeviceExtension->IdentifyPacketData) ) {
        // Asynchronous Notification is received. Notify Port Driver.
        // This async notification could be for media status, device status or device operation events.
        // Notification failure of STOR_STATUS_BUSY is ok as it means that notifications are being coalesced
        // and this is the only async notification.
        // Notification failure of STOR_STATUS_INVALID_DEVICE_REQUEST is ok as Storport may no longer find
        // the unit as it may have been surprise removed.
        asyncNotifyFlags = (RAID_ASYNC_NOTIFY_FLAG_MEDIA_STATUS | RAID_ASYNC_NOTIFY_FLAG_DEVICE_STATUS |
                            RAID_ASYNC_NOTIFY_FLAG_DEVICE_OPERATION);
        
        storStatus = StorPortAsyncNotificationDetected(AdapterExtension,
                                                       (PSTOR_ADDRESS)&channelExtension->DeviceExtension[0].DeviceAddress,
                                                       asyncNotifyFlags);
        NT_ASSERT((storStatus == STOR_STATUS_SUCCESS) || 
                  (storStatus == STOR_STATUS_BUSY) || 
                  (storStatus == STOR_STATUS_INVALID_DEVICE_REQUEST));
        // not actually use this variable.
        UNREFERENCED_PARAMETER(storStatus);
    }

    //2.3 Handle Normal Command Completion
    pxisMask.AsUlong =0;
    // Device to Host Register FIS Interrupt
    if (pxis.DHRS) {
        pxisMask.DHRS = 1;
    }
    // PIO Setup FIS Interrupt
    if (pxis.PSS) {
        pxisMask.PSS = 1;
    }
    // DMA Setup FIS Interrupt
    if (pxis.DSS) {
        pxisMask.DSS = 1;
    }
    // Set Device Bits Interrupt
    if (pxis.SDBS) {
        pxisMask.SDBS = 1;
    }
    // Descriptor Processed (A PRD with the �I� bit set has transferred all of its data)
    if (pxis.DPS) {
        pxisMask.DPS = 1;
    }
    if (pxisMask.AsUlong != 0 ) {
        StorPortWriteRegisterUlong(AdapterExtension, &channelExtension->Px->IS.AsUlong, pxisMask.AsUlong);
    }

   //2.4 error process
    if ( ErrorRecoveryIsPending(channelExtension) ) {
        AhciPortErrorRecovery(channelExtension);
    }

  //3. Clear channel interrupt
    is = 0;
    is |= (1 << channelExtension->PortNumber);
    StorPortWriteRegisterUlong(AdapterExtension, adapterExtension->IS, is);

  //4. Complete outstanding commands
    ci = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->CI);
    sact = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->SACT);

    // preserve taskfile for using in command completion process
    channelExtension->TaskFileData.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->TFD.AsUlong);

    outstanding = ci | sact;

    if( (channelExtension->SlotManager.CommandsIssued & ~outstanding) > 0 ) {
       // all completed commands by hardware will be marked compelted
        channelExtension->SlotManager.CommandsToComplete |= (channelExtension->SlotManager.CommandsIssued & ~outstanding);
        channelExtension->SlotManager.CommandsIssued &= outstanding;

      // recording execution history for completing SRB
        RecordInterruptHistory(channelExtension, pxis.AsUlong, ssts.AsUlong, serr.AsUlong, ci, sact, 0x20000005);   //AhciHwInterrupt complete IO

        AhciCompleteIssuedSRBs(channelExtension, SRB_STATUS_SUCCESS, TRUE);
    } else {
      // recording execution history for no SRB to be completed
        RecordInterruptHistory(channelExtension, pxis.AsUlong, ssts.AsUlong, serr.AsUlong, ci, sact, 0x20010005);   //AhciHwInterrupt No IO completed
    }

   //6.1 Partial to Slumber auto transit
    cmd.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->CMD.AsUlong);

    if (PartialToSlumberTransitionIsAllowed(channelExtension, cmd, ci, sact)) {
        ULONG status;
        // convert inverval value from ms to us. allow 20ms of coalescing with other timers
        status = StorPortRequestTimer(AdapterExtension, channelExtension->WorkerTimer, AhciAutoPartialToSlumber, channelExtension, channelExtension->AutoPartialToSlumberInterval * 1000, 20000);
        if (status == STOR_STATUS_SUCCESS) {
            StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Scheduled \n", channelExtension->PortNumber);
        }
    }

    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(channelExtension, 0x10000005);//Exit AhciHwInterrupt
    }

    return TRUE;
}

VOID
AhciHwTracingEnabled (
    _In_ PVOID AdapterExtension,
    _In_ BOOLEAN Enabled
    )
{
    PAHCI_ADAPTER_EXTENSION adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;

    adapterExtension->TracingEnabled = Enabled;

    return;
}

SCSI_UNIT_CONTROL_STATUS
AhciHwUnitControl (
    _In_ PVOID AdapterExtension,
    _In_ SCSI_UNIT_CONTROL_TYPE ControlType,
    _In_ PVOID Parameters
    )
/*++

Routine Description:

    This is a generic routine to allow for special unit control activities.
    It maianly handles device start irp for a specific device.

Arguments:

    AdapterExtension - Pointer to the device extension for adapter.

    ControlType - Specifies the type of call being made through this routine.

    Parameters - Pointer to parameters needed for this control type (optional).

Return Value:

    SCSI_UNIT_CONTROL_STATUS - currently either:
        ScsiUnitControlSuccess = 0,
        ScsiUnitControlUnsuccessful

--*/

{
    SCSI_UNIT_CONTROL_STATUS status = ScsiUnitControlSuccess;
    PAHCI_ADAPTER_EXTENSION adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
    PSCSI_SUPPORTED_CONTROL_TYPE_LIST controlTypeList;

    switch (ControlType) {
        // determine which control types (routines) are supported
        case ScsiQuerySupportedUnitControlTypes: {
            // get pointer to control type list
            controlTypeList = (PSCSI_SUPPORTED_CONTROL_TYPE_LIST)Parameters;

            // Report ScsiQuerySupportedUnitControlTypes, ScsiUnitStart and ScsiUnitPower are supported.
            if (ScsiQuerySupportedControlTypes < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiQuerySupportedUnitControlTypes] = TRUE;
            }
            if (ScsiUnitStart < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiUnitStart] = TRUE;
            }
            if (ScsiUnitPower < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiUnitPower] = TRUE;
            }
            if (ScsiUnitPoFxPowerInfo < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiUnitPoFxPowerInfo] = TRUE;
            }
            if (ScsiUnitPoFxPowerActive < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiUnitPoFxPowerActive] = TRUE;
            }
            if (ScsiUnitPoFxPowerSetFState < controlTypeList->MaxControlType) {
                controlTypeList->SupportedTypeList[ScsiUnitPoFxPowerSetFState] = TRUE;
            }
            status = ScsiUnitControlSuccess;
            break;
        }

        // ScsiUnitStart is called during Storport processes IRP_MN_START_DEVICE
        case ScsiUnitStart: {
            PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)Parameters;
            // this is the PnP call IRP_MN_START_DEVICE for the LUN. Need to do device initialization work.
            if (IsPortValid(adapterExtension, storAddrBtl8->Path)) {
                AhciDeviceStart(adapterExtension->PortExtension[storAddrBtl8->Path]);
                status = ScsiUnitControlSuccess;
            } else {
                status = ScsiUnitControlUnsuccessful;
            }
            break;
        }

        // ScsiUnitPower is called during Storport processes power Irp for the unit
        case ScsiUnitPower: {
            PSTOR_UNIT_CONTROL_POWER unitControlPower = (PSTOR_UNIT_CONTROL_POWER)Parameters;
            PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)unitControlPower->Address;
            // this is the power call for the LUN. Need to power up or power down the device as necessary.
            if (IsPortValid(adapterExtension, storAddrBtl8->Path)) {
                PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path];

                StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - %s\n", storAddrBtl8->Path, unitControlPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3");
                
                if (unitControlPower->PowerState == StorPowerDeviceD0) {
                    AhciPortPowerUp(channelExtension);
                    channelExtension->DevicePowerState = StorPowerDeviceD0;
                    status = ScsiUnitControlSuccess;

                } else if (unitControlPower->PowerState == StorPowerDeviceD3) {

                    AhciPortPowerDown(channelExtension);
                    channelExtension->DevicePowerState = StorPowerDeviceD3;
                    status = ScsiUnitControlSuccess;
                } else {
                    NT_ASSERT(FALSE);
                    status = ScsiUnitControlUnsuccessful;
                }
            } else {
                status = ScsiUnitControlUnsuccessful;
            }
            break;
        }

        case ScsiUnitPoFxPowerInfo: {
            PSTOR_POFX_UNIT_POWER_INFO  unitPowerInfo = (PSTOR_POFX_UNIT_POWER_INFO)Parameters;
            PSTOR_ADDR_BTL8             storAddrBtl8 = (PSTOR_ADDR_BTL8)unitPowerInfo->Header.Address;
            BOOLEAN                     d3ColdEnabled = FALSE;

            if (IsPortValid(adapterExtension, storAddrBtl8->Path)) {
                ULONG                   storStatus = STOR_STATUS_SUCCESS;
                ULONG                   bufferLength = STOR_POFX_DEVICE_SIZE + STOR_POFX_COMPONENT_V2_SIZE + STOR_POFX_COMPONENT_IDLE_STATE_SIZE;
                PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path];
                BOOLEAN                 reportF1State = FALSE;


                //
                // IdlePowerEnabled == TRUE indicates this unit is being
                // registered for runtime power management.  However, the call
                // to StorPortInitializePoFxPower() must succeed before the unit
                // is truly and successfully registered.
                //
                if (unitPowerInfo->IdlePowerEnabled) {

                    //
                    // Don't register more than once.
                    //
                    if (channelExtension->StateFlags.PoFxEnabled) {
                        status = ScsiUnitControlUnsuccessful;
                        break;
                    }

                    if (reportF1State) {
                        // the buffer should be big enough to contain one more F-State (F1)
                        bufferLength += STOR_POFX_COMPONENT_IDLE_STATE_SIZE;
                    }

                    // allocate STOR_POFX_DEVICE data structure for Port, initialize the structure and register for runtime power management.
                    if (channelExtension->PoFxDevice == NULL) {
                        storStatus = StorPortAllocatePool(AdapterExtension,
                                                          bufferLength,
                                                          AHCI_POOL_TAG,
                                                          (PVOID*)&channelExtension->PoFxDevice);

                    }

                    if (storStatus == STOR_STATUS_SUCCESS) {
                        PSTOR_POFX_COMPONENT_V2 component = (PSTOR_POFX_COMPONENT_V2)&(channelExtension->PoFxDevice->Components[0]);

                        AhciZeroMemory((PCHAR)channelExtension->PoFxDevice, bufferLength);

                        channelExtension->PoFxDevice->Version = STOR_POFX_DEVICE_VERSION_V1;
                        channelExtension->PoFxDevice->Size = STOR_POFX_DEVICE_SIZE;
                        channelExtension->PoFxDevice->ComponentCount = 1;                        

                        //
                        // The dump version of StorAHCI is not able to bring the
                        // unit out of D3Cold.
                        //
                        channelExtension->PoFxDevice->Flags = STOR_POFX_DEVICE_FLAG_NO_DUMP_ACTIVE;

                        component->Version = STOR_POFX_COMPONENT_VERSION_V2;
                        component->Size = STOR_POFX_COMPONENT_V2_SIZE;
                        component->FStateCount = reportF1State ? 2 : 1;
                        component->DeepestWakeableFState = 0;
                        component->DeepestAdapterPowerRequiredFState = 0;
                        component->DeepestCrashDumpReadyFState = 0;
                        component->Id = STORPORT_POFX_LUN_GUID;

                        // F0 State
                        component->FStates[0].Version = STOR_POFX_COMPONENT_IDLE_STATE_VERSION_V1;
                        component->FStates[0].Size = STOR_POFX_COMPONENT_IDLE_STATE_SIZE;
                        component->FStates[0].TransitionLatency = 0;
                        component->FStates[0].ResidencyRequirement = 0;
                        component->FStates[0].NominalPower = STOR_POFX_UNKNOWN_POWER;


                        // registry runtime power management for Unit
                        storStatus = StorPortInitializePoFxPower(AdapterExtension,
                                                                 (PSTOR_ADDRESS)storAddrBtl8,
                                                                 channelExtension->PoFxDevice,
                                                                 &d3ColdEnabled);

                        channelExtension->StateFlags.D3ColdEnabled = d3ColdEnabled;
                    }

                    if (storStatus != STOR_STATUS_SUCCESS) {
                        StorPortFreePool(AdapterExtension, channelExtension->PoFxDevice);
                        channelExtension->PoFxDevice = NULL;
                        channelExtension->StateFlags.PoFxEnabled = FALSE;
                        channelExtension->StateFlags.PoFxActive = FALSE;

                        status = ScsiUnitControlUnsuccessful;
                    } else {
                        channelExtension->StateFlags.PoFxEnabled = TRUE;
                        channelExtension->StateFlags.PoFxActive = TRUE;

                    }

                    channelExtension->PoFxFState = 0;

                } else {
                    //
                    // IdlePowerEnabled = FALSE, which means this unit is no
                    // longer registered for runtime power management.
                    //
                    if (channelExtension->PoFxDevice) {
                        StorPortFreePool(AdapterExtension, channelExtension->PoFxDevice);
                        channelExtension->PoFxDevice = NULL;
                    }
                    channelExtension->StateFlags.PoFxEnabled = FALSE;
                    channelExtension->StateFlags.PoFxActive = FALSE;

                    status = ScsiUnitControlSuccess;
                }
            } else {
                status = ScsiUnitControlUnsuccessful;
            }
            break;
        }

        case ScsiUnitPoFxPowerActive: {
            PSTOR_POFX_ACTIVE_CONTEXT activeContext = (PSTOR_POFX_ACTIVE_CONTEXT)Parameters;
            PSTOR_ADDR_BTL8           storAddrBtl8 = (PSTOR_ADDR_BTL8)activeContext->Header.Address;

            if ( IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(adapterExtension->PortExtension[storAddrBtl8->Path]) ) {
                PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path];

                channelExtension->StateFlags.PoFxActive = activeContext->Active ? 1 : 0;

                StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - %s\n", channelExtension->PortNumber, activeContext->Active ? "ACTIVE" : "IDLE");

                if (activeContext->Active) {
                    ULONG   busChangePending;
                    ULONG   restorePreservedSettingsPending;


                    busChangePending = InterlockedBitTestAndReset((LONG*)&channelExtension->PoFxPendingWork, 1);  //BusChange is at bit 1
                    restorePreservedSettingsPending = InterlockedBitTestAndReset((LONG*)&channelExtension->PoFxPendingWork, 0);  //RestorePreservedSettings is at bit 0

                    //perform pending Unit PoFx work
                    if (busChangePending == 1) {
                        PortBusChangeProcess(channelExtension);
                    }

                    if (restorePreservedSettingsPending == 1) {
                        // continue on processing RestorePreservedSettings


                        // Start the first command
                        IssuePreservedSettingCommands(channelExtension, NULL);

                        // Starts processing the command. Only need to do the first command if it exists. all others will be done by processing completion routine.
                        if (channelExtension->Local.Srb.SrbExtension != NULL) {
                            PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb);
                            if (srbExtension->AtaFunction != 0) {
                                AhciProcessIo(channelExtension, (PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb, FALSE);
                            }
                        }
                    }
                } else {
                }
            } else {
                status = ScsiUnitControlUnsuccessful;
            }

            break;
        }

        case ScsiUnitPoFxPowerSetFState: {
            PSTOR_POFX_FSTATE_CONTEXT fStateContext = (PSTOR_POFX_FSTATE_CONTEXT)Parameters;
            PSTOR_ADDR_BTL8           storAddrBtl8 = (PSTOR_ADDR_BTL8)fStateContext->Header.Address;
            PAHCI_CHANNEL_EXTENSION   channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path];

            if ( IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(channelExtension) ) {

                StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transition from F%u to F%u\n", channelExtension->PortNumber,
                                                                                                  channelExtension->PoFxFState,
                                                                                                  fStateContext->FState);

                channelExtension->PoFxFState = (UCHAR)fStateContext->FState;                

                if (fStateContext->FState == 0) {
                } else if (fStateContext->FState == 1) {
                } else {
                    NT_ASSERT(FALSE);
                    status = ScsiUnitControlUnsuccessful;
                }

            } else {
                status = ScsiUnitControlUnsuccessful;
            }

            break;
        }

        default:
            status = ScsiUnitControlUnsuccessful;
            break;

    } // end of switch

    return status;
}

#if _MSC_VER >= 1200
#pragma warning(pop)
#else
#pragma warning(default:4152)
#pragma warning(default:4214)
#pragma warning(default:4201)
#endif


Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS