Sample Code

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

/*++

Copyright (C) Microsoft Corporation, 2009

Module Name:

    io.c

Abstract:
    This file contains the core logic for programming and completing IO commands into an AHCI controller

Notes:

Revision History:

--*/

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

#pragma warning(disable:4214) // bit field types other than int
#pragma warning(disable:4201) // nameless struct/union

#include "generic.h"


ULONG
GetSlotToActivate(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ ULONG                   TargetSlots
    )
/*++
    Select Slot to activate based on maximum slot number allowed

Called by:
    Activate Queue

It performs:

Affected Variables/Registers:
    none

Return Value:
--*/
{

    UCHAR activeCount = 0;
    UCHAR emptyCount;
    UCHAR requestCount;
    UCHAR lastActiveSlot;
    ULONG slotToActivate = 0;
    UCHAR i;

  //1 Device's queue depth is smaller than Controller's

    NT_ASSERT(ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth <= ChannelExtension->AdapterExtension->CAP.NCS);

    //count the number of slots already in use
    if (ChannelExtension->SlotManager.CommandsIssued > 0) {
        activeCount = NumberOfSetBits(ChannelExtension->SlotManager.CommandsIssued);
    }
    //1.1 Check if all slots are active.
    if (activeCount >= ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth) {
        //if all possible slots are full, no matter what, return no work (0)
        return 0;
    }

  //2 Look for any entry from last active slot
    requestCount = NumberOfSetBits(TargetSlots);
    lastActiveSlot = ChannelExtension->LastActiveSlot;
    emptyCount = ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth - activeCount;

  //3.1 Look for any entry from last active slot
    for (i = lastActiveSlot; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) {
        if ((TargetSlots & (1 << i)) > 0) {
            slotToActivate |= (1 << i);
            emptyCount--;
            requestCount--;
            if (emptyCount == 0 || requestCount == 0) {
                ChannelExtension->LastActiveSlot = i;
                return slotToActivate;
            }
        }
    }

  //3.2 Look for any entry from beginning to last active slot
  //Slot 0 is reserved for internal command
    for (i = 1 ; i <= lastActiveSlot; i++) {
        if ((TargetSlots & (1 << i)) > 0) {
            slotToActivate |= (1 << i);
            emptyCount--;
            requestCount--;
            if (emptyCount == 0 || requestCount == 0) {
                ChannelExtension->LastActiveSlot = i;
                return slotToActivate;
            }
        }
    }

    NT_ASSERT(FALSE);
    return slotToActivate;
}


