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 PAHCI_MEMORY_REGISTERS abar
;
28 ULONG mappedLength
, portNumber
, ticks
;
29 PAHCI_ADAPTER_EXTENSION adapterExtension
;
30 STOR_PHYSICAL_ADDRESS commandListPhysical
, receivedFISPhysical
;
32 AhciDebugPrint("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 AhciDebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength
);
54 receivedFISPhysical
= StorPortGetPhysicalAddress(adapterExtension
,
56 PortExtension
->ReceivedFIS
,
59 if ((mappedLength
== 0) || ((receivedFISPhysical
.LowPart
% 256) != 0))
61 AhciDebugPrint("\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))
85 StorPortStallExecution(50000);
86 cmd
.Status
= StorPortReadRegisterUlong(adapterExtension
, &PortExtension
->Port
->CMD
);
89 AhciDebugPrint("\tAttempt to reset port failed: %x\n", cmd
);
94 while(cmd
.CR
!= 0 || cmd
.FR
!= 0);
97 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
98 // PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
99 // PxFB and PxFBU (if CAP.S64A is set to ‘1’)
100 // Note: Assuming 32bit support only
101 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->CLB
, commandListPhysical
.LowPart
);
102 if (IsAdapterCAPS64(adapterExtension
->CAP
))
104 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->CLBU
, commandListPhysical
.HighPart
);
107 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->FB
, receivedFISPhysical
.LowPart
);
108 if (IsAdapterCAPS64(adapterExtension
->CAP
))
110 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->FBU
, receivedFISPhysical
.HighPart
);
113 PortExtension
->IdentifyDeviceDataPhysicalAddress
= StorPortGetPhysicalAddress(adapterExtension
,
115 PortExtension
->IdentifyDeviceData
,
118 // set device power state flag to D0
119 PortExtension
->DevicePowerState
= StorPowerDeviceD0
;
121 // clear pending interrupts
122 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->SERR
, (ULONG
)~0);
123 StorPortWriteRegisterUlong(adapterExtension
, &PortExtension
->Port
->IS
, (ULONG
)~0);
124 StorPortWriteRegisterUlong(adapterExtension
, adapterExtension
->IS
, (1 << PortExtension
->PortNumber
));
127 }// -- AhciPortInitialize();
130 * @name AhciAllocateResourceForAdapter
133 * Allocate memory from poll for required pointers
135 * @param AdapterExtension
139 * return TRUE if allocation was successful
142 AhciAllocateResourceForAdapter (
143 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
144 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
147 PCHAR nonCachedExtension
, tmp
;
148 ULONG index
, NCS
, AlignedNCS
;
149 ULONG portCount
, portImplemented
, nonCachedExtensionSize
;
150 PAHCI_PORT_EXTENSION PortExtension
;
152 AhciDebugPrint("AhciAllocateResourceForAdapter()\n");
154 NCS
= AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
);
155 AlignedNCS
= ROUND_UP(NCS
, 8);
157 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
159 portImplemented
= AdapterExtension
->PortImplemented
;
161 NT_ASSERT(portImplemented
!= 0);
162 for (index
= MAXIMUM_AHCI_PORT_COUNT
- 1; index
> 0; index
--)
163 if ((portImplemented
& (1 << index
)) != 0)
166 portCount
= index
+ 1;
167 AhciDebugPrint("\tPort Count: %d\n", portCount
);
169 AdapterExtension
->PortCount
= portCount
;
170 nonCachedExtensionSize
= sizeof(AHCI_COMMAND_HEADER
) * AlignedNCS
+ //should be 1K aligned
171 sizeof(AHCI_RECEIVED_FIS
) +
172 sizeof(IDENTIFY_DEVICE_DATA
);
174 // align nonCachedExtensionSize to 1024
175 nonCachedExtensionSize
= ROUND_UP(nonCachedExtensionSize
, 1024);
177 AdapterExtension
->NonCachedExtension
= StorPortGetUncachedExtension(AdapterExtension
,
179 nonCachedExtensionSize
* portCount
);
181 if (AdapterExtension
->NonCachedExtension
== NULL
)
183 AhciDebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
187 nonCachedExtension
= AdapterExtension
->NonCachedExtension
;
188 AhciZeroMemory(nonCachedExtension
, nonCachedExtensionSize
* portCount
);
190 for (index
= 0; index
< portCount
; index
++)
192 PortExtension
= &AdapterExtension
->PortExtension
[index
];
194 PortExtension
->DeviceParams
.IsActive
= FALSE
;
195 if ((AdapterExtension
->PortImplemented
& (1 << index
)) != 0)
197 PortExtension
->PortNumber
= index
;
198 PortExtension
->DeviceParams
.IsActive
= TRUE
;
199 PortExtension
->AdapterExtension
= AdapterExtension
;
200 PortExtension
->CommandList
= (PAHCI_COMMAND_HEADER
)nonCachedExtension
;
202 tmp
= (PCHAR
)(nonCachedExtension
+ sizeof(AHCI_COMMAND_HEADER
) * AlignedNCS
);
204 PortExtension
->ReceivedFIS
= (PAHCI_RECEIVED_FIS
)tmp
;
205 PortExtension
->IdentifyDeviceData
= (PIDENTIFY_DEVICE_DATA
)(tmp
+ sizeof(AHCI_RECEIVED_FIS
));
206 PortExtension
->MaxPortQueueDepth
= NCS
;
207 nonCachedExtension
+= nonCachedExtensionSize
;
212 }// -- AhciAllocateResourceForAdapter();
215 * @name AhciStartPort
218 * Try to start the port device
220 * @param AdapterExtension
221 * @param PortExtension
226 __in PAHCI_PORT_EXTENSION PortExtension
231 AHCI_TASK_FILE_DATA tfd
;
232 AHCI_INTERRUPT_ENABLE ie
;
233 AHCI_SERIAL_ATA_STATUS ssts
;
234 AHCI_SERIAL_ATA_CONTROL sctl
;
235 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
237 AhciDebugPrint("AhciStartPort()\n");
239 AdapterExtension
= PortExtension
->AdapterExtension
;
240 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
242 if ((cmd
.FR
== 1) && (cmd
.CR
== 1) && (cmd
.FRE
== 1) && (cmd
.ST
== 1))
249 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
, cmd
.Status
);
251 if (((cmd
.FR
== 1) && (cmd
.FRE
== 0)) ||
252 ((cmd
.CR
== 1) && (cmd
.ST
== 0)))
254 AhciDebugPrint("\tCOMRESET\n");
258 // Software causes a port reset (COMRESET) by writing 1h to the PxSCTL.DET field to invoke a
259 // COMRESET on the interface and start a re-establishment of Phy layer communications. Software shall
260 // wait at least 1 millisecond before clearing PxSCTL.DET to 0h; this ensures that at least one COMRESET
261 // signal is sent over the interface. After clearing PxSCTL.DET to 0h, software should wait for
262 // communication to be re-established as indicated by PxSSTS.DET being set to 3h. Then software should
263 // write all 1s to the PxSERR register to clear any bits that were set as part of the port reset.
265 sctl
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SCTL
);
267 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SCTL
, sctl
.Status
);
269 StorPortStallExecution(1000);
271 sctl
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SCTL
);
273 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SCTL
, sctl
.Status
);
275 // Poll DET to verify if a device is attached to the port
279 StorPortStallExecution(1000);
280 ssts
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SSTS
);
291 ssts
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SSTS
);
296 NT_ASSERT(cmd
.ST
== 0);
298 // make sure FIS Recieve is enabled (cmd.FRE)
302 StorPortStallExecution(10000);
303 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
305 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
, cmd
.Status
);
308 while((cmd
.FR
!= 1) && (index
< 3));
312 // failed to start FIS DMA engine
313 // it can crash the driver later
314 // so better to turn this port off
318 // start port channel
321 NT_ASSERT(cmd
.FRE
== 1);
322 NT_ASSERT(cmd
.CR
== 0);
324 // why assert? well If we face such condition on DET = 0x3
325 // then we don't have port in idle state and hence before executing this part of code
326 // we must have restarted it.
327 tfd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->TFD
);
329 if ((tfd
.STS
.BSY
) || (tfd
.STS
.DRQ
))
331 AhciDebugPrint("\tUnhandled Case BSY-DRQ\n");
334 // clear pending interrupts
335 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SERR
, (ULONG
)~0);
336 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IS
, (ULONG
)~0);
337 StorPortWriteRegisterUlong(AdapterExtension
, AdapterExtension
->IS
, (1 << PortExtension
->PortNumber
));
340 ie
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IE
);
341 /* Device to Host Register FIS Interrupt Enable */
343 /* PIO Setup FIS Interrupt Enable */
345 /* DMA Setup FIS Interrupt Enable */
347 /* Set Device Bits FIS Interrupt Enable */
349 /* Unknown FIS Interrupt Enable */
351 /* Descriptor Processed Interrupt Enable */
353 /* Port Change Interrupt Enable */
355 /* Device Mechanical Presence Enable */
357 /* PhyRdy Change Interrupt Enable */
359 /* Incorrect Port Multiplier Enable */
361 /* Overflow Enable */
363 /* Interface Non-fatal Error Enable */
365 /* Interface Fatal Error Enable */
367 /* Host Bus Data Error Enable */
369 /* Host Bus Fatal Error Enable */
371 /* Task File Error Enable */
374 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
375 /* Cold Presence Detect Enable */
376 if (cmd
.CPD
) // does it support CPD?
378 // disable it for now
382 // should I replace this to single line?
383 // by directly setting ie.Status?
385 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IE
, ie
.Status
);
388 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
, cmd
.Status
);
389 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
393 AhciDebugPrint("\tFailed to start Port\n");
401 AhciDebugPrint("\tDET == %x Unsupported\n", ssts
.DET
);
404 }// -- AhciStartPort();
407 * @name AhciCommandCompletionDpcRoutine
410 * Handles Completed Commands
413 * @param AdapterExtension
414 * @param SystemArgument1
415 * @param SystemArgument2
418 AhciCommandCompletionDpcRoutine (
420 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
421 __in PAHCI_PORT_EXTENSION PortExtension
,
422 __in PVOID SystemArgument2
425 PSCSI_REQUEST_BLOCK Srb
;
426 PAHCI_SRB_EXTENSION SrbExtension
;
427 STOR_LOCK_HANDLE lockhandle
= {0};
428 PAHCI_COMPLETION_ROUTINE CompletionRoutine
;
430 UNREFERENCED_PARAMETER(Dpc
);
431 UNREFERENCED_PARAMETER(SystemArgument2
);
433 AhciDebugPrint("AhciCommandCompletionDpcRoutine()\n");
435 StorPortAcquireSpinLock(AdapterExtension
, InterruptLock
, NULL
, &lockhandle
);
436 Srb
= RemoveQueue(&PortExtension
->CompletionQueue
);
437 StorPortReleaseSpinLock(AdapterExtension
, &lockhandle
);
439 NT_ASSERT(Srb
!= NULL
);
441 if (Srb
->SrbStatus
== SRB_STATUS_PENDING
)
443 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
450 SrbExtension
= GetSrbExtension(Srb
);
452 CompletionRoutine
= SrbExtension
->CompletionRoutine
;
453 NT_ASSERT(CompletionRoutine
!= NULL
);
455 // now it's completion routine responsibility to set SrbStatus
456 CompletionRoutine(PortExtension
, Srb
);
458 StorPortNotification(RequestComplete
, AdapterExtension
, Srb
);
461 }// -- AhciCommandCompletionDpcRoutine();
464 * @name AhciHwPassiveInitialize
467 * initializes the HBA and finds all devices that are of interest to the miniport driver. (at PASSIVE LEVEL)
469 * @param adapterExtension
472 * return TRUE if intialization was successful
475 AhciHwPassiveInitialize (
476 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
480 PAHCI_PORT_EXTENSION PortExtension
;
482 AhciDebugPrint("AhciHwPassiveInitialize()\n");
484 for (index
= 0; index
< AdapterExtension
->PortCount
; index
++)
486 if ((AdapterExtension
->PortImplemented
& (0x1 << index
)) != 0)
488 PortExtension
= &AdapterExtension
->PortExtension
[index
];
489 PortExtension
->DeviceParams
.IsActive
= AhciStartPort(PortExtension
);
490 StorPortInitializeDpc(AdapterExtension
, &PortExtension
->CommandCompletion
, AhciCommandCompletionDpcRoutine
);
495 }// -- AhciHwPassiveInitialize();
498 * @name AhciHwInitialize
501 * initializes the HBA and finds all devices that are of interest to the miniport driver.
503 * @param adapterExtension
506 * return TRUE if intialization was successful
510 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
515 AhciDebugPrint("AhciHwInitialize()\n");
517 AdapterExtension
->StateFlags
.MessagePerPort
= FALSE
;
519 // First check what type of interrupt/synchronization device is using
520 ghc
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &AdapterExtension
->ABAR_Address
->GHC
);
522 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
523 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
524 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
525 // software has allocated the number of messages requested
528 AdapterExtension
->StateFlags
.MessagePerPort
= TRUE
;
529 AhciDebugPrint("\tMultiple MSI based message not supported\n");
532 StorPortEnablePassiveInitialization(AdapterExtension
, AhciHwPassiveInitialize
);
535 }// -- AhciHwInitialize();
538 * @name AhciCompleteIssuedSrb
541 * Complete issued Srbs
543 * @param PortExtension
547 AhciCompleteIssuedSrb (
548 __in PAHCI_PORT_EXTENSION PortExtension
,
549 __in ULONG CommandsToComplete
553 PSCSI_REQUEST_BLOCK Srb
;
554 PAHCI_SRB_EXTENSION SrbExtension
;
555 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
557 AhciDebugPrint("AhciCompleteIssuedSrb()\n");
559 NT_ASSERT(CommandsToComplete
!= 0);
561 AhciDebugPrint("\tCompleted Commands: %d\n", CommandsToComplete
);
563 AdapterExtension
= PortExtension
->AdapterExtension
;
564 NCS
= AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
);
566 for (i
= 0; i
< NCS
; i
++)
568 if (((1 << i
) & CommandsToComplete
) != 0)
570 Srb
= PortExtension
->Slot
[i
];
577 SrbExtension
= GetSrbExtension(Srb
);
578 NT_ASSERT(SrbExtension
!= NULL
);
580 if (SrbExtension
->CompletionRoutine
!= NULL
)
582 AddQueue(&PortExtension
->CompletionQueue
, Srb
);
583 StorPortIssueDpc(AdapterExtension
, &PortExtension
->CommandCompletion
, PortExtension
, Srb
);
587 NT_ASSERT(Srb
->SrbStatus
== SRB_STATUS_PENDING
);
588 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
589 StorPortNotification(RequestComplete
, AdapterExtension
, Srb
);
595 }// -- AhciCompleteIssuedSrb();
598 * @name AhciInterruptHandler
601 * Interrupt Handler for PortExtension
603 * @param PortExtension
607 AhciInterruptHandler (
608 __in PAHCI_PORT_EXTENSION PortExtension
611 ULONG is
, ci
, sact
, outstanding
;
612 AHCI_INTERRUPT_STATUS PxIS
;
613 AHCI_INTERRUPT_STATUS PxISMasked
;
614 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
616 AhciDebugPrint("AhciInterruptHandler()\n");
617 AhciDebugPrint("\tPort Number: %d\n", PortExtension
->PortNumber
);
619 AdapterExtension
= PortExtension
->AdapterExtension
;
620 NT_ASSERT(IsPortValid(AdapterExtension
, PortExtension
->PortNumber
));
623 // 1. Software determines the cause of the interrupt by reading the PxIS register.
624 // It is possible for multiple bits to be set
625 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
626 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
627 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
628 // the list of commands previously issued by software that are still outstanding.
629 // If executing native queued commands, software reads the PxSACT register and compares the current
630 // value to the list of commands previously issued by software.
631 // Software completes with success any outstanding command whose corresponding bit has been cleared in
632 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
633 // to determine commands that have completed, not to determine which commands have previously been issued.
634 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
635 PxISMasked
.Status
= 0;
636 PxIS
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IS
);
640 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
641 if (PxIS
.HBFS
|| PxIS
.HBDS
|| PxIS
.IFS
|| PxIS
.TFES
)
643 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
644 // any native command queuing commands. To recover, the port must be restarted
645 // To detect an error that requires software recovery actions to be performed,
646 // software should check whether any of the following status bits are set on an interrupt:
647 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
648 // software should perform the appropriate error recovery actions based on whether
649 // non-queued commands were being issued or native command queuing commands were being issued.
651 AhciDebugPrint("\tFatal Error: %x\n", PxIS
.Status
);
654 // Normal Command Completion
656 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
657 PxISMasked
.DHRS
= PxIS
.DHRS
;
658 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
659 PxISMasked
.PSS
= PxIS
.PSS
;
660 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
661 PxISMasked
.DSS
= PxIS
.DSS
;
662 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
663 PxISMasked
.SDBS
= PxIS
.SDBS
;
664 // A PRD with the ‘I’ bit set has transferred all of its data.
665 PxISMasked
.DPS
= PxIS
.DPS
;
667 if (PxISMasked
.Status
!= 0)
669 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->IS
, PxISMasked
.Status
);
673 // Clear port interrupt
674 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
675 is
= (1 << PortExtension
->PortNumber
);
676 StorPortWriteRegisterUlong(AdapterExtension
, AdapterExtension
->IS
, is
);
678 ci
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CI
);
679 sact
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SACT
);
681 outstanding
= ci
| sact
; // NOTE: Including both non-NCQ and NCQ based commands
682 if ((PortExtension
->CommandIssuedSlots
& (~outstanding
)) != 0)
684 AhciCompleteIssuedSrb(PortExtension
, (PortExtension
->CommandIssuedSlots
& (~outstanding
)));
685 PortExtension
->CommandIssuedSlots
&= outstanding
;
689 }// -- AhciInterruptHandler();
692 * @name AhciHwInterrupt
695 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
697 * @param AdapterExtension
700 * return TRUE Indicates that an interrupt was pending on adapter.
701 * return FALSE Indicates the interrupt was not ours.
705 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
708 ULONG portPending
, nextPort
, i
, portCount
;
710 if (AdapterExtension
->StateFlags
.Removed
)
715 portPending
= StorPortReadRegisterUlong(AdapterExtension
, AdapterExtension
->IS
);
717 // we process interrupt for implemented ports only
718 portCount
= AdapterExtension
->PortCount
;
719 portPending
= portPending
& AdapterExtension
->PortImplemented
;
721 if (portPending
== 0)
726 for (i
= 1; i
<= portCount
; i
++)
728 nextPort
= (AdapterExtension
->LastInterruptPort
+ i
) % portCount
;
729 if ((portPending
& (0x1 << nextPort
)) == 0)
732 NT_ASSERT(IsPortValid(AdapterExtension
, nextPort
));
734 if (AdapterExtension
->PortExtension
[nextPort
].DeviceParams
.IsActive
== FALSE
)
739 // we can assign this interrupt to this port
740 AdapterExtension
->LastInterruptPort
= nextPort
;
741 AhciInterruptHandler(&AdapterExtension
->PortExtension
[nextPort
]);
743 portPending
&= ~(1 << nextPort
);
745 // interrupt belongs to this device
746 // should always return TRUE
750 AhciDebugPrint("\tSomething went wrong");
752 }// -- AhciHwInterrupt();
755 * @name AhciHwStartIo
758 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
760 * @param adapterExtension
764 * return TRUE if the request was accepted
765 * return FALSE if the request must be submitted later
769 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
770 __in PSCSI_REQUEST_BLOCK Srb
773 AhciDebugPrint("AhciHwStartIo()\n");
775 if (!IsPortValid(AdapterExtension
, Srb
->PathId
))
777 Srb
->SrbStatus
= SRB_STATUS_NO_DEVICE
;
778 StorPortNotification(RequestComplete
, AdapterExtension
, Srb
);
782 switch(Srb
->Function
)
784 case SRB_FUNCTION_PNP
:
786 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
787 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
788 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
790 PSCSI_PNP_REQUEST_BLOCK pnpRequest
;
791 pnpRequest
= (PSCSI_PNP_REQUEST_BLOCK
)Srb
;
792 if ((pnpRequest
->SrbPnPFlags
& SRB_PNP_FLAGS_ADAPTER_REQUEST
) != 0)
794 switch(pnpRequest
->PnPAction
)
796 case StorRemoveDevice
:
797 case StorSurpriseRemoval
:
799 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
800 AdapterExtension
->StateFlags
.Removed
= 1;
801 AhciDebugPrint("\tAdapter removed\n");
806 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
807 AhciDebugPrint("\tRequested to Stop the adapter\n");
811 Srb
->SrbStatus
= SRB_STATUS_INVALID_REQUEST
;
817 Srb
->SrbStatus
= SRB_STATUS_INVALID_REQUEST
;
821 case SRB_FUNCTION_EXECUTE_SCSI
:
823 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
824 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
825 // routine does the following:
827 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
828 // logical unit, and/or SRB extensions
829 // For example, a miniport driver might set up a logical unit extension with pointers
830 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
831 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
832 // carried out on the HBA.
834 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
835 // for the requested operation
836 // For a device I/O operation, such an internal routine generally selects the target device
837 // and sends the CDB over the bus to the target logical unit.
838 PCDB cdb
= (PCDB
)&Srb
->Cdb
;
839 if (Srb
->CdbLength
== 0)
841 AhciDebugPrint("\tOperationCode: %d\n", cdb
->CDB10
.OperationCode
);
842 Srb
->SrbStatus
= SRB_STATUS_BAD_FUNCTION
;
846 NT_ASSERT(cdb
!= NULL
);
848 switch(cdb
->CDB10
.OperationCode
)
851 Srb
->SrbStatus
= DeviceInquiryRequest(AdapterExtension
, Srb
, cdb
);
853 case SCSIOP_REPORT_LUNS
:
854 Srb
->SrbStatus
= DeviceReportLuns(AdapterExtension
, Srb
, cdb
);
856 case SCSIOP_READ_CAPACITY
:
857 Srb
->SrbStatus
= DeviceRequestCapacity(AdapterExtension
, Srb
, cdb
);
859 case SCSIOP_TEST_UNIT_READY
:
860 Srb
->SrbStatus
= DeviceRequestComplete(AdapterExtension
, Srb
, cdb
);
862 case SCSIOP_MODE_SENSE
:
863 Srb
->SrbStatus
= DeviceRequestSense(AdapterExtension
, Srb
, cdb
);
867 Srb
->SrbStatus
= DeviceRequestReadWrite(AdapterExtension
, Srb
, cdb
);
870 AhciDebugPrint("\tOperationCode: %d\n", cdb
->CDB10
.OperationCode
);
871 Srb
->SrbStatus
= SRB_STATUS_INVALID_REQUEST
;
877 AhciDebugPrint("\tUnknown function code recieved: %x\n", Srb
->Function
);
878 Srb
->SrbStatus
= SRB_STATUS_INVALID_REQUEST
;
882 if (Srb
->SrbStatus
!= SRB_STATUS_PENDING
)
884 StorPortNotification(RequestComplete
, AdapterExtension
, Srb
);
888 AhciProcessIO(AdapterExtension
, Srb
->PathId
, Srb
);
891 }// -- AhciHwStartIo();
894 * @name AhciHwResetBus
897 * The HwStorResetBus routine is called by the port driver to clear error conditions.
899 * @param adapterExtension
903 * return TRUE if bus was successfully reset
907 __in PVOID AdapterExtension
,
911 STOR_LOCK_HANDLE lockhandle
= {0};
912 PAHCI_ADAPTER_EXTENSION adapterExtension
;
914 AhciDebugPrint("AhciHwResetBus()\n");
916 adapterExtension
= AdapterExtension
;
918 if (IsPortValid(AdapterExtension
, PathId
))
921 StorPortAcquireSpinLock(AdapterExtension
, InterruptLock
, NULL
, &lockhandle
);
923 // TODO: Perform port reset
926 StorPortReleaseSpinLock(AdapterExtension
, &lockhandle
);
930 }// -- AhciHwResetBus();
933 * @name AhciHwFindAdapter
936 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
937 * HBA is supported and, if it is, to return configuration information about that adapter.
939 * 10.1 Platform Communication
940 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
942 * @param DeviceExtension
944 * @param BusInformation
945 * @param ArgumentString
951 * 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.
954 * 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.
956 * SP_RETURN_BAD_CONFIG
957 * Indicates that the supplied configuration information was invalid for the adapter.
959 * SP_RETURN_NOT_FOUND
960 * Indicates that no supported HBA was found for the supplied configuration information.
962 * @remarks Called by Storport.
966 __in PVOID AdapterExtension
,
967 __in PVOID HwContext
,
968 __in PVOID BusInformation
,
969 __in PVOID ArgumentString
,
970 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo
,
971 __in PBOOLEAN Reserved3
975 ULONG index
, pci_cfg_len
;
976 PACCESS_RANGE accessRange
;
977 UCHAR pci_cfg_buf
[sizeof(PCI_COMMON_CONFIG
)];
979 PAHCI_MEMORY_REGISTERS abar
;
980 PPCI_COMMON_CONFIG pciConfigData
;
981 PAHCI_ADAPTER_EXTENSION adapterExtension
;
983 AhciDebugPrint("AhciHwFindAdapter()\n");
985 UNREFERENCED_PARAMETER(HwContext
);
986 UNREFERENCED_PARAMETER(BusInformation
);
987 UNREFERENCED_PARAMETER(ArgumentString
);
988 UNREFERENCED_PARAMETER(Reserved3
);
990 adapterExtension
= AdapterExtension
;
991 adapterExtension
->SlotNumber
= ConfigInfo
->SlotNumber
;
992 adapterExtension
->SystemIoBusNumber
= ConfigInfo
->SystemIoBusNumber
;
994 // get PCI configuration header
995 pci_cfg_len
= StorPortGetBusData(
998 adapterExtension
->SystemIoBusNumber
,
999 adapterExtension
->SlotNumber
,
1001 sizeof(PCI_COMMON_CONFIG
));
1003 if (pci_cfg_len
!= sizeof(PCI_COMMON_CONFIG
))
1005 AhciDebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG
), pci_cfg_len
);
1006 return SP_RETURN_ERROR
;//Not a valid device at the given bus number
1009 pciConfigData
= (PPCI_COMMON_CONFIG
)pci_cfg_buf
;
1010 adapterExtension
->VendorID
= pciConfigData
->VendorID
;
1011 adapterExtension
->DeviceID
= pciConfigData
->DeviceID
;
1012 adapterExtension
->RevisionID
= pciConfigData
->RevisionID
;
1013 // 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).
1014 adapterExtension
->AhciBaseAddress
= pciConfigData
->u
.type0
.BaseAddresses
[5] & (0xFFFFFFF0);
1016 AhciDebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension
->VendorID
,
1017 adapterExtension
->DeviceID
,
1018 adapterExtension
->RevisionID
);
1022 if (ConfigInfo
->NumberOfAccessRanges
> 0)
1024 accessRange
= *(ConfigInfo
->AccessRanges
);
1025 for (index
= 0; index
< ConfigInfo
->NumberOfAccessRanges
; index
++)
1027 if (accessRange
[index
].RangeStart
.QuadPart
== adapterExtension
->AhciBaseAddress
)
1029 abar
= StorPortGetDeviceBase(adapterExtension
,
1030 ConfigInfo
->AdapterInterfaceType
,
1031 ConfigInfo
->SystemIoBusNumber
,
1032 accessRange
[index
].RangeStart
,
1033 accessRange
[index
].RangeLength
,
1034 !accessRange
[index
].RangeInMemory
);
1042 AhciDebugPrint("\tabar == NULL\n");
1043 return SP_RETURN_ERROR
; // corrupted information supplied
1046 adapterExtension
->ABAR_Address
= abar
;
1047 adapterExtension
->CAP
= StorPortReadRegisterUlong(adapterExtension
, &abar
->CAP
);
1048 adapterExtension
->CAP2
= StorPortReadRegisterUlong(adapterExtension
, &abar
->CAP2
);
1049 adapterExtension
->Version
= StorPortReadRegisterUlong(adapterExtension
, &abar
->VS
);
1050 adapterExtension
->LastInterruptPort
= (ULONG
)-1;
1053 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
1054 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
1055 ghc
.Status
= StorPortReadRegisterUlong(adapterExtension
, &abar
->GHC
);
1056 // AE := Highest Significant bit of GHC
1057 if (ghc
.AE
!= 0)// Hmm, controller was already in power state
1059 // reset controller to have it in known state
1060 AhciDebugPrint("\tAE Already set, Reset()\n");
1061 if (!AhciAdapterReset(adapterExtension
))
1063 AhciDebugPrint("\tReset Failed!\n");
1064 return SP_RETURN_ERROR
;// reset failed
1069 ghc
.AE
= 1;// only AE=1
1070 // tell the controller that we know about AHCI
1071 StorPortWriteRegisterUlong(adapterExtension
, &abar
->GHC
, ghc
.Status
);
1073 adapterExtension
->IS
= &abar
->IS
;
1074 adapterExtension
->PortImplemented
= StorPortReadRegisterUlong(adapterExtension
, &abar
->PI
);
1076 if (adapterExtension
->PortImplemented
== 0)
1078 AhciDebugPrint("\tadapterExtension->PortImplemented == 0\n");
1079 return SP_RETURN_ERROR
;
1082 ConfigInfo
->Master
= TRUE
;
1083 ConfigInfo
->AlignmentMask
= 0x3;
1084 ConfigInfo
->ScatterGather
= TRUE
;
1085 ConfigInfo
->DmaWidth
= Width32Bits
;
1086 ConfigInfo
->WmiDataProvider
= FALSE
;
1087 ConfigInfo
->Dma32BitAddresses
= TRUE
;
1089 if (IsAdapterCAPS64(adapterExtension
->CAP
))
1091 ConfigInfo
->Dma64BitAddresses
= TRUE
;
1094 ConfigInfo
->MaximumNumberOfTargets
= 1;
1095 ConfigInfo
->ResetTargetSupported
= TRUE
;
1096 ConfigInfo
->NumberOfPhysicalBreaks
= 0x21;
1097 ConfigInfo
->MaximumNumberOfLogicalUnits
= 1;
1098 ConfigInfo
->NumberOfBuses
= MAXIMUM_AHCI_PORT_COUNT
;
1099 ConfigInfo
->MaximumTransferLength
= MAXIMUM_TRANSFER_LENGTH
;
1100 ConfigInfo
->SynchronizationModel
= StorSynchronizeFullDuplex
;
1102 // Turn IE -- Interrupt Enabled
1103 ghc
.Status
= StorPortReadRegisterUlong(adapterExtension
, &abar
->GHC
);
1105 StorPortWriteRegisterUlong(adapterExtension
, &abar
->GHC
, ghc
.Status
);
1107 // allocate necessary resource for each port
1108 if (!AhciAllocateResourceForAdapter(adapterExtension
, ConfigInfo
))
1111 return SP_RETURN_ERROR
;
1114 for (index
= 0; index
< adapterExtension
->PortCount
; index
++)
1116 if ((adapterExtension
->PortImplemented
& (0x1 << index
)) != 0)
1117 AhciPortInitialize(&adapterExtension
->PortExtension
[index
]);
1120 return SP_RETURN_FOUND
;
1121 }// -- AhciHwFindAdapter();
1127 * Initial Entrypoint for storahci miniport driver
1129 * @param DriverObject
1130 * @param RegistryPath
1133 * NT_STATUS in case of driver loaded successfully.
1137 __in PVOID DriverObject
,
1138 __in PVOID RegistryPath
1142 // initialize the hardware data structure
1143 HW_INITIALIZATION_DATA hwInitializationData
= {0};
1145 // set size of hardware initialization structure
1146 hwInitializationData
.HwInitializationDataSize
= sizeof(HW_INITIALIZATION_DATA
);
1148 // identity required miniport entry point routines
1149 hwInitializationData
.HwStartIo
= AhciHwStartIo
;
1150 hwInitializationData
.HwResetBus
= AhciHwResetBus
;
1151 hwInitializationData
.HwInterrupt
= AhciHwInterrupt
;
1152 hwInitializationData
.HwInitialize
= AhciHwInitialize
;
1153 hwInitializationData
.HwFindAdapter
= AhciHwFindAdapter
;
1155 // adapter specific information
1156 hwInitializationData
.TaggedQueuing
= TRUE
;
1157 hwInitializationData
.AutoRequestSense
= TRUE
;
1158 hwInitializationData
.MultipleRequestPerLu
= TRUE
;
1159 hwInitializationData
.NeedPhysicalAddresses
= TRUE
;
1161 hwInitializationData
.NumberOfAccessRanges
= 6;
1162 hwInitializationData
.AdapterInterfaceType
= PCIBus
;
1163 hwInitializationData
.MapBuffers
= STOR_MAP_NON_READ_WRITE_BUFFERS
;
1165 // set required extension sizes
1166 hwInitializationData
.SrbExtensionSize
= sizeof(AHCI_SRB_EXTENSION
);
1167 hwInitializationData
.DeviceExtensionSize
= sizeof(AHCI_ADAPTER_EXTENSION
);
1169 // register our hw init data
1170 status
= StorPortInitialize(DriverObject
,
1172 &hwInitializationData
,
1175 NT_ASSERT(status
== STATUS_SUCCESS
);
1177 }// -- DriverEntry();
1180 * @name AhciATA_CFIS
1183 * create ATA CFIS from Srb
1185 * @param PortExtension
1189 * Number of CFIS fields used in DWORD
1193 __in PAHCI_PORT_EXTENSION PortExtension
,
1194 __in PAHCI_SRB_EXTENSION SrbExtension
1197 PAHCI_COMMAND_TABLE cmdTable
;
1199 UNREFERENCED_PARAMETER(PortExtension
);
1201 AhciDebugPrint("AhciATA_CFIS()\n");
1203 cmdTable
= (PAHCI_COMMAND_TABLE
)SrbExtension
;
1205 AhciZeroMemory((PCHAR
)cmdTable
->CFIS
, sizeof(cmdTable
->CFIS
));
1207 cmdTable
->CFIS
[AHCI_ATA_CFIS_FisType
] = FIS_TYPE_REG_H2D
; // FIS Type
1208 cmdTable
->CFIS
[AHCI_ATA_CFIS_PMPort_C
] = (1 << 7); // PM Port & C
1209 cmdTable
->CFIS
[AHCI_ATA_CFIS_CommandReg
] = SrbExtension
->CommandReg
;
1211 cmdTable
->CFIS
[AHCI_ATA_CFIS_FeaturesLow
] = SrbExtension
->FeaturesLow
;
1212 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA0
] = SrbExtension
->LBA0
;
1213 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA1
] = SrbExtension
->LBA1
;
1214 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA2
] = SrbExtension
->LBA2
;
1215 cmdTable
->CFIS
[AHCI_ATA_CFIS_Device
] = SrbExtension
->Device
;
1216 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA3
] = SrbExtension
->LBA3
;
1217 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA4
] = SrbExtension
->LBA4
;
1218 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA5
] = SrbExtension
->LBA5
;
1219 cmdTable
->CFIS
[AHCI_ATA_CFIS_FeaturesHigh
] = SrbExtension
->FeaturesHigh
;
1220 cmdTable
->CFIS
[AHCI_ATA_CFIS_SectorCountLow
] = SrbExtension
->SectorCountLow
;
1221 cmdTable
->CFIS
[AHCI_ATA_CFIS_SectorCountHigh
] = SrbExtension
->SectorCountHigh
;
1224 }// -- AhciATA_CFIS();
1227 * @name AhciATAPI_CFIS
1230 * create ATAPI CFIS from Srb
1232 * @param PortExtension
1236 * Number of CFIS fields used in DWORD
1240 __in PAHCI_PORT_EXTENSION PortExtension
,
1241 __in PAHCI_SRB_EXTENSION SrbExtension
1244 PAHCI_COMMAND_TABLE cmdTable
;
1245 UNREFERENCED_PARAMETER(PortExtension
);
1247 AhciDebugPrint("AhciATAPI_CFIS()\n");
1249 cmdTable
= (PAHCI_COMMAND_TABLE
)SrbExtension
;
1251 NT_ASSERT(SrbExtension
->CommandReg
== IDE_COMMAND_ATAPI_PACKET
);
1253 AhciZeroMemory((PCHAR
)cmdTable
->CFIS
, sizeof(cmdTable
->CFIS
));
1255 cmdTable
->CFIS
[AHCI_ATA_CFIS_FisType
] = FIS_TYPE_REG_H2D
; // FIS Type
1256 cmdTable
->CFIS
[AHCI_ATA_CFIS_PMPort_C
] = (1 << 7); // PM Port & C
1257 cmdTable
->CFIS
[AHCI_ATA_CFIS_CommandReg
] = SrbExtension
->CommandReg
;
1259 cmdTable
->CFIS
[AHCI_ATA_CFIS_FeaturesLow
] = SrbExtension
->FeaturesLow
;
1260 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA0
] = SrbExtension
->LBA0
;
1261 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA1
] = SrbExtension
->LBA1
;
1262 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA2
] = SrbExtension
->LBA2
;
1263 cmdTable
->CFIS
[AHCI_ATA_CFIS_Device
] = SrbExtension
->Device
;
1264 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA3
] = SrbExtension
->LBA3
;
1265 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA4
] = SrbExtension
->LBA4
;
1266 cmdTable
->CFIS
[AHCI_ATA_CFIS_LBA5
] = SrbExtension
->LBA5
;
1267 cmdTable
->CFIS
[AHCI_ATA_CFIS_FeaturesHigh
] = SrbExtension
->FeaturesHigh
;
1268 cmdTable
->CFIS
[AHCI_ATA_CFIS_SectorCountLow
] = SrbExtension
->SectorCountLow
;
1269 cmdTable
->CFIS
[AHCI_ATA_CFIS_SectorCountHigh
] = SrbExtension
->SectorCountHigh
;
1272 }// -- AhciATAPI_CFIS();
1275 * @name AhciBuild_PRDT
1278 * Build PRDT for data transfer
1280 * @param PortExtension
1284 * Return number of entries in PRDT.
1288 __in PAHCI_PORT_EXTENSION PortExtension
,
1289 __in PAHCI_SRB_EXTENSION SrbExtension
1293 PAHCI_COMMAND_TABLE cmdTable
;
1294 PLOCAL_SCATTER_GATHER_LIST sgl
;
1295 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
1297 AhciDebugPrint("AhciBuild_PRDT()\n");
1299 sgl
= SrbExtension
->pSgl
;
1300 cmdTable
= (PAHCI_COMMAND_TABLE
)SrbExtension
;
1301 AdapterExtension
= PortExtension
->AdapterExtension
;
1303 NT_ASSERT(sgl
!= NULL
);
1304 NT_ASSERT(sgl
->NumberOfElements
< MAXIMUM_AHCI_PRDT_ENTRIES
);
1306 for (index
= 0; index
< sgl
->NumberOfElements
; index
++)
1308 NT_ASSERT(sgl
->List
[index
].Length
<= MAXIMUM_TRANSFER_LENGTH
);
1310 cmdTable
->PRDT
[index
].DBA
= sgl
->List
[index
].PhysicalAddress
.LowPart
;
1311 if (IsAdapterCAPS64(AdapterExtension
->CAP
))
1313 cmdTable
->PRDT
[index
].DBAU
= sgl
->List
[index
].PhysicalAddress
.HighPart
;
1316 // Data Byte Count (DBC): A ‘0’ based value that Indicates the length, in bytes, of the data block.
1317 // A maximum of length of 4MB may exist for any entry. Bit ‘0’ of this field must always be ‘1’ to
1318 // indicate an even byte count. A value of ‘1’ indicates 2 bytes, ‘3’ indicates 4 bytes, etc.
1319 cmdTable
->PRDT
[index
].DBC
= sgl
->List
[index
].Length
- 1;
1322 return sgl
->NumberOfElements
;
1323 }// -- AhciBuild_PRDT();
1326 * @name AhciProcessSrb
1329 * Prepare Srb for IO processing
1331 * @param PortExtension
1338 __in PAHCI_PORT_EXTENSION PortExtension
,
1339 __in PSCSI_REQUEST_BLOCK Srb
,
1340 __in ULONG SlotIndex
1343 ULONG prdtlen
, sig
, length
, cfl
;
1344 PAHCI_SRB_EXTENSION SrbExtension
;
1345 PAHCI_COMMAND_HEADER CommandHeader
;
1346 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
1347 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress
;
1349 AhciDebugPrint("AhciProcessSrb()\n");
1351 NT_ASSERT(Srb
->PathId
== PortExtension
->PortNumber
);
1353 SrbExtension
= GetSrbExtension(Srb
);
1354 AdapterExtension
= PortExtension
->AdapterExtension
;
1356 NT_ASSERT(SrbExtension
!= NULL
);
1357 NT_ASSERT(SrbExtension
->AtaFunction
!= 0);
1359 if ((SrbExtension
->AtaFunction
== ATA_FUNCTION_ATA_IDENTIFY
) &&
1360 (SrbExtension
->CommandReg
== IDE_COMMAND_NOT_VALID
))
1362 // Here we are safe to check SIG register
1363 sig
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->SIG
);
1366 AhciDebugPrint("\tATA Device Found!\n");
1367 SrbExtension
->CommandReg
= IDE_COMMAND_IDENTIFY
;
1371 AhciDebugPrint("\tATAPI Device Found!\n");
1372 SrbExtension
->CommandReg
= IDE_COMMAND_ATAPI_IDENTIFY
;
1376 NT_ASSERT(SlotIndex
< AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
));
1377 SrbExtension
->SlotIndex
= SlotIndex
;
1379 // program the CFIS in the CommandTable
1380 CommandHeader
= &PortExtension
->CommandList
[SlotIndex
];
1383 if (IsAtapiCommand(SrbExtension
->AtaFunction
))
1385 cfl
= AhciATAPI_CFIS(PortExtension
, SrbExtension
);
1387 else if (IsAtaCommand(SrbExtension
->AtaFunction
))
1389 cfl
= AhciATA_CFIS(PortExtension
, SrbExtension
);
1397 if (IsDataTransferNeeded(SrbExtension
))
1399 prdtlen
= AhciBuild_PRDT(PortExtension
, SrbExtension
);
1400 NT_ASSERT(prdtlen
!= -1);
1403 // Program the command header
1404 CommandHeader
->DI
.PRDTL
= prdtlen
; // number of entries in PRD table
1405 CommandHeader
->DI
.CFL
= cfl
;
1406 CommandHeader
->DI
.A
= (SrbExtension
->AtaFunction
& ATA_FUNCTION_ATAPI_COMMAND
) ? 1 : 0;
1407 CommandHeader
->DI
.W
= (SrbExtension
->Flags
& ATA_FLAGS_DATA_OUT
) ? 1 : 0;
1408 CommandHeader
->DI
.P
= 0; // ATA Specifications says so
1409 CommandHeader
->DI
.PMP
= 0; // Port Multiplier
1411 // Reset -- Manual Configuation
1412 CommandHeader
->DI
.R
= 0;
1413 CommandHeader
->DI
.B
= 0;
1414 CommandHeader
->DI
.C
= 0;
1416 CommandHeader
->PRDBC
= 0;
1418 CommandHeader
->Reserved
[0] = 0;
1419 CommandHeader
->Reserved
[1] = 0;
1420 CommandHeader
->Reserved
[2] = 0;
1421 CommandHeader
->Reserved
[3] = 0;
1423 // set CommandHeader CTBA
1424 CommandTablePhysicalAddress
= StorPortGetPhysicalAddress(AdapterExtension
,
1429 NT_ASSERT(length
!= 0);
1431 // command table alignment
1432 NT_ASSERT((CommandTablePhysicalAddress
.LowPart
% 128) == 0);
1434 CommandHeader
->CTBA
= CommandTablePhysicalAddress
.LowPart
;
1436 if (IsAdapterCAPS64(AdapterExtension
->CAP
))
1438 CommandHeader
->CTBA_U
= CommandTablePhysicalAddress
.HighPart
;
1442 PortExtension
->Slot
[SlotIndex
] = Srb
;
1443 PortExtension
->QueueSlots
|= 1 << SlotIndex
;
1445 }// -- AhciProcessSrb();
1448 * @name AhciActivatePort
1451 * Program Port and populate command list
1453 * @param PortExtension
1458 __in PAHCI_PORT_EXTENSION PortExtension
1462 ULONG QueueSlots
, slotToActivate
, tmp
;
1463 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
1465 AhciDebugPrint("AhciActivatePort()\n");
1467 AdapterExtension
= PortExtension
->AdapterExtension
;
1468 QueueSlots
= PortExtension
->QueueSlots
;
1470 if (QueueSlots
== 0)
1476 // Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’
1477 cmd
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CMD
);
1479 if (cmd
.ST
== 0) // PxCMD.ST == 0
1484 // get the lowest set bit
1485 tmp
= QueueSlots
& (QueueSlots
- 1);
1488 slotToActivate
= QueueSlots
;
1490 slotToActivate
= (QueueSlots
& (~tmp
));
1492 // mark that bit off in QueueSlots
1493 // so we can know we it is really needed to activate port or not
1494 PortExtension
->QueueSlots
&= ~slotToActivate
;
1495 // mark this CommandIssuedSlots
1496 // to validate in completeIssuedCommand
1497 PortExtension
->CommandIssuedSlots
|= slotToActivate
;
1499 // tell the HBA to issue this Command Slot to the given port
1500 StorPortWriteRegisterUlong(AdapterExtension
, &PortExtension
->Port
->CI
, slotToActivate
);
1503 }// -- AhciActivatePort();
1506 * @name AhciProcessIO
1509 * Acquire Exclusive lock to port, populate pending commands to command List
1510 * program controller's port to process new commands in command list.
1512 * @param AdapterExtension
1519 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1521 __in PSCSI_REQUEST_BLOCK Srb
1524 PSCSI_REQUEST_BLOCK tmpSrb
;
1525 STOR_LOCK_HANDLE lockhandle
= {0};
1526 PAHCI_PORT_EXTENSION PortExtension
;
1527 ULONG commandSlotMask
, occupiedSlots
, slotIndex
, NCS
;
1529 AhciDebugPrint("AhciProcessIO()\n");
1530 AhciDebugPrint("\tPathId: %d\n", PathId
);
1532 PortExtension
= &AdapterExtension
->PortExtension
[PathId
];
1534 NT_ASSERT(PathId
< AdapterExtension
->PortCount
);
1537 StorPortAcquireSpinLock(AdapterExtension
, InterruptLock
, NULL
, &lockhandle
);
1540 AddQueue(&PortExtension
->SrbQueue
, Srb
);
1542 if (PortExtension
->DeviceParams
.IsActive
== FALSE
)
1545 StorPortReleaseSpinLock(AdapterExtension
, &lockhandle
);
1546 return; // we should wait for device to get active
1549 occupiedSlots
= (PortExtension
->QueueSlots
| PortExtension
->CommandIssuedSlots
); // Busy command slots for given port
1550 NCS
= AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
);
1551 commandSlotMask
= (1 << NCS
) - 1; // available slots mask
1553 commandSlotMask
= (commandSlotMask
& ~occupiedSlots
);
1554 if(commandSlotMask
!= 0)
1556 // iterate over HBA port slots
1557 for (slotIndex
= 0; slotIndex
< NCS
; slotIndex
++)
1559 // find first free slot
1560 if ((commandSlotMask
& (1 << slotIndex
)) != 0)
1562 tmpSrb
= RemoveQueue(&PortExtension
->SrbQueue
);
1565 NT_ASSERT(tmpSrb
->PathId
== PathId
);
1566 AhciProcessSrb(PortExtension
, tmpSrb
, slotIndex
);
1581 AhciActivatePort(PortExtension
);
1584 StorPortReleaseSpinLock(AdapterExtension
, &lockhandle
);
1587 }// -- AhciProcessIO();
1590 * @name AtapiInquiryCompletion
1593 * AtapiInquiryCompletion routine should be called after device signals
1594 * for device inquiry request is completed (through interrupt) -- ATAPI Device only
1596 * @param PortExtension
1601 AtapiInquiryCompletion (
1602 __in PAHCI_PORT_EXTENSION PortExtension
,
1603 __in PSCSI_REQUEST_BLOCK Srb
1607 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
1609 AhciDebugPrint("AtapiInquiryCompletion()\n");
1611 NT_ASSERT(Srb
!= NULL
);
1612 NT_ASSERT(PortExtension
!= NULL
);
1614 AdapterExtension
= PortExtension
->AdapterExtension
;
1617 status
= StorPortSetDeviceQueueDepth(PortExtension
->AdapterExtension
,
1621 AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
));
1623 NT_ASSERT(status
== TRUE
);
1625 }// -- AtapiInquiryCompletion();
1628 * @name InquiryCompletion
1631 * InquiryCompletion routine should be called after device signals
1632 * for device inquiry request is completed (through interrupt)
1634 * @param PortExtension
1640 __in PAHCI_PORT_EXTENSION PortExtension
,
1641 __in PSCSI_REQUEST_BLOCK Srb
1646 PINQUIRYDATA InquiryData
;
1647 PAHCI_SRB_EXTENSION SrbExtension
;
1648 PAHCI_ADAPTER_EXTENSION AdapterExtension
;
1649 PIDENTIFY_DEVICE_DATA IdentifyDeviceData
;
1651 AhciDebugPrint("InquiryCompletion()\n");
1653 NT_ASSERT(Srb
!= NULL
);
1654 NT_ASSERT(PortExtension
!= NULL
);
1656 cdb
= (PCDB
)&Srb
->Cdb
;
1657 InquiryData
= Srb
->DataBuffer
;
1658 SrbExtension
= GetSrbExtension(Srb
);
1659 AdapterExtension
= PortExtension
->AdapterExtension
;
1660 IdentifyDeviceData
= PortExtension
->IdentifyDeviceData
;
1662 if (Srb
->SrbStatus
!= SRB_STATUS_SUCCESS
)
1664 if (Srb
->SrbStatus
== SRB_STATUS_NO_DEVICE
)
1666 PortExtension
->DeviceParams
.DeviceType
= AHCI_DEVICE_TYPE_NODEVICE
;
1671 NT_ASSERT(InquiryData
!= NULL
);
1672 NT_ASSERT(Srb
->SrbStatus
== SRB_STATUS_SUCCESS
);
1674 // Device specific data
1675 PortExtension
->DeviceParams
.MaxLba
.QuadPart
= 0;
1677 if (SrbExtension
->CommandReg
== IDE_COMMAND_IDENTIFY
)
1679 PortExtension
->DeviceParams
.DeviceType
= AHCI_DEVICE_TYPE_ATA
;
1680 if (IdentifyDeviceData
->GeneralConfiguration
.RemovableMedia
)
1682 PortExtension
->DeviceParams
.RemovableDevice
= 1;
1685 if ((IdentifyDeviceData
->CommandSetSupport
.BigLba
) && (IdentifyDeviceData
->CommandSetActive
.BigLba
))
1687 PortExtension
->DeviceParams
.Lba48BitMode
= 1;
1690 PortExtension
->DeviceParams
.AccessType
= DIRECT_ACCESS_DEVICE
;
1692 /* Device max address lba */
1693 if (PortExtension
->DeviceParams
.Lba48BitMode
)
1695 PortExtension
->DeviceParams
.MaxLba
.LowPart
= IdentifyDeviceData
->Max48BitLBA
[0];
1696 PortExtension
->DeviceParams
.MaxLba
.HighPart
= IdentifyDeviceData
->Max48BitLBA
[1];
1700 PortExtension
->DeviceParams
.MaxLba
.LowPart
= IdentifyDeviceData
->UserAddressableSectors
;
1703 /* Bytes Per Logical Sector */
1704 if (IdentifyDeviceData
->PhysicalLogicalSectorSize
.LogicalSectorLongerThan256Words
)
1706 AhciDebugPrint("\tBytesPerLogicalSector != DEVICE_ATA_BLOCK_SIZE\n");
1710 PortExtension
->DeviceParams
.BytesPerLogicalSector
= DEVICE_ATA_BLOCK_SIZE
;
1712 /* Bytes Per Physical Sector */
1713 if (IdentifyDeviceData
->PhysicalLogicalSectorSize
.MultipleLogicalSectorsPerPhysicalSector
)
1715 AhciDebugPrint("\tBytesPerPhysicalSector != DEVICE_ATA_BLOCK_SIZE\n");
1719 PortExtension
->DeviceParams
.BytesPerPhysicalSector
= DEVICE_ATA_BLOCK_SIZE
;
1721 // last byte should be NULL
1722 StorPortCopyMemory(PortExtension
->DeviceParams
.VendorId
, IdentifyDeviceData
->ModelNumber
, sizeof(PortExtension
->DeviceParams
.VendorId
) - 1);
1723 StorPortCopyMemory(PortExtension
->DeviceParams
.RevisionID
, IdentifyDeviceData
->FirmwareRevision
, sizeof(PortExtension
->DeviceParams
.RevisionID
) - 1);
1724 StorPortCopyMemory(PortExtension
->DeviceParams
.SerialNumber
, IdentifyDeviceData
->SerialNumber
, sizeof(PortExtension
->DeviceParams
.SerialNumber
) - 1);
1726 PortExtension
->DeviceParams
.VendorId
[sizeof(PortExtension
->DeviceParams
.VendorId
) - 1] = '\0';
1727 PortExtension
->DeviceParams
.RevisionID
[sizeof(PortExtension
->DeviceParams
.RevisionID
) - 1] = '\0';
1728 PortExtension
->DeviceParams
.SerialNumber
[sizeof(PortExtension
->DeviceParams
.SerialNumber
) - 1] = '\0';
1730 // TODO: Add other device params
1731 AhciDebugPrint("\tATA Device\n");
1735 AhciDebugPrint("\tATAPI Device\n");
1736 PortExtension
->DeviceParams
.DeviceType
= AHCI_DEVICE_TYPE_ATAPI
;
1737 PortExtension
->DeviceParams
.AccessType
= READ_ONLY_DIRECT_ACCESS_DEVICE
;
1740 // INQUIRYDATABUFFERSIZE = 36 ; Defined in storport.h
1741 if (Srb
->DataTransferLength
< INQUIRYDATABUFFERSIZE
)
1743 AhciDebugPrint("\tDataBufferLength < sizeof(INQUIRYDATA), Could crash the driver.\n");
1747 // update data transfer length
1748 Srb
->DataTransferLength
= INQUIRYDATABUFFERSIZE
;
1750 // prepare data to send
1751 InquiryData
->Versions
= 2;
1752 InquiryData
->Wide32Bit
= 1;
1753 InquiryData
->CommandQueue
= 0; // NCQ not supported
1754 InquiryData
->ResponseDataFormat
= 0x2;
1755 InquiryData
->DeviceTypeModifier
= 0;
1756 InquiryData
->DeviceTypeQualifier
= DEVICE_CONNECTED
;
1757 InquiryData
->AdditionalLength
= INQUIRYDATABUFFERSIZE
- 5;
1758 InquiryData
->DeviceType
= PortExtension
->DeviceParams
.AccessType
;
1759 InquiryData
->RemovableMedia
= PortExtension
->DeviceParams
.RemovableDevice
;
1761 // Fill VendorID, Product Revision Level and other string fields
1762 StorPortCopyMemory(InquiryData
->VendorId
, PortExtension
->DeviceParams
.VendorId
, sizeof(InquiryData
->VendorId
) - 1);
1763 StorPortCopyMemory(InquiryData
->ProductId
, PortExtension
->DeviceParams
.RevisionID
, sizeof(PortExtension
->DeviceParams
.RevisionID
));
1764 StorPortCopyMemory(InquiryData
->ProductRevisionLevel
, PortExtension
->DeviceParams
.SerialNumber
, sizeof(InquiryData
->ProductRevisionLevel
) - 1);
1766 InquiryData
->VendorId
[sizeof(InquiryData
->VendorId
) - 1] = '\0';
1767 InquiryData
->ProductId
[sizeof(InquiryData
->ProductId
) - 1] = '\0';
1768 InquiryData
->ProductRevisionLevel
[sizeof(InquiryData
->ProductRevisionLevel
) - 1] = '\0';
1771 status
= StorPortSetDeviceQueueDepth(PortExtension
->AdapterExtension
,
1775 AHCI_Global_Port_CAP_NCS(AdapterExtension
->CAP
));
1777 NT_ASSERT(status
== TRUE
);
1779 }// -- InquiryCompletion();
1782 * @name AhciATAPICommand
1785 * Handles ATAPI Requests commands
1787 * @param AdapterExtension
1792 * return STOR status for AhciATAPICommand
1796 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1797 __in PSCSI_REQUEST_BLOCK Srb
,
1801 ULONG SrbFlags
, DataBufferLength
;
1802 PAHCI_SRB_EXTENSION SrbExtension
;
1803 PAHCI_PORT_EXTENSION PortExtension
;
1805 AhciDebugPrint("AhciATAPICommand()\n");
1807 SrbFlags
= Srb
->SrbFlags
;
1808 SrbExtension
= GetSrbExtension(Srb
);
1809 DataBufferLength
= Srb
->DataTransferLength
;
1810 PortExtension
= &AdapterExtension
->PortExtension
[Srb
->PathId
];
1812 NT_ASSERT(PortExtension
->DeviceParams
.DeviceType
== AHCI_DEVICE_TYPE_ATAPI
);
1814 NT_ASSERT(SrbExtension
!= NULL
);
1816 SrbExtension
->AtaFunction
= ATA_FUNCTION_ATAPI_COMMAND
;
1817 SrbExtension
->Flags
= 0;
1819 if (SrbFlags
& SRB_FLAGS_DATA_IN
)
1821 SrbExtension
->Flags
|= ATA_FLAGS_DATA_IN
;
1824 if (SrbFlags
& SRB_FLAGS_DATA_OUT
)
1826 SrbExtension
->Flags
|= ATA_FLAGS_DATA_OUT
;
1829 SrbExtension
->FeaturesLow
= 0;
1831 SrbExtension
->CompletionRoutine
= NULL
;
1833 NT_ASSERT(Cdb
!= NULL
);
1834 switch(Cdb
->CDB10
.OperationCode
)
1836 case SCSIOP_INQUIRY
:
1837 SrbExtension
->Flags
|= ATA_FLAGS_DATA_IN
;
1838 SrbExtension
->CompletionRoutine
= AtapiInquiryCompletion
;
1841 SrbExtension
->Flags
|= ATA_FLAGS_USE_DMA
;
1842 SrbExtension
->FeaturesLow
= 0x5;
1845 SrbExtension
->Flags
|= ATA_FLAGS_USE_DMA
;
1846 SrbExtension
->FeaturesLow
= 0x1;
1850 SrbExtension
->CommandReg
= IDE_COMMAND_ATAPI_PACKET
;
1852 SrbExtension
->LBA0
= 0;
1853 SrbExtension
->LBA1
= (UCHAR
)(DataBufferLength
>> 0);
1854 SrbExtension
->LBA2
= (UCHAR
)(DataBufferLength
>> 8);
1855 SrbExtension
->Device
= 0;
1856 SrbExtension
->LBA3
= 0;
1857 SrbExtension
->LBA4
= 0;
1858 SrbExtension
->LBA5
= 0;
1859 SrbExtension
->FeaturesHigh
= 0;
1860 SrbExtension
->SectorCountLow
= 0;
1861 SrbExtension
->SectorCountHigh
= 0;
1863 if ((SrbExtension
->Flags
& ATA_FLAGS_DATA_IN
) || (SrbExtension
->Flags
& ATA_FLAGS_DATA_OUT
))
1865 SrbExtension
->pSgl
= (PLOCAL_SCATTER_GATHER_LIST
)StorPortGetScatterGatherList(AdapterExtension
, Srb
);
1868 return SRB_STATUS_PENDING
;
1869 }// -- AhciATAPICommand();
1872 * @name DeviceRequestSense
1875 * Handle SCSIOP_MODE_SENSE OperationCode
1877 * @param AdapterExtension
1882 * return STOR status for DeviceRequestSense
1885 DeviceRequestSense (
1886 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1887 __in PSCSI_REQUEST_BLOCK Srb
,
1891 PMODE_PARAMETER_HEADER ModeHeader
;
1892 PAHCI_PORT_EXTENSION PortExtension
;
1894 AhciDebugPrint("DeviceRequestSense()\n");
1896 NT_ASSERT(IsPortValid(AdapterExtension
, Srb
->PathId
));
1897 NT_ASSERT(Cdb
->CDB10
.OperationCode
== SCSIOP_MODE_SENSE
);
1899 PortExtension
= &AdapterExtension
->PortExtension
[Srb
->PathId
];
1901 if (PortExtension
->DeviceParams
.DeviceType
== AHCI_DEVICE_TYPE_ATAPI
)
1903 return AhciATAPICommand(AdapterExtension
, Srb
, Cdb
);
1906 ModeHeader
= (PMODE_PARAMETER_HEADER
)Srb
->DataBuffer
;
1908 NT_ASSERT(ModeHeader
!= NULL
);
1910 AhciZeroMemory((PCHAR
)ModeHeader
, Srb
->DataTransferLength
);
1912 ModeHeader
->ModeDataLength
= sizeof(MODE_PARAMETER_HEADER
);
1913 ModeHeader
->MediumType
= 0;
1914 ModeHeader
->DeviceSpecificParameter
= 0;
1915 ModeHeader
->BlockDescriptorLength
= 0;
1917 if (Cdb
->MODE_SENSE
.PageCode
== MODE_SENSE_CURRENT_VALUES
)
1919 ModeHeader
->ModeDataLength
= sizeof(MODE_PARAMETER_HEADER
) + sizeof(MODE_PARAMETER_BLOCK
);
1920 ModeHeader
->BlockDescriptorLength
= sizeof(MODE_PARAMETER_BLOCK
);
1923 return SRB_STATUS_SUCCESS
;
1924 }// -- DeviceRequestSense();
1927 * @name DeviceRequestReadWrite
1930 * Handle SCSIOP_READ SCSIOP_WRITE OperationCode
1932 * @param AdapterExtension
1937 * return STOR status for DeviceRequestReadWrite
1940 DeviceRequestReadWrite (
1941 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
1942 __in PSCSI_REQUEST_BLOCK Srb
,
1947 ULONG64 StartOffset
;
1948 PAHCI_SRB_EXTENSION SrbExtension
;
1949 PAHCI_PORT_EXTENSION PortExtension
;
1950 ULONG DataTransferLength
, BytesPerSector
, SectorCount
;
1952 AhciDebugPrint("DeviceRequestReadWrite()\n");
1954 NT_ASSERT(IsPortValid(AdapterExtension
, Srb
->PathId
));
1955 NT_ASSERT((Cdb
->CDB10
.OperationCode
== SCSIOP_READ
) || (Cdb
->CDB10
.OperationCode
== SCSIOP_WRITE
));
1957 SrbExtension
= GetSrbExtension(Srb
);
1958 PortExtension
= &AdapterExtension
->PortExtension
[Srb
->PathId
];
1960 if (PortExtension
->DeviceParams
.DeviceType
== AHCI_DEVICE_TYPE_ATAPI
)
1962 return AhciATAPICommand(AdapterExtension
, Srb
, Cdb
);
1965 DataTransferLength
= Srb
->DataTransferLength
;
1966 BytesPerSector
= PortExtension
->DeviceParams
.BytesPerLogicalSector
;
1968 NT_ASSERT(BytesPerSector
> 0);
1970 //ROUND_UP(DataTransferLength, BytesPerSector);
1972 SectorCount
= DataTransferLength
/ BytesPerSector
;
1974 Srb
->DataTransferLength
= SectorCount
* BytesPerSector
;
1976 StartOffset
= AhciGetLba(Cdb
, Srb
->CdbLength
);
1977 IsReading
= (Cdb
->CDB10
.OperationCode
== SCSIOP_READ
);
1979 NT_ASSERT(SectorCount
> 0);
1981 SrbExtension
->AtaFunction
= ATA_FUNCTION_ATA_READ
;
1982 SrbExtension
->Flags
|= ATA_FLAGS_USE_DMA
;
1983 SrbExtension
->CompletionRoutine
= NULL
;
1987 SrbExtension
->Flags
|= ATA_FLAGS_DATA_IN
;
1988 SrbExtension
->CommandReg
= IDE_COMMAND_READ_DMA
;
1992 SrbExtension
->Flags
|= ATA_FLAGS_DATA_OUT
;
1993 SrbExtension
->CommandReg
= IDE_COMMAND_WRITE_DMA
;
1996 SrbExtension
->FeaturesLow
= 0;
1997 SrbExtension
->LBA0
= (StartOffset
>> 0) & 0xFF;
1998 SrbExtension
->LBA1
= (StartOffset
>> 8) & 0xFF;
1999 SrbExtension
->LBA2
= (StartOffset
>> 16) & 0xFF;
2001 SrbExtension
->Device
= (0xA0 | IDE_LBA_MODE
);
2003 if (PortExtension
->DeviceParams
.Lba48BitMode
)
2005 SrbExtension
->Flags
|= ATA_FLAGS_48BIT_COMMAND
;
2009 SrbExtension
->CommandReg
= IDE_COMMAND_READ_DMA_EXT
;
2013 SrbExtension
->CommandReg
= IDE_COMMAND_WRITE_DMA_EXT
;
2016 SrbExtension
->LBA3
= (StartOffset
>> 24) & 0xFF;
2017 SrbExtension
->LBA4
= (StartOffset
>> 32) & 0xFF;
2018 SrbExtension
->LBA5
= (StartOffset
>> 40) & 0xFF;
2025 SrbExtension
->FeaturesHigh
= 0;
2026 SrbExtension
->SectorCountLow
= (SectorCount
>> 0) & 0xFF;
2027 SrbExtension
->SectorCountHigh
= (SectorCount
>> 8) & 0xFF;
2029 NT_ASSERT(SectorCount
< 0x100);
2031 SrbExtension
->pSgl
= (PLOCAL_SCATTER_GATHER_LIST
)StorPortGetScatterGatherList(AdapterExtension
, Srb
);
2033 return SRB_STATUS_PENDING
;
2034 }// -- DeviceRequestReadWrite();
2037 * @name DeviceRequestCapacity
2040 * Handle SCSIOP_READ_CAPACITY OperationCode
2042 * @param AdapterExtension
2047 * return STOR status for DeviceRequestCapacity
2050 DeviceRequestCapacity (
2051 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
2052 __in PSCSI_REQUEST_BLOCK Srb
,
2056 ULONG MaxLba
, BytesPerLogicalSector
;
2057 PREAD_CAPACITY_DATA ReadCapacity
;
2058 PAHCI_PORT_EXTENSION PortExtension
;
2060 AhciDebugPrint("DeviceRequestCapacity()\n");
2062 UNREFERENCED_PARAMETER(AdapterExtension
);
2063 UNREFERENCED_PARAMETER(Cdb
);
2065 NT_ASSERT(Srb
->DataBuffer
!= NULL
);
2066 NT_ASSERT(IsPortValid(AdapterExtension
, Srb
->PathId
));
2069 PortExtension
= &AdapterExtension
->PortExtension
[Srb
->PathId
];
2071 if (PortExtension
->DeviceParams
.DeviceType
== AHCI_DEVICE_TYPE_ATAPI
)
2073 return AhciATAPICommand(AdapterExtension
, Srb
, Cdb
);
2076 if (Cdb
->CDB10
.OperationCode
== SCSIOP_READ_CAPACITY
)
2078 ReadCapacity
= (PREAD_CAPACITY_DATA
)Srb
->DataBuffer
;
2080 BytesPerLogicalSector
= PortExtension
->DeviceParams
.BytesPerLogicalSector
;
2081 MaxLba
= (ULONG
)PortExtension
->DeviceParams
.MaxLba
.QuadPart
- 1;
2083 // I trust you windows :D
2084 NT_ASSERT(Srb
->DataTransferLength
>= sizeof(READ_CAPACITY_DATA
));
2086 // I trust you user :D
2087 NT_ASSERT(PortExtension
->DeviceParams
.MaxLba
.QuadPart
< (ULONG
)-1);
2089 // Actually I don't trust anyone :p
2090 Srb
->DataTransferLength
= sizeof(READ_CAPACITY_DATA
);
2092 REVERSE_BYTES(&ReadCapacity
->BytesPerBlock
, &BytesPerLogicalSector
);
2093 REVERSE_BYTES(&ReadCapacity
->LogicalBlockAddress
, &MaxLba
);
2097 AhciDebugPrint("\tSCSIOP_READ_CAPACITY16 not supported\n");
2101 return SRB_STATUS_SUCCESS
;
2102 }// -- DeviceRequestCapacity();
2105 * @name DeviceRequestComplete
2108 * Handle UnHandled Requests
2110 * @param AdapterExtension
2115 * return STOR status for DeviceRequestComplete
2118 DeviceRequestComplete (
2119 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
2120 __in PSCSI_REQUEST_BLOCK Srb
,
2124 AhciDebugPrint("DeviceRequestComplete()\n");
2126 UNREFERENCED_PARAMETER(AdapterExtension
);
2127 UNREFERENCED_PARAMETER(Cdb
);
2129 Srb
->ScsiStatus
= SCSISTAT_GOOD
;
2131 return SRB_STATUS_SUCCESS
;
2132 }// -- DeviceRequestComplete();
2135 * @name DeviceReportLuns
2138 * Handle SCSIOP_REPORT_LUNS OperationCode
2140 * @param AdapterExtension
2145 * return STOR status for DeviceReportLuns
2149 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
2150 __in PSCSI_REQUEST_BLOCK Srb
,
2155 PAHCI_PORT_EXTENSION PortExtension
;
2157 AhciDebugPrint("DeviceReportLuns()\n");
2159 UNREFERENCED_PARAMETER(Cdb
);
2161 PortExtension
= &AdapterExtension
->PortExtension
[Srb
->PathId
];
2163 NT_ASSERT(Srb
->DataTransferLength
>= sizeof(LUN_LIST
));
2164 NT_ASSERT(Cdb
->CDB10
.OperationCode
== SCSIOP_REPORT_LUNS
);
2166 if (PortExtension
->DeviceParams
.DeviceType
== AHCI_DEVICE_TYPE_ATAPI
)
2168 return AhciATAPICommand(AdapterExtension
, Srb
, Cdb
);
2171 LunList
= (PLUN_LIST
)Srb
->DataBuffer
;
2173 NT_ASSERT(LunList
!= NULL
);
2175 AhciZeroMemory((PCHAR
)LunList
, sizeof(LUN_LIST
));
2177 LunList
->LunListLength
[3] = 8;
2179 Srb
->ScsiStatus
= SCSISTAT_GOOD
;
2180 Srb
->DataTransferLength
= sizeof(LUN_LIST
);
2182 return SRB_STATUS_SUCCESS
;
2183 }// -- DeviceReportLuns();
2186 * @name DeviceInquiryRequest
2189 * Tells wheather given port is implemented or not
2191 * @param AdapterExtension
2196 * return STOR status for DeviceInquiryRequest
2199 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
2202 DeviceInquiryRequest (
2203 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
2204 __in PSCSI_REQUEST_BLOCK Srb
,
2209 PAHCI_SRB_EXTENSION SrbExtension
;
2210 PAHCI_PORT_EXTENSION PortExtension
;
2211 PVPD_SUPPORTED_PAGES_PAGE VpdOutputBuffer
;
2212 ULONG DataBufferLength
, RequiredDataBufferLength
;
2214 AhciDebugPrint("DeviceInquiryRequest()\n");
2216 NT_ASSERT(Cdb
->CDB10
.OperationCode
== SCSIOP_INQUIRY
);
2217 NT_ASSERT(IsPortValid(AdapterExtension
, Srb
->PathId
));
2219 SrbExtension
= GetSrbExtension(Srb
);
2220 PortExtension
= &AdapterExtension
->PortExtension
[Srb
->PathId
];
2222 if (PortExtension
->DeviceParams
.DeviceType
== AHCI_DEVICE_TYPE_ATAPI
)
2224 return AhciATAPICommand(AdapterExtension
, Srb
, Cdb
);
2229 return SRB_STATUS_SELECTION_TIMEOUT
;
2231 else if (Cdb
->CDB6INQUIRY3
.EnableVitalProductData
== 0)
2234 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
2235 AhciDebugPrint("\tEVPD Inquired\n");
2236 NT_ASSERT(SrbExtension
!= NULL
);
2238 SrbExtension
->AtaFunction
= ATA_FUNCTION_ATA_IDENTIFY
;
2239 SrbExtension
->Flags
|= ATA_FLAGS_DATA_IN
;
2240 SrbExtension
->CompletionRoutine
= InquiryCompletion
;
2241 SrbExtension
->CommandReg
= IDE_COMMAND_NOT_VALID
;
2243 // TODO: Should use AhciZeroMemory
2244 SrbExtension
->FeaturesLow
= 0;
2245 SrbExtension
->LBA0
= 0;
2246 SrbExtension
->LBA1
= 0;
2247 SrbExtension
->LBA2
= 0;
2248 SrbExtension
->Device
= 0xA0;
2249 SrbExtension
->LBA3
= 0;
2250 SrbExtension
->LBA4
= 0;
2251 SrbExtension
->LBA5
= 0;
2252 SrbExtension
->FeaturesHigh
= 0;
2253 SrbExtension
->SectorCountLow
= 0;
2254 SrbExtension
->SectorCountHigh
= 0;
2256 SrbExtension
->Sgl
.NumberOfElements
= 1;
2257 SrbExtension
->Sgl
.List
[0].PhysicalAddress
.LowPart
= PortExtension
->IdentifyDeviceDataPhysicalAddress
.LowPart
;
2258 SrbExtension
->Sgl
.List
[0].PhysicalAddress
.HighPart
= PortExtension
->IdentifyDeviceDataPhysicalAddress
.HighPart
;
2259 SrbExtension
->Sgl
.List
[0].Length
= sizeof(IDENTIFY_DEVICE_DATA
);
2261 SrbExtension
->pSgl
= &SrbExtension
->Sgl
;
2262 return SRB_STATUS_PENDING
;
2266 AhciDebugPrint("\tVPD Inquired\n");
2268 DataBuffer
= Srb
->DataBuffer
;
2269 DataBufferLength
= Srb
->DataTransferLength
;
2270 RequiredDataBufferLength
= DataBufferLength
; // make the compiler happy :p
2272 if (DataBuffer
== NULL
)
2274 return SRB_STATUS_INVALID_REQUEST
;
2277 AhciZeroMemory(DataBuffer
, DataBufferLength
);
2279 switch(Cdb
->CDB6INQUIRY3
.PageCode
)
2281 case VPD_SUPPORTED_PAGES
:
2283 AhciDebugPrint("\tVPD_SUPPORTED_PAGES\n");
2284 RequiredDataBufferLength
= sizeof(VPD_SUPPORTED_PAGES_PAGE
) + 1;
2286 if (DataBufferLength
< RequiredDataBufferLength
)
2288 AhciDebugPrint("\tDataBufferLength: %d Required: %d\n", DataBufferLength
, RequiredDataBufferLength
);
2289 return SRB_STATUS_INVALID_REQUEST
;
2292 VpdOutputBuffer
= (PVPD_SUPPORTED_PAGES_PAGE
)DataBuffer
;
2294 VpdOutputBuffer
->DeviceType
= PortExtension
->DeviceParams
.AccessType
;
2295 VpdOutputBuffer
->DeviceTypeQualifier
= 0;
2296 VpdOutputBuffer
->PageCode
= VPD_SUPPORTED_PAGES
;
2297 VpdOutputBuffer
->PageLength
= 1;
2298 VpdOutputBuffer
->SupportedPageList
[0] = VPD_SUPPORTED_PAGES
;
2299 //VpdOutputBuffer->SupportedPageList[1] = VPD_SERIAL_NUMBER;
2300 //VpdOutputBuffer->SupportedPageList[2] = VPD_DEVICE_IDENTIFIERS;
2302 NT_ASSERT(VpdOutputBuffer
->DeviceType
== DIRECT_ACCESS_DEVICE
);
2305 case VPD_SERIAL_NUMBER
:
2307 AhciDebugPrint("\tVPD_SERIAL_NUMBER\n");
2310 case VPD_DEVICE_IDENTIFIERS
:
2312 AhciDebugPrint("\tVPD_DEVICE_IDENTIFIERS\n");
2316 AhciDebugPrint("\tPageCode: %x\n", Cdb
->CDB6INQUIRY3
.PageCode
);
2317 return SRB_STATUS_INVALID_REQUEST
;
2320 Srb
->DataTransferLength
= RequiredDataBufferLength
;
2321 return SRB_STATUS_SUCCESS
;
2323 }// -- DeviceInquiryRequest();
2326 * @name AhciAdapterReset
2330 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
2331 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
2332 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
2333 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
2334 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
2335 * the HBA reset has completed.
2336 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
2337 * a hung or locked state.
2339 * @param AdapterExtension
2342 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
2346 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
2351 PAHCI_MEMORY_REGISTERS abar
= NULL
;
2353 AhciDebugPrint("AhciAdapterReset()\n");
2355 abar
= AdapterExtension
->ABAR_Address
;
2356 if (abar
== NULL
) // basic sanity
2361 // HR -- Very first bit (lowest significant)
2363 StorPortWriteRegisterUlong(AdapterExtension
, &abar
->GHC
, ghc
.Status
);
2365 for (ticks
= 0; ticks
< 50; ++ticks
)
2367 ghc
.Status
= StorPortReadRegisterUlong(AdapterExtension
, &abar
->GHC
);
2372 StorPortStallExecution(20000);
2375 if (ticks
== 50)// 1 second
2377 AhciDebugPrint("\tDevice Timeout\n");
2382 }// -- AhciAdapterReset();
2385 * @name AhciZeroMemory
2388 * Clear buffer by filling zeros
2397 __in ULONG BufferSize
2401 for (i
= 0; i
< BufferSize
; i
++)
2407 }// -- AhciZeroMemory();
2413 * Tells wheather given port is implemented or not
2415 * @param AdapterExtension
2419 * return TRUE if provided port is valid (implemented) or not
2424 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
,
2428 NT_ASSERT(pathId
< MAXIMUM_AHCI_PORT_COUNT
);
2430 if (pathId
>= AdapterExtension
->PortCount
)
2435 return AdapterExtension
->PortExtension
[pathId
].DeviceParams
.IsActive
;
2436 }// -- IsPortValid()
2448 * return TRUE if Srb is successfully added to Queue
2454 __inout PAHCI_QUEUE Queue
,
2458 NT_ASSERT(Queue
->Head
< MAXIMUM_QUEUE_BUFFER_SIZE
);
2459 NT_ASSERT(Queue
->Tail
< MAXIMUM_QUEUE_BUFFER_SIZE
);
2461 if (Queue
->Tail
== ((Queue
->Head
+ 1) % MAXIMUM_QUEUE_BUFFER_SIZE
))
2464 Queue
->Buffer
[Queue
->Head
++] = Srb
;
2465 Queue
->Head
%= MAXIMUM_QUEUE_BUFFER_SIZE
;
2474 * Remove and return Srb from Queue
2485 __inout PAHCI_QUEUE Queue
2490 NT_ASSERT(Queue
->Head
< MAXIMUM_QUEUE_BUFFER_SIZE
);
2491 NT_ASSERT(Queue
->Tail
< MAXIMUM_QUEUE_BUFFER_SIZE
);
2493 if (Queue
->Head
== Queue
->Tail
)
2496 Srb
= Queue
->Buffer
[Queue
->Tail
++];
2497 Queue
->Tail
%= MAXIMUM_QUEUE_BUFFER_SIZE
;
2500 }// -- RemoveQueue();
2503 * @name GetSrbExtension
2506 * GetSrbExtension from Srb make sure It is properly aligned
2511 * return SrbExtension
2517 __in PSCSI_REQUEST_BLOCK Srb
2521 ULONG_PTR SrbExtension
;
2523 SrbExtension
= (ULONG_PTR
)Srb
->SrbExtension
;
2524 Offset
= SrbExtension
% 128;
2526 // CommandTable should be 128 byte aligned
2528 Offset
= 128 - Offset
;
2530 return (PAHCI_SRB_EXTENSION
)(SrbExtension
+ Offset
);
2531 }// -- PAHCI_SRB_EXTENSION();
2537 * Find the logical address of demand block from Cdb
2542 * return Logical Address of the block
2549 __in ULONG CdbLength
2554 NT_ASSERT(Cdb
!= NULL
);
2555 NT_ASSERT(CdbLength
!= 0);
2557 if (CdbLength
== 0x10)
2559 REVERSE_BYTES_QUAD(&lba
, Cdb
->CDB16
.LogicalBlock
);
2563 lba
|= Cdb
->CDB10
.LogicalBlockByte3
<< 0;
2564 lba
|= Cdb
->CDB10
.LogicalBlockByte2
<< 8;
2565 lba
|= Cdb
->CDB10
.LogicalBlockByte1
<< 16;
2566 lba
|= Cdb
->CDB10
.LogicalBlockByte0
<< 24;
2570 }// -- AhciGetLba();