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
27 ULONG mappedLength
, portNumber
;
28 PAHCI_MEMORY_REGISTERS abar
;
29 PAHCI_ADAPTER_EXTENSION adapterExtension
;
30 STOR_PHYSICAL_ADDRESS commandListPhysical
, receivedFISPhysical
;
32 DebugPrint("AhciPortInitialize()\n");
34 adapterExtension
= PortExtension
->AdapterExtension
;
35 abar
= adapterExtension
->ABAR_Address
;
36 portNumber
= PortExtension
->PortNumber
;
38 NT_ASSERT(abar
!= NULL
);
39 NT_ASSERT(portNumber
< adapterExtension
->PortCount
);
41 PortExtension
->Port
= &abar
->PortList
[portNumber
];
43 commandListPhysical
= StorPortGetPhysicalAddress(adapterExtension
,
45 PortExtension
->CommandList
,
48 if ((mappedLength
== 0) || ((commandListPhysical
.LowPart
% 1024) != 0))
50 DebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength
);
54 receivedFISPhysical
= StorPortGetPhysicalAddress(adapterExtension
,
56 PortExtension
->ReceivedFIS
,
59 if ((mappedLength
== 0) || ((receivedFISPhysical
.LowPart
% 256) != 0))
61 DebugPrint("\treceivedFISPhysical mappedLength:%d\n", mappedLength
);
65 // Ensure that the controller is not in the running state by reading and examining each
66 // implemented port’s PxCMD register. If PxCMD.ST, PxCMD.CR, PxCMD.FRE and
67 // PxCMD.FR are all cleared, the port is in an idle state. Otherwise, the port is not idle and
68 // should be placed in the idle state prior to manipulating HBA and port specific registers.
69 // System software places a port into the idle state by clearing PxCMD.ST and waiting for
70 // PxCMD.CR to return ‘0’ when read. Software should wait at least 500 milliseconds for
71 // this to occur. If PxCMD.FRE is set to ‘1’, software should clear it to ‘0’ and wait at least
72 // 500 milliseconds for PxCMD.FR to return ‘0’ when read. If PxCMD.CR or PxCMD.FR do
73 // not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recove
75 // TODO: Check if port is in idle state or not, if not then restart port
76 cmd
.Status
= StorPortReadRegisterUlong(adapterExtension
, &PortExtension
->Port
->CMD
);
77 if ((cmd
.FR
!= 0) || (cmd
.CR
!= 0) || (cmd
.FRE
!= 0) || (cmd
.ST
!= 0))
79 DebugPrint("\tPort is not idle: %x\n", cmd
);
82 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
83 // PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
84 // PxFB and PxFBU (if CAP.S64A is set to ‘1’)
85 // Note: Assuming 32bit support only
86 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->CLB
, commandListPhysical
.LowPart
);
87 if (IsAdapterCAPS64(adapterExtension
->CAP
))
89 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->CLBU
, commandListPhysical
.HighPart
);
92 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->FB
, receivedFISPhysical
.LowPart
);
93 if (IsAdapterCAPS64(adapterExtension
->CAP
))
95 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->FBU
, receivedFISPhysical
.HighPart
);
98 PortExtension
->IdentifyDeviceDataPhysicalAddress
= StorPortGetPhysicalAddress(adapterExtension
,
100 PortExtension
->IdentifyDeviceData
,
103 // set device power state flag to D0
104 PortExtension
->DevicePowerState
= StorPowerDeviceD0
;
106 // clear pending interrupts
107 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->SERR
, (ULONG
)~0);
108 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->IS
, (ULONG
)~0);
109 StorPortWriteRegisterUlong(adapterExtension
, adapterExtension
->IS
, (1 << PortExtension
->PortNumber
));
112 }// -- AhciPortInitialize();
115 * @name AhciAllocateResourceForAdapter
118 * Allocate memory from poll for required pointers
120 * @param AdapterExtension
124 * return TRUE if allocation was successful
127 AhciAllocateResourceForAdapter (
128 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
129 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
132 PCHAR nonCachedExtension
, tmp
;
133 ULONG index
, NCS
, AlignedNCS
;
134 ULONG portCount
, portImplemented
, nonCachedExtensionSize
;
136 DebugPrint("AhciAllocateResourceForAdapter()\n");
138 NCS
= AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
);
139 AlignedNCS
= ROUND_UP(NCS
, 8);
141 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
143 portImplemented
= AdapterExtension
->PortImplemented
;
145 NT_ASSERT(portImplemented
!= 0);
146 for (index
= MAXIMUM_AHCI_PORT_COUNT
- 1; index
> 0; index
--)
147 if ((portImplemented
& (1 << index
)) != 0)
150 portCount
= index
+ 1;
151 DebugPrint("\tPort Count: %d\n", portCount
);
153 AdapterExtension
->PortCount
= portCount
;
154 nonCachedExtensionSize
= sizeof(AHCI_COMMAND_HEADER
) * AlignedNCS
+ //should be 1K aligned
155 sizeof(AHCI_RECEIVED_FIS
) +
156 sizeof(IDENTIFY_DEVICE_DATA
);
158 // align nonCachedExtensionSize to 1024
159 nonCachedExtensionSize
= ROUND_UP(nonCachedExtensionSize
, 1024);
161 AdapterExtension
->NonCachedExtension
= StorPortGetUncachedExtension(AdapterExtension
,
163 nonCachedExtensionSize
* portCount
);
165 if (AdapterExtension
->NonCachedExtension
== NULL
)
167 DebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
171 nonCachedExtension
= AdapterExtension
->NonCachedExtension
;
172 AhciZeroMemory(nonCachedExtension
, nonCachedExtensionSize
* portCount
);
174 for (index
= 0; index
< portCount
; index
++)
176 AdapterExtension
->PortExtension
[index
].IsActive
= FALSE
;
177 if ((AdapterExtension
->PortImplemented
& (1 << index
)) != 0)
179 AdapterExtension
->PortExtension
[index
].PortNumber
= index
;
180 AdapterExtension
->PortExtension
[index
].IsActive
= TRUE
;
181 AdapterExtension
->PortExtension
[index
].AdapterExtension
= AdapterExtension
;
182 AdapterExtension
->PortExtension
[index
].CommandList
= (PAHCI_COMMAND_HEADER
)nonCachedExtension
;
184 tmp
= (PCHAR
)(nonCachedExtension
+ sizeof(AHCI_COMMAND_HEADER
) * AlignedNCS
);
186 AdapterExtension
->PortExtension
[index
].ReceivedFIS
= (PAHCI_RECEIVED_FIS
)tmp
;
187 AdapterExtension
->PortExtension
[index
].IdentifyDeviceData
= (PIDENTIFY_DEVICE_DATA
)(tmp
+ sizeof(AHCI_RECEIVED_FIS
));
188 nonCachedExtension
+= nonCachedExtensionSize
;
193 }// -- AhciAllocateResourceForAdapter();
196 * @name AhciStartPort
199 * Try to start the port device
201 * @param AdapterExtension
202 * @param PortExtension
207 __in PAHCI_PORT_EXTENSION PortExtension
212 AHCI_TASK_FILE_DATA tfd
;
213 AHCI_INTERRUPT_ENABLE ie
;
214 AHCI_SERIAL_ATA_STATUS ssts
;
215 AHCI_SERIAL_ATA_CONTROL sctl
;
216 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
218 DebugPrint("AhciStartPort()\n");
220 AdapterExtension
= PortExtension
->AdapterExtension
;
221 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
223 if ((cmd
.FR
== 1) && (cmd
.CR
== 1) && (cmd
.FRE
== 1) && (cmd
.ST
== 1))
230 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
, cmd
.Status
);
232 if (((cmd
.FR
== 1) && (cmd
.FRE
== 0)) ||
233 ((cmd
.CR
== 1) && (cmd
.ST
== 0)))
235 DebugPrint("\tCOMRESET\n");
239 // Software causes a port reset (COMRESET) by writing 1h to the PxSCTL.DET field to invoke a
240 // COMRESET on the interface and start a re-establishment of Phy layer communications. Software shall
241 // wait at least 1 millisecond before clearing PxSCTL.DET to 0h; this ensures that at least one COMRESET
242 // signal is sent over the interface. After clearing PxSCTL.DET to 0h, software should wait for
243 // communication to be re-established as indicated by PxSSTS.DET being set to 3h. Then software should
244 // write all 1s to the PxSERR register to clear any bits that were set as part of the port reset.
246 sctl
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SCTL
);
248 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SCTL
, sctl
.Status
);
250 StorPortStallExecution(1000);
252 sctl
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SCTL
);
254 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SCTL
, sctl
.Status
);
256 // Poll DET to verify if a device is attached to the port
260 StorPortStallExecution(1000);
261 ssts
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SSTS
);
272 ssts
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SSTS
);
280 DebugPrint("\tDET == %x Unsupported\n", ssts
.DET
);
284 NT_ASSERT(cmd
.ST
== 0);
286 // make sure FIS Recieve is enabled (cmd.FRE)
290 StorPortStallExecution(10000);
291 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
293 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
, cmd
.Status
);
296 while((cmd
.FR
!= 1) && (index
< 3));
300 // failed to start FIS DMA engine
301 // it can crash the driver later
305 // start port channel
308 NT_ASSERT(cmd
.FRE
== 1);
309 NT_ASSERT(cmd
.CR
== 0);
311 // why assert? well If we face such condition on DET = 0x3
312 // then we don't have port in idle state and hence before executing this part of code
313 // we must have restarted it.
314 tfd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->TFD
);
316 if ((tfd
.STS
.BSY
) || (tfd
.STS
.DRQ
))
318 DebugPrint("\tUnhandled Case BSY-DRQ\n");
321 // clear pending interrupts
322 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SERR
, (ULONG
)~0);
323 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IS
, (ULONG
)~0);
324 StorPortWriteRegisterUlong(AdapterExtension
, AdapterExtension
->IS
, (1 << PortExtension
->PortNumber
));
327 ie
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IE
);
348 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
351 StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IE
, ie
.Status
);
354 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
, cmd
.Status
);
355 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
359 DebugPrint("\tFailed to start Port\n");
370 DebugPrint("\tInvalid DET value: %x\n", ssts
.DET
);
372 }// -- AhciStartPort();
375 * @name AhciHwInitialize
378 * initializes the HBA and finds all devices that are of interest to the miniport driver.
380 * @param adapterExtension
383 * return TRUE if intialization was successful
387 __in PVOID AdapterExtension
391 PAHCI_PORT_EXTENSION PortExtension
;
392 PAHCI_ADAPTER_EXTENSION adapterExtension
;
394 DebugPrint("AhciHwInitialize()\n");
396 adapterExtension
= AdapterExtension
;
397 adapterExtension
->StateFlags
.MessagePerPort
= FALSE
;
399 // First check what type of interrupt/synchronization device is using
400 ghc
= StorPortReadRegisterUlong(adapterExtension
, &adapterExtension
->ABAR_Address
->GHC
);
402 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
403 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
404 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
405 // software has allocated the number of messages requested
406 if ((ghc
& AHCI_Global_HBA_CONTROL_MRSM
) == 0)
408 adapterExtension
->StateFlags
.MessagePerPort
= TRUE
;
409 DebugPrint("\tMultiple MSI based message not supported\n");
412 for (index
= 0; index
< adapterExtension
->PortCount
; index
++)
414 if ((adapterExtension
->PortImplemented
& (0x1 << index
)) != 0)
416 PortExtension
= &adapterExtension
->PortExtension
[index
];
417 PortExtension
->IsActive
= AhciStartPort(PortExtension
);
422 }// -- AhciHwInitialize();
425 * @name AhciCompleteIssuedSrb
428 * Complete issued Srbs
430 * @param PortExtension
434 AhciCompleteIssuedSrb (
435 __in PAHCI_PORT_EXTENSION PortExtension
,
436 __in ULONG CommandsToComplete
440 PSCSI_REQUEST_BLOCK Srb
;
441 PAHCI_SRB_EXTENSION SrbExtension
;
442 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
443 PAHCI_COMPLETION_ROUTINE CompletionRoutine
;
445 DebugPrint("AhciCompleteIssuedSrb()\n");
447 NT_ASSERT(CommandsToComplete
!= 0);
449 DebugPrint("\tCompleted Commands: %d\n", CommandsToComplete
);
451 AdapterExtension
= PortExtension
->AdapterExtension
;
452 NCS
= AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
);
454 for (i
= 0; i
< NCS
; i
++)
456 if (((1 << i
) & CommandsToComplete
) != 0)
458 Srb
= PortExtension
->Slot
[i
];
459 NT_ASSERT(Srb
!= NULL
);
461 if (Srb
->SrbStatus
== SRB_STATUS_PENDING
)
463 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
466 SrbExtension
= GetSrbExtension(Srb
);
467 CompletionRoutine
= SrbExtension
->CompletionRoutine
;
469 if (CompletionRoutine
!= NULL
)
471 // now it's completion routine responsibility to set SrbStatus
472 CompletionRoutine(AdapterExtension
, PortExtension
, Srb
);
476 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
477 StorPortNotification(RequestComplete
, AdapterExtension
, Srb
);
483 }// -- AhciCompleteIssuedSrb();
486 * @name AhciInterruptHandler
489 * Interrupt Handler for PortExtension
491 * @param PortExtension
495 AhciInterruptHandler (
496 __in PAHCI_PORT_EXTENSION PortExtension
499 ULONG is
, ci
, sact
, outstanding
;
500 AHCI_INTERRUPT_STATUS PxIS
;
501 AHCI_INTERRUPT_STATUS PxISMasked
;
502 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
504 DebugPrint("AhciInterruptHandler()\n");
505 DebugPrint("\tPort Number: %d\n", PortExtension
->PortNumber
);
507 AdapterExtension
= PortExtension
->AdapterExtension
;
508 NT_ASSERT(IsPortValid(AdapterExtension
, PortExtension
->PortNumber
));
511 // 1. Software determines the cause of the interrupt by reading the PxIS register.
512 // It is possible for multiple bits to be set
513 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
514 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
515 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
516 // the list of commands previously issued by software that are still outstanding.
517 // If executing native queued commands, software reads the PxSACT register and compares the current
518 // value to the list of commands previously issued by software.
519 // Software completes with success any outstanding command whose corresponding bit has been cleared in
520 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
521 // to determine commands that have completed, not to determine which commands have previously been issued.
522 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
523 PxISMasked
.Status
= 0;
524 PxIS
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IS
);
528 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
529 if (PxIS
.HBFS
|| PxIS
.HBDS
|| PxIS
.IFS
|| PxIS
.TFES
)
531 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
532 // any native command queuing commands. To recover, the port must be restarted
533 // To detect an error that requires software recovery actions to be performed,
534 // software should check whether any of the following status bits are set on an interrupt:
535 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
536 // software should perform the appropriate error recovery actions based on whether
537 // non-queued commands were being issued or native command queuing commands were being issued.
539 DebugPrint("\tFatal Error: %x\n", PxIS
.Status
);
542 // Normal Command Completion
544 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
545 PxISMasked
.DHRS
= PxIS
.DHRS
;
546 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
547 PxISMasked
.PSS
= PxIS
.PSS
;
548 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
549 PxISMasked
.DSS
= PxIS
.DSS
;
550 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
551 PxISMasked
.SDBS
= PxIS
.SDBS
;
552 // A PRD with the ‘I’ bit set has transferred all of its data.
553 PxISMasked
.DPS
= PxIS
.DPS
;
555 if (PxISMasked
.Status
!= 0)
557 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IS
, PxISMasked
.Status
);
561 // Clear port interrupt
562 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
563 is
= (1 << PortExtension
->PortNumber
);
564 StorPortWriteRegisterUlong(AdapterExtension
, AdapterExtension
->IS
, is
);
566 ci
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CI
);
567 sact
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SACT
);
569 outstanding
= ci
| sact
; // NOTE: Including both non-NCQ and NCQ based commands
570 if ((PortExtension
->CommandIssuedSlots
& (~outstanding
)) != 0)
572 AhciCompleteIssuedSrb(PortExtension
, (PortExtension
->CommandIssuedSlots
& (~outstanding
)));
573 PortExtension
->CommandIssuedSlots
&= outstanding
;
577 }// -- AhciInterruptHandler();
580 * @name AhciHwInterrupt
583 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
585 * @param AdapterExtension
588 * return TRUE Indicates that an interrupt was pending on adapter.
589 * return FALSE Indicates the interrupt was not ours.
593 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
596 ULONG portPending
, nextPort
, i
, portCount
;
598 DebugPrint("AhciHwInterrupt()\n");
600 if (AdapterExtension
->StateFlags
.Removed
)
605 portPending
= StorPortReadRegisterUlong(AdapterExtension
, AdapterExtension
->IS
);
606 // we process interrupt for implemented ports only
607 portCount
= AdapterExtension
->PortCount
;
608 DebugPrint("\tPortPending: %d\n", portPending
);
609 portPending
= portPending
& AdapterExtension
->PortImplemented
;
611 if (portPending
== 0)
616 for (i
= 1; i
<= portCount
; i
++)
618 nextPort
= (AdapterExtension
->LastInterruptPort
+ i
) % portCount
;
620 if ((portPending
& (0x1 << nextPort
)) == 0)
623 NT_ASSERT(IsPortValid(AdapterExtension
, nextPort
));
625 if (nextPort
== AdapterExtension
->LastInterruptPort
)
630 if (AdapterExtension
->PortExtension
[nextPort
].IsActive
== FALSE
)
635 // we can assign this interrupt to this port
636 AdapterExtension
->LastInterruptPort
= nextPort
;
637 AhciInterruptHandler(&AdapterExtension
->PortExtension
[nextPort
]);
639 // interrupt belongs to this device
640 // should always return TRUE
644 DebugPrint("\tSomething went wrong");
646 }// -- AhciHwInterrupt();
649 * @name AhciHwStartIo
652 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
654 * @param adapterExtension
658 * return TRUE if the request was accepted
659 * return FALSE if the request must be submitted later
663 __in PVOID AdapterExtension
,
664 __in PSCSI_REQUEST_BLOCK Srb
667 UCHAR function
, pathId
;
668 PAHCI_ADAPTER_EXTENSION adapterExtension
;
670 DebugPrint("AhciHwStartIo()\n");
672 pathId
= Srb
->PathId
;
673 function
= Srb
->Function
;
674 adapterExtension
= AdapterExtension
;
676 if (!IsPortValid(adapterExtension
, pathId
))
678 Srb
->SrbStatus
= SRB_STATUS_NO_DEVICE
;
679 StorPortNotification(RequestComplete
, adapterExtension
, Srb
);
683 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
684 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
685 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
686 if (function
== SRB_FUNCTION_PNP
)
688 PSCSI_PNP_REQUEST_BLOCK pnpRequest
;
690 pnpRequest
= (PSCSI_PNP_REQUEST_BLOCK
)Srb
;
691 if ((pnpRequest
->SrbPnPFlags
& SRB_PNP_FLAGS_ADAPTER_REQUEST
) != 0)
693 if ((pnpRequest
->PnPAction
== StorRemoveDevice
) ||
694 (pnpRequest
->PnPAction
== StorSurpriseRemoval
))
696 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
697 adapterExtension
->StateFlags
.Removed
= 1;
698 DebugPrint("\tAdapter removed\n");
700 else if (pnpRequest
->PnPAction
== StorStopDevice
)
702 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
703 DebugPrint("\tRequested to Stop the adapter\n");
707 Srb
->SrbStatus
= SRB_STATUS_INVALID_REQUEST
;
710 StorPortNotification(RequestComplete
, adapterExtension
, Srb
);
715 if (function
== SRB_FUNCTION_EXECUTE_SCSI
)
717 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
718 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
719 // routine does the following:
721 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
722 // logical unit, and/or SRB extensions
723 // For example, a miniport driver might set up a logical unit extension with pointers
724 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
725 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
726 // carried out on the HBA.
728 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
729 // for the requested operation
730 // For a device I/O operation, such an internal routine generally selects the target device
731 // and sends the CDB over the bus to the target logical unit.
732 if (Srb
->CdbLength
> 0)
734 PCDB cdb
= (PCDB
)&Srb
->Cdb
;
735 if (cdb
->CDB10
.OperationCode
== SCSIOP_INQUIRY
)
737 Srb
->SrbStatus
= DeviceInquiryRequest(adapterExtension
, Srb
, cdb
);
741 Srb
->SrbStatus
= SRB_STATUS_NO_DEVICE
;
746 Srb
->SrbStatus
= SRB_STATUS_BAD_FUNCTION
;
749 StorPortNotification(RequestComplete
, adapterExtension
, Srb
);
753 DebugPrint("\tUnknown function code recieved: %x\n", function
);
754 Srb
->SrbStatus
= SRB_STATUS_BAD_FUNCTION
;
755 StorPortNotification(RequestComplete
, adapterExtension
, Srb
);
757 }// -- AhciHwStartIo();
760 * @name AhciHwResetBus
763 * The HwStorResetBus routine is called by the port driver to clear error conditions.
765 * @param adapterExtension
769 * return TRUE if bus was successfully reset
773 __in PVOID AdapterExtension
,
777 STOR_LOCK_HANDLE lockhandle
;
778 PAHCI_ADAPTER_EXTENSION adapterExtension
;
780 DebugPrint("AhciHwResetBus()\n");
782 adapterExtension
= AdapterExtension
;
784 if (IsPortValid(AdapterExtension
, PathId
))
786 AhciZeroMemory(&lockhandle
, sizeof(lockhandle
));
789 StorPortAcquireSpinLock(AdapterExtension
, InterruptLock
, NULL
, &lockhandle
);
791 // TODO: Perform port reset
794 StorPortReleaseSpinLock(AdapterExtension
, &lockhandle
);
798 }// -- AhciHwResetBus();
801 * @name AhciHwFindAdapter
804 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
805 * HBA is supported and, if it is, to return configuration information about that adapter.
807 * 10.1 Platform Communication
808 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
810 * @param DeviceExtension
812 * @param BusInformation
813 * @param ArgumentString
819 * 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.
822 * 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.
824 * SP_RETURN_BAD_CONFIG
825 * Indicates that the supplied configuration information was invalid for the adapter.
827 * SP_RETURN_NOT_FOUND
828 * Indicates that no supported HBA was found for the supplied configuration information.
830 * @remarks Called by Storport.
834 __in PVOID AdapterExtension
,
835 __in PVOID HwContext
,
836 __in PVOID BusInformation
,
837 __in PVOID ArgumentString
,
838 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo
,
839 __in PBOOLEAN Reserved3
842 ULONG ghc
, index
, pci_cfg_len
;
843 UCHAR pci_cfg_buf
[sizeof(PCI_COMMON_CONFIG
)];
844 PACCESS_RANGE accessRange
;
846 PAHCI_MEMORY_REGISTERS abar
;
847 PPCI_COMMON_CONFIG pciConfigData
;
848 PAHCI_ADAPTER_EXTENSION adapterExtension
;
850 DebugPrint("AhciHwFindAdapter()\n");
852 UNREFERENCED_PARAMETER(HwContext
);
853 UNREFERENCED_PARAMETER(BusInformation
);
854 UNREFERENCED_PARAMETER(ArgumentString
);
855 UNREFERENCED_PARAMETER(Reserved3
);
857 adapterExtension
= AdapterExtension
;
858 adapterExtension
->SlotNumber
= ConfigInfo
->SlotNumber
;
859 adapterExtension
->SystemIoBusNumber
= ConfigInfo
->SystemIoBusNumber
;
861 // get PCI configuration header
862 pci_cfg_len
= StorPortGetBusData(
865 adapterExtension
->SystemIoBusNumber
,
866 adapterExtension
->SlotNumber
,
868 sizeof(PCI_COMMON_CONFIG
));
870 if (pci_cfg_len
!= sizeof(PCI_COMMON_CONFIG
))
872 DebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG
), pci_cfg_len
);
873 return SP_RETURN_ERROR
;//Not a valid device at the given bus number
876 pciConfigData
= pci_cfg_buf
;
877 adapterExtension
->VendorID
= pciConfigData
->VendorID
;
878 adapterExtension
->DeviceID
= pciConfigData
->DeviceID
;
879 adapterExtension
->RevisionID
= pciConfigData
->RevisionID
;
880 // 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).
881 adapterExtension
->AhciBaseAddress
= pciConfigData
->u
.type0
.BaseAddresses
[5] & (0xFFFFFFF0);
883 DebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension
->VendorID
,
884 adapterExtension
->DeviceID
,
885 adapterExtension
->RevisionID
);
889 if (ConfigInfo
->NumberOfAccessRanges
> 0)
891 accessRange
= *(ConfigInfo
->AccessRanges
);
892 for (index
= 0; index
< ConfigInfo
->NumberOfAccessRanges
; index
++)
894 if (accessRange
[index
].RangeStart
.QuadPart
== adapterExtension
->AhciBaseAddress
)
896 abar
= StorPortGetDeviceBase(adapterExtension
,
897 ConfigInfo
->AdapterInterfaceType
,
898 ConfigInfo
->SystemIoBusNumber
,
899 accessRange
[index
].RangeStart
,
900 accessRange
[index
].RangeLength
,
901 !accessRange
[index
].RangeInMemory
);
909 DebugPrint("\tabar == NULL\n");
910 return SP_RETURN_ERROR
; // corrupted information supplied
913 adapterExtension
->ABAR_Address
= abar
;
914 adapterExtension
->CAP
= StorPortReadRegisterUlong(adapterExtension
, &abar
->CAP
);
915 adapterExtension
->CAP2
= StorPortReadRegisterUlong(adapterExtension
, &abar
->CAP2
);
916 adapterExtension
->Version
= StorPortReadRegisterUlong(adapterExtension
, &abar
->VS
);
917 adapterExtension
->LastInterruptPort
= -1;
920 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
921 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
922 ghc
= StorPortReadRegisterUlong(adapterExtension
, &abar
->GHC
);
923 // AE := Highest Significant bit of GHC
924 if ((ghc
& AHCI_Global_HBA_CONTROL_AE
) != 0)// Hmm, controller was already in power state
926 // reset controller to have it in known state
927 DebugPrint("\tAE Already set, Reset()\n");
928 if (!AhciAdapterReset(adapterExtension
))
930 DebugPrint("\tReset Failed!\n");
931 return SP_RETURN_ERROR
;// reset failed
935 ghc
= AHCI_Global_HBA_CONTROL_AE
;// only AE=1
936 // tell the controller that we know about AHCI
937 StorPortWriteRegisterUlong(adapterExtension
, &abar
->GHC
, ghc
);
939 adapterExtension
->IS
= &abar
->IS
;
940 adapterExtension
->PortImplemented
= StorPortReadRegisterUlong(adapterExtension
, &abar
->PI
);
942 if (adapterExtension
->PortImplemented
== 0)
944 DebugPrint("\tadapterExtension->PortImplemented == 0\n");
945 return SP_RETURN_ERROR
;
948 ConfigInfo
->MaximumTransferLength
= MAXIMUM_TRANSFER_LENGTH
;//128 KB
949 ConfigInfo
->NumberOfPhysicalBreaks
= 0x21;
950 ConfigInfo
->MaximumNumberOfTargets
= 1;
951 ConfigInfo
->MaximumNumberOfLogicalUnits
= 1;
952 ConfigInfo
->ResetTargetSupported
= TRUE
;
953 ConfigInfo
->NumberOfBuses
= MAXIMUM_AHCI_PORT_COUNT
;
954 ConfigInfo
->SynchronizationModel
= StorSynchronizeFullDuplex
;
955 ConfigInfo
->ScatterGather
= TRUE
;
957 // allocate necessary resource for each port
958 if (!AhciAllocateResourceForAdapter(adapterExtension
, ConfigInfo
))
960 DebugPrint("\tAhciAllocateResourceForAdapter() == FALSE\n");
961 return SP_RETURN_ERROR
;
964 for (index
= 0; index
< adapterExtension
->PortCount
; index
++)
966 if ((adapterExtension
->PortImplemented
& (0x1 << index
)) != 0)
967 AhciPortInitialize(&adapterExtension
->PortExtension
[index
]);
970 // Turn IE -- Interrupt Enabled
971 ghc
= StorPortReadRegisterUlong(adapterExtension
, &abar
->GHC
);
972 ghc
|= AHCI_Global_HBA_CONTROL_IE
;
973 StorPortWriteRegisterUlong(adapterExtension
, &abar
->GHC
, ghc
);
975 return SP_RETURN_FOUND
;
976 }// -- AhciHwFindAdapter();
982 * Initial Entrypoint for storahci miniport driver
984 * @param DriverObject
985 * @param RegistryPath
988 * NT_STATUS in case of driver loaded successfully.
992 __in PVOID DriverObject
,
993 __in PVOID RegistryPath
997 HW_INITIALIZATION_DATA hwInitializationData
;
999 DebugPrint("Storahci Loaded\n");
1001 // initialize the hardware data structure
1002 AhciZeroMemory(&hwInitializationData
, sizeof(HW_INITIALIZATION_DATA
));
1004 // set size of hardware initialization structure
1005 hwInitializationData
.HwInitializationDataSize
= sizeof(HW_INITIALIZATION_DATA
);
1007 // identity required miniport entry point routines
1008 hwInitializationData
.HwStartIo
= AhciHwStartIo
;
1009 hwInitializationData
.HwResetBus
= AhciHwResetBus
;
1010 hwInitializationData
.HwInterrupt
= AhciHwInterrupt
;
1011 hwInitializationData
.HwInitialize
= AhciHwInitialize
;
1012 hwInitializationData
.HwFindAdapter
= AhciHwFindAdapter
;
1014 // adapter specific information
1015 hwInitializationData
.NeedPhysicalAddresses
= TRUE
;
1016 hwInitializationData
.TaggedQueuing
= TRUE
;
1017 hwInitializationData
.AutoRequestSense
= TRUE
;
1018 hwInitializationData
.MultipleRequestPerLu
= TRUE
;
1020 hwInitializationData
.NumberOfAccessRanges
= 6;
1021 hwInitializationData
.AdapterInterfaceType
= PCIBus
;
1022 hwInitializationData
.MapBuffers
= STOR_MAP_NON_READ_WRITE_BUFFERS
;
1024 // set required extension sizes
1025 hwInitializationData
.SrbExtensionSize
= sizeof(AHCI_SRB_EXTENSION
);
1026 hwInitializationData
.DeviceExtensionSize
= sizeof(AHCI_ADAPTER_EXTENSION
);
1028 // register our hw init data
1029 status
= StorPortInitialize(DriverObject
,
1031 &hwInitializationData
,
1034 DebugPrint("\tstatus: %x\n", status
);
1036 }// -- DriverEntry();
1039 * @name AhciATA_CFIS
1042 * create ATA CFIS from Srb
1044 * @param PortExtension
1050 __in PAHCI_PORT_EXTENSION PortExtension
,
1051 __in PAHCI_SRB_EXTENSION SrbExtension
1054 PAHCI_COMMAND_TABLE cmdTable
;
1056 UNREFERENCED_PARAMETER(PortExtension
);
1058 DebugPrint("AhciATA_CFIS()\n");
1060 cmdTable
= (PAHCI_COMMAND_TABLE
)SrbExtension
;
1062 NT_ASSERT(sizeof(cmdTable
->CFIS
) == 64);
1064 AhciZeroMemory(&cmdTable
->CFIS
, sizeof(cmdTable
->CFIS
));
1066 cmdTable
->CFIS
[AHCI_ATA_CFIS_FisType
] = 0x27; // FIS Type
1067 cmdTable
->CFIS
[AHCI_ATA_CFIS_PMPort_C
] = (1 << 7); // PM Port & C
1068 cmdTable
->CFIS
[AHCI_ATA_CFIS_CommandReg
] = SrbExtension
->CommandReg
;
1070 cmdTable
->CFIS
[AHCI_ATA_CFIS_FeaturesLow
] = SrbExtension
->FeaturesLow
;
1071 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA0
] = SrbExtension
->LBA0
;
1072 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA1
] = SrbExtension
->LBA1
;
1073 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA2
] = SrbExtension
->LBA2
;
1074 cmdTable
->CFIS
[AHCI_ATA_CFIS_Device
] = SrbExtension
->Device
;
1075 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA3
] = SrbExtension
->LBA3
;
1076 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA4
] = SrbExtension
->LBA4
;
1077 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA5
] = SrbExtension
->LBA5
;
1078 cmdTable
->CFIS
[AHCI_ATA_CFIS_FeaturesHigh
] = SrbExtension
->FeaturesHigh
;
1079 cmdTable
->CFIS
[AHCI_ATA_CFIS_SectorCountLow
] = SrbExtension
->SectorCountLow
;
1080 cmdTable
->CFIS
[AHCI_ATA_CFIS_SectorCountHigh
] = SrbExtension
->SectorCountHigh
;
1083 }// -- AhciATA_CFIS();
1086 * @name AhciATAPI_CFIS
1089 * create ATAPI CFIS from Srb
1091 * @param PortExtension
1097 __in PAHCI_PORT_EXTENSION PortExtension
,
1098 __in PAHCI_SRB_EXTENSION SrbExtension
1101 UNREFERENCED_PARAMETER(PortExtension
);
1102 UNREFERENCED_PARAMETER(SrbExtension
);
1104 DebugPrint("AhciATAPI_CFIS()\n");
1107 }// -- AhciATAPI_CFIS();
1110 * @name AhciBuild_PRDT
1113 * Build PRDT for data transfer
1115 * @param PortExtension
1119 * Return number of entries in PRDT.
1123 __in PAHCI_PORT_EXTENSION PortExtension
,
1124 __in PAHCI_SRB_EXTENSION SrbExtension
1128 PAHCI_COMMAND_TABLE cmdTable
;
1129 PLOCAL_SCATTER_GATHER_LIST sgl
;
1130 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
1132 DebugPrint("AhciBuild_PRDT()\n");
1134 sgl
= &SrbExtension
->Sgl
;
1135 cmdTable
= (PAHCI_COMMAND_TABLE
)SrbExtension
;
1136 AdapterExtension
= PortExtension
->AdapterExtension
;
1138 NT_ASSERT(sgl
!= NULL
);
1139 NT_ASSERT(sgl
->NumberOfElements
< MAXIMUM_AHCI_PRDT_ENTRIES
);
1141 for (index
= 0; index
< sgl
->NumberOfElements
; index
++)
1143 NT_ASSERT(sgl
->List
[index
].Length
<= MAXIMUM_TRANSFER_LENGTH
);
1145 cmdTable
->PRDT
[index
].DBA
= sgl
->List
[index
].PhysicalAddress
.LowPart
;
1146 if (IsAdapterCAPS64(AdapterExtension
->CAP
))
1148 cmdTable
->PRDT
[index
].DBAU
= sgl
->List
[index
].PhysicalAddress
.HighPart
;
1152 return sgl
->NumberOfElements
;
1153 }// -- AhciBuild_PRDT();
1156 * @name AhciProcessSrb
1159 * Prepare Srb for IO processing
1161 * @param PortExtension
1168 __in PAHCI_PORT_EXTENSION PortExtension
,
1169 __in PSCSI_REQUEST_BLOCK Srb
,
1170 __in ULONG SlotIndex
1173 ULONG prdtlen
, sig
, length
;
1174 PAHCI_SRB_EXTENSION SrbExtension
;
1175 PAHCI_COMMAND_HEADER CommandHeader
;
1176 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
1177 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress
;
1179 DebugPrint("AhciProcessSrb()\n");
1181 NT_ASSERT(Srb
->PathId
== PortExtension
->PortNumber
);
1183 SrbExtension
= GetSrbExtension(Srb
);
1184 AdapterExtension
= PortExtension
->AdapterExtension
;
1186 NT_ASSERT(SrbExtension
!= NULL
);
1187 NT_ASSERT(SrbExtension
->AtaFunction
!= 0);
1189 if ((SrbExtension
->AtaFunction
== ATA_FUNCTION_ATA_IDENTIFY
) &&
1190 (SrbExtension
->CommandReg
== IDE_COMMAND_NOT_VALID
))
1192 // Here we are safe to check SIG register
1193 sig
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SIG
);
1196 SrbExtension
->CommandReg
= IDE_COMMAND_IDENTIFY
;
1200 SrbExtension
->CommandReg
= IDE_COMMAND_ATAPI_IDENTIFY
;
1204 NT_ASSERT(SlotIndex
< AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
));
1205 SrbExtension
->SlotIndex
= SlotIndex
;
1207 // program the CFIS in the CommandTable
1208 CommandHeader
= &PortExtension
->CommandList
[SlotIndex
];
1210 if (IsAtaCommand(SrbExtension
->AtaFunction
))
1212 AhciATA_CFIS(PortExtension
, SrbExtension
);
1214 else if (IsAtapiCommand(SrbExtension
->AtaFunction
))
1216 AhciATAPI_CFIS(PortExtension
, SrbExtension
);
1220 if (IsDataTransferNeeded(SrbExtension
))
1222 prdtlen
= AhciBuild_PRDT(PortExtension
, SrbExtension
);
1223 NT_ASSERT(prdtlen
!= -1);
1226 // Program the command header
1227 CommandHeader
->DI
.PRDTL
= prdtlen
; // number of entries in PRD table
1228 CommandHeader
->DI
.CFL
= 5;
1229 CommandHeader
->DI
.W
= (SrbExtension
->Flags
& ATA_FLAGS_DATA_OUT
) ? 1 : 0;
1230 CommandHeader
->DI
.P
= 0; // ATA Specifications says so
1231 CommandHeader
->DI
.PMP
= 0; // Port Multiplier
1233 // Reset -- Manual Configuation
1234 CommandHeader
->DI
.R
= 0;
1235 CommandHeader
->DI
.B
= 0;
1236 CommandHeader
->DI
.C
= 0;
1238 CommandHeader
->PRDBC
= 0;
1240 CommandHeader
->Reserved
[0] = 0;
1241 CommandHeader
->Reserved
[1] = 0;
1242 CommandHeader
->Reserved
[2] = 0;
1243 CommandHeader
->Reserved
[3] = 0;
1245 // set CommandHeader CTBA
1246 // I am really not sure if SrbExtension is 128 byte aligned or not
1247 // Command FIS will not work if it is not so.
1248 CommandTablePhysicalAddress
= StorPortGetPhysicalAddress(AdapterExtension
,
1253 // command table alignment
1254 NT_ASSERT((CommandTablePhysicalAddress
.LowPart
% 128) == 0);
1256 CommandHeader
->CTBA0
= CommandTablePhysicalAddress
.LowPart
;
1258 if (IsAdapterCAPS64(AdapterExtension
->CAP
))
1260 CommandHeader
->CTBA_U0
= CommandTablePhysicalAddress
.HighPart
;
1264 PortExtension
->Slot
[SlotIndex
] = Srb
;
1265 PortExtension
->QueueSlots
|= 1 << SlotIndex
;
1267 }// -- AhciProcessSrb();
1270 * @name AhciActivatePort
1273 * Program Port and populate command list
1275 * @param PortExtension
1280 __in PAHCI_PORT_EXTENSION PortExtension
1284 ULONG QueueSlots
, slotToActivate
, tmp
;
1285 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
1287 DebugPrint("AhciActivatePort()\n");
1289 AdapterExtension
= PortExtension
->AdapterExtension
;
1290 QueueSlots
= PortExtension
->QueueSlots
;
1292 if (QueueSlots
== 0)
1296 // Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’
1297 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
1299 if (cmd
.ST
== 0) // PxCMD.ST == 0
1304 // get the lowest set bit
1305 tmp
= QueueSlots
& (QueueSlots
- 1);
1308 slotToActivate
= QueueSlots
;
1310 slotToActivate
= (QueueSlots
& (~tmp
));
1312 // mark that bit off in QueueSlots
1313 // so we can know we it is really needed to activate port or not
1314 PortExtension
->QueueSlots
&= ~slotToActivate
;
1315 // mark this CommandIssuedSlots
1316 // to validate in completeIssuedCommand
1317 PortExtension
->CommandIssuedSlots
|= slotToActivate
;
1319 DebugPrint("\tslotToActivate: %d\n", slotToActivate
);
1321 // tell the HBA to issue this Command Slot to the given port
1322 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CI
, slotToActivate
);
1325 }// -- AhciActivatePort();
1328 * @name AhciProcessIO
1331 * Acquire Exclusive lock to port, populate pending commands to command List
1332 * program controller's port to process new commands in command list.
1334 * @param AdapterExtension
1341 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1343 __in PSCSI_REQUEST_BLOCK Srb
1346 STOR_LOCK_HANDLE lockhandle
;
1347 PSCSI_REQUEST_BLOCK tmpSrb
;
1348 PAHCI_PORT_EXTENSION PortExtension
;
1349 ULONG commandSlotMask
, occupiedSlots
, slotIndex
, NCS
;
1351 DebugPrint("AhciProcessIO()\n");
1352 DebugPrint("\tPathId: %d\n", PathId
);
1354 PortExtension
= &AdapterExtension
->PortExtension
[PathId
];
1356 NT_ASSERT(PathId
< AdapterExtension
->PortCount
);
1359 AddQueue(&PortExtension
->SrbQueue
, Srb
);
1361 if (PortExtension
->IsActive
== FALSE
)
1362 return; // we should wait for device to get active
1364 AhciZeroMemory(&lockhandle
, sizeof(lockhandle
));
1367 StorPortAcquireSpinLock(AdapterExtension
, InterruptLock
, NULL
, &lockhandle
);
1369 occupiedSlots
= (PortExtension
->QueueSlots
| PortExtension
->CommandIssuedSlots
); // Busy command slots for given port
1370 NCS
= AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
);
1371 commandSlotMask
= (1 << NCS
) - 1; // available slots mask
1373 commandSlotMask
= (commandSlotMask
& ~occupiedSlots
);
1374 if(commandSlotMask
!= 0)
1376 // iterate over HBA port slots
1377 for (slotIndex
= 0; slotIndex
< NCS
; slotIndex
++)
1379 // find first free slot
1380 if ((commandSlotMask
& (1 << slotIndex
)) != 0)
1382 tmpSrb
= RemoveQueue(&PortExtension
->SrbQueue
);
1385 NT_ASSERT(tmpSrb
->PathId
== PathId
);
1386 AhciProcessSrb(PortExtension
, tmpSrb
, slotIndex
);
1401 AhciActivatePort(PortExtension
);
1404 StorPortReleaseSpinLock(AdapterExtension
, &lockhandle
);
1407 }// -- AhciProcessIO();
1410 * @name InquiryCompletion
1413 * InquiryCompletion routine should be called after device signals
1414 * for device inquiry request is completed (through interrupt)
1416 * @param PortExtension
1422 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1423 __in PAHCI_PORT_EXTENSION PortExtension
,
1424 __in PSCSI_REQUEST_BLOCK Srb
1428 PAHCI_SRB_EXTENSION SrbExtension
;
1430 DebugPrint("InquiryCompletion()\n");
1432 NT_ASSERT(PortExtension
!= NULL
);
1433 NT_ASSERT(Srb
!= NULL
);
1435 SrbStatus
= Srb
->SrbStatus
;
1436 SrbExtension
= GetSrbExtension(Srb
);
1438 if (SrbStatus
== SRB_STATUS_SUCCESS
)
1440 if (SrbExtension
->CommandReg
== IDE_COMMAND_IDENTIFY
)
1442 DebugPrint("Device: ATA\n");
1443 AdapterExtension
->DeviceParams
.DeviceType
= AHCI_DEVICE_TYPE_ATA
;
1447 DebugPrint("Device: ATAPI\n");
1448 AdapterExtension
->DeviceParams
.DeviceType
= AHCI_DEVICE_TYPE_ATAPI
;
1450 // TODO: Set Device Paramters
1452 else if (SrbStatus
== SRB_STATUS_NO_DEVICE
)
1454 DebugPrint("Device: No Device\n");
1455 AdapterExtension
->DeviceParams
.DeviceType
= AHCI_DEVICE_TYPE_NODEVICE
;
1463 }// -- InquiryCompletion();
1466 * @name DeviceInquiryRequest
1469 * Tells wheather given port is implemented or not
1471 * @param AdapterExtension
1476 * return STOR status for DeviceInquiryRequest
1479 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
1482 DeviceInquiryRequest (
1483 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1484 __in PSCSI_REQUEST_BLOCK Srb
,
1489 ULONG DataBufferLength
;
1490 PAHCI_PORT_EXTENSION PortExtension
;
1491 PAHCI_SRB_EXTENSION SrbExtension
;
1493 DebugPrint("DeviceInquiryRequest()\n");
1495 NT_ASSERT(IsPortValid(AdapterExtension
, Srb
->PathId
));
1497 SrbExtension
= GetSrbExtension(Srb
);
1498 PortExtension
= &AdapterExtension
->PortExtension
[Srb
->PathId
];
1501 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
1502 if (Cdb
->CDB6INQUIRY3
.EnableVitalProductData
== 0)
1504 DebugPrint("\tEVPD Inquired\n");
1505 NT_ASSERT(SrbExtension
!= NULL
);
1507 SrbExtension
->AtaFunction
= ATA_FUNCTION_ATA_IDENTIFY
;
1508 SrbExtension
->Flags
|= ATA_FLAGS_DATA_IN
;
1509 SrbExtension
->CompletionRoutine
= InquiryCompletion
;
1510 SrbExtension
->CommandReg
= IDE_COMMAND_NOT_VALID
;
1512 // TODO: Should use AhciZeroMemory
1513 SrbExtension
->FeaturesLow
= 0;
1514 SrbExtension
->LBA0
= 0;
1515 SrbExtension
->LBA1
= 0;
1516 SrbExtension
->LBA2
= 0;
1517 SrbExtension
->Device
= 0;
1518 SrbExtension
->LBA3
= 0;
1519 SrbExtension
->LBA4
= 0;
1520 SrbExtension
->LBA5
= 0;
1521 SrbExtension
->FeaturesHigh
= 0;
1522 SrbExtension
->SectorCountLow
= 0;
1523 SrbExtension
->SectorCountHigh
= 0;
1525 SrbExtension
->Sgl
.NumberOfElements
= 1;
1526 SrbExtension
->Sgl
.List
[0].PhysicalAddress
.LowPart
= PortExtension
->IdentifyDeviceDataPhysicalAddress
.LowPart
;
1527 SrbExtension
->Sgl
.List
[0].PhysicalAddress
.HighPart
= PortExtension
->IdentifyDeviceDataPhysicalAddress
.HighPart
;
1528 SrbExtension
->Sgl
.List
[0].Length
= sizeof(IDENTIFY_DEVICE_DATA
);
1532 DebugPrint("\tVPD Inquired\n");
1534 DataBuffer
= Srb
->DataBuffer
;
1535 DataBufferLength
= Srb
->DataTransferLength
;
1537 if (DataBuffer
== NULL
)
1539 return SRB_STATUS_INVALID_REQUEST
;
1542 AhciZeroMemory(DataBuffer
, DataBufferLength
);
1545 return SRB_STATUS_BAD_FUNCTION
;
1548 AhciProcessIO(AdapterExtension
, Srb
->PathId
, Srb
);
1549 return SRB_STATUS_PENDING
;
1550 }// -- DeviceInquiryRequest();
1553 * @name AhciAdapterReset
1557 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
1558 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
1559 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
1560 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
1561 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
1562 * the HBA reset has completed.
1563 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
1564 * a hung or locked state.
1566 * @param AdapterExtension
1569 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
1573 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
1576 ULONG ghc
, ticks
, ghcStatus
;
1577 PAHCI_MEMORY_REGISTERS abar
= NULL
;
1579 DebugPrint("AhciAdapterReset()\n");
1581 abar
= AdapterExtension
->ABAR_Address
;
1582 if (abar
== NULL
) // basic sanity
1587 // HR -- Very first bit (lowest significant)
1588 ghc
= AHCI_Global_HBA_CONTROL_HR
;
1589 StorPortWriteRegisterUlong(AdapterExtension
, &abar
->GHC
, ghc
);
1591 for (ticks
= 0; ticks
< 50; ++ticks
)
1593 ghcStatus
= StorPortReadRegisterUlong(AdapterExtension
, &abar
->GHC
);
1594 if ((ghcStatus
& AHCI_Global_HBA_CONTROL_HR
) == 0)
1598 StorPortStallExecution(20000);
1601 if (ticks
== 50)// 1 second
1603 DebugPrint("\tDevice Timeout\n");
1608 }// -- AhciAdapterReset();
1611 * @name AhciZeroMemory
1614 * Clear buffer by filling zeros
1623 __in ULONG BufferSize
1627 for (i
= 0; i
< BufferSize
; i
++)
1633 }// -- AhciZeroMemory();
1639 * Tells wheather given port is implemented or not
1641 * @param AdapterExtension
1645 * return TRUE if provided port is valid (implemented) or not
1650 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1654 NT_ASSERT(pathId
< MAXIMUM_AHCI_PORT_COUNT
);
1656 if (pathId
>= AdapterExtension
->PortCount
)
1661 return AdapterExtension
->PortExtension
[pathId
].IsActive
;
1662 }// -- IsPortValid()
1674 * return TRUE if Srb is successfully added to Queue
1680 __inout PAHCI_QUEUE Queue
,
1684 NT_ASSERT(Queue
->Head
< MAXIMUM_QUEUE_BUFFER_SIZE
);
1685 NT_ASSERT(Queue
->Tail
< MAXIMUM_QUEUE_BUFFER_SIZE
);
1687 if (Queue
->Tail
== ((Queue
->Head
+ 1) % MAXIMUM_QUEUE_BUFFER_SIZE
))
1690 Queue
->Buffer
[Queue
->Head
++] = Srb
;
1691 Queue
->Head
%= MAXIMUM_QUEUE_BUFFER_SIZE
;
1700 * Remove and return Srb from Queue
1711 __inout PAHCI_QUEUE Queue
1716 NT_ASSERT(Queue
->Head
< MAXIMUM_QUEUE_BUFFER_SIZE
);
1717 NT_ASSERT(Queue
->Tail
< MAXIMUM_QUEUE_BUFFER_SIZE
);
1719 if (Queue
->Head
== Queue
->Tail
)
1722 Srb
= Queue
->Buffer
[Queue
->Tail
++];
1723 Queue
->Tail
%= MAXIMUM_QUEUE_BUFFER_SIZE
;
1726 }// -- RemoveQueue();
1729 * @name GetSrbExtension
1732 * GetSrbExtension from Srb make sure It is properly aligned
1737 * return SrbExtension
1743 __in PSCSI_REQUEST_BLOCK Srb
1747 ULONG_PTR SrbExtension
;
1749 SrbExtension
= (ULONG_PTR
)Srb
->SrbExtension
;
1750 Offset
= SrbExtension
% 128;
1752 // CommandTable should be 128 byte aligned
1754 Offset
= 128 - Offset
;
1756 return (PAHCI_SRB_EXTENSION
)(SrbExtension
+ Offset
);
1757 }// -- PAHCI_SRB_EXTENSION();