UCHAR
GetSingleIo(
    PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
/*++
    Selects an IO circularly from the programmed Single IO slice starting with the slot after the most recently programmed slot
    This ensures the issuing of the SingleIO is FIFO

It assumes:
    Single IO are never high priority

Called by:
    Activate Queue

It performs:
    (overview)
    1 Initialize
    2 Chose the slot
    (details)
    1.1 Initialize variables
    2.1 Chose the slot circularly starting with CCS

Affected Variables/Registers:
    none

Return Value:
    Slot number of the oldest programmed Single IO Slice slot
    If no slots are programmed the tag returned is 0xFF
--*/
{
    UCHAR limit;
    UCHAR i;

    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;

  //1.1 Initialize variables
    limit = ChannelExtension->CurrentCommandSlot;

  // if there is internal request pending, always get it first.
    if ( (ChannelExtension->SlotManager.SingleIoSlice & 1) > 0 ) {
        return 0;
    }

  //2.1 Chose the slot circularly starting with CCS
    for (i = limit; i <= adapterExtension->CAP.NCS; i++) {
        if ( (ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0){
            return i;
        }
    }

    for (i = 0; i < limit; i++) {
        if ( (ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0){
            return i;
        }
    }

    return 0xff;
}

VOID
AddQueue (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _Inout_ PSTORAHCI_QUEUE Queue,
    _In_ PSTORAGE_REQUEST_BLOCK Srb,
    _In_ ULONG Signature,
    _In_ UCHAR Tag
    )
/*
Input Parameters:
    ChannelExtension
    Queue
    Srb
    Signature:  used to mark the location of last Srb in history log
    Tag:        bit 31 ~ 24, Queue->CurrentDepth: bit 23 ~ 0
*/
{
    PVOID tempTail, foundSrb;
    ULONG srbsFound;

    if (Queue->Tail == NULL) {
        Queue->Tail = (PVOID) Srb;
        Queue->Head = (PVOID) Srb;
    } else {
        tempTail = Queue->Tail;
        if (SrbGetNextSrb(tempTail) == NULL) {    //Verify SRBs are not about to be severed from the Queue
            SrbSetNextSrb(tempTail, (PVOID)Srb);
        } else {
            //intentional bugcheck to check queuing errors. Suppress null pointer dereferencing warnings
            #pragma warning (suppress: 6011)
            NT_ASSERT(FALSE); ChannelExtension = NULL; ChannelExtension->PortNumber++;
        }
        Queue->Tail = (PVOID)Srb;
        if (SrbGetNextSrb(Srb) != NULL) {         //Verify NextSrb is not in use by anyone
            //intentional bugcheck to check queuing errors. Suppress null pointer dereferencing warnings
            #pragma warning (suppress: 6011)
            NT_ASSERT(FALSE); ChannelExtension = NULL; ChannelExtension->PortNumber++;
        }
    }
    Queue->CurrentDepth++;
    if (Queue->Head == NULL) {              //It is impossible for Head to be NULL here
        //intentional bugcheck to check queuing errors. Suppress null pointer dereferencing warnings
        #pragma warning (suppress: 6011)
        NT_ASSERT(FALSE); ChannelExtension = NULL; ChannelExtension->PortNumber++;
        srbsFound = 0;
    } else {
        foundSrb = Queue->Head;
        srbsFound = 1;
        foundSrb = SrbGetNextSrb(foundSrb);
        while (foundSrb) {
            srbsFound++;
            foundSrb = SrbGetNextSrb(foundSrb);
        }
    }
    Queue->DepthHistory[Queue->DepthHistoryIndex] = ( (Tag << 24) | Queue->CurrentDepth );
    Queue->DepthHistoryIndex++;
    Queue->DepthHistoryIndex %= 100;
    Queue->DepthHistory[Queue->DepthHistoryIndex] = Signature;
    if (Queue->CurrentDepth != srbsFound ) {
        //intentional bugcheck to check queuing errors. Suppress null pointer dereferencing warnings
        #pragma warning (suppress: 6011)
        NT_ASSERT(FALSE); ChannelExtension = NULL; ChannelExtension->PortNumber++;
    }
    if (Queue->CurrentDepth > Queue->DeepestDepth) {
        Queue->DeepestDepth = Queue->CurrentDepth;
    }
}

PSTORAGE_REQUEST_BLOCK
RemoveQueue (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _Inout_ PSTORAHCI_QUEUE Queue,
    _In_ ULONG Signature,
    _In_ UCHAR Tag
    )
{
    PVOID nextSrb, foundSrb;
    ULONG srbsFound;

    //Check to see if the queue is empty
    if (Queue->Head == NULL) {
        return NULL;
    }

    //if it is not empty, pop
    nextSrb = Queue->Head;
    Queue->Head = SrbGetNextSrb(nextSrb);
    SrbSetNextSrb(nextSrb, NULL);                //This should have been NULL to start with, return it to it's NULL state

    if (Queue->Head == NULL) {
        Queue->Tail = NULL;
    }
    Queue->CurrentDepth--;
    if (Queue->Head == NULL) {
        srbsFound = 0;
    } else {
        foundSrb = Queue->Head;
        srbsFound = 1;
        foundSrb = SrbGetNextSrb(foundSrb);
        while (foundSrb) {
            srbsFound++;
            foundSrb = SrbGetNextSrb(foundSrb);
        }
    }
    Queue->DepthHistory[Queue->DepthHistoryIndex] = ( (Tag << 24) | Queue->CurrentDepth );
    Queue->DepthHistoryIndex++;
    Queue->DepthHistoryIndex %= 100;
    Queue->DepthHistory[Queue->DepthHistoryIndex] = Signature;
    if (Queue->CurrentDepth != srbsFound ) {
        //intentional bugcheck to check queuing errors. Suppress null pointer dereferencing warnings
        #pragma warning (suppress: 6011)
        NT_ASSERT(FALSE); ChannelExtension = NULL; ChannelExtension->PortNumber++;
    }
    if (Queue->CurrentDepth > Queue->DeepestDepth) {
        Queue->DeepestDepth = Queue->CurrentDepth;
    }
    return (PSTORAGE_REQUEST_BLOCK)nextSrb;
}


BOOLEAN
ActivateQueue(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN AtDIRQL
    )
/*++

It assumes:

Called by:

It performs:
    (overview)
    1. Ensures the channel is ready for programming
    2. Program the next IO according to the priority scheme
    (details)
    1.1  Initialize variables
    1.2 If the programming should not happen now, leave, ActivateQueue will be called again when these conditions are changed
    2.1 Choose the Queue with which to program the controller
        Algorithm:
            2.1.1 Single IO SRBs (including Request Sense and non data control commands) have highest priority.
            2.1.2 When there are no Single IO commands, Normal IO get the next highest priority
            2.1.3 When there are no Single or Normal commands, NCQ commands get the next highest priority
            2.1.4 In the case that no IO is present in any Slices, program nothing
    2.2 Program all the IO from the chosen queue into the controller

Affected Variables/Registers:
    channelExtension
    CI
    SACT
Return Values:
--*/
{
    AHCI_COMMAND    cmd;
    ULONG           sact;
    ULONG           ci;
    ULONG           slotsToActivate;
    BOOLEAN         activateNcq;
    int             i;

    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;

    UNREFERENCED_PARAMETER(AtDIRQL);

  //1.1 Initialize variables
    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000022);//ActivateQueue
    }

    slotsToActivate = 0;
    activateNcq = FALSE;

  //1.1.1 If there is no command to program, leave
    if ( (ChannelExtension->SlotManager.SingleIoSlice == 0) &&
         (ChannelExtension->SlotManager.NormalQueueSlice == 0) &&
         (ChannelExtension->SlotManager.NCQueueSlice == 0) ) {
        //
        if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
            RecordExecutionHistory(ChannelExtension, 0x10040022);//ActivateQueue, No Commands to program
        }
        return FALSE;
    }

  //1.2 If the programming should not happen now, leave
    if ( !IsPortStartCapable(ChannelExtension) ) {
        RecordExecutionHistory(ChannelExtension, 0x10030022);//ActivateQueue, Channel Not Start Capable
        return FALSE;
    }

    if ( ErrorRecoveryIsPending(ChannelExtension) ) {
        RecordExecutionHistory(ChannelExtension, 0x10070022);//ActivateQueue, Error Recovery is pending.
        return FALSE;
    }

    if (ChannelExtension->StateFlags.QueuePaused == TRUE) {
        RecordExecutionHistory(ChannelExtension, 0x10020022);//ActivateQueue, Channel Queue Paused
        return FALSE;
    }

    cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong);
    if (cmd.ST == 0) {
        RecordExecutionHistory(ChannelExtension, 0x10010022);//ActivateQueue, Channel Not Yet Started
        return FALSE;
    }

  //2.1 Choose the Queue with which to program the controller
    sact = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->SACT);
    ci = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CI);
  //2.1.2 Single IO SRBs have highest priority.
    if(ChannelExtension->SlotManager.SingleIoSlice != 0) {
        if ( ( sact == 0 ) && ( ci == 0 ) ) {
            //Safely get Single IO in round robin fashion
            i = GetSingleIo(ChannelExtension);
            if (i != 0xff) {
                slotsToActivate = (1 << i);
                ChannelExtension->SlotManager.SingleIoSlice &= ~slotsToActivate;
                ChannelExtension->StateFlags.QueuePaused = TRUE;            //and pause the queue so no other IO get programmed
            }
        }
  //2.1.2 When there are no Single IO commands, Normal IO get the next highest priority
    } else if (ChannelExtension->SlotManager.NormalQueueSlice != 0) {
        // Normal commands can not be sent when NCQ commands are outstanding.  When the NCQ commands complete ActivateQueue will get called again.
        if (sact == 0) {
            //Grab the High Priority Normal IO before the Low Priority Normal IO
            slotsToActivate = ChannelExtension->SlotManager.HighPriorityAttribute & ChannelExtension->SlotManager.NormalQueueSlice;
            //If there aren't any High Priority, grab everything else
            if (slotsToActivate > 0) {
                ChannelExtension->SlotManager.NormalQueueSlice &= ~slotsToActivate;
            } else {
                slotsToActivate = ChannelExtension->SlotManager.NormalQueueSlice;
                ChannelExtension->SlotManager.NormalQueueSlice = 0;
            }
        }
  //2.1.3 When there are no Single or Normal commands, NCQ commands get the next highest priority
    } else if (ChannelExtension->SlotManager.NCQueueSlice != 0) {
        // NCQ commands can not be sent when Normal commands are outstanding.  When the Normal commands complete, Activate Queue will get called again.
        if ( ( ci != 0 ) && (sact == 0) ) {
            slotsToActivate = 0;
        } else {
            //Grab the High Priority NCQ IO before the Low Priority NCQ IO
            slotsToActivate = ChannelExtension->SlotManager.HighPriorityAttribute & ChannelExtension->SlotManager.NCQueueSlice;
            //If there aren't any High Priority, grab everything else
            if (slotsToActivate == 0) {
                slotsToActivate = ChannelExtension->SlotManager.NCQueueSlice;
            }
            //and apply any device outstanding IO limits to filter down which IO to activate
            if (slotsToActivate > 0) {
                if (ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth < ChannelExtension->MaxPortQueueDepth ) {
                    // get allowed slots if the device queue depth is less than the port can support.
                    slotsToActivate = GetSlotToActivate(ChannelExtension, slotsToActivate);
                }
                if (slotsToActivate > 0) {
                    //and if there are any IO still selected, clear them from the NCQueue
                    activateNcq = TRUE;     //Remember to program SACT for these commands
                    ChannelExtension->SlotManager.NCQueueSlice &= ~slotsToActivate;
                    //the selected IO will be activated at the end of this function
                }
            }
        }
  //2.1.4 In the case that no IO is present in any Slices, program nothing
    }

  //2.1 Program all the IO from the chosen queue into the controller
    if (slotsToActivate != 0) {
        //2.2 Get command start time
        if(adapterExtension->TracingEnabled) {
            LARGE_INTEGER perfCounter = {0};
            ULONG pendingProgrammingCommands = slotsToActivate;

            i = 0;

            StorPortQueryPerformanceCounter((PVOID)adapterExtension, NULL, &perfCounter);

            while (pendingProgrammingCommands) {
                if (pendingProgrammingCommands & 1) {
                    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(ChannelExtension->Slot[i].Srb);
                    srbExtension->StartTime = perfCounter.QuadPart;
                }

                i++;
                pendingProgrammingCommands >>= 1;
            }
        }

        ChannelExtension->SlotManager.CommandsIssued |= slotsToActivate;

        // program registers
        if (activateNcq) {
            StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->SACT, slotsToActivate);
        }
        StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CI, slotsToActivate);

    }

    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordInterruptHistory(ChannelExtension, slotsToActivate, 0, 0, ci, sact, 0x10060022); //ActivateQueue, after getting IO slices,
        RecordExecutionHistory(ChannelExtension, 0x10050022);//Exit ActivateQueue
    }

    return TRUE;
}

