reactos/drivers/storage/storahci/storahci.c

2608 lines
81 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* PROJECT: ReactOS Kernel
* LICENSE: GNU GPLv2 only as published by the Free Software Foundation
* PURPOSE: To Implement AHCI Miniport driver targeting storport NT 5.2
* PROGRAMMERS: Aman Priyadarshi (aman.eureka@gmail.com)
*/
#include "storahci.h"
/**
* @name AhciPortInitialize
* @implemented
*
* Initialize port by setting up PxCLB & PxFB Registers
*
* @param PortExtension
*
* @return
* Return true if intialization was successful
*/
BOOLEAN
NTAPI
AhciPortInitialize (
__in PVOID DeviceExtension
)
{
PAHCI_PORT_EXTENSION PortExtension;
AHCI_PORT_CMD cmd;
PAHCI_MEMORY_REGISTERS abar;
ULONG mappedLength, portNumber, ticks;
PAHCI_ADAPTER_EXTENSION adapterExtension;
STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical;
AhciDebugPrint("AhciPortInitialize()\n");
PortExtension = (PAHCI_PORT_EXTENSION)DeviceExtension;
adapterExtension = PortExtension->AdapterExtension;
abar = adapterExtension->ABAR_Address;
portNumber = PortExtension->PortNumber;
NT_ASSERT(abar != NULL);
NT_ASSERT(portNumber < adapterExtension->PortCount);
PortExtension->Port = &abar->PortList[portNumber];
commandListPhysical = StorPortGetPhysicalAddress(adapterExtension,
NULL,
PortExtension->CommandList,
&mappedLength);
if ((mappedLength == 0) || ((commandListPhysical.LowPart % 1024) != 0))
{
AhciDebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength);
return FALSE;
}
receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension,
NULL,
PortExtension->ReceivedFIS,
&mappedLength);
if ((mappedLength == 0) || ((receivedFISPhysical.LowPart % 256) != 0))
{
AhciDebugPrint("\treceivedFISPhysical mappedLength:%d\n", mappedLength);
return FALSE;
}
// Ensure that the controller is not in the running state by reading and examining each
// implemented ports PxCMD register. If PxCMD.ST, PxCMD.CR, PxCMD.FRE and
// PxCMD.FR are all cleared, the port is in an idle state. Otherwise, the port is not idle and
// should be placed in the idle state prior to manipulating HBA and port specific registers.
// System software places a port into the idle state by clearing PxCMD.ST and waiting for
// PxCMD.CR to return 0 when read. Software should wait at least 500 milliseconds for
// this to occur. If PxCMD.FRE is set to 1, software should clear it to 0 and wait at least
// 500 milliseconds for PxCMD.FR to return 0 when read. If PxCMD.CR or PxCMD.FR do
// not clear to 0 correctly, then software may attempt a port reset or a full HBA reset to recove
// TODO: Check if port is in idle state or not, if not then restart port
cmd.Status = StorPortReadRegisterUlong(adapterExtension, &PortExtension->Port->CMD);
if ((cmd.FR != 0) || (cmd.CR != 0) || (cmd.FRE != 0) || (cmd.ST != 0))
{
cmd.ST = 0;
cmd.FRE = 0;
ticks = 3;
do
{
StorPortStallExecution(50000);
cmd.Status = StorPortReadRegisterUlong(adapterExtension, &PortExtension->Port->CMD);
if (ticks == 0)
{
AhciDebugPrint("\tAttempt to reset port failed: %x\n", cmd);
return FALSE;
}
ticks--;
}
while(cmd.CR != 0 || cmd.FR != 0);
}
// 10.1.2 For each implemented port, system software shall allocate memory for and program:
// ? PxCLB and PxCLBU (if CAP.S64A is set to 1)
// ? PxFB and PxFBU (if CAP.S64A is set to 1)
// Note: Assuming 32bit support only
StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLB, commandListPhysical.LowPart);
if (IsAdapterCAPS64(adapterExtension->CAP))
{
StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLBU, commandListPhysical.HighPart);
}
StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FB, receivedFISPhysical.LowPart);
if (IsAdapterCAPS64(adapterExtension->CAP))
{
StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FBU, receivedFISPhysical.HighPart);
}
PortExtension->IdentifyDeviceDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension,
NULL,
PortExtension->IdentifyDeviceData,
&mappedLength);
// set device power state flag to D0
PortExtension->DevicePowerState = StorPowerDeviceD0;
// clear pending interrupts
StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->SERR, (ULONG)~0);
StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->IS, (ULONG)~0);
StorPortWriteRegisterUlong(adapterExtension, adapterExtension->IS, (1 << PortExtension->PortNumber));
return TRUE;
}// -- AhciPortInitialize();
/**
* @name AhciAllocateResourceForAdapter
* @implemented
*
* Allocate memory from poll for required pointers
*
* @param AdapterExtension
* @param ConfigInfo
*
* @return
* return TRUE if allocation was successful
*/
BOOLEAN
AhciAllocateResourceForAdapter (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PPORT_CONFIGURATION_INFORMATION ConfigInfo
)
{
PCHAR nonCachedExtension, tmp;
ULONG index, NCS, AlignedNCS;
ULONG portCount, portImplemented, nonCachedExtensionSize;
PAHCI_PORT_EXTENSION PortExtension;
AhciDebugPrint("AhciAllocateResourceForAdapter()\n");
NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
AlignedNCS = ROUND_UP(NCS, 8);
// get port count -- Number of set bits in `AdapterExtension->PortImplemented`
portCount = 0;
portImplemented = AdapterExtension->PortImplemented;
NT_ASSERT(portImplemented != 0);
for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--)
if ((portImplemented & (1 << index)) != 0)
break;
portCount = index + 1;
AhciDebugPrint("\tPort Count: %d\n", portCount);
AdapterExtension->PortCount = portCount;
nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
sizeof(AHCI_RECEIVED_FIS) +
sizeof(IDENTIFY_DEVICE_DATA);
// align nonCachedExtensionSize to 1024
nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024);
AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension,
ConfigInfo,
nonCachedExtensionSize * portCount);
if (AdapterExtension->NonCachedExtension == NULL)
{
AhciDebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
return FALSE;
}
nonCachedExtension = AdapterExtension->NonCachedExtension;
AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount);
for (index = 0; index < portCount; index++)
{
PortExtension = &AdapterExtension->PortExtension[index];
PortExtension->DeviceParams.IsActive = FALSE;
if ((AdapterExtension->PortImplemented & (1 << index)) != 0)
{
PortExtension->PortNumber = index;
PortExtension->DeviceParams.IsActive = TRUE;
PortExtension->AdapterExtension = AdapterExtension;
PortExtension->CommandList = (PAHCI_COMMAND_HEADER)nonCachedExtension;
tmp = (PCHAR)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
PortExtension->ReceivedFIS = (PAHCI_RECEIVED_FIS)tmp;
PortExtension->IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)(tmp + sizeof(AHCI_RECEIVED_FIS));
PortExtension->MaxPortQueueDepth = NCS;
nonCachedExtension += nonCachedExtensionSize;
}
}
return TRUE;
}// -- AhciAllocateResourceForAdapter();
/**
* @name AhciStartPort
* @implemented
*
* Try to start the port device
*
* @param AdapterExtension
* @param PortExtension
*
*/
BOOLEAN
AhciStartPort (
__in PAHCI_PORT_EXTENSION PortExtension
)
{
ULONG index;
AHCI_PORT_CMD cmd;
AHCI_TASK_FILE_DATA tfd;
AHCI_INTERRUPT_ENABLE ie;
AHCI_SERIAL_ATA_STATUS ssts;
AHCI_SERIAL_ATA_CONTROL sctl;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
AhciDebugPrint("AhciStartPort()\n");
AdapterExtension = PortExtension->AdapterExtension;
cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
if ((cmd.FR == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.ST == 1))
{
// Already Running
return TRUE;
}
cmd.SUD = 1;
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
if (((cmd.FR == 1) && (cmd.FRE == 0)) ||
((cmd.CR == 1) && (cmd.ST == 0)))
{
AhciDebugPrint("\tCOMRESET\n");
// perform COMRESET
// section 10.4.2
// Software causes a port reset (COMRESET) by writing 1h to the PxSCTL.DET field to invoke a
// COMRESET on the interface and start a re-establishment of Phy layer communications. Software shall
// wait at least 1 millisecond before clearing PxSCTL.DET to 0h; this ensures that at least one COMRESET
// signal is sent over the interface. After clearing PxSCTL.DET to 0h, software should wait for
// communication to be re-established as indicated by PxSSTS.DET being set to 3h. Then software should
// write all 1s to the PxSERR register to clear any bits that were set as part of the port reset.
sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
sctl.DET = 1;
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
StorPortStallExecution(1000);
sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
sctl.DET = 0;
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
// Poll DET to verify if a device is attached to the port
index = 0;
do
{
StorPortStallExecution(1000);
ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
index++;
if (ssts.DET != 0)
{
break;
}
}
while(index < 30);
}
ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
switch (ssts.DET)
{
case 0x3:
{
NT_ASSERT(cmd.ST == 0);
// make sure FIS Recieve is enabled (cmd.FRE)
index = 0;
do
{
StorPortStallExecution(10000);
cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
cmd.FRE = 1;
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
index++;
}
while((cmd.FR != 1) && (index < 3));
if (cmd.FR != 1)
{
// failed to start FIS DMA engine
// it can crash the driver later
// so better to turn this port off
return FALSE;
}
// start port channel
// set cmd.ST
NT_ASSERT(cmd.FRE == 1);
NT_ASSERT(cmd.CR == 0);
// why assert? well If we face such condition on DET = 0x3
// then we don't have port in idle state and hence before executing this part of code
// we must have restarted it.
tfd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->TFD);
if ((tfd.STS.BSY) || (tfd.STS.DRQ))
{
AhciDebugPrint("\tUnhandled Case BSY-DRQ\n");
}
// clear pending interrupts
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SERR, (ULONG)~0);
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, (ULONG)~0);
StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, (1 << PortExtension->PortNumber));
// set IE
ie.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IE);
/* Device to Host Register FIS Interrupt Enable */
ie.DHRE = 1;
/* PIO Setup FIS Interrupt Enable */
ie.PSE = 1;
/* DMA Setup FIS Interrupt Enable */
ie.DSE = 1;
/* Set Device Bits FIS Interrupt Enable */
ie.SDBE = 1;
/* Unknown FIS Interrupt Enable */
ie.UFE = 0;
/* Descriptor Processed Interrupt Enable */
ie.DPE = 0;
/* Port Change Interrupt Enable */
ie.PCE = 1;
/* Device Mechanical Presence Enable */
ie.DMPE = 0;
/* PhyRdy Change Interrupt Enable */
ie.PRCE = 1;
/* Incorrect Port Multiplier Enable */
ie.IPME = 0;
/* Overflow Enable */
ie.OFE = 1;
/* Interface Non-fatal Error Enable */
ie.INFE = 1;
/* Interface Fatal Error Enable */
ie.IFE = 1;
/* Host Bus Data Error Enable */
ie.HBDE = 1;
/* Host Bus Fatal Error Enable */
ie.HBFE = 1;
/* Task File Error Enable */
ie.TFEE = 1;
cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
/* Cold Presence Detect Enable */
if (cmd.CPD) // does it support CPD?
{
// disable it for now
ie.CPDE = 0;
}
// should I replace this to single line?
// by directly setting ie.Status?
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IE, ie.Status);
cmd.ST = 1;
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
if (cmd.ST != 1)
{
AhciDebugPrint("\tFailed to start Port\n");
return FALSE;
}
return TRUE;
}
default:
// unhandled case
AhciDebugPrint("\tDET == %x Unsupported\n", ssts.DET);
return FALSE;
}
}// -- AhciStartPort();
/**
* @name AhciCommandCompletionDpcRoutine
* @implemented
*
* Handles Completed Commands
*
* @param Dpc
* @param AdapterExtension
* @param SystemArgument1
* @param SystemArgument2
*/
VOID
AhciCommandCompletionDpcRoutine (
__in PSTOR_DPC Dpc,
__in PVOID HwDeviceExtension,
__in PVOID SystemArgument1,
__in PVOID SystemArgument2
)
{
PSCSI_REQUEST_BLOCK Srb;
PAHCI_SRB_EXTENSION SrbExtension;
STOR_LOCK_HANDLE lockhandle = {0};
PAHCI_COMPLETION_ROUTINE CompletionRoutine;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
PAHCI_PORT_EXTENSION PortExtension;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument2);
AhciDebugPrint("AhciCommandCompletionDpcRoutine()\n");
AdapterExtension = (PAHCI_ADAPTER_EXTENSION)HwDeviceExtension;
PortExtension = (PAHCI_PORT_EXTENSION)SystemArgument1;
StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
Srb = RemoveQueue(&PortExtension->CompletionQueue);
StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
NT_ASSERT(Srb != NULL);
if (Srb->SrbStatus == SRB_STATUS_PENDING)
{
Srb->SrbStatus = SRB_STATUS_SUCCESS;
}
else
{
return;
}
SrbExtension = GetSrbExtension(Srb);
CompletionRoutine = SrbExtension->CompletionRoutine;
NT_ASSERT(CompletionRoutine != NULL);
// now it's completion routine responsibility to set SrbStatus
CompletionRoutine(PortExtension, Srb);
StorPortNotification(RequestComplete, AdapterExtension, Srb);
return;
}// -- AhciCommandCompletionDpcRoutine();
/**
* @name AhciHwPassiveInitialize
* @implemented
*
* initializes the HBA and finds all devices that are of interest to the miniport driver. (at PASSIVE LEVEL)
*
* @param adapterExtension
*
* @return
* return TRUE if intialization was successful
*/
BOOLEAN
AhciHwPassiveInitialize (
__in PVOID DeviceExtension
)
{
ULONG index;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
PAHCI_PORT_EXTENSION PortExtension;
AhciDebugPrint("AhciHwPassiveInitialize()\n");
AdapterExtension = (PAHCI_ADAPTER_EXTENSION)DeviceExtension;
for (index = 0; index < AdapterExtension->PortCount; index++)
{
if ((AdapterExtension->PortImplemented & (0x1 << index)) != 0)
{
PortExtension = &AdapterExtension->PortExtension[index];
PortExtension->DeviceParams.IsActive = AhciStartPort(PortExtension);
StorPortInitializeDpc(AdapterExtension, &PortExtension->CommandCompletion, AhciCommandCompletionDpcRoutine);
}
}
return TRUE;
}// -- AhciHwPassiveInitialize();
/**
* @name AhciHwInitialize
* @implemented
*
* initializes the HBA and finds all devices that are of interest to the miniport driver.
*
* @param adapterExtension
*
* @return
* return TRUE if intialization was successful
*/
BOOLEAN
NTAPI
AhciHwInitialize (
__in PVOID DeviceExtension
)
{
PAHCI_ADAPTER_EXTENSION AdapterExtension;
AHCI_GHC ghc;
AhciDebugPrint("AhciHwInitialize()\n");
AdapterExtension = (PAHCI_ADAPTER_EXTENSION)DeviceExtension;
AdapterExtension->StateFlags.MessagePerPort = FALSE;
// First check what type of interrupt/synchronization device is using
ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &AdapterExtension->ABAR_Address->GHC);
// When set to 1 by hardware, indicates that the HBA requested more than one MSI vector
// but has reverted to using the first vector only. When this bit is cleared to 0,
// the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
// software has allocated the number of messages requested
if (ghc.MRSM == 0)
{
AdapterExtension->StateFlags.MessagePerPort = TRUE;
AhciDebugPrint("\tMultiple MSI based message not supported\n");
}
StorPortEnablePassiveInitialization(AdapterExtension, AhciHwPassiveInitialize);
return TRUE;
}// -- AhciHwInitialize();
/**
* @name AhciCompleteIssuedSrb
* @implemented
*
* Complete issued Srbs
*
* @param PortExtension
*
*/
VOID
AhciCompleteIssuedSrb (
__in PAHCI_PORT_EXTENSION PortExtension,
__in ULONG CommandsToComplete
)
{
ULONG NCS, i;
PSCSI_REQUEST_BLOCK Srb;
PAHCI_SRB_EXTENSION SrbExtension;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
AhciDebugPrint("AhciCompleteIssuedSrb()\n");
NT_ASSERT(CommandsToComplete != 0);
AhciDebugPrint("\tCompleted Commands: %d\n", CommandsToComplete);
AdapterExtension = PortExtension->AdapterExtension;
NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
for (i = 0; i < NCS; i++)
{
if (((1 << i) & CommandsToComplete) != 0)
{
Srb = PortExtension->Slot[i];
if (Srb == NULL)
{
continue;
}
SrbExtension = GetSrbExtension(Srb);
NT_ASSERT(SrbExtension != NULL);
if (SrbExtension->CompletionRoutine != NULL)
{
AddQueue(&PortExtension->CompletionQueue, Srb);
StorPortIssueDpc(AdapterExtension, &PortExtension->CommandCompletion, PortExtension, Srb);
}
else
{
NT_ASSERT(Srb->SrbStatus == SRB_STATUS_PENDING);
Srb->SrbStatus = SRB_STATUS_SUCCESS;
StorPortNotification(RequestComplete, AdapterExtension, Srb);
}
}
}
return;
}// -- AhciCompleteIssuedSrb();
/**
* @name AhciInterruptHandler
* @not_implemented
*
* Interrupt Handler for PortExtension
*
* @param PortExtension
*
*/
VOID
AhciInterruptHandler (
__in PAHCI_PORT_EXTENSION PortExtension
)
{
ULONG is, ci, sact, outstanding;
AHCI_INTERRUPT_STATUS PxIS;
AHCI_INTERRUPT_STATUS PxISMasked;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
AhciDebugPrint("AhciInterruptHandler()\n");
AhciDebugPrint("\tPort Number: %d\n", PortExtension->PortNumber);
AdapterExtension = PortExtension->AdapterExtension;
NT_ASSERT(IsPortValid(AdapterExtension, PortExtension->PortNumber));
// 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. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
// 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
// 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.
// 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
PxISMasked.Status = 0;
PxIS.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IS);
// 6.2.2
// Fatal Error
// signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
if (PxIS.HBFS || PxIS.HBDS || PxIS.IFS || PxIS.TFES)
{
// In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
// any native command queuing commands. To recover, the port must be restarted
// To detect an error that requires software recovery actions to be performed,
// software should check whether any of the following status bits are set on an interrupt:
// PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
// software should perform the appropriate error recovery actions based on whether
// non-queued commands were being issued or native command queuing commands were being issued.
AhciDebugPrint("\tFatal Error: %x\n", PxIS.Status);
}
// Normal Command Completion
// 3.3.5
// A D2H Register FIS has been received with the I bit set, and has been copied into system memory.
PxISMasked.DHRS = PxIS.DHRS;
// A PIO Setup FIS has been received with the I bit set, it has been copied into system memory.
PxISMasked.PSS = PxIS.PSS;
// A DMA Setup FIS has been received with the I bit set and has been copied into system memory.
PxISMasked.DSS = PxIS.DSS;
// A Set Device Bits FIS has been received with the I bit set and has been copied into system memory/
PxISMasked.SDBS = PxIS.SDBS;
// A PRD with the I bit set has transferred all of its data.
PxISMasked.DPS = PxIS.DPS;
if (PxISMasked.Status != 0)
{
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, PxISMasked.Status);
}
// 10.7.1.1
// Clear port interrupt
// It is set by the level of the virtual interrupt line being a set, and cleared by a write of 1 from the software.
is = (1 << PortExtension->PortNumber);
StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, is);
ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI);
sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT);
outstanding = ci | sact; // NOTE: Including both non-NCQ and NCQ based commands
if ((PortExtension->CommandIssuedSlots & (~outstanding)) != 0)
{
AhciCompleteIssuedSrb(PortExtension, (PortExtension->CommandIssuedSlots & (~outstanding)));
PortExtension->CommandIssuedSlots &= outstanding;
}
return;
}// -- AhciInterruptHandler();
/**
* @name AhciHwInterrupt
* @implemented
*
* The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
*
* @param AdapterExtension
*
* @return
* return TRUE Indicates that an interrupt was pending on adapter.
* return FALSE Indicates the interrupt was not ours.
*/
BOOLEAN
NTAPI
AhciHwInterrupt (
__in PVOID DeviceExtension
)
{
PAHCI_ADAPTER_EXTENSION AdapterExtension;
ULONG portPending, nextPort, i, portCount;
AdapterExtension = (PAHCI_ADAPTER_EXTENSION)DeviceExtension;
if (AdapterExtension->StateFlags.Removed)
{
return FALSE;
}
portPending = StorPortReadRegisterUlong(AdapterExtension, AdapterExtension->IS);
// we process interrupt for implemented ports only
portCount = AdapterExtension->PortCount;
portPending = portPending & AdapterExtension->PortImplemented;
if (portPending == 0)
{
return FALSE;
}
for (i = 1; i <= portCount; i++)
{
nextPort = (AdapterExtension->LastInterruptPort + i) % portCount;
if ((portPending & (0x1 << nextPort)) == 0)
continue;
NT_ASSERT(IsPortValid(AdapterExtension, nextPort));
if (AdapterExtension->PortExtension[nextPort].DeviceParams.IsActive == FALSE)
{
continue;
}
// we can assign this interrupt to this port
AdapterExtension->LastInterruptPort = nextPort;
AhciInterruptHandler(&AdapterExtension->PortExtension[nextPort]);
portPending &= ~(1 << nextPort);
// interrupt belongs to this device
// should always return TRUE
return TRUE;
}
AhciDebugPrint("\tSomething went wrong");
return FALSE;
}// -- AhciHwInterrupt();
/**
* @name AhciHwStartIo
* @not_implemented
*
* The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
*
* @param adapterExtension
* @param Srb
*
* @return
* return TRUE if the request was accepted
* return FALSE if the request must be submitted later
*/
BOOLEAN
NTAPI
AhciHwStartIo (
__in PVOID DeviceExtension,
__in PSCSI_REQUEST_BLOCK Srb
)
{
PAHCI_ADAPTER_EXTENSION AdapterExtension;
AhciDebugPrint("AhciHwStartIo()\n");
AdapterExtension = (PAHCI_ADAPTER_EXTENSION)DeviceExtension;
if (!IsPortValid(AdapterExtension, Srb->PathId))
{
Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
StorPortNotification(RequestComplete, AdapterExtension, Srb);
return TRUE;
}
switch(Srb->Function)
{
case SRB_FUNCTION_PNP:
{
// https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
// If the function member of an SRB is set to SRB_FUNCTION_PNP,
// the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
PSCSI_PNP_REQUEST_BLOCK pnpRequest;
pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb;
if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0)
{
switch(pnpRequest->PnPAction)
{
case StorRemoveDevice:
case StorSurpriseRemoval:
{
Srb->SrbStatus = SRB_STATUS_SUCCESS;
AdapterExtension->StateFlags.Removed = 1;
AhciDebugPrint("\tAdapter removed\n");
}
break;
case StorStopDevice:
{
Srb->SrbStatus = SRB_STATUS_SUCCESS;
AhciDebugPrint("\tRequested to Stop the adapter\n");
}
break;
default:
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
break;
}
}
else
{
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
}
}
break;
case SRB_FUNCTION_EXECUTE_SCSI:
{
// https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
// On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
// routine does the following:
//
// - Gets and/or sets up whatever context the miniport driver maintains in its device,
// logical unit, and/or SRB extensions
// For example, a miniport driver might set up a logical unit extension with pointers
// to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
// and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
// carried out on the HBA.
//
// - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
// for the requested operation
// For a device I/O operation, such an internal routine generally selects the target device
// and sends the CDB over the bus to the target logical unit.
PCDB cdb = (PCDB)&Srb->Cdb;
if (Srb->CdbLength == 0)
{
AhciDebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode);
Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
break;
}
NT_ASSERT(cdb != NULL);
switch(cdb->CDB10.OperationCode)
{
case SCSIOP_INQUIRY:
Srb->SrbStatus = DeviceInquiryRequest(AdapterExtension, Srb, cdb);
break;
case SCSIOP_REPORT_LUNS:
Srb->SrbStatus = DeviceReportLuns(AdapterExtension, Srb, cdb);
break;
case SCSIOP_READ_CAPACITY:
Srb->SrbStatus = DeviceRequestCapacity(AdapterExtension, Srb, cdb);
break;
case SCSIOP_TEST_UNIT_READY:
Srb->SrbStatus = DeviceRequestComplete(AdapterExtension, Srb, cdb);
break;
case SCSIOP_MODE_SENSE:
Srb->SrbStatus = DeviceRequestSense(AdapterExtension, Srb, cdb);
break;
case SCSIOP_READ:
case SCSIOP_WRITE:
Srb->SrbStatus = DeviceRequestReadWrite(AdapterExtension, Srb, cdb);
break;
default:
AhciDebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode);
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
break;
}
}
break;
default:
AhciDebugPrint("\tUnknown function code recieved: %x\n", Srb->Function);
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
break;
}
if (Srb->SrbStatus != SRB_STATUS_PENDING)
{
StorPortNotification(RequestComplete, AdapterExtension, Srb);
}
else
{
AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
}
return TRUE;
}// -- AhciHwStartIo();
/**
* @name AhciHwResetBus
* @not_implemented
*
* The HwStorResetBus routine is called by the port driver to clear error conditions.
*
* @param adapterExtension
* @param PathId
*
* @return
* return TRUE if bus was successfully reset
*/
BOOLEAN
NTAPI
AhciHwResetBus (
__in PVOID AdapterExtension,
__in ULONG PathId
)
{
STOR_LOCK_HANDLE lockhandle = {0};
// PAHCI_ADAPTER_EXTENSION adapterExtension;
AhciDebugPrint("AhciHwResetBus()\n");
// adapterExtension = AdapterExtension;
if (IsPortValid(AdapterExtension, PathId))
{
// Acquire Lock
StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
// TODO: Perform port reset
// Release lock
StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
}
return FALSE;
}// -- AhciHwResetBus();
/**
* @name AhciHwFindAdapter
* @implemented
*
* The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
* HBA is supported and, if it is, to return configuration information about that adapter.
*
* 10.1 Platform Communication
* http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
* @param DeviceExtension
* @param HwContext
* @param BusInformation
* @param ArgumentString
* @param ConfigInfo
* @param Reserved3
*
* @return
* SP_RETURN_FOUND
* Indicates that a supported HBA was found and that the HBA-relevant configuration information was successfully determined and set in the PORT_CONFIGURATION_INFORMATION structure.
*
* SP_RETURN_ERROR
* Indicates that an HBA was found but there was an error obtaining the configuration information. If possible, such an error should be logged with StorPortLogError.
*
* SP_RETURN_BAD_CONFIG
* Indicates that the supplied configuration information was invalid for the adapter.
*
* SP_RETURN_NOT_FOUND
* Indicates that no supported HBA was found for the supplied configuration information.
*
* @remarks Called by Storport.
*/
ULONG
NTAPI
AhciHwFindAdapter (
__in PVOID DeviceExtension,
__in PVOID HwContext,
__in PVOID BusInformation,
__in PCHAR ArgumentString,
__inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
__in PBOOLEAN Reserved3
)
{
AHCI_GHC ghc;
ULONG index, pci_cfg_len;
PACCESS_RANGE accessRange;
UCHAR pci_cfg_buf[sizeof(PCI_COMMON_CONFIG)];
PAHCI_MEMORY_REGISTERS abar;
PPCI_COMMON_CONFIG pciConfigData;
PAHCI_ADAPTER_EXTENSION adapterExtension;
AhciDebugPrint("AhciHwFindAdapter()\n");
UNREFERENCED_PARAMETER(HwContext);
UNREFERENCED_PARAMETER(BusInformation);
UNREFERENCED_PARAMETER(ArgumentString);
UNREFERENCED_PARAMETER(Reserved3);
adapterExtension = DeviceExtension;
adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
// get PCI configuration header
pci_cfg_len = StorPortGetBusData(
adapterExtension,
PCIConfiguration,
adapterExtension->SystemIoBusNumber,
adapterExtension->SlotNumber,
pci_cfg_buf,
sizeof(PCI_COMMON_CONFIG));
if (pci_cfg_len != sizeof(PCI_COMMON_CONFIG))
{
AhciDebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG), pci_cfg_len);
return SP_RETURN_ERROR;//Not a valid device at the given bus number
}
pciConfigData = (PPCI_COMMON_CONFIG)pci_cfg_buf;
adapterExtension->VendorID = pciConfigData->VendorID;
adapterExtension->DeviceID = pciConfigData->DeviceID;
adapterExtension->RevisionID = pciConfigData->RevisionID;
// The last PCI base address register (BAR[5], header offset 0x24) points to the AHCI base memory, its called ABAR (AHCI Base Memory Register).
adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
AhciDebugPrint("\tVendorID: %04x DeviceID: %04x RevisionID: %02x\n",
adapterExtension->VendorID,
adapterExtension->DeviceID,
adapterExtension->RevisionID);
// 2.1.11
abar = NULL;
if (ConfigInfo->NumberOfAccessRanges > 0)
{
accessRange = *(ConfigInfo->AccessRanges);
for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
{
if (accessRange[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
{
abar = StorPortGetDeviceBase(adapterExtension,
ConfigInfo->AdapterInterfaceType,
ConfigInfo->SystemIoBusNumber,
accessRange[index].RangeStart,
accessRange[index].RangeLength,
!accessRange[index].RangeInMemory);
break;
}
}
}
if (abar == NULL)
{
AhciDebugPrint("\tabar == NULL\n");
return SP_RETURN_ERROR; // corrupted information supplied
}
adapterExtension->ABAR_Address = abar;
adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
adapterExtension->LastInterruptPort = (ULONG)-1;
// 10.1.2
// 1. Indicate that system software is AHCI aware by setting GHC.AE to 1.
// 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
// AE := Highest Significant bit of GHC
if (ghc.AE != 0)// Hmm, controller was already in power state
{
// reset controller to have it in known state
AhciDebugPrint("\tAE Already set, Reset()\n");
if (!AhciAdapterReset(adapterExtension))
{
AhciDebugPrint("\tReset Failed!\n");
return SP_RETURN_ERROR;// reset failed
}
}
ghc.Status = 0;
ghc.AE = 1;// only AE=1
// tell the controller that we know about AHCI
StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status);
adapterExtension->IS = &abar->IS;
adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
if (adapterExtension->PortImplemented == 0)
{
AhciDebugPrint("\tadapterExtension->PortImplemented == 0\n");
return SP_RETURN_ERROR;
}
ConfigInfo->Master = TRUE;
ConfigInfo->AlignmentMask = 0x3;
ConfigInfo->ScatterGather = TRUE;
ConfigInfo->DmaWidth = Width32Bits;
ConfigInfo->WmiDataProvider = FALSE;
ConfigInfo->Dma32BitAddresses = TRUE;
if (IsAdapterCAPS64(adapterExtension->CAP))
{
ConfigInfo->Dma64BitAddresses = TRUE;
}
ConfigInfo->MaximumNumberOfTargets = 1;
ConfigInfo->ResetTargetSupported = TRUE;
ConfigInfo->NumberOfPhysicalBreaks = 0x21;
ConfigInfo->MaximumNumberOfLogicalUnits = 1;
ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT;
ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;
ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
// Turn IE -- Interrupt Enabled
ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
ghc.IE = 1;
StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status);
// allocate necessary resource for each port
if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
{
NT_ASSERT(FALSE);
return SP_RETURN_ERROR;
}
for (index = 0; index < adapterExtension->PortCount; index++)
{
if ((adapterExtension->PortImplemented & (0x1 << index)) != 0)
AhciPortInitialize(&adapterExtension->PortExtension[index]);
}
return SP_RETURN_FOUND;
}// -- AhciHwFindAdapter();
/**
* @name DriverEntry
* @implemented
*
* Initial Entrypoint for storahci miniport driver
*
* @param DriverObject
* @param RegistryPath
*
* @return
* NT_STATUS in case of driver loaded successfully.
*/
ULONG
NTAPI
DriverEntry (
__in PVOID DriverObject,
__in PVOID RegistryPath
)
{
ULONG status;
// initialize the hardware data structure
HW_INITIALIZATION_DATA hwInitializationData = {0};
// set size of hardware initialization structure
hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
// identity required miniport entry point routines
hwInitializationData.HwStartIo = AhciHwStartIo;
hwInitializationData.HwResetBus = AhciHwResetBus;
hwInitializationData.HwInterrupt = AhciHwInterrupt;
hwInitializationData.HwInitialize = AhciHwInitialize;
hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
// adapter specific information
hwInitializationData.TaggedQueuing = TRUE;
hwInitializationData.AutoRequestSense = TRUE;
hwInitializationData.MultipleRequestPerLu = TRUE;
hwInitializationData.NeedPhysicalAddresses = TRUE;
hwInitializationData.NumberOfAccessRanges = 6;
hwInitializationData.AdapterInterfaceType = PCIBus;
hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
// set required extension sizes
hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
// register our hw init data
status = StorPortInitialize(DriverObject,
RegistryPath,
&hwInitializationData,
NULL);
NT_ASSERT(status == STATUS_SUCCESS);
return status;
}// -- DriverEntry();
/**
* @name AhciATA_CFIS
* @implemented
*
* create ATA CFIS from Srb
*
* @param PortExtension
* @param Srb
*
* @return
* Number of CFIS fields used in DWORD
*/
ULONG
AhciATA_CFIS (
__in PAHCI_PORT_EXTENSION PortExtension,
__in PAHCI_SRB_EXTENSION SrbExtension
)
{
PAHCI_COMMAND_TABLE cmdTable;
UNREFERENCED_PARAMETER(PortExtension);
AhciDebugPrint("AhciATA_CFIS()\n");
cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
AhciZeroMemory((PCHAR)cmdTable->CFIS, sizeof(cmdTable->CFIS));
cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = FIS_TYPE_REG_H2D; // FIS Type
cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C
cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg;
cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2;
cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5;
cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh;
cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow;
cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh;
return 5;
}// -- AhciATA_CFIS();
/**
* @name AhciATAPI_CFIS
* @not_implemented
*
* create ATAPI CFIS from Srb
*
* @param PortExtension
* @param Srb
*
* @return
* Number of CFIS fields used in DWORD
*/
ULONG
AhciATAPI_CFIS (
__in PAHCI_PORT_EXTENSION PortExtension,
__in PAHCI_SRB_EXTENSION SrbExtension
)
{
PAHCI_COMMAND_TABLE cmdTable;
UNREFERENCED_PARAMETER(PortExtension);
AhciDebugPrint("AhciATAPI_CFIS()\n");
cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
NT_ASSERT(SrbExtension->CommandReg == IDE_COMMAND_ATAPI_PACKET);
AhciZeroMemory((PCHAR)cmdTable->CFIS, sizeof(cmdTable->CFIS));
cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = FIS_TYPE_REG_H2D; // FIS Type
cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C
cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg;
cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2;
cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4;
cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5;
cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh;
cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow;
cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh;
return 5;
}// -- AhciATAPI_CFIS();
/**
* @name AhciBuild_PRDT
* @implemented
*
* Build PRDT for data transfer
*
* @param PortExtension
* @param Srb
*
* @return
* Return number of entries in PRDT.
*/
ULONG
AhciBuild_PRDT (
__in PAHCI_PORT_EXTENSION PortExtension,
__in PAHCI_SRB_EXTENSION SrbExtension
)
{
ULONG index;
PAHCI_COMMAND_TABLE cmdTable;
PLOCAL_SCATTER_GATHER_LIST sgl;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
AhciDebugPrint("AhciBuild_PRDT()\n");
sgl = SrbExtension->pSgl;
cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
AdapterExtension = PortExtension->AdapterExtension;
NT_ASSERT(sgl != NULL);
NT_ASSERT(sgl->NumberOfElements < MAXIMUM_AHCI_PRDT_ENTRIES);
for (index = 0; index < sgl->NumberOfElements; index++)
{
NT_ASSERT(sgl->List[index].Length <= MAXIMUM_TRANSFER_LENGTH);
cmdTable->PRDT[index].DBA = sgl->List[index].PhysicalAddress.LowPart;
if (IsAdapterCAPS64(AdapterExtension->CAP))
{
cmdTable->PRDT[index].DBAU = sgl->List[index].PhysicalAddress.HighPart;
}
// Data Byte Count (DBC): A 0 based value that Indicates the length, in bytes, of the data block.
// A maximum of length of 4MB may exist for any entry. Bit 0 of this field must always be 1 to
// indicate an even byte count. A value of 1 indicates 2 bytes, 3 indicates 4 bytes, etc.
cmdTable->PRDT[index].DBC = sgl->List[index].Length - 1;
}
return sgl->NumberOfElements;
}// -- AhciBuild_PRDT();
/**
* @name AhciProcessSrb
* @implemented
*
* Prepare Srb for IO processing
*
* @param PortExtension
* @param Srb
* @param SlotIndex
*
*/
VOID
AhciProcessSrb (
__in PAHCI_PORT_EXTENSION PortExtension,
__in PSCSI_REQUEST_BLOCK Srb,
__in ULONG SlotIndex
)
{
ULONG prdtlen, sig, length, cfl;
PAHCI_SRB_EXTENSION SrbExtension;
PAHCI_COMMAND_HEADER CommandHeader;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress;
AhciDebugPrint("AhciProcessSrb()\n");
NT_ASSERT(Srb->PathId == PortExtension->PortNumber);
SrbExtension = GetSrbExtension(Srb);
AdapterExtension = PortExtension->AdapterExtension;
NT_ASSERT(SrbExtension != NULL);
NT_ASSERT(SrbExtension->AtaFunction != 0);
if ((SrbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) &&
(SrbExtension->CommandReg == IDE_COMMAND_NOT_VALID))
{
// Here we are safe to check SIG register
sig = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SIG);
if (sig == 0x101)
{
AhciDebugPrint("\tATA Device Found!\n");
SrbExtension->CommandReg = IDE_COMMAND_IDENTIFY;
}
else
{
AhciDebugPrint("\tATAPI Device Found!\n");
SrbExtension->CommandReg = IDE_COMMAND_ATAPI_IDENTIFY;
}
}
NT_ASSERT(SlotIndex < AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
SrbExtension->SlotIndex = SlotIndex;
// program the CFIS in the CommandTable
CommandHeader = &PortExtension->CommandList[SlotIndex];
cfl = 0;
if (IsAtapiCommand(SrbExtension->AtaFunction))
{
cfl = AhciATAPI_CFIS(PortExtension, SrbExtension);
}
else if (IsAtaCommand(SrbExtension->AtaFunction))
{
cfl = AhciATA_CFIS(PortExtension, SrbExtension);
}
else
{
NT_ASSERT(FALSE);
}
prdtlen = 0;
if (IsDataTransferNeeded(SrbExtension))
{
prdtlen = AhciBuild_PRDT(PortExtension, SrbExtension);
NT_ASSERT(prdtlen != -1);
}
// Program the command header
CommandHeader->DI.PRDTL = prdtlen; // number of entries in PRD table
CommandHeader->DI.CFL = cfl;
CommandHeader->DI.A = (SrbExtension->AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) ? 1 : 0;
CommandHeader->DI.W = (SrbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0;
CommandHeader->DI.P = 0; // ATA Specifications says so
CommandHeader->DI.PMP = 0; // Port Multiplier
// Reset -- Manual Configuation
CommandHeader->DI.R = 0;
CommandHeader->DI.B = 0;
CommandHeader->DI.C = 0;
CommandHeader->PRDBC = 0;
CommandHeader->Reserved[0] = 0;
CommandHeader->Reserved[1] = 0;
CommandHeader->Reserved[2] = 0;
CommandHeader->Reserved[3] = 0;
// set CommandHeader CTBA
CommandTablePhysicalAddress = StorPortGetPhysicalAddress(AdapterExtension,
NULL,
SrbExtension,
&length);
NT_ASSERT(length != 0);
// command table alignment
NT_ASSERT((CommandTablePhysicalAddress.LowPart % 128) == 0);
CommandHeader->CTBA = CommandTablePhysicalAddress.LowPart;
if (IsAdapterCAPS64(AdapterExtension->CAP))
{
CommandHeader->CTBA_U = CommandTablePhysicalAddress.HighPart;
}
// mark this slot
PortExtension->Slot[SlotIndex] = Srb;
PortExtension->QueueSlots |= 1 << SlotIndex;
return;
}// -- AhciProcessSrb();
/**
* @name AhciActivatePort
* @implemented
*
* Program Port and populate command list
*
* @param PortExtension
*
*/
VOID
AhciActivatePort (
__in PAHCI_PORT_EXTENSION PortExtension
)
{
AHCI_PORT_CMD cmd;
ULONG QueueSlots, slotToActivate, tmp;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
AhciDebugPrint("AhciActivatePort()\n");
AdapterExtension = PortExtension->AdapterExtension;
QueueSlots = PortExtension->QueueSlots;
if (QueueSlots == 0)
{
return;
}
// section 3.3.14
// Bits in this field shall only be set to 1 by software when PxCMD.ST is set to 1
cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
if (cmd.ST == 0) // PxCMD.ST == 0
{
return;
}
// get the lowest set bit
tmp = QueueSlots & (QueueSlots - 1);
if (tmp == 0)
slotToActivate = QueueSlots;
else
slotToActivate = (QueueSlots & (~tmp));
// mark that bit off in QueueSlots
// so we can know we it is really needed to activate port or not
PortExtension->QueueSlots &= ~slotToActivate;
// mark this CommandIssuedSlots
// to validate in completeIssuedCommand
PortExtension->CommandIssuedSlots |= slotToActivate;
// tell the HBA to issue this Command Slot to the given port
StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CI, slotToActivate);
return;
}// -- AhciActivatePort();
/**
* @name AhciProcessIO
* @implemented
*
* Acquire Exclusive lock to port, populate pending commands to command List
* program controller's port to process new commands in command list.
*
* @param AdapterExtension
* @param PathId
* @param Srb
*
*/
VOID
AhciProcessIO (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in UCHAR PathId,
__in PSCSI_REQUEST_BLOCK Srb
)
{
PSCSI_REQUEST_BLOCK tmpSrb;
STOR_LOCK_HANDLE lockhandle = {0};
PAHCI_PORT_EXTENSION PortExtension;
ULONG commandSlotMask, occupiedSlots, slotIndex, NCS;
AhciDebugPrint("AhciProcessIO()\n");
AhciDebugPrint("\tPathId: %d\n", PathId);
PortExtension = &AdapterExtension->PortExtension[PathId];
NT_ASSERT(PathId < AdapterExtension->PortCount);
// Acquire Lock
StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
// add Srb to queue
AddQueue(&PortExtension->SrbQueue, Srb);
if (PortExtension->DeviceParams.IsActive == FALSE)
{
// Release Lock
StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
return; // we should wait for device to get active
}
occupiedSlots = (PortExtension->QueueSlots | PortExtension->CommandIssuedSlots); // Busy command slots for given port
NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
commandSlotMask = (1 << NCS) - 1; // available slots mask
commandSlotMask = (commandSlotMask & ~occupiedSlots);
if(commandSlotMask != 0)
{
// iterate over HBA port slots
for (slotIndex = 0; slotIndex < NCS; slotIndex++)
{
// find first free slot
if ((commandSlotMask & (1 << slotIndex)) != 0)
{
tmpSrb = RemoveQueue(&PortExtension->SrbQueue);
if (tmpSrb != NULL)
{
NT_ASSERT(tmpSrb->PathId == PathId);
AhciProcessSrb(PortExtension, tmpSrb, slotIndex);
}
else
{
break;
}
}
else
{
break;
}
}
}
// program HBA port
AhciActivatePort(PortExtension);
// Release Lock
StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
return;
}// -- AhciProcessIO();
/**
* @name AtapiInquiryCompletion
* @implemented
*
* AtapiInquiryCompletion routine should be called after device signals
* for device inquiry request is completed (through interrupt) -- ATAPI Device only
*
* @param PortExtension
* @param Srb
*
*/
VOID
AtapiInquiryCompletion (
__in PVOID _Extension,
__in PVOID _Srb
)
{
PAHCI_PORT_EXTENSION PortExtension;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
PSCSI_REQUEST_BLOCK Srb;
BOOLEAN status;
AhciDebugPrint("AtapiInquiryCompletion()\n");
PortExtension = (PAHCI_PORT_EXTENSION)_Extension;
Srb = (PSCSI_REQUEST_BLOCK)_Srb;
NT_ASSERT(Srb != NULL);
NT_ASSERT(PortExtension != NULL);
AdapterExtension = PortExtension->AdapterExtension;
// send queue depth
status = StorPortSetDeviceQueueDepth(PortExtension->AdapterExtension,
Srb->PathId,
Srb->TargetId,
Srb->Lun,
AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
NT_ASSERT(status == TRUE);
return;
}// -- AtapiInquiryCompletion();
/**
* @name InquiryCompletion
* @implemented
*
* InquiryCompletion routine should be called after device signals
* for device inquiry request is completed (through interrupt)
*
* @param PortExtension
* @param Srb
*
*/
VOID
InquiryCompletion (
__in PVOID _Extension,
__in PVOID _Srb
)
{
PAHCI_PORT_EXTENSION PortExtension;
PSCSI_REQUEST_BLOCK Srb;
// PCDB cdb;
BOOLEAN status;
PINQUIRYDATA InquiryData;
PAHCI_SRB_EXTENSION SrbExtension;
PAHCI_ADAPTER_EXTENSION AdapterExtension;
PIDENTIFY_DEVICE_DATA IdentifyDeviceData;
AhciDebugPrint("InquiryCompletion()\n");
PortExtension = (PAHCI_PORT_EXTENSION)_Extension;
Srb = (PSCSI_REQUEST_BLOCK)_Srb;
NT_ASSERT(Srb != NULL);
NT_ASSERT(PortExtension != NULL);
// cdb = (PCDB)&Srb->Cdb;
InquiryData = Srb->DataBuffer;
SrbExtension = GetSrbExtension(Srb);
AdapterExtension = PortExtension->AdapterExtension;
IdentifyDeviceData = PortExtension->IdentifyDeviceData;
if (Srb->SrbStatus != SRB_STATUS_SUCCESS)
{
if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE)
{
PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_NODEVICE;
}
return;
}
NT_ASSERT(InquiryData != NULL);
NT_ASSERT(Srb->SrbStatus == SRB_STATUS_SUCCESS);
// Device specific data
PortExtension->DeviceParams.MaxLba.QuadPart = 0;
if (SrbExtension->CommandReg == IDE_COMMAND_IDENTIFY)
{
PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATA;
if (IdentifyDeviceData->GeneralConfiguration.RemovableMedia)
{
PortExtension->DeviceParams.RemovableDevice = 1;
}
if ((IdentifyDeviceData->CommandSetSupport.BigLba) && (IdentifyDeviceData->CommandSetActive.BigLba))
{
PortExtension->DeviceParams.Lba48BitMode = 1;
}
PortExtension->DeviceParams.AccessType = DIRECT_ACCESS_DEVICE;
/* Device max address lba */
if (PortExtension->DeviceParams.Lba48BitMode)
{
PortExtension->DeviceParams.MaxLba.LowPart = IdentifyDeviceData->Max48BitLBA[0];
PortExtension->DeviceParams.MaxLba.HighPart = IdentifyDeviceData->Max48BitLBA[1];
}
else
{
PortExtension->DeviceParams.MaxLba.LowPart = IdentifyDeviceData->UserAddressableSectors;
}
/* Bytes Per Logical Sector */
if (IdentifyDeviceData->PhysicalLogicalSectorSize.LogicalSectorLongerThan256Words)
{
AhciDebugPrint("\tBytesPerLogicalSector != DEVICE_ATA_BLOCK_SIZE\n");
NT_ASSERT(FALSE);
}
PortExtension->DeviceParams.BytesPerLogicalSector = DEVICE_ATA_BLOCK_SIZE;
/* Bytes Per Physical Sector */
if (IdentifyDeviceData->PhysicalLogicalSectorSize.MultipleLogicalSectorsPerPhysicalSector)
{
AhciDebugPrint("\tBytesPerPhysicalSector != DEVICE_ATA_BLOCK_SIZE\n");
NT_ASSERT(FALSE);
}
PortExtension->DeviceParams.BytesPerPhysicalSector = DEVICE_ATA_BLOCK_SIZE;
// last byte should be NULL
StorPortCopyMemory(PortExtension->DeviceParams.VendorId, IdentifyDeviceData->ModelNumber, sizeof(PortExtension->DeviceParams.VendorId) - 1);
StorPortCopyMemory(PortExtension->DeviceParams.RevisionID, IdentifyDeviceData->FirmwareRevision, sizeof(PortExtension->DeviceParams.RevisionID) - 1);
StorPortCopyMemory(PortExtension->DeviceParams.SerialNumber, IdentifyDeviceData->SerialNumber, sizeof(PortExtension->DeviceParams.SerialNumber) - 1);
PortExtension->DeviceParams.VendorId[sizeof(PortExtension->DeviceParams.VendorId) - 1] = '\0';
PortExtension->DeviceParams.RevisionID[sizeof(PortExtension->DeviceParams.RevisionID) - 1] = '\0';
PortExtension->DeviceParams.SerialNumber[sizeof(PortExtension->DeviceParams.SerialNumber) - 1] = '\0';
// TODO: Add other device params
AhciDebugPrint("\tATA Device\n");
}
else
{
AhciDebugPrint("\tATAPI Device\n");
PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATAPI;
PortExtension->DeviceParams.AccessType = READ_ONLY_DIRECT_ACCESS_DEVICE;
}
// INQUIRYDATABUFFERSIZE = 36 ; Defined in storport.h
if (Srb->DataTransferLength < INQUIRYDATABUFFERSIZE)
{
AhciDebugPrint("\tDataBufferLength < sizeof(INQUIRYDATA), Could crash the driver.\n");
NT_ASSERT(FALSE);
}
// update data transfer length
Srb->DataTransferLength = INQUIRYDATABUFFERSIZE;
// prepare data to send
InquiryData->Versions = 2;
InquiryData->Wide32Bit = 1;
InquiryData->CommandQueue = 0; // NCQ not supported
InquiryData->ResponseDataFormat = 0x2;
InquiryData->DeviceTypeModifier = 0;
InquiryData->DeviceTypeQualifier = DEVICE_CONNECTED;
InquiryData->AdditionalLength = INQUIRYDATABUFFERSIZE - 5;
InquiryData->DeviceType = PortExtension->DeviceParams.AccessType;
InquiryData->RemovableMedia = PortExtension->DeviceParams.RemovableDevice;
// Fill VendorID, Product Revision Level and other string fields
StorPortCopyMemory(InquiryData->VendorId, PortExtension->DeviceParams.VendorId, sizeof(InquiryData->VendorId) - 1);
StorPortCopyMemory(InquiryData->ProductId, PortExtension->DeviceParams.RevisionID, sizeof(PortExtension->DeviceParams.RevisionID));
StorPortCopyMemory(InquiryData->ProductRevisionLevel, PortExtension->DeviceParams.SerialNumber, sizeof(InquiryData->ProductRevisionLevel) - 1);
InquiryData->VendorId[sizeof(InquiryData->VendorId) - 1] = '\0';
InquiryData->ProductId[sizeof(InquiryData->ProductId) - 1] = '\0';
InquiryData->ProductRevisionLevel[sizeof(InquiryData->ProductRevisionLevel) - 1] = '\0';
// send queue depth
status = StorPortSetDeviceQueueDepth(PortExtension->AdapterExtension,
Srb->PathId,
Srb->TargetId,
Srb->Lun,
AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
NT_ASSERT(status == TRUE);
return;
}// -- InquiryCompletion();
/**
* @name AhciATAPICommand
* @implemented
*
* Handles ATAPI Requests commands
*
* @param AdapterExtension
* @param Srb
* @param Cdb
*
* @return
* return STOR status for AhciATAPICommand
*/
UCHAR
AhciATAPICommand (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PSCSI_REQUEST_BLOCK Srb,
__in PCDB Cdb
)
{
ULONG SrbFlags, DataBufferLength;
PAHCI_SRB_EXTENSION SrbExtension;
PAHCI_PORT_EXTENSION PortExtension;
AhciDebugPrint("AhciATAPICommand()\n");
SrbFlags = Srb->SrbFlags;
SrbExtension = GetSrbExtension(Srb);
DataBufferLength = Srb->DataTransferLength;
PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
NT_ASSERT(PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI);
NT_ASSERT(SrbExtension != NULL);
SrbExtension->AtaFunction = ATA_FUNCTION_ATAPI_COMMAND;
SrbExtension->Flags = 0;
if (SrbFlags & SRB_FLAGS_DATA_IN)
{
SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
}
if (SrbFlags & SRB_FLAGS_DATA_OUT)
{
SrbExtension->Flags |= ATA_FLAGS_DATA_OUT;
}
SrbExtension->FeaturesLow = 0;
SrbExtension->CompletionRoutine = NULL;
NT_ASSERT(Cdb != NULL);
switch(Cdb->CDB10.OperationCode)
{
case SCSIOP_INQUIRY:
SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
SrbExtension->CompletionRoutine = AtapiInquiryCompletion;
break;
case SCSIOP_READ:
SrbExtension->Flags |= ATA_FLAGS_USE_DMA;
SrbExtension->FeaturesLow = 0x5;
break;
case SCSIOP_WRITE:
SrbExtension->Flags |= ATA_FLAGS_USE_DMA;
SrbExtension->FeaturesLow = 0x1;
break;
}
SrbExtension->CommandReg = IDE_COMMAND_ATAPI_PACKET;
SrbExtension->LBA0 = 0;
SrbExtension->LBA1 = (UCHAR)(DataBufferLength >> 0);
SrbExtension->LBA2 = (UCHAR)(DataBufferLength >> 8);
SrbExtension->Device = 0;
SrbExtension->LBA3 = 0;
SrbExtension->LBA4 = 0;
SrbExtension->LBA5 = 0;
SrbExtension->FeaturesHigh = 0;
SrbExtension->SectorCountLow = 0;
SrbExtension->SectorCountHigh = 0;
if ((SrbExtension->Flags & ATA_FLAGS_DATA_IN) || (SrbExtension->Flags & ATA_FLAGS_DATA_OUT))
{
SrbExtension->pSgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(AdapterExtension, Srb);
}
return SRB_STATUS_PENDING;
}// -- AhciATAPICommand();
/**
* @name DeviceRequestSense
* @implemented
*
* Handle SCSIOP_MODE_SENSE OperationCode
*
* @param AdapterExtension
* @param Srb
* @param Cdb
*
* @return
* return STOR status for DeviceRequestSense
*/
UCHAR
DeviceRequestSense (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PSCSI_REQUEST_BLOCK Srb,
__in PCDB Cdb
)
{
PMODE_PARAMETER_HEADER ModeHeader;
PAHCI_PORT_EXTENSION PortExtension;
AhciDebugPrint("DeviceRequestSense()\n");
NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_MODE_SENSE);
PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
{
return AhciATAPICommand(AdapterExtension, Srb, Cdb);
}
ModeHeader = (PMODE_PARAMETER_HEADER)Srb->DataBuffer;
NT_ASSERT(ModeHeader != NULL);
AhciZeroMemory((PCHAR)ModeHeader, Srb->DataTransferLength);
ModeHeader->ModeDataLength = sizeof(MODE_PARAMETER_HEADER);
ModeHeader->MediumType = 0;
ModeHeader->DeviceSpecificParameter = 0;
ModeHeader->BlockDescriptorLength = 0;
if (Cdb->MODE_SENSE.PageCode == MODE_SENSE_CURRENT_VALUES)
{
ModeHeader->ModeDataLength = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
ModeHeader->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
}
return SRB_STATUS_SUCCESS;
}// -- DeviceRequestSense();
/**
* @name DeviceRequestReadWrite
* @implemented
*
* Handle SCSIOP_READ SCSIOP_WRITE OperationCode
*
* @param AdapterExtension
* @param Srb
* @param Cdb
*
* @return
* return STOR status for DeviceRequestReadWrite
*/
UCHAR
DeviceRequestReadWrite (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PSCSI_REQUEST_BLOCK Srb,
__in PCDB Cdb
)
{
BOOLEAN IsReading;
ULONG64 StartOffset;
PAHCI_SRB_EXTENSION SrbExtension;
PAHCI_PORT_EXTENSION PortExtension;
ULONG DataTransferLength, BytesPerSector, SectorCount;
AhciDebugPrint("DeviceRequestReadWrite()\n");
NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
NT_ASSERT((Cdb->CDB10.OperationCode == SCSIOP_READ) || (Cdb->CDB10.OperationCode == SCSIOP_WRITE));
SrbExtension = GetSrbExtension(Srb);
PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
{
return AhciATAPICommand(AdapterExtension, Srb, Cdb);
}
DataTransferLength = Srb->DataTransferLength;
BytesPerSector = PortExtension->DeviceParams.BytesPerLogicalSector;
NT_ASSERT(BytesPerSector > 0);
//ROUND_UP(DataTransferLength, BytesPerSector);
SectorCount = DataTransferLength / BytesPerSector;
Srb->DataTransferLength = SectorCount * BytesPerSector;
StartOffset = AhciGetLba(Cdb, Srb->CdbLength);
IsReading = (Cdb->CDB10.OperationCode == SCSIOP_READ);
NT_ASSERT(SectorCount > 0);
SrbExtension->AtaFunction = ATA_FUNCTION_ATA_READ;
SrbExtension->Flags |= ATA_FLAGS_USE_DMA;
SrbExtension->CompletionRoutine = NULL;
if (IsReading)
{
SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
SrbExtension->CommandReg = IDE_COMMAND_READ_DMA;
}
else
{
SrbExtension->Flags |= ATA_FLAGS_DATA_OUT;
SrbExtension->CommandReg = IDE_COMMAND_WRITE_DMA;
}
SrbExtension->FeaturesLow = 0;
SrbExtension->LBA0 = (StartOffset >> 0) & 0xFF;
SrbExtension->LBA1 = (StartOffset >> 8) & 0xFF;
SrbExtension->LBA2 = (StartOffset >> 16) & 0xFF;
SrbExtension->Device = (0xA0 | IDE_LBA_MODE);
if (PortExtension->DeviceParams.Lba48BitMode)
{
SrbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND;
if (IsReading)
{
SrbExtension->CommandReg = IDE_COMMAND_READ_DMA_EXT;
}
else
{
SrbExtension->CommandReg = IDE_COMMAND_WRITE_DMA_EXT;
}
SrbExtension->LBA3 = (StartOffset >> 24) & 0xFF;
SrbExtension->LBA4 = (StartOffset >> 32) & 0xFF;
SrbExtension->LBA5 = (StartOffset >> 40) & 0xFF;
}
else
{
NT_ASSERT(FALSE);
}
SrbExtension->FeaturesHigh = 0;
SrbExtension->SectorCountLow = (SectorCount >> 0) & 0xFF;
SrbExtension->SectorCountHigh = (SectorCount >> 8) & 0xFF;
NT_ASSERT(SectorCount < 0x100);
SrbExtension->pSgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(AdapterExtension, Srb);
return SRB_STATUS_PENDING;
}// -- DeviceRequestReadWrite();
/**
* @name DeviceRequestCapacity
* @implemented
*
* Handle SCSIOP_READ_CAPACITY OperationCode
*
* @param AdapterExtension
* @param Srb
* @param Cdb
*
* @return
* return STOR status for DeviceRequestCapacity
*/
UCHAR
DeviceRequestCapacity (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PSCSI_REQUEST_BLOCK Srb,
__in PCDB Cdb
)
{
ULONG MaxLba, BytesPerLogicalSector;
PREAD_CAPACITY_DATA ReadCapacity;
PAHCI_PORT_EXTENSION PortExtension;
AhciDebugPrint("DeviceRequestCapacity()\n");
UNREFERENCED_PARAMETER(AdapterExtension);
UNREFERENCED_PARAMETER(Cdb);
NT_ASSERT(Srb->DataBuffer != NULL);
NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
{
return AhciATAPICommand(AdapterExtension, Srb, Cdb);
}
if (Cdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY)
{
ReadCapacity = (PREAD_CAPACITY_DATA)Srb->DataBuffer;
BytesPerLogicalSector = PortExtension->DeviceParams.BytesPerLogicalSector;
MaxLba = (ULONG)PortExtension->DeviceParams.MaxLba.QuadPart - 1;
// I trust you windows :D
NT_ASSERT(Srb->DataTransferLength >= sizeof(READ_CAPACITY_DATA));
// I trust you user :D
NT_ASSERT(PortExtension->DeviceParams.MaxLba.QuadPart < (ULONG)-1);
// Actually I don't trust anyone :p
Srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
REVERSE_BYTES(&ReadCapacity->BytesPerBlock, &BytesPerLogicalSector);
REVERSE_BYTES(&ReadCapacity->LogicalBlockAddress, &MaxLba);
}
else
{
AhciDebugPrint("\tSCSIOP_READ_CAPACITY16 not supported\n");
NT_ASSERT(FALSE);
}
return SRB_STATUS_SUCCESS;
}// -- DeviceRequestCapacity();
/**
* @name DeviceRequestComplete
* @implemented
*
* Handle UnHandled Requests
*
* @param AdapterExtension
* @param Srb
* @param Cdb
*
* @return
* return STOR status for DeviceRequestComplete
*/
UCHAR
DeviceRequestComplete (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PSCSI_REQUEST_BLOCK Srb,
__in PCDB Cdb
)
{
AhciDebugPrint("DeviceRequestComplete()\n");
UNREFERENCED_PARAMETER(AdapterExtension);
UNREFERENCED_PARAMETER(Cdb);
Srb->ScsiStatus = SCSISTAT_GOOD;
return SRB_STATUS_SUCCESS;
}// -- DeviceRequestComplete();
/**
* @name DeviceReportLuns
* @implemented
*
* Handle SCSIOP_REPORT_LUNS OperationCode
*
* @param AdapterExtension
* @param Srb
* @param Cdb
*
* @return
* return STOR status for DeviceReportLuns
*/
UCHAR
DeviceReportLuns (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PSCSI_REQUEST_BLOCK Srb,
__in PCDB Cdb
)
{
PLUN_LIST LunList;
PAHCI_PORT_EXTENSION PortExtension;
AhciDebugPrint("DeviceReportLuns()\n");
UNREFERENCED_PARAMETER(Cdb);
PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
NT_ASSERT(Srb->DataTransferLength >= sizeof(LUN_LIST));
NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS);
if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
{
return AhciATAPICommand(AdapterExtension, Srb, Cdb);
}
LunList = (PLUN_LIST)Srb->DataBuffer;
NT_ASSERT(LunList != NULL);
AhciZeroMemory((PCHAR)LunList, sizeof(LUN_LIST));
LunList->LunListLength[3] = 8;
Srb->ScsiStatus = SCSISTAT_GOOD;
Srb->DataTransferLength = sizeof(LUN_LIST);
return SRB_STATUS_SUCCESS;
}// -- DeviceReportLuns();
/**
* @name DeviceInquiryRequest
* @implemented
*
* Tells wheather given port is implemented or not
*
* @param AdapterExtension
* @param Srb
* @param Cdb
*
* @return
* return STOR status for DeviceInquiryRequest
*
* @remark
* http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
*/
UCHAR
DeviceInquiryRequest (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PSCSI_REQUEST_BLOCK Srb,
__in PCDB Cdb
)
{
PVOID DataBuffer;
PAHCI_SRB_EXTENSION SrbExtension;
PAHCI_PORT_EXTENSION PortExtension;
PVPD_SUPPORTED_PAGES_PAGE VpdOutputBuffer;
ULONG DataBufferLength, RequiredDataBufferLength;
AhciDebugPrint("DeviceInquiryRequest()\n");
NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_INQUIRY);
NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
SrbExtension = GetSrbExtension(Srb);
PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
{
return AhciATAPICommand(AdapterExtension, Srb, Cdb);
}
if (Srb->Lun != 0)
{
return SRB_STATUS_SELECTION_TIMEOUT;
}
else if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0)
{
// 3.6.1
// If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
AhciDebugPrint("\tEVPD Inquired\n");
NT_ASSERT(SrbExtension != NULL);
SrbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY;
SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
SrbExtension->CompletionRoutine = InquiryCompletion;
SrbExtension->CommandReg = IDE_COMMAND_NOT_VALID;
// TODO: Should use AhciZeroMemory
SrbExtension->FeaturesLow = 0;
SrbExtension->LBA0 = 0;
SrbExtension->LBA1 = 0;
SrbExtension->LBA2 = 0;
SrbExtension->Device = 0xA0;
SrbExtension->LBA3 = 0;
SrbExtension->LBA4 = 0;
SrbExtension->LBA5 = 0;
SrbExtension->FeaturesHigh = 0;
SrbExtension->SectorCountLow = 0;
SrbExtension->SectorCountHigh = 0;
SrbExtension->Sgl.NumberOfElements = 1;
SrbExtension->Sgl.List[0].PhysicalAddress.LowPart = PortExtension->IdentifyDeviceDataPhysicalAddress.LowPart;
SrbExtension->Sgl.List[0].PhysicalAddress.HighPart = PortExtension->IdentifyDeviceDataPhysicalAddress.HighPart;
SrbExtension->Sgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA);
SrbExtension->pSgl = &SrbExtension->Sgl;
return SRB_STATUS_PENDING;
}
else
{
AhciDebugPrint("\tVPD Inquired\n");
DataBuffer = Srb->DataBuffer;
DataBufferLength = Srb->DataTransferLength;
RequiredDataBufferLength = DataBufferLength; // make the compiler happy :p
if (DataBuffer == NULL)
{
return SRB_STATUS_INVALID_REQUEST;
}
AhciZeroMemory(DataBuffer, DataBufferLength);
switch(Cdb->CDB6INQUIRY3.PageCode)
{
case VPD_SUPPORTED_PAGES:
{
AhciDebugPrint("\tVPD_SUPPORTED_PAGES\n");
RequiredDataBufferLength = sizeof(VPD_SUPPORTED_PAGES_PAGE) + 1;
if (DataBufferLength < RequiredDataBufferLength)
{
AhciDebugPrint("\tDataBufferLength: %d Required: %d\n", DataBufferLength, RequiredDataBufferLength);
return SRB_STATUS_INVALID_REQUEST;
}
VpdOutputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)DataBuffer;
VpdOutputBuffer->DeviceType = PortExtension->DeviceParams.AccessType;
VpdOutputBuffer->DeviceTypeQualifier = 0;
VpdOutputBuffer->PageCode = VPD_SUPPORTED_PAGES;
VpdOutputBuffer->PageLength = 1;
VpdOutputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES;
//VpdOutputBuffer->SupportedPageList[1] = VPD_SERIAL_NUMBER;
//VpdOutputBuffer->SupportedPageList[2] = VPD_DEVICE_IDENTIFIERS;
NT_ASSERT(VpdOutputBuffer->DeviceType == DIRECT_ACCESS_DEVICE);
}
break;
case VPD_SERIAL_NUMBER:
{
AhciDebugPrint("\tVPD_SERIAL_NUMBER\n");
}
break;
case VPD_DEVICE_IDENTIFIERS:
{
AhciDebugPrint("\tVPD_DEVICE_IDENTIFIERS\n");
}
break;
default:
AhciDebugPrint("\tPageCode: %x\n", Cdb->CDB6INQUIRY3.PageCode);
return SRB_STATUS_INVALID_REQUEST;
}
Srb->DataTransferLength = RequiredDataBufferLength;
return SRB_STATUS_SUCCESS;
}
}// -- DeviceInquiryRequest();
/**
* @name AhciAdapterReset
* @implemented
*
* 10.4.3 HBA Reset
* If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
* problem, software may reset the entire HBA by setting GHC.HR to 1. When software sets the GHC.HR
* bit to 1, the HBA shall perform an internal reset action. The bit shall be cleared to 0 by the HBA when
* the reset is complete. A software write of 0 to GHC.HR shall have no effect. To perform the HBA reset,
* software sets GHC.HR to 1 and may poll until this bit is read to be 0, at which point software knows that
* the HBA reset has completed.
* If the HBA has not cleared GHC.HR to 0 within 1 second of software setting GHC.HR to 1, the HBA is in
* a hung or locked state.
*
* @param AdapterExtension
*
* @return
* TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
*/
BOOLEAN
AhciAdapterReset (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension
)
{
ULONG ticks;
AHCI_GHC ghc;
PAHCI_MEMORY_REGISTERS abar = NULL;
AhciDebugPrint("AhciAdapterReset()\n");
abar = AdapterExtension->ABAR_Address;
if (abar == NULL) // basic sanity
{
return FALSE;
}
// HR -- Very first bit (lowest significant)
ghc.HR = 1;
StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc.Status);
for (ticks = 0; ticks < 50; ++ticks)
{
ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC);
if (ghc.HR == 0)
{
break;
}
StorPortStallExecution(20000);
}
if (ticks == 50)// 1 second
{
AhciDebugPrint("\tDevice Timeout\n");
return FALSE;
}
return TRUE;
}// -- AhciAdapterReset();
/**
* @name AhciZeroMemory
* @implemented
*
* Clear buffer by filling zeros
*
* @param Buffer
* @param BufferSize
*/
__inline
VOID
AhciZeroMemory (
__out PCHAR Buffer,
__in ULONG BufferSize
)
{
ULONG i;
for (i = 0; i < BufferSize; i++)
{
Buffer[i] = 0;
}
return;
}// -- AhciZeroMemory();
/**
* @name IsPortValid
* @implemented
*
* Tells wheather given port is implemented or not
*
* @param AdapterExtension
* @param PathId
*
* @return
* return TRUE if provided port is valid (implemented) or not
*/
__inline
BOOLEAN
IsPortValid (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in ULONG pathId
)
{
NT_ASSERT(pathId < MAXIMUM_AHCI_PORT_COUNT);
if (pathId >= AdapterExtension->PortCount)
{
return FALSE;
}
return AdapterExtension->PortExtension[pathId].DeviceParams.IsActive;
}// -- IsPortValid()
/**
* @name AddQueue
* @implemented
*
* Add Srb to Queue
*
* @param Queue
* @param Srb
*
* @return
* return TRUE if Srb is successfully added to Queue
*
*/
__inline
BOOLEAN
AddQueue (
__inout PAHCI_QUEUE Queue,
__in PVOID Srb
)
{
NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
if (Queue->Tail == ((Queue->Head + 1) % MAXIMUM_QUEUE_BUFFER_SIZE))
return FALSE;
Queue->Buffer[Queue->Head++] = Srb;
Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE;
return TRUE;
}// -- AddQueue();
/**
* @name RemoveQueue
* @implemented
*
* Remove and return Srb from Queue
*
* @param Queue
*
* @return
* return Srb
*
*/
__inline
PVOID
RemoveQueue (
__inout PAHCI_QUEUE Queue
)
{
PVOID Srb;
NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
if (Queue->Head == Queue->Tail)
return NULL;
Srb = Queue->Buffer[Queue->Tail++];
Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE;
return Srb;
}// -- RemoveQueue();
/**
* @name GetSrbExtension
* @implemented
*
* GetSrbExtension from Srb make sure It is properly aligned
*
* @param Srb
*
* @return
* return SrbExtension
*
*/
__inline
PAHCI_SRB_EXTENSION
GetSrbExtension (
__in PSCSI_REQUEST_BLOCK Srb
)
{
ULONG Offset;
ULONG_PTR SrbExtension;
SrbExtension = (ULONG_PTR)Srb->SrbExtension;
Offset = SrbExtension % 128;
// CommandTable should be 128 byte aligned
if (Offset != 0)
Offset = 128 - Offset;
return (PAHCI_SRB_EXTENSION)(SrbExtension + Offset);
}// -- PAHCI_SRB_EXTENSION();
/**
* @name AhciGetLba
* @implemented
*
* Find the logical address of demand block from Cdb
*
* @param Srb
*
* @return
* return Logical Address of the block
*
*/
__inline
ULONG64
AhciGetLba (
__in PCDB Cdb,
__in ULONG CdbLength
)
{
ULONG64 lba = 0;
NT_ASSERT(Cdb != NULL);
NT_ASSERT(CdbLength != 0);
if (CdbLength == 0x10)
{
REVERSE_BYTES_QUAD(&lba, Cdb->CDB16.LogicalBlock);
}
else
{
lba |= Cdb->CDB10.LogicalBlockByte3 << 0;
lba |= Cdb->CDB10.LogicalBlockByte2 << 8;
lba |= Cdb->CDB10.LogicalBlockByte1 << 16;
lba |= Cdb->CDB10.LogicalBlockByte0 << 24;
}
return lba;
}// -- AhciGetLba();