2 * PROJECT: ReactOS Kernel
3 * LICENSE: GNU GPLv2 only as published by the Free Software Foundation
4 * PURPOSE: To Implement AHCI Miniport driver targeting storport NT 5.2
5 * PROGRAMMERS: Aman Priyadarshi (aman.eureka@gmail.com)
11 * @name AhciPortInitialize
14 * Initialize port by setting up PxCLB & PxFB Registers
16 * @param PortExtension
19 * Return true if intialization was successful
23 __in PAHCI_PORT_EXTENSION PortExtension
26 ULONG mappedLength
, portNumber
;
27 PAHCI_MEMORY_REGISTERS abar
;
28 PAHCI_ADAPTER_EXTENSION adapterExtension
;
29 STOR_PHYSICAL_ADDRESS commandListPhysical
, receivedFISPhysical
;
31 DebugPrint("AhciPortInitialize()\n");
33 adapterExtension
= PortExtension
->AdapterExtension
;
34 abar
= adapterExtension
->ABAR_Address
;
35 portNumber
= PortExtension
->PortNumber
;
37 NT_ASSERT(abar
!= NULL
);
38 NT_ASSERT(portNumber
< adapterExtension
->PortCount
);
40 PortExtension
->Port
= &abar
->PortList
[portNumber
];
42 commandListPhysical
= StorPortGetPhysicalAddress(adapterExtension
,
44 PortExtension
->CommandList
,
47 if ((mappedLength
== 0) || ((commandListPhysical
.LowPart
% 1024) != 0))
49 DebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength
);
53 receivedFISPhysical
= StorPortGetPhysicalAddress(adapterExtension
,
55 PortExtension
->ReceivedFIS
,
58 if ((mappedLength
== 0) || ((receivedFISPhysical
.LowPart
% 256) != 0))
60 DebugPrint("\treceivedFISPhysical mappedLength:%d\n", mappedLength
);
64 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
65 // PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
66 // PxFB and PxFBU (if CAP.S64A is set to ‘1’)
67 // Note: Assuming 32bit support only
68 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->CLB
, commandListPhysical
.LowPart
);
69 if (IsAdapterCAPS64(adapterExtension
->CAP
))
71 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->CLBU
, commandListPhysical
.HighPart
);
74 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->FB
, receivedFISPhysical
.LowPart
);
75 if (IsAdapterCAPS64(adapterExtension
->CAP
))
77 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->FBU
, receivedFISPhysical
.HighPart
);
80 // set device power state flag to D0
81 PortExtension
->DevicePowerState
= StorPowerDeviceD0
;
83 // clear pending interrupts
84 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->SERR
, (ULONG
)-1);
85 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->IS
, (ULONG
)-1);
86 StorPortWriteRegisterUlong(adapterExtension
, PortExtension
->AdapterExtension
->IS
, (1 << PortExtension
->PortNumber
));
89 }// -- AhciPortInitialize();
92 * @name AhciAllocateResourceForAdapter
95 * Allocate memory from poll for required pointers
97 * @param AdapterExtension
101 * return TRUE if allocation was successful
104 AhciAllocateResourceForAdapter (
105 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
106 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
109 PVOID portsExtension
= NULL
;
110 PCHAR nonCachedExtension
;
111 ULONG status
, index
, NCS
, AlignedNCS
;
112 ULONG portCount
, portImplemented
, nonCachedExtensionSize
;
114 DebugPrint("AhciAllocateResourceForAdapter()\n");
116 NCS
= AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
);
117 AlignedNCS
= ROUND_UP(NCS
, 8);
119 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
121 portImplemented
= AdapterExtension
->PortImplemented
;
123 NT_ASSERT(portImplemented
!= 0);
124 for (index
= MAXIMUM_AHCI_PORT_COUNT
- 1; index
> 0; index
--)
125 if ((portImplemented
& (1 << index
)) != 0)
128 portCount
= index
+ 1;
129 DebugPrint("\tPort Count: %d\n", portCount
);
131 AdapterExtension
->PortCount
= portCount
;
132 nonCachedExtensionSize
= sizeof(AHCI_COMMAND_HEADER
) * AlignedNCS
+ //should be 1K aligned
133 sizeof(AHCI_RECEIVED_FIS
);
135 // align nonCachedExtensionSize to 1024
136 nonCachedExtensionSize
= ROUND_UP(nonCachedExtensionSize
, 1024);
138 AdapterExtension
->NonCachedExtension
= StorPortGetUncachedExtension(AdapterExtension
,
140 nonCachedExtensionSize
* portCount
);
142 if (AdapterExtension
->NonCachedExtension
== NULL
)
144 DebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
148 nonCachedExtension
= AdapterExtension
->NonCachedExtension
;
149 AhciZeroMemory(nonCachedExtension
, nonCachedExtensionSize
* portCount
);
151 for (index
= 0; index
< portCount
; index
++)
153 AdapterExtension
->PortExtension
[index
].IsActive
= FALSE
;
154 if ((AdapterExtension
->PortImplemented
& (1 << index
)) != 0)
156 AdapterExtension
->PortExtension
[index
].PortNumber
= index
;
157 AdapterExtension
->PortExtension
[index
].IsActive
= TRUE
;
158 AdapterExtension
->PortExtension
[index
].AdapterExtension
= AdapterExtension
;
159 AdapterExtension
->PortExtension
[index
].CommandList
= nonCachedExtension
;
160 AdapterExtension
->PortExtension
[index
].ReceivedFIS
= (PAHCI_RECEIVED_FIS
)(nonCachedExtension
+ sizeof(AHCI_COMMAND_HEADER
) * AlignedNCS
);
161 nonCachedExtension
+= nonCachedExtensionSize
;
166 }// -- AhciAllocateResourceForAdapter();
169 * @name AhciHwInitialize
172 * initializes the HBA and finds all devices that are of interest to the miniport driver.
174 * @param adapterExtension
177 * return TRUE if intialization was successful
181 __in PVOID AdapterExtension
184 ULONG ghc
, messageCount
, status
;
185 PAHCI_ADAPTER_EXTENSION adapterExtension
;
187 DebugPrint("AhciHwInitialize()\n");
189 adapterExtension
= AdapterExtension
;
190 adapterExtension
->StateFlags
.MessagePerPort
= FALSE
;
192 // First check what type of interrupt/synchronization device is using
193 ghc
= StorPortReadRegisterUlong(adapterExtension
, &adapterExtension
->ABAR_Address
->GHC
);
195 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
196 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
197 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
198 // software has allocated the number of messages requested
199 if ((ghc
& AHCI_Global_HBA_CONTROL_MRSM
) == 0)
201 adapterExtension
->StateFlags
.MessagePerPort
= TRUE
;
202 DebugPrint("\tMultiple MSI based message not supported\n");
206 }// -- AhciHwInitialize();
209 * @name AhciInterruptHandler
212 * Interrupt Handler for PortExtension
214 * @param PortExtension
218 AhciInterruptHandler (
219 __in PAHCI_PORT_EXTENSION PortExtension
223 AHCI_INTERRUPT_STATUS PxIS
;
224 AHCI_INTERRUPT_STATUS PxISMasked
;
225 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
227 DebugPrint("AhciInterruptHandler()\n");
228 DebugPrint("\tPort Number: %d\n", PortExtension
->PortNumber
);
230 AdapterExtension
= PortExtension
->AdapterExtension
;
231 NT_ASSERT(IsPortValid(AdapterExtension
, PortExtension
->PortNumber
));
234 // 1. Software determines the cause of the interrupt by reading the PxIS register.
235 // It is possible for multiple bits to be set
236 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
237 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
238 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
239 // the list of commands previously issued by software that are still outstanding.
240 // If executing native queued commands, software reads the PxSACT register and compares the current
241 // value to the list of commands previously issued by software.
242 // Software completes with success any outstanding command whose corresponding bit has been cleared in
243 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
244 // to determine commands that have completed, not to determine which commands have previously been issued.
245 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
246 PxISMasked
.Status
= 0;
247 PxIS
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IS
);
251 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
252 if (PxIS
.HBFS
|| PxIS
.HBDS
|| PxIS
.IFS
|| PxIS
.TFES
)
254 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
255 // any native command queuing commands. To recover, the port must be restarted
256 // To detect an error that requires software recovery actions to be performed,
257 // software should check whether any of the following status bits are set on an interrupt:
258 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
259 // software should perform the appropriate error recovery actions based on whether
260 // non-queued commands were being issued or native command queuing commands were being issued.
262 DebugPrint("\tFatal Error: %x\n", PxIS
.Status
);
265 // Normal Command Completion
267 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
268 PxISMasked
.DHRS
= PxIS
.DHRS
;
269 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
270 PxISMasked
.PSS
= PxIS
.PSS
;
271 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
272 PxISMasked
.DSS
= PxIS
.DSS
;
273 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
274 PxISMasked
.SDBS
= PxIS
.SDBS
;
275 // A PRD with the ‘I’ bit set has transferred all of its data.
276 PxISMasked
.DPS
= PxIS
.DPS
;
278 if (PxISMasked
.Status
!= 0)
280 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IS
, PxISMasked
.Status
);
284 // Clear port interrupt
285 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
286 IS
= (1 << PortExtension
->PortNumber
);
287 StorPortWriteRegisterUlong(AdapterExtension
, AdapterExtension
->IS
, IS
);
290 }// -- AhciInterruptHandler();
293 * @name AhciHwInterrupt
296 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
298 * @param AdapterExtension
301 * return TRUE Indicates that an interrupt was pending on adapter.
302 * return FALSE Indicates the interrupt was not ours.
306 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
309 ULONG portPending
, nextPort
, i
, portCount
;
311 DebugPrint("AhciHwInterrupt()\n");
313 if (AdapterExtension
->StateFlags
.Removed
)
318 portPending
= StorPortReadRegisterUlong(AdapterExtension
, AdapterExtension
->IS
);
319 // we process interrupt for implemented ports only
320 portCount
= AdapterExtension
->PortCount
;
321 portPending
= portPending
& AdapterExtension
->PortImplemented
;
323 if (portPending
== 0)
328 for (i
= 1; i
<= portCount
; i
++)
330 nextPort
= (AdapterExtension
->LastInterruptPort
+ i
) % portCount
;
332 if ((portPending
& (0x1 << nextPort
)) == 0)
335 NT_ASSERT(IsPortValid(AdapterExtension
, nextPort
));
337 if ((nextPort
== AdapterExtension
->LastInterruptPort
) ||
338 (AdapterExtension
->PortExtension
[nextPort
].IsActive
== FALSE
))
343 // we can assign this interrupt to this port
344 AdapterExtension
->LastInterruptPort
= nextPort
;
345 AhciInterruptHandler(&AdapterExtension
->PortExtension
[nextPort
]);
347 // interrupt belongs to this device
348 // should always return TRUE
352 DebugPrint("\tSomething went wrong");
354 }// -- AhciHwInterrupt();
357 * @name AhciHwStartIo
360 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
362 * @param adapterExtension
366 * return TRUE if the request was accepted
367 * return FALSE if the request must be submitted later
371 __in PVOID AdapterExtension
,
372 __in PSCSI_REQUEST_BLOCK Srb
375 UCHAR function
, pathId
;
376 PAHCI_ADAPTER_EXTENSION adapterExtension
;
378 DebugPrint("AhciHwStartIo()\n");
380 pathId
= Srb
->PathId
;
381 function
= Srb
->Function
;
382 adapterExtension
= AdapterExtension
;
384 if (!IsPortValid(adapterExtension
, pathId
))
386 Srb
->SrbStatus
= SRB_STATUS_NO_DEVICE
;
387 StorPortNotification(RequestComplete
, adapterExtension
, Srb
);
391 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
392 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
393 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
394 if (function
== SRB_FUNCTION_PNP
)
396 PSCSI_PNP_REQUEST_BLOCK pnpRequest
;
398 pnpRequest
= (PSCSI_PNP_REQUEST_BLOCK
)Srb
;
399 if ((pnpRequest
->SrbPnPFlags
& SRB_PNP_FLAGS_ADAPTER_REQUEST
) != 0)
401 if ((pnpRequest
->PnPAction
== StorRemoveDevice
) ||
402 (pnpRequest
->PnPAction
== StorSurpriseRemoval
))
404 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
405 adapterExtension
->StateFlags
.Removed
= 1;
406 DebugPrint("\tAdapter removed\n");
408 else if (pnpRequest
->PnPAction
== StorStopDevice
)
410 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
411 DebugPrint("\tRequested to Stop the adapter\n");
415 Srb
->SrbStatus
= SRB_STATUS_INVALID_REQUEST
;
418 StorPortNotification(RequestComplete
, adapterExtension
, Srb
);
423 if (function
== SRB_FUNCTION_EXECUTE_SCSI
)
425 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
426 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
427 // routine does the following:
429 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
430 // logical unit, and/or SRB extensions
431 // For example, a miniport driver might set up a logical unit extension with pointers
432 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
433 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
434 // carried out on the HBA.
436 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
437 // for the requested operation
438 // For a device I/O operation, such an internal routine generally selects the target device
439 // and sends the CDB over the bus to the target logical unit.
440 if (Srb
->CdbLength
> 0)
442 PCDB cdb
= (PCDB
)&Srb
->Cdb
;
443 if (cdb
->CDB10
.OperationCode
== SCSIOP_INQUIRY
)
445 Srb
->SrbStatus
= DeviceInquiryRequest(adapterExtension
, Srb
, cdb
);
449 Srb
->SrbStatus
= SRB_STATUS_NO_DEVICE
;
454 Srb
->SrbStatus
= SRB_STATUS_BAD_FUNCTION
;
457 StorPortNotification(RequestComplete
, adapterExtension
, Srb
);
461 DebugPrint("\tUnknown function code recieved: %x\n", function
);
462 Srb
->SrbStatus
= SRB_STATUS_BAD_FUNCTION
;
463 StorPortNotification(RequestComplete
, adapterExtension
, Srb
);
465 }// -- AhciHwStartIo();
468 * @name AhciHwResetBus
471 * The HwStorResetBus routine is called by the port driver to clear error conditions.
473 * @param adapterExtension
477 * return TRUE if bus was successfully reset
481 __in PVOID AdapterExtension
,
485 STOR_LOCK_HANDLE lockhandle
;
486 PAHCI_ADAPTER_EXTENSION adapterExtension
;
488 DebugPrint("AhciHwResetBus()\n");
490 adapterExtension
= AdapterExtension
;
492 if (IsPortValid(AdapterExtension
, PathId
))
494 AhciZeroMemory(&lockhandle
, sizeof(lockhandle
));
497 StorPortAcquireSpinLock(AdapterExtension
, InterruptLock
, NULL
, &lockhandle
);
499 // TODO: Perform port reset
502 StorPortReleaseSpinLock(AdapterExtension
, &lockhandle
);
506 }// -- AhciHwResetBus();
509 * @name AhciHwFindAdapter
512 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
513 * HBA is supported and, if it is, to return configuration information about that adapter.
515 * 10.1 Platform Communication
516 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
518 * @param DeviceExtension
520 * @param BusInformation
521 * @param ArgumentString
527 * 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.
530 * 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.
532 * SP_RETURN_BAD_CONFIG
533 * Indicates that the supplied configuration information was invalid for the adapter.
535 * SP_RETURN_NOT_FOUND
536 * Indicates that no supported HBA was found for the supplied configuration information.
538 * @remarks Called by Storport.
542 __in PVOID AdapterExtension
,
543 __in PVOID HwContext
,
544 __in PVOID BusInformation
,
545 __in PVOID ArgumentString
,
546 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo
,
547 __in PBOOLEAN Reserved3
552 ULONG portCount
, portImplemented
;
554 UCHAR pci_cfg_buf
[sizeof(PCI_COMMON_CONFIG
)];
555 PACCESS_RANGE accessRange
;
557 PAHCI_MEMORY_REGISTERS abar
;
558 PPCI_COMMON_CONFIG pciConfigData
;
559 PAHCI_ADAPTER_EXTENSION adapterExtension
;
561 DebugPrint("AhciHwFindAdapter()\n");
563 adapterExtension
= AdapterExtension
;
564 adapterExtension
->SlotNumber
= ConfigInfo
->SlotNumber
;
565 adapterExtension
->SystemIoBusNumber
= ConfigInfo
->SystemIoBusNumber
;
567 // get PCI configuration header
568 pci_cfg_len
= StorPortGetBusData(
571 adapterExtension
->SystemIoBusNumber
,
572 adapterExtension
->SlotNumber
,
574 sizeof(PCI_COMMON_CONFIG
));
576 if (pci_cfg_len
!= sizeof(PCI_COMMON_CONFIG
))
578 DebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG
), pci_cfg_len
);
579 return SP_RETURN_ERROR
;//Not a valid device at the given bus number
582 pciConfigData
= pci_cfg_buf
;
583 adapterExtension
->VendorID
= pciConfigData
->VendorID
;
584 adapterExtension
->DeviceID
= pciConfigData
->DeviceID
;
585 adapterExtension
->RevisionID
= pciConfigData
->RevisionID
;
586 // The last PCI base address register (BAR[5], header offset 0x24) points to the AHCI base memory, it’s called ABAR (AHCI Base Memory Register).
587 adapterExtension
->AhciBaseAddress
= pciConfigData
->u
.type0
.BaseAddresses
[5] & (0xFFFFFFF0);
589 DebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension
->VendorID
,
590 adapterExtension
->DeviceID
,
591 adapterExtension
->RevisionID
);
595 if (ConfigInfo
->NumberOfAccessRanges
> 0)
597 for (index
= 0; index
< ConfigInfo
->NumberOfAccessRanges
; index
++)
599 accessRange
= *ConfigInfo
->AccessRanges
;
600 if (accessRange
[index
].RangeStart
.QuadPart
== adapterExtension
->AhciBaseAddress
)
602 abar
= StorPortGetDeviceBase(adapterExtension
,
603 ConfigInfo
->AdapterInterfaceType
,
604 ConfigInfo
->SystemIoBusNumber
,
605 accessRange
[index
].RangeStart
,
606 accessRange
[index
].RangeLength
,
607 !accessRange
[index
].RangeInMemory
);
615 DebugPrint("\tabar == NULL\n");
616 return SP_RETURN_ERROR
; // corrupted information supplied
619 adapterExtension
->ABAR_Address
= abar
;
620 adapterExtension
->CAP
= StorPortReadRegisterUlong(adapterExtension
, &abar
->CAP
);
621 adapterExtension
->CAP2
= StorPortReadRegisterUlong(adapterExtension
, &abar
->CAP2
);
622 adapterExtension
->Version
= StorPortReadRegisterUlong(adapterExtension
, &abar
->VS
);
623 adapterExtension
->LastInterruptPort
= -1;
626 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
627 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
628 ghc
= StorPortReadRegisterUlong(adapterExtension
, &abar
->GHC
);
629 // AE := Highest Significant bit of GHC
630 if ((ghc
& AHCI_Global_HBA_CONTROL_AE
) != 0)// Hmm, controller was already in power state
632 // reset controller to have it in known state
633 DebugPrint("\tAE Already set, Reset()\n");
634 if (!AhciAdapterReset(adapterExtension
))
636 DebugPrint("\tReset Failed!\n");
637 return SP_RETURN_ERROR
;// reset failed
641 ghc
= AHCI_Global_HBA_CONTROL_AE
;// only AE=1
642 StorPortWriteRegisterUlong(adapterExtension
, &abar
->GHC
, ghc
);
644 adapterExtension
->IS
= &abar
->IS
;
645 adapterExtension
->PortImplemented
= StorPortReadRegisterUlong(adapterExtension
, &abar
->PI
);
647 if (adapterExtension
->PortImplemented
== 0)
649 DebugPrint("\tadapterExtension->PortImplemented == 0\n");
650 return SP_RETURN_ERROR
;
653 ConfigInfo
->MaximumTransferLength
= MAXIMUM_TRANSFER_LENGTH
;//128 KB
654 ConfigInfo
->NumberOfPhysicalBreaks
= 0x21;
655 ConfigInfo
->MaximumNumberOfTargets
= 1;
656 ConfigInfo
->MaximumNumberOfLogicalUnits
= 1;
657 ConfigInfo
->ResetTargetSupported
= TRUE
;
658 ConfigInfo
->NumberOfBuses
= MAXIMUM_AHCI_PORT_COUNT
;
659 ConfigInfo
->SynchronizationModel
= StorSynchronizeFullDuplex
;
660 ConfigInfo
->ScatterGather
= TRUE
;
662 // Turn IE -- Interrupt Enabled
663 ghc
|= AHCI_Global_HBA_CONTROL_IE
;
664 StorPortWriteRegisterUlong(adapterExtension
, &abar
->GHC
, ghc
);
666 // allocate necessary resource for each port
667 if (!AhciAllocateResourceForAdapter(adapterExtension
, ConfigInfo
))
669 DebugPrint("\tAhciAllocateResourceForAdapter() == FALSE\n");
670 return SP_RETURN_ERROR
;
673 for (index
= 0; index
< adapterExtension
->PortCount
; index
++)
675 if ((adapterExtension
->PortImplemented
& (0x1 << index
)) != 0)
676 AhciPortInitialize(&adapterExtension
->PortExtension
[index
]);
679 return SP_RETURN_FOUND
;
680 }// -- AhciHwFindAdapter();
686 * Initial Entrypoint for storahci miniport driver
688 * @param DriverObject
689 * @param RegistryPath
692 * NT_STATUS in case of driver loaded successfully.
696 __in PVOID DriverObject
,
697 __in PVOID RegistryPath
700 HW_INITIALIZATION_DATA hwInitializationData
;
703 DebugPrint("Storahci Loaded\n");
705 // initialize the hardware data structure
706 AhciZeroMemory(&hwInitializationData
, sizeof(HW_INITIALIZATION_DATA
));
708 // set size of hardware initialization structure
709 hwInitializationData
.HwInitializationDataSize
= sizeof(HW_INITIALIZATION_DATA
);
711 // identity required miniport entry point routines
712 hwInitializationData
.HwStartIo
= AhciHwStartIo
;
713 hwInitializationData
.HwResetBus
= AhciHwResetBus
;
714 hwInitializationData
.HwInterrupt
= AhciHwInterrupt
;
715 hwInitializationData
.HwInitialize
= AhciHwInitialize
;
716 hwInitializationData
.HwFindAdapter
= AhciHwFindAdapter
;
718 // adapter specific information
719 hwInitializationData
.NeedPhysicalAddresses
= TRUE
;
720 hwInitializationData
.TaggedQueuing
= TRUE
;
721 hwInitializationData
.AutoRequestSense
= TRUE
;
722 hwInitializationData
.MultipleRequestPerLu
= TRUE
;
724 hwInitializationData
.NumberOfAccessRanges
= 6;
725 hwInitializationData
.AdapterInterfaceType
= PCIBus
;
726 hwInitializationData
.MapBuffers
= STOR_MAP_NON_READ_WRITE_BUFFERS
;
728 // set required extension sizes
729 hwInitializationData
.SrbExtensionSize
= sizeof(AHCI_SRB_EXTENSION
);
730 hwInitializationData
.DeviceExtensionSize
= sizeof(AHCI_ADAPTER_EXTENSION
);
732 // register our hw init data
733 status
= StorPortInitialize(DriverObject
,
735 &hwInitializationData
,
738 DebugPrint("\tstatus:%x\n", status
);
740 }// -- DriverEntry();
746 * create ATA CFIS from Srb
748 * @param PortExtension
754 __in PAHCI_PORT_EXTENSION PortExtension
,
755 __in PAHCI_SRB_EXTENSION SrbExtension
758 DebugPrint("AhciATA_CFIS()\n");
760 }// -- AhciATA_CFIS();
763 * @name AhciATAPI_CFIS
766 * create ATAPI CFIS from Srb
768 * @param PortExtension
774 __in PAHCI_PORT_EXTENSION PortExtension
,
775 __in PAHCI_SRB_EXTENSION SrbExtension
778 DebugPrint("AhciATAPI_CFIS()\n");
780 }// -- AhciATAPI_CFIS();
783 * @name AhciBuild_PRDT
786 * Build PRDT for data transfer
788 * @param PortExtension
792 * Return number of entries in PRDT.
796 __in PAHCI_PORT_EXTENSION PortExtension
,
797 __in PAHCI_SRB_EXTENSION SrbExtension
801 PAHCI_COMMAND_TABLE cmdTable
;
802 PLOCAL_SCATTER_GATHER_LIST sgl
;
803 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
805 DebugPrint("AhciBuild_PRDT()\n");
807 sgl
= &SrbExtension
->Sgl
;
808 cmdTable
= (PAHCI_COMMAND_TABLE
)SrbExtension
;
809 AdapterExtension
= PortExtension
->AdapterExtension
;
811 NT_ASSERT(sgl
!= NULL
);
812 NT_ASSERT(sgl
->NumberOfElements
< MAXIMUM_AHCI_PRDT_ENTRIES
);
814 for (index
= 0; index
< sgl
->NumberOfElements
; index
++)
816 NT_ASSERT(sgl
->List
[index
].Length
<= MAXIMUM_TRANSFER_LENGTH
);
818 cmdTable
->PRDT
[index
].DBA
= sgl
->List
[index
].PhysicalAddress
.LowPart
;
819 if (IsAdapterCAPS64(AdapterExtension
->CAP
))
821 cmdTable
->PRDT
[index
].DBAU
= sgl
->List
[index
].PhysicalAddress
.HighPart
;
825 return sgl
->NumberOfElements
;
826 }// -- AhciBuild_PRDT();
829 * @name AhciProcessSrb
832 * Prepare Srb for IO processing
834 * @param PortExtension
841 __in PAHCI_PORT_EXTENSION PortExtension
,
842 __in PSCSI_REQUEST_BLOCK Srb
,
846 ULONG prdtlen
, sig
, length
;
847 PAHCI_SRB_EXTENSION SrbExtension
;
848 PAHCI_COMMAND_HEADER CommandHeader
;
849 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
850 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress
;
852 DebugPrint("AhciProcessSrb()\n");
854 NT_ASSERT(Srb
->PathId
== PortExtension
->PortNumber
);
856 SrbExtension
= GetSrbExtension(Srb
);
857 AdapterExtension
= PortExtension
->AdapterExtension
;
859 NT_ASSERT(SrbExtension
!= NULL
);
860 NT_ASSERT(SrbExtension
->AtaFunction
!= 0);
862 if ((SrbExtension
->AtaFunction
== ATA_FUNCTION_ATA_IDENTIFY
) &&
863 (SrbExtension
->CommandReg
== IDE_COMMAND_NOT_VALID
))
865 // Here we are safe to check SIG register
866 sig
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SIG
);
869 SrbExtension
->CommandReg
= IDE_COMMAND_IDENTIFY
;
873 SrbExtension
->CommandReg
= IDE_COMMAND_ATAPI_IDENTIFY
;
877 NT_ASSERT(SlotIndex
< AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
));
878 SrbExtension
->SlotIndex
= SlotIndex
;
880 // program the CFIS in the CommandTable
881 CommandHeader
= &PortExtension
->CommandList
[SlotIndex
];
883 if (IsAtaCommand(SrbExtension
->AtaFunction
))
885 AhciATA_CFIS(PortExtension
, SrbExtension
);
887 else if (IsAtapiCommand(SrbExtension
->AtaFunction
))
889 AhciATAPI_CFIS(PortExtension
, SrbExtension
);
893 if (IsDataTransferNeeded(SrbExtension
))
895 prdtlen
= AhciBuild_PRDT(PortExtension
, SrbExtension
);
896 NT_ASSERT(prdtlen
!= -1);
899 // Program the command header
900 CommandHeader
->DI
.PRDTL
= prdtlen
; // number of entries in PRD table
901 CommandHeader
->DI
.CFL
= 5;
902 CommandHeader
->DI
.W
= (SrbExtension
->Flags
& ATA_FLAGS_DATA_OUT
) ? 1 : 0;
903 CommandHeader
->DI
.P
= 0; // ATA Specifications says so
904 CommandHeader
->DI
.PMP
= 0; // Port Multiplier
906 // Reset -- Manual Configuation
907 CommandHeader
->DI
.R
= 0;
908 CommandHeader
->DI
.B
= 0;
909 CommandHeader
->DI
.C
= 0;
911 CommandHeader
->PRDBC
= 0;
913 CommandHeader
->Reserved
[0] = 0;
914 CommandHeader
->Reserved
[1] = 0;
915 CommandHeader
->Reserved
[2] = 0;
916 CommandHeader
->Reserved
[3] = 0;
918 // set CommandHeader CTBA
919 // I am really not sure if SrbExtension is 128 byte aligned or not
920 // Command FIS will not work if it is not so.
921 CommandTablePhysicalAddress
= StorPortGetPhysicalAddress(AdapterExtension
,
926 // command table alignment
927 NT_ASSERT((CommandTablePhysicalAddress
.LowPart
% 128) == 0);
929 CommandHeader
->CTBA0
= CommandTablePhysicalAddress
.LowPart
;
931 if (IsAdapterCAPS64(AdapterExtension
->CAP
))
933 CommandHeader
->CTBA_U0
= CommandTablePhysicalAddress
.HighPart
;
937 PortExtension
->OccupiedSlots
|= SlotIndex
;
939 }// -- AhciProcessSrb();
942 * @name AhciActivatePort
945 * Program Port and populate command list
947 * @param PortExtension
952 __in PAHCI_PORT_EXTENSION PortExtension
955 DebugPrint("AhciActivatePort()\n");
958 }// -- AhciActivatePort();
961 * @name AhciProcessIO
964 * Acquire Exclusive lock to port, populate pending commands to command List
965 * program controller's port to process new commands in command list.
967 * @param AdapterExtension
974 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
976 __in PSCSI_REQUEST_BLOCK Srb
979 STOR_LOCK_HANDLE lockhandle
;
980 PSCSI_REQUEST_BLOCK tmpSrb
;
981 PAHCI_PORT_EXTENSION PortExtension
;
982 ULONG commandSlotMask
, occupiedSlots
, slotIndex
, NCS
;
984 DebugPrint("AhciProcessIO()\n");
985 DebugPrint("\tPathId: %d\n", PathId
);
987 PortExtension
= &AdapterExtension
->PortExtension
[PathId
];
989 NT_ASSERT(PathId
< AdapterExtension
->PortCount
);
992 AddQueue(&PortExtension
->SrbQueue
, Srb
);
994 if (PortExtension
->IsActive
== FALSE
)
995 return; // we should wait for device to get active
997 AhciZeroMemory(&lockhandle
, sizeof(lockhandle
));
1000 StorPortAcquireSpinLock(AdapterExtension
, InterruptLock
, NULL
, &lockhandle
);
1002 occupiedSlots
= PortExtension
->OccupiedSlots
; // Busy command slots for given port
1003 NCS
= AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
);
1004 commandSlotMask
= (1 << NCS
) - 1; // available slots mask
1006 commandSlotMask
= (commandSlotMask
& ~occupiedSlots
);
1007 if(commandSlotMask
!= 0)
1009 // iterate over HBA port slots
1010 for (slotIndex
= 0; slotIndex
< NCS
; slotIndex
++)
1012 // find first free slot
1013 if ((commandSlotMask
& (1 << slotIndex
)) != 0)
1015 tmpSrb
= RemoveQueue(&PortExtension
->SrbQueue
);
1018 NT_ASSERT(tmpSrb
->PathId
== PathId
);
1019 AhciProcessSrb(PortExtension
, tmpSrb
, slotIndex
);
1034 AhciActivatePort(PortExtension
);
1037 StorPortReleaseSpinLock(AdapterExtension
, &lockhandle
);
1040 }// -- AhciProcessIO();
1043 * @name DeviceInquiryRequest
1046 * Tells wheather given port is implemented or not
1048 * @param AdapterExtension
1053 * return STOR status for DeviceInquiryRequest
1056 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
1059 DeviceInquiryRequest (
1060 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1061 __in PSCSI_REQUEST_BLOCK Srb
,
1066 ULONG DataBufferLength
;
1067 PAHCI_SRB_EXTENSION SrbExtension
;
1069 DebugPrint("DeviceInquiryRequest()\n");
1071 SrbExtension
= GetSrbExtension(Srb
);
1074 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
1075 if (Cdb
->CDB6INQUIRY3
.EnableVitalProductData
== 0)
1077 DebugPrint("\tEVPD Inquired\n");
1078 NT_ASSERT(SrbExtension
!= NULL
);
1080 SrbExtension
->AtaFunction
= ATA_FUNCTION_ATA_IDENTIFY
;
1081 SrbExtension
->CommandReg
= IDE_COMMAND_NOT_VALID
;
1085 DebugPrint("\tVPD Inquired\n");
1087 DataBuffer
= Srb
->DataBuffer
;
1088 DataBufferLength
= Srb
->DataTransferLength
;
1090 if (DataBuffer
== NULL
)
1092 return SRB_STATUS_INVALID_REQUEST
;
1095 AhciZeroMemory(DataBuffer
, DataBufferLength
);
1098 AhciProcessIO(AdapterExtension
, Srb
->PathId
, Srb
);
1099 return SRB_STATUS_SUCCESS
;
1100 }// -- DeviceInquiryRequest();
1103 * @name AhciAdapterReset
1107 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
1108 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
1109 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
1110 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
1111 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
1112 * the HBA reset has completed.
1113 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
1114 * a hung or locked state.
1116 * @param AdapterExtension
1119 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
1123 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
1126 ULONG ghc
, ticks
, ghcStatus
;
1127 PAHCI_MEMORY_REGISTERS abar
= NULL
;
1129 DebugPrint("AhciAdapterReset()\n");
1131 abar
= AdapterExtension
->ABAR_Address
;
1132 if (abar
== NULL
) // basic sanity
1137 // HR -- Very first bit (lowest significant)
1138 ghc
= AHCI_Global_HBA_CONTROL_HR
;
1139 StorPortWriteRegisterUlong(AdapterExtension
, &abar
->GHC
, ghc
);
1141 for (ticks
= 0; ticks
< 50; ++ticks
)
1143 ghcStatus
= StorPortReadRegisterUlong(AdapterExtension
, &abar
->GHC
);
1144 if ((ghcStatus
& AHCI_Global_HBA_CONTROL_HR
) == 0)
1148 StorPortStallExecution(20000);
1151 if (ticks
== 50)// 1 second
1153 DebugPrint("\tDevice Timeout\n");
1158 }// -- AhciAdapterReset();
1161 * @name AhciZeroMemory
1164 * Clear buffer by filling zeros
1173 __in ULONG BufferSize
1177 for (i
= 0; i
< BufferSize
; i
++)
1183 }// -- AhciZeroMemory();
1189 * Tells wheather given port is implemented or not
1191 * @param AdapterExtension
1195 * return TRUE if provided port is valid (implemented) or not
1200 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1204 NT_ASSERT(pathId
>= 0);
1206 if (pathId
>= AdapterExtension
->PortCount
)
1211 return AdapterExtension
->PortExtension
[pathId
].IsActive
;
1212 }// -- IsPortValid()
1224 * return TRUE if Srb is successfully added to Queue
1230 __inout PAHCI_QUEUE Queue
,
1234 NT_ASSERT(Queue
->Head
< MAXIMUM_QUEUE_BUFFER_SIZE
);
1235 NT_ASSERT(Queue
->Tail
< MAXIMUM_QUEUE_BUFFER_SIZE
);
1237 if (Queue
->Tail
== ((Queue
->Head
+ 1) % MAXIMUM_QUEUE_BUFFER_SIZE
))
1240 Queue
->Buffer
[Queue
->Head
++] = Srb
;
1241 Queue
->Head
%= MAXIMUM_QUEUE_BUFFER_SIZE
;
1250 * Remove and return Srb from Queue
1261 __inout PAHCI_QUEUE Queue
1266 NT_ASSERT(Queue
->Head
< MAXIMUM_QUEUE_BUFFER_SIZE
);
1267 NT_ASSERT(Queue
->Tail
< MAXIMUM_QUEUE_BUFFER_SIZE
);
1269 if (Queue
->Head
== Queue
->Tail
)
1272 Srb
= Queue
->Buffer
[Queue
->Tail
++];
1273 Queue
->Tail
%= MAXIMUM_QUEUE_BUFFER_SIZE
;
1276 }// -- RemoveQueue();
1279 * @name GetSrbExtension
1282 * GetSrbExtension from Srb make sure It is properly aligned
1287 * return SrbExtension
1293 __in PSCSI_REQUEST_BLOCK Srb
1297 ULONG_PTR SrbExtension
;
1299 SrbExtension
= Srb
->SrbExtension
;
1300 Offset
= SrbExtension
% 128;
1302 // CommandTable should be 128 byte aligned
1304 Offset
= 128 - Offset
;
1306 return (PAHCI_SRB_EXTENSION
)(SrbExtension
+ Offset
);
1307 }// -- PAHCI_SRB_EXTENSION();