__inline
ULONGLONG
CalculateTimeDurationIn100ns (
    _In_ ULONGLONG TimeDuration,
    _In_ ULONGLONG CounterFrequency
    )
{
    ULONGLONG timeIn100ns = 0;

    // as the counter can hold more than 20,000 years for the same power cylce,
    // the situation about the counter start over again is considered an error case of inputs.
    if (CounterFrequency > 0) {
        // difference between performance counters, needs to convert to 100ns.
        ULONGLONG countersDiff = TimeDuration;

        // get seconds
        timeIn100ns = countersDiff / CounterFrequency;

        // get milliseconds
        countersDiff = (countersDiff % CounterFrequency) * 1000;
        timeIn100ns *= 1000;
        timeIn100ns += countersDiff / CounterFrequency;

        // get 100 nanoseconds
        countersDiff = (countersDiff % CounterFrequency) * 10000;
        timeIn100ns *= 10000;
        timeIn100ns += countersDiff / CounterFrequency;
    }

    return timeIn100ns;
}

VOID
AhciCompleteIssuedSRBs(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ UCHAR SrbStatus,
    _In_ BOOLEAN AtDIRQL
  )
/*++
This function completes a command that has been programmed into a Slot

It assumes:
    The command in not running on the AHCI controller
    ATAPI commands will be sent 1 at a time

Called by:
    AhciHwInterrupt
    AhciPortReset
    AhciNonQueuedErrorRecovery

It performs:
    (overview)
    1 Initialize
    2 Process all commands in CommandsToComplete
    3 Start the next batch of commands
    (details)
    1.1 Initialize variables
    2.1 For every command marked as completed
    2.2 Set the status
    2.3 Monitor to see that any NCQ commands are completing
    2.4 Give the slot back
    3.1 Start the next IO(s) if any

Affected Variables/Registers:
    ChannelExtension->Slot[srbExtension->QueueTag].Srb
    ChannelExtension->SlotManager.CommandsToComplete

--*/
{
    PSLOT_CONTENT       slotContent;
    UCHAR               i;

    PAHCI_ADAPTER_EXTENSION adapterExtension;
    PAHCI_SRB_EXTENSION     srbExtension;

    LARGE_INTEGER           perfCounter = {0};
    LARGE_INTEGER           perfFrequency = {0};


  //1.1 Initialize variables
    adapterExtension = ChannelExtension->AdapterExtension;
    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000046);//AhciCompleteIssuedSRBs
    }

    if( adapterExtension->TracingEnabled && (ChannelExtension->SlotManager.CommandsToComplete) ) {
        StorPortQueryPerformanceCounter((PVOID)adapterExtension, &perfFrequency, &perfCounter);
    }

  //2.1 For every command marked as completed
    for (i = 0; i <= (adapterExtension->CAP.NCS); i++) {
        if( ( ChannelExtension->SlotManager.CommandsToComplete & (1 << i) ) > 0) {
            slotContent = &ChannelExtension->Slot[i];
            srbExtension = GetSrbExtension(slotContent->Srb);

            if (slotContent->Srb == NULL) {
                //This shall never happen.
                //The completed slot has no SRB so it can not be completed back to Storport.
                //Give back the empty slot
                NT_ASSERT(FALSE);
                ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << i);
                ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << i);
                continue;
            }

            if (slotContent->CmdHeader == NULL) {
                //This shall never happen.
                //Give back the empty slot
                NT_ASSERT(FALSE);
                ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << i);
                ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << i);
                //It is now impossible to determine if a data transfer completed correctly
                slotContent->Srb->SrbStatus = SRB_STATUS_ABORTED;
                AhciCompleteRequest(ChannelExtension, slotContent->Srb, AtDIRQL);
                continue;
            }

          //2.1.2 Log command execution time, if it's allowed
            if ( adapterExtension->TracingEnabled &&
                 (srbExtension->StartTime != 0) &&
                 (perfCounter.QuadPart != 0) &&
                 !IsMiniportInternalSrb(ChannelExtension, slotContent->Srb) ) {

                ULONGLONG durationTime = CalculateTimeDurationIn100ns((perfCounter.QuadPart - srbExtension->StartTime), perfFrequency.QuadPart);
                StorPortNotification(IoTargetRequestServiceTime, (PVOID)adapterExtension, durationTime, slotContent->Srb);
            }

          //2.2 Set the status
            if( (SrbStatus == SRB_STATUS_SUCCESS) &&
                (!IsRequestSenseSrb(srbExtension->AtaFunction)) &&
                (srbExtension->AtaFunction != ATA_FUNCTION_ATA_SMART) &&
                (!IsNCQCommand(srbExtension)) &&
                (slotContent->CmdHeader->PRDBC != RequestGetDataTransferLength(slotContent->Srb)) ) {
                //
                if (slotContent->CmdHeader->PRDBC < RequestGetDataTransferLength(slotContent->Srb)) {
                    //buffer underrun,
                    RequestSetDataTransferLength(slotContent->Srb, slotContent->CmdHeader->PRDBC);
                    slotContent->Srb->SrbStatus = SrbStatus;
                } else {
                    //buffer overrun, return error
                    NT_ASSERT(FALSE);
                    slotContent->Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
                }
            } else {
                //If anything has set a STATUS on this SRB, honor that over the one passed in
                if (slotContent->Srb->SrbStatus == SRB_STATUS_PENDING) {
                    slotContent->Srb->SrbStatus = SrbStatus;
                }
            }

          //2.3 Monitor to see that any NCQ commands are completing
            if ( (slotContent->Srb->SrbStatus == SRB_STATUS_SUCCESS) &&
                 (IsNCQCommand(srbExtension)) ) {
                ChannelExtension->StateFlags.NCQ_Succeeded = TRUE;
            }

          //2.4 Give the slot back
            ReleaseSlottedCommand(ChannelExtension, i, AtDIRQL); // Request sense is handled here.

            if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
                RecordExecutionHistory(ChannelExtension, 0x10000046);//Completed one SRB
            }
        }
    }

  //3.1 Start the next IO(s) if any
    AhciGetNextIos(ChannelExtension, AtDIRQL);

    return;
}

VOID
SRBtoATA_CFIS(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PSLOT_CONTENT SlotContent
  )
/*++
    Populates CFIS structure with an ATA command in the given slot
It assumes:
    The slot is empty and not being used
Called by:
    AhciHwStartIo

It performs:
    (overview)
    1 Fills in the CFIS structure
    (details)
    1.1 Map SRB fields to CFIS fields
    1.2 Specail case mapping of NCQ

Affected Variables/Registers:
    Command Table
--*/
{
    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb);
    PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension;

    UNREFERENCED_PARAMETER(ChannelExtension);

  //1.1 Map SRB fields to CFIS fields
    cmdTable->CFIS.FisType = 0x27;
    cmdTable->CFIS.PMPort = 0;      // StorAHCI doesn't support Port Multiplier
    cmdTable->CFIS.Reserved1 = 0;
    cmdTable->CFIS.C = 1;
    cmdTable->CFIS.Command = srbExtension->TaskFile.Current.bCommandReg;
  //1.2 Specail case mapping of NCQ
    if( IsNCQCommand(srbExtension) ){
        cmdTable->CFIS.Features = srbExtension->TaskFile.Current.bSectorCountReg;
        cmdTable->CFIS.Features_Exp = srbExtension->TaskFile.Previous.bSectorCountReg;
        cmdTable->CFIS.SectorCount = (srbExtension->QueueTag << 3);
        cmdTable->CFIS.Dev_Head = 0xF & srbExtension->TaskFile.Current.bDriveHeadReg;
        cmdTable->CFIS.Dev_Head |= (1 << 6);

        if( SlotContent->StateFlags.FUA ){
            cmdTable->CFIS.Dev_Head |= ATA_NCQ_FUA_BIT;
        } else {
            cmdTable->CFIS.Dev_Head &= ~ATA_NCQ_FUA_BIT;
        }

    } else {
        cmdTable->CFIS.Features = srbExtension->TaskFile.Current.bFeaturesReg;
        cmdTable->CFIS.Features_Exp = srbExtension->TaskFile.Previous.bFeaturesReg;
        cmdTable->CFIS.SectorCount = srbExtension->TaskFile.Current.bSectorCountReg;
        cmdTable->CFIS.SectorCount_Exp = srbExtension->TaskFile.Previous.bSectorCountReg;
        cmdTable->CFIS.Dev_Head = srbExtension->TaskFile.Current.bDriveHeadReg;
    }

  //1.1 Map SRB fields to CFIS fields
    cmdTable->CFIS.SectorNumber = srbExtension->TaskFile.Current.bSectorNumberReg;
    cmdTable->CFIS.SecNum_Exp = srbExtension->TaskFile.Previous.bSectorNumberReg;

    cmdTable->CFIS.CylLow = srbExtension->TaskFile.Current.bCylLowReg;
    cmdTable->CFIS.CylLow_Exp = srbExtension->TaskFile.Previous.bCylLowReg;

    cmdTable->CFIS.CylHigh = srbExtension->TaskFile.Current.bCylHighReg;
    cmdTable->CFIS.CylHigh_Exp = srbExtension->TaskFile.Previous.bCylHighReg;

    cmdTable->CFIS.ICC = 0;
    cmdTable->CFIS.Control = 0; // Device control consists of the 48bit HighOrderByte, SRST and nIEN.  None apply here.

    cmdTable->CFIS.Auxiliary7_0 = 0;
    cmdTable->CFIS.Auxiliary15_8 = 0;
    cmdTable->CFIS.Auxiliary23_16 = 0;
    cmdTable->CFIS.Auxiliary31_24 = 0;
}

VOID
SRBtoATAPI_CFIS(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PSLOT_CONTENT SlotContent
  )
/*++
    Populates CFIS structure with an ATAPI command in the given slot
It assumes:
    The slot is empty and not being used
Called by:
    AhciHwStartIo

It performs:
    (overview)
    1 Popluate the ACMD
    2 Popluate the PACKET command
    (details)
    1.1 Memcopy CDB into ACMD
    2.1 Put the PACKET Command in the CFIS
    2.2 Populate DMA bit properly

Affected Variables/Registers:
    Command Table
--*/
{
                        //These are used for copying a CDB to ACMD on ATAPI commands.
    ULONG dataLength;
    PVOID cdb;

  //1.1 Memcopy CDB into ACMD
    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb);
    PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension;

    UNREFERENCED_PARAMETER(ChannelExtension);

    cdb = SrbGetCdb(SlotContent->Srb);
    dataLength = RequestGetDataTransferLength(SlotContent->Srb);

    if (cdb != NULL) {
        StorPortCopyMemory((PVOID)cmdTable->ACMD, cdb, 16);
    } else {
        NT_ASSERT(cdb != NULL);
    }

  //2.1 Put the PACKET Command in the CFIS
    cmdTable->CFIS.FisType = 0x27;
    cmdTable->CFIS.PMPort = 0;      // StorAHCI doesn't support Port Multiplier
    cmdTable->CFIS.Reserved1 = 0;
    cmdTable->CFIS.C = 1;
    cmdTable->CFIS.Command = IDE_COMMAND_ATAPI_PACKET;   //A0 is the PACKET command

  //2.2 Populate DMA bit properly, only use it for data transfer
    if (IsDmaCommand(srbExtension->Flags)) {
        cmdTable->CFIS.Features = 0x1;

        //If word 62 bit 15 is set to one, then the DMADIR bit in the PACKET command is required by the device for
        //PACKET commands using the DMA data transfer protocol and:
        //  a) word 63 bits (2:0);        -- MultiWordDMASupport & 0x7 == 0
        //  b) word 49 bit 15;            -- Capabilities.InterleavedDmaSupported
        //  c) word 49 bit 8; and         -- Capabilities.DmaSupported
        //  d) word 88 bits (6:0),        -- UltraDMASupport & 0x7F == 0
        //shall be cleared to zero.
        if ( (ChannelExtension->DeviceExtension[0].IdentifyPacketData->DMADIR.DMADIRBitRequired != 0) &&
             (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.DmaSupported != 0) &&
             (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.InterleavedDmaSupported == 0) &&
             ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->MultiWordDMASupport & 0x7) == 0) &&
             ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->UltraDMASupport & 0x7F) == 0) ) {
            //bit 0 is DMA, bit 2 is DMADIR. (0 = transfer to the device; 4 = transfer to the host).
            cmdTable->CFIS.Features |= (srbExtension->Flags & ATA_FLAGS_DATA_IN) ? 4 : 0;
        }
    } else {
        cmdTable->CFIS.Features = 0x0;
    }

  //2.1 Put the PACKET Command in the CFIS
    cmdTable->CFIS.SectorNumber = 0;
    cmdTable->CFIS.CylLow = (UCHAR)dataLength; //put the low byte of the in CylLow
    dataLength >>= 8;
    cmdTable->CFIS.CylHigh = (UCHAR)dataLength; //put the high byte in CylHigh
    cmdTable->CFIS.Dev_Head = 0;

    cmdTable->CFIS.SecNum_Exp = 0;
    cmdTable->CFIS.CylLow_Exp = 0;
    cmdTable->CFIS.CylHigh_Exp = 0;
    cmdTable->CFIS.Features_Exp = 0;

    cmdTable->CFIS.SectorCount = 0;
    cmdTable->CFIS.SectorCount_Exp = 0;
    cmdTable->CFIS.ICC = 0;
    cmdTable->CFIS.Control = 0; // Device control consists of the 48bit HighOrderByte, SRST and nIEN.  None apply here.

    cmdTable->CFIS.Auxiliary7_0 = 0;
    cmdTable->CFIS.Auxiliary15_8 = 0;
    cmdTable->CFIS.Auxiliary23_16 = 0;
    cmdTable->CFIS.Auxiliary31_24 = 0;
}

VOID
CfistoATA_CFIS(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSLOT_CONTENT SlotContent
  )
/*++
    Populates CFIS structure with an ATA command in the given slot from a CFIS data structure.
It assumes:
    The slot is empty and not being used
Called by:
    AhciHwStartIo

It performs:
    1 Copy CFIS structure from SrbExtension
    2 Make sure common data fields value are appropriate

Affected Variables/Registers:
    Command Table
--*/
{
    PAHCI_SRB_EXTENSION     srbExtension = GetSrbExtension(SlotContent->Srb);
    PAHCI_COMMAND_TABLE     cmdTable = (PAHCI_COMMAND_TABLE)srbExtension;

    UNREFERENCED_PARAMETER(ChannelExtension);

    //
    // Copy CFIS data structure.
    //
    StorPortCopyMemory(&cmdTable->CFIS, &srbExtension->Cfis, sizeof(AHCI_H2D_REGISTER_FIS));

    //
    // When write a hiber file, the request can be retried.
    // Retry the command without Hybrid Hinting info.
    //
    if (IsDumpHiberMode(ChannelExtension->AdapterExtension) && 
        IsNCQWriteCommand(srbExtension) &&
        (srbExtension->RetryCount > 0) &&
        (ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile == 0)) {
        cmdTable->CFIS.Auxiliary23_16 = 0;
    }

    //
    // Set common data fields.
    //
    if( IsNCQCommand(srbExtension) ) {
        cmdTable->CFIS.Count7_0 = (srbExtension->QueueTag << 3);
    }
    cmdTable->CFIS.FisType = 0x27;
    cmdTable->CFIS.PMPort = 0;      // StorAHCI doesn't support Port Multiplier
    cmdTable->CFIS.Reserved1 = 0;
    cmdTable->CFIS.C = 1;

    cmdTable->CFIS.Auxiliary15_8 = 0;
    cmdTable->CFIS.Auxiliary31_24 = 0;

    cmdTable->CFIS.ICC = 0;
    cmdTable->CFIS.Control = 0;     // Device control consists of the 48bit HighOrderByte, SRST and nIEN.  None apply here.
}


ULONG
SRBtoPRDT(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSLOT_CONTENT SlotContent
  )
/*++

It assumes:
    MDLs and ScatterGatherList entries will not violate PRDT rules
Called by:
    AhciHwStartIo
It performs:
    (overview)
    1 Get the DataBuffer's address
    2 Map SGL entries into PRDT entries
    (details)
    1.1 Get the ScatterGatherList
    1.2 Verify that the DataBuffer is properly aligned
    2.1 Map SGL entries into PRDT entries
    2.2 Break up all 128K single entry IO into 2 64K IO entries
    2.3 Verify that the DataLength is even

Affected Variables/Registers:

Return Values:
    The number of entries generated to fill the PRDT
    If the value returned is -1 the PRDT could not be built.
--*/
{
    ULONG                       i;
    PAHCI_SRB_EXTENSION         srbExtension = GetSrbExtension(SlotContent->Srb);
    PAHCI_COMMAND_TABLE         cmdTable = (PAHCI_COMMAND_TABLE)srbExtension;
    PLOCAL_SCATTER_GATHER_LIST  sgl = srbExtension->Sgl;

    if (sgl == NULL) {
       //return as invalid request in case of cannot get scatter gather list.
        NT_ASSERT(FALSE);
        return (ULONG)-1;
    }

    for (i = 0; i < sgl->NumberOfElements; i++) {
      //1.2 Verify that the DataBuffer is properly aligned
        if ( (sgl->List[i].PhysicalAddress.LowPart & 0x1) == 0) {
          //2.1 Map SGL entries into PRDT entries
            if (sgl->List[i].Length != 0x20000) {
                cmdTable->PRDT[i].DBA.AsUlong = sgl->List[i].PhysicalAddress.LowPart;
                if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too
                    cmdTable->PRDT[i].DBAU = sgl->List[i].PhysicalAddress.HighPart;
                }
          //2.2 Break up a 128K single entry IO into 2 64K IO entries (128K is max transfer so there can be only 1 in any SGL)
          //    although one entry can represent at max 4M length IO, some adapters cannot handle a DBC >= 128K.
            } else {
                // Entry 0
                cmdTable->PRDT[0].DBA.AsUlong = sgl->List[0].PhysicalAddress.LowPart;
                if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too
                    cmdTable->PRDT[0].DBAU = sgl->List[0].PhysicalAddress.HighPart;
                }
                cmdTable->PRDT[0].DI.DBC = (0x10000 - 1);
                // Entry 1
                cmdTable->PRDT[1].DBA.AsUlong = (sgl->List[0].PhysicalAddress.LowPart + 0x10000);
                if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too
                    if ( (sgl->List[0].PhysicalAddress.LowPart + 0x10000) < sgl->List[0].PhysicalAddress.LowPart) {
                        cmdTable->PRDT[1].DBAU = (sgl->List[0].PhysicalAddress.HighPart + 1); //add 1 to the highpart if adding 0x10000 caused a rollover
                    } else {
                        cmdTable->PRDT[1].DBAU = sgl->List[0].PhysicalAddress.HighPart;
                    }
                }
                cmdTable->PRDT[1].DI.DBC = (0x10000 - 1);
                return 2;
            }
        } else {
            NT_ASSERT(FALSE); //Shall Not Pass
            return (ULONG)-1;
        }

      //1.3 Verify that the DataLength is even
        // all SATA transfers must be even
        // DBC is a 0 based number (i.e. 0 is 1, 1 is 2, etc.
        // sgl->Elements.Length is not (i.e. 0 is 0, 1 is 1, etc.
        if( (sgl->List[i].Length & 1) == 0 ) {                    //therefore length must be even here
          //2.3 Set Datalength in the PRDT entries
            cmdTable->PRDT[i].DI.DBC = sgl->List[i].Length - 1;         //but it must be  odd here
        } else if (sgl->List[i].Length <= RequestGetDataTransferLength(SlotContent->Srb)) {
            // Storport may send down SCSI commands with odd number of data transfer length, and it builds SGL using that transfer length value.
            // we use the length -1 to get as much data as we can. If the data length is over (length - 1), buffer overrun will be reported when the command is completed.
            RequestSetDataTransferLength(SlotContent->Srb, RequestGetDataTransferLength(SlotContent->Srb) - 1);
            cmdTable->PRDT[i].DI.DBC = sgl->List[i].Length - 2;
        } else {
            NT_ASSERT(FALSE); //Shall Not Pass
            return (ULONG)-1;
        }
    }
    return sgl->NumberOfElements;
}

VOID
SRBtoCmdHeader(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSLOT_CONTENT SlotContent,
    _In_ ULONG Length,
    _In_ BOOLEAN Reset
    )
/*++
It assumes:
    The chosen slot is available and not in use
Called by:
    AhciHwStartIo
It performs:
    Steps defined in AHCI 1.2 section 5.5.1 step #3
Affected Variables/Registers:
    Command Header
--*/
{
    PAHCI_COMMAND_HEADER    cmdHeader = SlotContent->CmdHeader;
    PSTORAGE_REQUEST_BLOCK  srb = SlotContent->Srb;
    PAHCI_SRB_EXTENSION     srbExtension = GetSrbExtension(srb);

    UNREFERENCED_PARAMETER(ChannelExtension);

//  a.  PRDTL containing the number of entries in the PRD table
    cmdHeader->DI.PRDTL = Length;
//  b.  CFL set to the length of the command in the CFIS area
    cmdHeader->DI.CFL = 5;
//  c.  A bit set if it is an ATAPI command
    cmdHeader->DI.A = (srbExtension->AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) ? 1 : 0;
//  d.  W (Write) bit set if data is going to the device
    cmdHeader->DI.W = (srbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0;
//  e.  P (Prefetch) bit optionally set (see rules in section 5.5.2)
    //Some controllers have problems if P is set.
    cmdHeader->DI.P = 0;
//  f.  If a Port Multiplier is attached, the PMP field set to the correct Port Multiplier port.
    cmdHeader->DI.PMP = 0;

    //Reset
    cmdHeader->DI.R = Reset;
    cmdHeader->DI.B = 0;
    cmdHeader->DI.C = Reset;

    //initialize the PRD byte count
    cmdHeader->PRDBC = 0;

    cmdHeader->Reserved[0] = 0;
    cmdHeader->Reserved[1] = 0;
    cmdHeader->Reserved[2] = 0;
    cmdHeader->Reserved[3] = 0;
}

BOOLEAN
AhciProcessIo(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ BOOLEAN AtDIRQL
    )
/*
This routine does following:
    1. Check if no device command associated with Srb, or the port failed to start. Bail out in these cases.
    2. Central place for specail command handling.
    3. Get available slot
    4. Call AhciFormIo() to Fill the slot; Program command Table & Header; Put IO in Slice

Note: This routine can be called even the Port is stopped.
      This routine and the calling routine AhciFormIo() does NOT program IO to adapter.
*/
{
    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb);
    UCHAR   pathId = 0;
    UCHAR   targetId = 0;
    UCHAR   lun = 0;

    SrbGetPathTargetLun(Srb, &pathId, &targetId, &lun);

    //1.0 complete Srb if no command should be sent to device.
    if (srbExtension->AtaFunction == 0) {
        NT_ASSERT(FALSE); // should investigate if this ASSERT fires
        Srb->SrbStatus = SRB_STATUS_ERROR;
        MarkSrbToBeCompleted(Srb);
        AhciCompleteRequest(ChannelExtension, Srb, AtDIRQL);
        return TRUE;
    }

  //1.1 Check if command processing should happen
    if (ChannelExtension->StartState.ChannelNextStartState == StartFailed) {
        Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
        MarkSrbToBeCompleted(Srb);
        AhciCompleteRequest(ChannelExtension, Srb, AtDIRQL);
        return TRUE;
    }

  //2. central palce for command special handling. this part cannot be put in command translation layer, as there might be internal command also needs special handling.
    //2.1 central place for special handling of Enable/Disable WRITE CACHE, update persistent settings command.
    if ( (srbExtension->AtaFunction == ATA_FUNCTION_ATA_COMMAND) &&
         (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_SET_FEATURE) ) {
        ////
        if (srbExtension->TaskFile.Current.bFeaturesReg == IDE_FEATURE_DISABLE_WRITE_CACHE) {
            //IDE_FEATURE_DISABLE_WRITE_CACHE => Modify Persistent Settings
            UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_ENABLE_WRITE_CACHE, IDE_FEATURE_DISABLE_WRITE_CACHE, 0, 0);
        }
        if (srbExtension->TaskFile.Current.bFeaturesReg == IDE_FEATURE_ENABLE_WRITE_CACHE) {
            //IDE_FEATURE_ENABLE_WRITE_CACHE => Modify Persistent Settings
            UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_DISABLE_WRITE_CACHE, IDE_FEATURE_ENABLE_WRITE_CACHE, 0, 0);
        }
    }

    //2.2 update the Identify command for enumeration. This needs to be here waiting for PxSIG to be accessible.
    if ( (srbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) &&
         (srbExtension->CompletionRoutine == AhciPortIdentifyDevice) &&
         (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_NOT_VALID) ) {
        // for enumeration command, it's safe now to check the PxSIG register as starting port has been executed.
        ULONG   sig = 0;
        sig = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong);

        if (sig == 0x101) { //ATA
            srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_IDENTIFY;
        } else {            //ATAPI
            srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY;
        }
    }

    //2.3 this is central place to update this register value in case it's wrongly set.
    if (IsAtaCommand(srbExtension->AtaFunction)) {
        SetDeviceReg((&srbExtension->TaskFile.Current), 0);   //make sure set to device0
    }

  //3 Find an available slot/tag (AHCI 1.1 Section 5.5.1)
    GetAvailableSlot(ChannelExtension, Srb);    //srbExtension->QueueTag will be set

    // 3.1 If no tag is available, reject the command to be retried later
    if ( srbExtension->QueueTag > ChannelExtension->AdapterExtension->CAP.NCS ) {
        //wait for 8 IO (random picked number) being completed before re-starting sending IO to miniport for this device.
        // if filled up,
        if (!IsMiniportInternalSrb(ChannelExtension, Srb)) {
            // report busy only for IOs from Storport
            StorPortDeviceBusy(ChannelExtension->AdapterExtension, pathId, targetId, lun, min(8, ChannelExtension->AdapterExtension->CAP.NCS));
        }
        Srb->SrbStatus = SRB_STATUS_BUSY;
        MarkSrbToBeCompleted(Srb);
        AhciCompleteRequest(ChannelExtension, Srb, AtDIRQL);
        RecordExecutionHistory(ChannelExtension, 0x10030020);   //AllocateQueueTagFailed
        return TRUE;
    }

#ifdef DBG
    {
        ULONG   ci;
        ULONG   sact;
        ci = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI);
        sact = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT);
        // 3.2 if the tag returned can't be used, something is seriously wrong. Reject the command to be retried later
        if( ( ( (1 << srbExtension->QueueTag) & ci) > 0) || ( ( (1 << srbExtension->QueueTag) & sact) > 0) ) {
            NT_ASSERT(FALSE); //can this happen? catch it.

            if (!IsMiniportInternalSrb(ChannelExtension, Srb)) {
                // pause device only for IOs from Storport
                StorPortPauseDevice(ChannelExtension->AdapterExtension, pathId, targetId, lun, 1);   //pause for 1 seconds.
            }
            Srb->SrbStatus = SRB_STATUS_BUSY;
            MarkSrbToBeCompleted(Srb);
            AhciCompleteRequest(ChannelExtension, Srb, AtDIRQL);
            RecordExecutionHistory(ChannelExtension, 0x10040020);//Tag given for slot in use
            return TRUE;
        }
    }
#endif
    //srbExtension->QueueTag and Slot[srbExtension->QueueTag] are now guaranteed ready.

    return AhciFormIo (ChannelExtension, Srb, AtDIRQL);
}

BOOLEAN
AhciFormIo(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ BOOLEAN AtDIRQL
    )
/*
    Caller: only AhciProcessIo()
*/
{
    PAHCI_SRB_EXTENSION   srbExtension;
    PSLOT_CONTENT         slotContent;
    PAHCI_COMMAND_TABLE   cmdTable;
    PAHCI_COMMAND_HEADER  cmdHeader;
    STOR_PHYSICAL_ADDRESS cmdTablePhysicalAddress;
    ULONG                 prdtLength = 0;
    PCDB                  cdb = SrbGetCdb(Srb);

    srbExtension = GetSrbExtension(Srb);

    // command header is allocated in ChannelExtension for all command slots, get the correct one.
    cmdHeader = ChannelExtension->CommandList;
    cmdHeader += srbExtension->QueueTag;

    slotContent = &ChannelExtension->Slot[srbExtension->QueueTag];

  // 1. setup slot content and log history
    slotContent->Srb = Srb;
    slotContent->CmdHeader = cmdHeader;

  //1.1 Update FUA tracking flag if needed
    if( !IsReturnResults(srbExtension->Flags) &&
        (cdb != NULL) &&
        ((cdb->CDB10.OperationCode == SCSIOP_WRITE) || (cdb->CDB10.OperationCode == SCSIOP_WRITE16)) &&
        (cdb->CDB10.ForceUnitAccess == 1) &&
        IsFuaSupported(ChannelExtension) ) {
        // Keep track of FUA to add it back in when the command is put in the FIS
        slotContent->StateFlags.FUA = TRUE;
    }

    if (LogCommand(ChannelExtension->AdapterExtension->LogFlags)) {
        // 1/2 Log command starting part for our debugging records
        PCOMMAND_HISTORY cmdHistory;

        ChannelExtension->CommandHistoryNextAvailableIndex++;
        ChannelExtension->CommandHistoryNextAvailableIndex %= 64;
        slotContent->CommandHistoryIndex = ChannelExtension->CommandHistoryNextAvailableIndex;

        cmdHistory = &ChannelExtension->CommandHistory[slotContent->CommandHistoryIndex];
        cmdHistory->Tag = srbExtension->QueueTag;
        StorPortCopyMemory(&cmdHistory->InitialTaskFile, &srbExtension->TaskFile, 0x10);
        StorPortCopyMemory(&cmdHistory->InitialPx, ChannelExtension->Px, 0x40);
        cmdHistory->Function = SrbGetSrbFunction(slotContent->Srb);
        cmdHistory->SrbStatus = 0;
    }

    //already get a slot, after this point, any request completion effort needs to release the slot.
    // just like what's done in: ReleaseSlottedCommand()

  //2. Program the CFIS in the CommandTable (allocated in srbExtension)
    cmdTable = (PAHCI_COMMAND_TABLE)srbExtension;
    AhciZeroMemory((PCHAR)cmdTable, sizeof(AHCI_COMMAND_TABLE));

    if ( IsAtapiCommand(srbExtension->AtaFunction) ) {
        SRBtoATAPI_CFIS(ChannelExtension, slotContent);
    } else if ( IsAtaCfisPayload(srbExtension->AtaFunction) ) {
        CfistoATA_CFIS(ChannelExtension, slotContent);
    } else if ( IsAtaCommand(srbExtension->AtaFunction) ) {
        SRBtoATA_CFIS(ChannelExtension, slotContent);
    } else {
        NT_ASSERT(FALSE);
        slotContent->Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
        AhciCompleteJustSlottedRequest(ChannelExtension, Srb, AtDIRQL);
        RecordExecutionHistory(ChannelExtension, 0x10070020);   //Invalid ATA Function
        return TRUE;
    }


  //3. Build the PRD Table in CommandTable.
    if( IsDataTransferNeeded(slotContent->Srb) ) {
        prdtLength = SRBtoPRDT(ChannelExtension, slotContent);
        if (prdtLength == -1) {
            NT_ASSERT(FALSE);
            slotContent->Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
            AhciCompleteJustSlottedRequest(ChannelExtension, Srb, AtDIRQL);
            RecordExecutionHistory(ChannelExtension, 0x10060020);   //Invalid SGL
            return TRUE;
        }
    }

  //4. Program the Command Header (allocated in ChannelExtension for all command slots)
    SRBtoCmdHeader(ChannelExtension, slotContent, prdtLength, FALSE);

  //4.1. Get the Command Table's physical address to verify the alignment and program  cmdHeader->CTBA
    if ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb == Srb) {
        cmdTablePhysicalAddress = ChannelExtension->Local.SrbExtensionPhysicalAddress;
    } else if ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb == Srb) {
        cmdTablePhysicalAddress = ChannelExtension->Sense.SrbExtensionPhysicalAddress;
    } else {
        ULONG length;
        cmdTablePhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension,
                                                             NULL,
                                                             srbExtension,
                                                             &length);
    }
    if ((cmdTablePhysicalAddress.LowPart % 128) == 0 ) {
        cmdHeader->CTBA.AsUlong = cmdTablePhysicalAddress.LowPart;
    } else {
        slotContent->Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
        AhciCompleteJustSlottedRequest(ChannelExtension, Srb, AtDIRQL);
        RecordExecutionHistory(ChannelExtension, 0x10080020);//Invalid PhyscialAddress alignment
        return TRUE;
    }
    //If the controller supports 64 bits, write the high part too
    if (ChannelExtension->AdapterExtension->CAP.S64A)    {
        cmdHeader->CTBAU = cmdTablePhysicalAddress.HighPart;
    }

  //5. Sort it, put request in one of IO Slices.
    //5.1. Keep track of high priority outside of the Slices
    if ( IsHighPriorityCommand(srbExtension->Flags) ){
        ChannelExtension->SlotManager.HighPriorityAttribute |= ( 1 << srbExtension->QueueTag);
    }
    //5.2. Put the slot content in the correct Slice
    if ( IsNCQCommand(srbExtension) ) {
        ChannelExtension->SlotManager.NCQueueSlice |= ( 1 << srbExtension->QueueTag);
    } else if ( IsReturnResults(srbExtension->Flags) ) {
        ChannelExtension->SlotManager.SingleIoSlice |= ( 1 << srbExtension->QueueTag);
    } else if ( IsNormalCommand(slotContent->Srb) ) {
        ChannelExtension->SlotManager.NormalQueueSlice |= ( 1 << srbExtension->QueueTag);
    } else {
        ChannelExtension->SlotManager.SingleIoSlice |= ( 1 << srbExtension->QueueTag);
    }


    if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x10000020);//Exit AhciHwStartIo
    }

    return TRUE;
}

PSCSI_REQUEST_BLOCK
BuildRequestSenseSrb(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  FailingSrb
    )
{
    STOR_PHYSICAL_ADDRESS   dataBufferPhysicalAddress;
    ULONG                   length;
    PCDB                    cdb;
    PSCSI_REQUEST_BLOCK     senseSrb = &ChannelExtension->Sense.Srb;
    PAHCI_SRB_EXTENSION     srbExtension = ChannelExtension->Sense.SrbExtension;
    PVOID                   srbSenseBuffer = NULL;
    UCHAR                   srbSenseBufferLength = 0;

    RequestGetSrbScsiData(FailingSrb, NULL, NULL, &srbSenseBuffer, &srbSenseBufferLength);

    //1. valdiate if senseSrb should be used.
    if ( (srbExtension == NULL) || (srbExtension->AtaFunction != 0) ||
         (srbSenseBuffer == NULL) || (srbSenseBufferLength == 0) ) {
        // Request Sense is for ATAPI commands which are single IOs. At the same time there should be only one command failed and needs this Srb.
        NT_ASSERT(srbExtension && srbExtension->AtaFunction == 0);
        return NULL;
    }

    //2. initialize Srb and SrbExtension structures.
    AhciZeroMemory((PCHAR)senseSrb, sizeof(SCSI_REQUEST_BLOCK));
    AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION));

    //3. setup Srb and CDB. Note that Sense Srb uses SCSI_REQUEST_BLOCK type.
    senseSrb->Length = sizeof(SCSI_REQUEST_BLOCK);
    senseSrb->Function = SRB_FUNCTION_EXECUTE_SCSI;
    senseSrb->PathId = (UCHAR)ChannelExtension->PortNumber;
    senseSrb->SrbFlags = SRB_FLAGS_DATA_IN;
    senseSrb->DataBuffer = srbSenseBuffer;
    senseSrb->DataTransferLength = srbSenseBufferLength;
    senseSrb->SrbExtension = (PVOID)srbExtension;
    senseSrb->OriginalRequest = (PVOID)FailingSrb;
    senseSrb->TimeOutValue = 1;
    senseSrb->CdbLength = 6;

    cdb = (PCDB)&senseSrb->Cdb;

    cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
    cdb->CDB6INQUIRY.AllocationLength = (UCHAR)senseSrb->DataTransferLength;

    //4. fill in the srbExtension fields
    srbExtension->AtaFunction = ATA_FUNCTION_REQUEST_SENSE;
    srbExtension->Flags = ATA_FLAGS_DATA_IN;

    dataBufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, srbSenseBuffer, &length);
    if (dataBufferPhysicalAddress.QuadPart == 0) {
        NT_ASSERT(FALSE);  //Shall Not Pass
        return NULL;
    }

    srbExtension->LocalSgl.NumberOfElements = 1;
    srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = dataBufferPhysicalAddress.LowPart;
    srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = dataBufferPhysicalAddress.HighPart;
    srbExtension->LocalSgl.List[0].Length = senseSrb->DataTransferLength;
    srbExtension->Sgl = &srbExtension->LocalSgl;

    return senseSrb;
}

VOID
AhciPortFailAllIos(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ UCHAR   SrbStatus,
    _In_ BOOLEAN AtDIRQL
    )
{
    PSTORAGE_REQUEST_BLOCK srb;
    UCHAR i;

    // complete all rquests still in queue
    srb = RemoveQueue(ChannelExtension, &ChannelExtension->SrbQueue, 0xDEADC0DE, 0x1F);
    while (srb != NULL) {
        srb->SrbStatus = SrbStatus;
        MarkSrbToBeCompleted(srb);
        AhciCompleteRequest(ChannelExtension, srb, AtDIRQL);
        srb = RemoveQueue(ChannelExtension, &ChannelExtension->SrbQueue, 0xDEADC0DE, 0x1F);
    }

    // complete all requests in slots
    for (i = 0; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) {
        if (ChannelExtension->Slot[i].Srb != NULL) {
            ChannelExtension->Slot[i].Srb->SrbStatus = SrbStatus;
            AhciCompleteJustSlottedRequest(ChannelExtension, ChannelExtension->Slot[i].Srb, AtDIRQL);
        }
    }

    // requests in completion queue will be completed by AhciPortSrbCompletionDpcRoutine.

    return;
}

VOID
AhciPortSrbCompletionDpcRoutine(
    _In_ PSTOR_DPC  Dpc,
    _In_ PVOID      AdapterExtension,
    _In_opt_ PVOID  SystemArgument1,
    _In_opt_ PVOID  SystemArgument2
  )
{
    PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)SystemArgument1;
    STOR_LOCK_HANDLE        lockhandle = {0};
    PSTORAGE_REQUEST_BLOCK  srb = NULL;

    UNREFERENCED_PARAMETER(Dpc);
    UNREFERENCED_PARAMETER(SystemArgument2);

    if (channelExtension == NULL) {
        NT_ASSERT(channelExtension != NULL);
        return;
    }

    do {
        StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
        srb = RemoveQueue(channelExtension, &channelExtension->CompletionQueue, 0xDEADC0DE, 0x9F);
        StorPortReleaseSpinLock(AdapterExtension, &lockhandle);

        if (srb != NULL) {
            PAHCI_SRB_EXTENSION     srbExtension = GetSrbExtension(srb);
            PSRB_COMPLETION_ROUTINE completionRoutine = srbExtension->CompletionRoutine;

            BOOLEAN completeSrb = TRUE;

            srbExtension->AtaFunction = 0; // clear this field.
            srbExtension->CompletionRoutine = NULL;

            if (completionRoutine != NULL) {

                completionRoutine(channelExtension, srb);

                // a request with STATUS_BUS_RESET should be completed after running completion routine.
                if ( (srbExtension->AtaFunction != 0) &&
                     (!SrbShouldBeCompleted(srbExtension->Flags)) &&
                     (srb->SrbStatus != SRB_STATUS_BUS_RESET) ) {
                    // new command associated needs to be processed, do not complete the request.
                    StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
                    AhciProcessIo(channelExtension, srb, TRUE);
                    ActivateQueue(channelExtension, TRUE);
                    StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
                    // this Srb should not be completed yet
                    completeSrb = FALSE;
                } else if (SrbShouldBeCompleted(srbExtension->Flags)) {
                    // clear the flag
                    CLRMASK(srbExtension->Flags, ATA_FLAGS_COMPLETE_SRB);
                }
            } else {
                // a Srb without completion routine should be completed.
            }

            if (completeSrb) {
                // release active reference for port/device and adapter.
                if ((srbExtension->Flags & ATA_FLAGS_ACTIVE_REFERENCE) != 0) {
                    PortReleaseActiveReference(channelExtension, srb);
                }

                if (!IsMiniportInternalSrb(channelExtension, srb)) {
                    NT_ASSERT(srb->SrbStatus != SRB_STATUS_PENDING);
                    StorPortNotification(RequestComplete, AdapterExtension, srb);
                }
            }
        }

    } while (srb != NULL);

    return;
}

#if _MSC_VER >= 1200
#pragma warning(pop)
#else
#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