- Completed Interrupt Handler Routine
[reactos.git] / drivers / storage / storahci / storahci.c
1 /*
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)
6 */
7
8 #include "storahci.h"
9
10 /**
11 * @name AhciPortInitialize
12 * @implemented
13 *
14 * Initialize port by setting up PxCLB & PxFB Registers
15 *
16 * @param PortExtension
17 *
18 * @return
19 * Return true if intialization was successful
20 */
21 BOOLEAN
22 AhciPortInitialize (
23 __in PAHCI_PORT_EXTENSION PortExtension
24 )
25 {
26 ULONG mappedLength, portNumber;
27 PAHCI_MEMORY_REGISTERS abar;
28 PAHCI_ADAPTER_EXTENSION adapterExtension;
29 STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical;
30
31 DebugPrint("AhciPortInitialize()\n");
32
33 adapterExtension = PortExtension->AdapterExtension;
34 abar = adapterExtension->ABAR_Address;
35 portNumber = PortExtension->PortNumber;
36
37 NT_ASSERT(abar != NULL);
38 NT_ASSERT(portNumber < adapterExtension->PortCount);
39
40 PortExtension->Port = &abar->PortList[portNumber];
41
42 commandListPhysical = StorPortGetPhysicalAddress(adapterExtension,
43 NULL,
44 PortExtension->CommandList,
45 &mappedLength);
46
47 if ((mappedLength == 0) || ((commandListPhysical.LowPart % 1024) != 0))
48 {
49 DebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength);
50 return FALSE;
51 }
52
53 receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension,
54 NULL,
55 PortExtension->ReceivedFIS,
56 &mappedLength);
57
58 if ((mappedLength == 0) || ((receivedFISPhysical.LowPart % 256) != 0))
59 {
60 DebugPrint("\treceivedFISPhysical mappedLength:%d\n", mappedLength);
61 return FALSE;
62 }
63
64 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
65 //  PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
66 //  PxFB and PxFBU (if CAP.S64A is set to ‘1’)
67 // Note: Assuming 32bit support only
68 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLB, commandListPhysical.LowPart);
69 if (IsAdapterCAPS64(adapterExtension->CAP))
70 {
71 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLBU, commandListPhysical.HighPart);
72 }
73
74 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FB, receivedFISPhysical.LowPart);
75 if (IsAdapterCAPS64(adapterExtension->CAP))
76 {
77 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FBU, receivedFISPhysical.HighPart);
78 }
79
80 PortExtension->IdentifyDeviceDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension,
81 NULL,
82 PortExtension->IdentifyDeviceData,
83 &mappedLength);
84
85 NT_ASSERT(mappedLength == sizeof(IDENTIFY_DEVICE_DATA));
86
87 // set device power state flag to D0
88 PortExtension->DevicePowerState = StorPowerDeviceD0;
89
90 // clear pending interrupts
91 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->SERR, (ULONG)-1);
92 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->IS, (ULONG)-1);
93 StorPortWriteRegisterUlong(adapterExtension, PortExtension->AdapterExtension->IS, (1 << PortExtension->PortNumber));
94
95 return TRUE;
96 }// -- AhciPortInitialize();
97
98 /**
99 * @name AhciAllocateResourceForAdapter
100 * @implemented
101 *
102 * Allocate memory from poll for required pointers
103 *
104 * @param AdapterExtension
105 * @param ConfigInfo
106 *
107 * @return
108 * return TRUE if allocation was successful
109 */
110 BOOLEAN
111 AhciAllocateResourceForAdapter (
112 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
113 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
114 )
115 {
116 PVOID portsExtension = NULL;
117 PCHAR nonCachedExtension, tmp;
118 ULONG status, index, NCS, AlignedNCS;
119 ULONG portCount, portImplemented, nonCachedExtensionSize;
120
121 DebugPrint("AhciAllocateResourceForAdapter()\n");
122
123 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
124 AlignedNCS = ROUND_UP(NCS, 8);
125
126 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
127 portCount = 0;
128 portImplemented = AdapterExtension->PortImplemented;
129
130 NT_ASSERT(portImplemented != 0);
131 for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--)
132 if ((portImplemented & (1 << index)) != 0)
133 break;
134
135 portCount = index + 1;
136 DebugPrint("\tPort Count: %d\n", portCount);
137
138 AdapterExtension->PortCount = portCount;
139 nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
140 sizeof(AHCI_RECEIVED_FIS) +
141 sizeof(IDENTIFY_DEVICE_DATA);
142
143 // align nonCachedExtensionSize to 1024
144 nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024);
145
146 AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension,
147 ConfigInfo,
148 nonCachedExtensionSize * portCount);
149
150 if (AdapterExtension->NonCachedExtension == NULL)
151 {
152 DebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
153 return FALSE;
154 }
155
156 nonCachedExtension = AdapterExtension->NonCachedExtension;
157 AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount);
158
159 for (index = 0; index < portCount; index++)
160 {
161 AdapterExtension->PortExtension[index].IsActive = FALSE;
162 if ((AdapterExtension->PortImplemented & (1 << index)) != 0)
163 {
164 AdapterExtension->PortExtension[index].PortNumber = index;
165 AdapterExtension->PortExtension[index].IsActive = TRUE;
166 AdapterExtension->PortExtension[index].AdapterExtension = AdapterExtension;
167 AdapterExtension->PortExtension[index].CommandList = nonCachedExtension;
168
169 tmp = (PCHAR)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
170
171 AdapterExtension->PortExtension[index].ReceivedFIS = (PAHCI_RECEIVED_FIS)tmp;
172 AdapterExtension->PortExtension[index].IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)(tmp + sizeof(AHCI_RECEIVED_FIS));
173 nonCachedExtension += nonCachedExtensionSize;
174 }
175 }
176
177 return TRUE;
178 }// -- AhciAllocateResourceForAdapter();
179
180 /**
181 * @name AhciHwInitialize
182 * @implemented
183 *
184 * initializes the HBA and finds all devices that are of interest to the miniport driver.
185 *
186 * @param adapterExtension
187 *
188 * @return
189 * return TRUE if intialization was successful
190 */
191 BOOLEAN
192 AhciHwInitialize (
193 __in PVOID AdapterExtension
194 )
195 {
196 ULONG ghc, messageCount, status;
197 PAHCI_ADAPTER_EXTENSION adapterExtension;
198
199 DebugPrint("AhciHwInitialize()\n");
200
201 adapterExtension = AdapterExtension;
202 adapterExtension->StateFlags.MessagePerPort = FALSE;
203
204 // First check what type of interrupt/synchronization device is using
205 ghc = StorPortReadRegisterUlong(adapterExtension, &adapterExtension->ABAR_Address->GHC);
206
207 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
208 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
209 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
210 // software has allocated the number of messages requested
211 if ((ghc & AHCI_Global_HBA_CONTROL_MRSM) == 0)
212 {
213 adapterExtension->StateFlags.MessagePerPort = TRUE;
214 DebugPrint("\tMultiple MSI based message not supported\n");
215 }
216
217 return TRUE;
218 }// -- AhciHwInitialize();
219
220 /**
221 * @name AhciCompleteIssuedSrb
222 * @implemented
223 *
224 * Complete issued Srbs
225 *
226 * @param PortExtension
227 *
228 */
229 VOID
230 AhciCompleteIssuedSrb (
231 __in PAHCI_PORT_EXTENSION PortExtension,
232 __in ULONG CommandsToComplete
233 )
234 {
235 ULONG NCS, i;
236 PSCSI_REQUEST_BLOCK Srb;
237 PAHCI_SRB_EXTENSION SrbExtension;
238 PAHCI_ADAPTER_EXTENSION AdapterExtension;
239 PAHCI_COMPLETION_ROUTINE CompletionRoutine;
240
241 DebugPrint("AhciCompleteIssuedSrb()\n");
242
243 NT_ASSERT(CommandsToComplete != 0);
244
245 DebugPrint("\tCompleted Commands: %d\n", CommandsToComplete);
246
247 AdapterExtension = PortExtension->AdapterExtension;
248 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
249
250 for (i = 0; i < NCS; i++)
251 {
252 if (((1 << i) & CommandsToComplete) != 0)
253 {
254 Srb = &PortExtension->Slot[i];
255 NT_ASSERT(Srb != NULL);
256
257 if (Srb->SrbStatus == SRB_STATUS_PENDING)
258 {
259 Srb->SrbStatus = SRB_STATUS_SUCCESS;
260 }
261
262 SrbExtension = GetSrbExtension(Srb);
263 CompletionRoutine = SrbExtension->CompletionRoutine;
264
265 if (CompletionRoutine != NULL)
266 {
267 // now it's completion routine responsibility to set SrbStatus
268 CompletionRoutine(AdapterExtension, PortExtension, Srb);
269 }
270 else
271 {
272 Srb->SrbStatus = SRB_STATUS_SUCCESS;
273 StorPortNotification(RequestComplete, AdapterExtension, Srb);
274 }
275 }
276 }
277
278 return;
279 }// -- AhciCompleteIssuedSrb();
280
281 /**
282 * @name AhciInterruptHandler
283 * @not_implemented
284 *
285 * Interrupt Handler for PortExtension
286 *
287 * @param PortExtension
288 *
289 */
290 VOID
291 AhciInterruptHandler (
292 __in PAHCI_PORT_EXTENSION PortExtension
293 )
294 {
295 ULONG is, ci, sact, outstanding;
296 AHCI_INTERRUPT_STATUS PxIS;
297 AHCI_INTERRUPT_STATUS PxISMasked;
298 PAHCI_ADAPTER_EXTENSION AdapterExtension;
299
300 DebugPrint("AhciInterruptHandler()\n");
301 DebugPrint("\tPort Number: %d\n", PortExtension->PortNumber);
302
303 AdapterExtension = PortExtension->AdapterExtension;
304 NT_ASSERT(IsPortValid(AdapterExtension, PortExtension->PortNumber));
305
306 // 5.5.3
307 // 1. Software determines the cause of the interrupt by reading the PxIS register.
308 // It is possible for multiple bits to be set
309 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
310 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
311 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
312 // the list of commands previously issued by software that are still outstanding.
313 // If executing native queued commands, software reads the PxSACT register and compares the current
314 // value to the list of commands previously issued by software.
315 // Software completes with success any outstanding command whose corresponding bit has been cleared in
316 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
317 // to determine commands that have completed, not to determine which commands have previously been issued.
318 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
319 PxISMasked.Status = 0;
320 PxIS.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IS);
321
322 // 6.2.2
323 // Fatal Error
324 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
325 if (PxIS.HBFS || PxIS.HBDS || PxIS.IFS || PxIS.TFES)
326 {
327 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
328 // any native command queuing commands. To recover, the port must be restarted
329 // To detect an error that requires software recovery actions to be performed,
330 // software should check whether any of the following status bits are set on an interrupt:
331 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
332 // software should perform the appropriate error recovery actions based on whether
333 // non-queued commands were being issued or native command queuing commands were being issued.
334
335 DebugPrint("\tFatal Error: %x\n", PxIS.Status);
336 }
337
338 // Normal Command Completion
339 // 3.3.5
340 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
341 PxISMasked.DHRS = PxIS.DHRS;
342 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
343 PxISMasked.PSS = PxIS.PSS;
344 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
345 PxISMasked.DSS = PxIS.DSS;
346 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
347 PxISMasked.SDBS = PxIS.SDBS;
348 // A PRD with the ‘I’ bit set has transferred all of its data.
349 PxISMasked.DPS = PxIS.DPS;
350
351 if (PxISMasked.Status != 0)
352 {
353 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, PxISMasked.Status);
354 }
355
356 // 10.7.1.1
357 // Clear port interrupt
358 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
359 is = (1 << PortExtension->PortNumber);
360 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, is);
361
362 ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI);
363 sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT);
364
365 outstanding = ci | sact; // NOTE: Including both non-NCQ and NCQ based commands
366 if ((PortExtension->CommandIssuedSlots & (~outstanding)) != 0)
367 {
368 AhciCompleteIssuedSrb(PortExtension, (PortExtension->CommandIssuedSlots & (~outstanding)));
369 PortExtension->CommandIssuedSlots &= outstanding;
370 }
371
372 return;
373 }// -- AhciInterruptHandler();
374
375 /**
376 * @name AhciHwInterrupt
377 * @implemented
378 *
379 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
380 *
381 * @param AdapterExtension
382 *
383 * @return
384 * return TRUE Indicates that an interrupt was pending on adapter.
385 * return FALSE Indicates the interrupt was not ours.
386 */
387 BOOLEAN
388 AhciHwInterrupt(
389 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
390 )
391 {
392 ULONG portPending, nextPort, i, portCount;
393
394 DebugPrint("AhciHwInterrupt()\n");
395
396 if (AdapterExtension->StateFlags.Removed)
397 {
398 return FALSE;
399 }
400
401 portPending = StorPortReadRegisterUlong(AdapterExtension, AdapterExtension->IS);
402 // we process interrupt for implemented ports only
403 portCount = AdapterExtension->PortCount;
404 portPending = portPending & AdapterExtension->PortImplemented;
405
406 if (portPending == 0)
407 {
408 return FALSE;
409 }
410
411 for (i = 1; i <= portCount; i++)
412 {
413 nextPort = (AdapterExtension->LastInterruptPort + i) % portCount;
414
415 if ((portPending & (0x1 << nextPort)) == 0)
416 continue;
417
418 NT_ASSERT(IsPortValid(AdapterExtension, nextPort));
419
420 if ((nextPort == AdapterExtension->LastInterruptPort) ||
421 (AdapterExtension->PortExtension[nextPort].IsActive == FALSE))
422 {
423 return FALSE;
424 }
425
426 // we can assign this interrupt to this port
427 AdapterExtension->LastInterruptPort = nextPort;
428 AhciInterruptHandler(&AdapterExtension->PortExtension[nextPort]);
429
430 // interrupt belongs to this device
431 // should always return TRUE
432 return TRUE;
433 }
434
435 DebugPrint("\tSomething went wrong");
436 return FALSE;
437 }// -- AhciHwInterrupt();
438
439 /**
440 * @name AhciHwStartIo
441 * @not_implemented
442 *
443 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
444 *
445 * @param adapterExtension
446 * @param Srb
447 *
448 * @return
449 * return TRUE if the request was accepted
450 * return FALSE if the request must be submitted later
451 */
452 BOOLEAN
453 AhciHwStartIo (
454 __in PVOID AdapterExtension,
455 __in PSCSI_REQUEST_BLOCK Srb
456 )
457 {
458 UCHAR function, pathId;
459 PAHCI_ADAPTER_EXTENSION adapterExtension;
460
461 DebugPrint("AhciHwStartIo()\n");
462
463 pathId = Srb->PathId;
464 function = Srb->Function;
465 adapterExtension = AdapterExtension;
466
467 if (!IsPortValid(adapterExtension, pathId))
468 {
469 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
470 StorPortNotification(RequestComplete, adapterExtension, Srb);
471 return TRUE;
472 }
473
474 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
475 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
476 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
477 if (function == SRB_FUNCTION_PNP)
478 {
479 PSCSI_PNP_REQUEST_BLOCK pnpRequest;
480
481 pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb;
482 if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0)
483 {
484 if ((pnpRequest->PnPAction == StorRemoveDevice) ||
485 (pnpRequest->PnPAction == StorSurpriseRemoval))
486 {
487 Srb->SrbStatus = SRB_STATUS_SUCCESS;
488 adapterExtension->StateFlags.Removed = 1;
489 DebugPrint("\tAdapter removed\n");
490 }
491 else if (pnpRequest->PnPAction == StorStopDevice)
492 {
493 Srb->SrbStatus = SRB_STATUS_SUCCESS;
494 DebugPrint("\tRequested to Stop the adapter\n");
495 }
496 else
497 {
498 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
499 }
500
501 StorPortNotification(RequestComplete, adapterExtension, Srb);
502 return TRUE;
503 }
504 }
505
506 if (function == SRB_FUNCTION_EXECUTE_SCSI)
507 {
508 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
509 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
510 // routine does the following:
511 //
512 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
513 // logical unit, and/or SRB extensions
514 // For example, a miniport driver might set up a logical unit extension with pointers
515 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
516 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
517 // carried out on the HBA.
518 //
519 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
520 // for the requested operation
521 // For a device I/O operation, such an internal routine generally selects the target device
522 // and sends the CDB over the bus to the target logical unit.
523 if (Srb->CdbLength > 0)
524 {
525 PCDB cdb = (PCDB)&Srb->Cdb;
526 if (cdb->CDB10.OperationCode == SCSIOP_INQUIRY)
527 {
528 Srb->SrbStatus = DeviceInquiryRequest(adapterExtension, Srb, cdb);
529 }
530 else
531 {
532 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
533 }
534 }
535 else
536 {
537 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
538 }
539
540 StorPortNotification(RequestComplete, adapterExtension, Srb);
541 return TRUE;
542 }
543
544 DebugPrint("\tUnknown function code recieved: %x\n", function);
545 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
546 StorPortNotification(RequestComplete, adapterExtension, Srb);
547 return TRUE;
548 }// -- AhciHwStartIo();
549
550 /**
551 * @name AhciHwResetBus
552 * @not_implemented
553 *
554 * The HwStorResetBus routine is called by the port driver to clear error conditions.
555 *
556 * @param adapterExtension
557 * @param PathId
558 *
559 * @return
560 * return TRUE if bus was successfully reset
561 */
562 BOOLEAN
563 AhciHwResetBus (
564 __in PVOID AdapterExtension,
565 __in ULONG PathId
566 )
567 {
568 STOR_LOCK_HANDLE lockhandle;
569 PAHCI_ADAPTER_EXTENSION adapterExtension;
570
571 DebugPrint("AhciHwResetBus()\n");
572
573 adapterExtension = AdapterExtension;
574
575 if (IsPortValid(AdapterExtension, PathId))
576 {
577 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
578
579 // Acquire Lock
580 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
581
582 // TODO: Perform port reset
583
584 // Release lock
585 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
586 }
587
588 return FALSE;
589 }// -- AhciHwResetBus();
590
591 /**
592 * @name AhciHwFindAdapter
593 * @implemented
594 *
595 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
596 * HBA is supported and, if it is, to return configuration information about that adapter.
597 *
598 * 10.1 Platform Communication
599 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
600
601 * @param DeviceExtension
602 * @param HwContext
603 * @param BusInformation
604 * @param ArgumentString
605 * @param ConfigInfo
606 * @param Reserved3
607 *
608 * @return
609 * SP_RETURN_FOUND
610 * 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.
611 *
612 * SP_RETURN_ERROR
613 * 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.
614 *
615 * SP_RETURN_BAD_CONFIG
616 * Indicates that the supplied configuration information was invalid for the adapter.
617 *
618 * SP_RETURN_NOT_FOUND
619 * Indicates that no supported HBA was found for the supplied configuration information.
620 *
621 * @remarks Called by Storport.
622 */
623 ULONG
624 AhciHwFindAdapter (
625 __in PVOID AdapterExtension,
626 __in PVOID HwContext,
627 __in PVOID BusInformation,
628 __in PVOID ArgumentString,
629 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
630 __in PBOOLEAN Reserved3
631 )
632 {
633 ULONG ghc;
634 ULONG index;
635 ULONG portCount, portImplemented;
636 ULONG pci_cfg_len;
637 UCHAR pci_cfg_buf[sizeof(PCI_COMMON_CONFIG)];
638 PACCESS_RANGE accessRange;
639
640 PAHCI_MEMORY_REGISTERS abar;
641 PPCI_COMMON_CONFIG pciConfigData;
642 PAHCI_ADAPTER_EXTENSION adapterExtension;
643
644 DebugPrint("AhciHwFindAdapter()\n");
645
646 adapterExtension = AdapterExtension;
647 adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
648 adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
649
650 // get PCI configuration header
651 pci_cfg_len = StorPortGetBusData(
652 adapterExtension,
653 PCIConfiguration,
654 adapterExtension->SystemIoBusNumber,
655 adapterExtension->SlotNumber,
656 pci_cfg_buf,
657 sizeof(PCI_COMMON_CONFIG));
658
659 if (pci_cfg_len != sizeof(PCI_COMMON_CONFIG))
660 {
661 DebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG), pci_cfg_len);
662 return SP_RETURN_ERROR;//Not a valid device at the given bus number
663 }
664
665 pciConfigData = pci_cfg_buf;
666 adapterExtension->VendorID = pciConfigData->VendorID;
667 adapterExtension->DeviceID = pciConfigData->DeviceID;
668 adapterExtension->RevisionID = pciConfigData->RevisionID;
669 // 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).
670 adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
671
672 DebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension->VendorID,
673 adapterExtension->DeviceID,
674 adapterExtension->RevisionID);
675
676 // 2.1.11
677 abar = NULL;
678 if (ConfigInfo->NumberOfAccessRanges > 0)
679 {
680 for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
681 {
682 accessRange = *ConfigInfo->AccessRanges;
683 if (accessRange[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
684 {
685 abar = StorPortGetDeviceBase(adapterExtension,
686 ConfigInfo->AdapterInterfaceType,
687 ConfigInfo->SystemIoBusNumber,
688 accessRange[index].RangeStart,
689 accessRange[index].RangeLength,
690 !accessRange[index].RangeInMemory);
691 break;
692 }
693 }
694 }
695
696 if (abar == NULL)
697 {
698 DebugPrint("\tabar == NULL\n");
699 return SP_RETURN_ERROR; // corrupted information supplied
700 }
701
702 adapterExtension->ABAR_Address = abar;
703 adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
704 adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
705 adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
706 adapterExtension->LastInterruptPort = -1;
707
708 // 10.1.2
709 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
710 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
711 ghc = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
712 // AE := Highest Significant bit of GHC
713 if ((ghc & AHCI_Global_HBA_CONTROL_AE) != 0)// Hmm, controller was already in power state
714 {
715 // reset controller to have it in known state
716 DebugPrint("\tAE Already set, Reset()\n");
717 if (!AhciAdapterReset(adapterExtension))
718 {
719 DebugPrint("\tReset Failed!\n");
720 return SP_RETURN_ERROR;// reset failed
721 }
722 }
723
724 ghc = AHCI_Global_HBA_CONTROL_AE;// only AE=1
725 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
726
727 adapterExtension->IS = &abar->IS;
728 adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
729
730 if (adapterExtension->PortImplemented == 0)
731 {
732 DebugPrint("\tadapterExtension->PortImplemented == 0\n");
733 return SP_RETURN_ERROR;
734 }
735
736 ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;//128 KB
737 ConfigInfo->NumberOfPhysicalBreaks = 0x21;
738 ConfigInfo->MaximumNumberOfTargets = 1;
739 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
740 ConfigInfo->ResetTargetSupported = TRUE;
741 ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT;
742 ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
743 ConfigInfo->ScatterGather = TRUE;
744
745 // Turn IE -- Interrupt Enabled
746 ghc |= AHCI_Global_HBA_CONTROL_IE;
747 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
748
749 // allocate necessary resource for each port
750 if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
751 {
752 DebugPrint("\tAhciAllocateResourceForAdapter() == FALSE\n");
753 return SP_RETURN_ERROR;
754 }
755
756 for (index = 0; index < adapterExtension->PortCount; index++)
757 {
758 if ((adapterExtension->PortImplemented & (0x1 << index)) != 0)
759 AhciPortInitialize(&adapterExtension->PortExtension[index]);
760 }
761
762 return SP_RETURN_FOUND;
763 }// -- AhciHwFindAdapter();
764
765 /**
766 * @name DriverEntry
767 * @implemented
768 *
769 * Initial Entrypoint for storahci miniport driver
770 *
771 * @param DriverObject
772 * @param RegistryPath
773 *
774 * @return
775 * NT_STATUS in case of driver loaded successfully.
776 */
777 ULONG
778 DriverEntry (
779 __in PVOID DriverObject,
780 __in PVOID RegistryPath
781 )
782 {
783 HW_INITIALIZATION_DATA hwInitializationData;
784 ULONG i, status;
785
786 DebugPrint("Storahci Loaded\n");
787
788 // initialize the hardware data structure
789 AhciZeroMemory(&hwInitializationData, sizeof(HW_INITIALIZATION_DATA));
790
791 // set size of hardware initialization structure
792 hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
793
794 // identity required miniport entry point routines
795 hwInitializationData.HwStartIo = AhciHwStartIo;
796 hwInitializationData.HwResetBus = AhciHwResetBus;
797 hwInitializationData.HwInterrupt = AhciHwInterrupt;
798 hwInitializationData.HwInitialize = AhciHwInitialize;
799 hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
800
801 // adapter specific information
802 hwInitializationData.NeedPhysicalAddresses = TRUE;
803 hwInitializationData.TaggedQueuing = TRUE;
804 hwInitializationData.AutoRequestSense = TRUE;
805 hwInitializationData.MultipleRequestPerLu = TRUE;
806
807 hwInitializationData.NumberOfAccessRanges = 6;
808 hwInitializationData.AdapterInterfaceType = PCIBus;
809 hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
810
811 // set required extension sizes
812 hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
813 hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
814
815 // register our hw init data
816 status = StorPortInitialize(DriverObject,
817 RegistryPath,
818 &hwInitializationData,
819 NULL);
820
821 DebugPrint("\tstatus:%x\n", status);
822 return status;
823 }// -- DriverEntry();
824
825 /**
826 * @name AhciATA_CFIS
827 * @implemented
828 *
829 * create ATA CFIS from Srb
830 *
831 * @param PortExtension
832 * @param Srb
833 *
834 */
835 VOID
836 AhciATA_CFIS (
837 __in PAHCI_PORT_EXTENSION PortExtension,
838 __in PAHCI_SRB_EXTENSION SrbExtension
839 )
840 {
841 PAHCI_COMMAND_TABLE cmdTable;
842
843 DebugPrint("AhciATA_CFIS()\n");
844
845 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
846
847 NT_ASSERT(sizeof(cmdTable->CFIS) == 64);
848
849 AhciZeroMemory(&cmdTable->CFIS, sizeof(cmdTable->CFIS));
850
851 cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = 0x27; // FIS Type
852 cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C
853 cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg;
854
855 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow;
856 cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0;
857 cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1;
858 cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2;
859 cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device;
860 cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3;
861 cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4;
862 cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5;
863 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh;
864 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow;
865 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh;
866
867 return;
868 }// -- AhciATA_CFIS();
869
870 /**
871 * @name AhciATAPI_CFIS
872 * @not_implemented
873 *
874 * create ATAPI CFIS from Srb
875 *
876 * @param PortExtension
877 * @param Srb
878 *
879 */
880 VOID
881 AhciATAPI_CFIS (
882 __in PAHCI_PORT_EXTENSION PortExtension,
883 __in PAHCI_SRB_EXTENSION SrbExtension
884 )
885 {
886 DebugPrint("AhciATAPI_CFIS()\n");
887
888 }// -- AhciATAPI_CFIS();
889
890 /**
891 * @name AhciBuild_PRDT
892 * @implemented
893 *
894 * Build PRDT for data transfer
895 *
896 * @param PortExtension
897 * @param Srb
898 *
899 * @return
900 * Return number of entries in PRDT.
901 */
902 ULONG
903 AhciBuild_PRDT (
904 __in PAHCI_PORT_EXTENSION PortExtension,
905 __in PAHCI_SRB_EXTENSION SrbExtension
906 )
907 {
908 ULONG index;
909 PAHCI_COMMAND_TABLE cmdTable;
910 PLOCAL_SCATTER_GATHER_LIST sgl;
911 PAHCI_ADAPTER_EXTENSION AdapterExtension;
912
913 DebugPrint("AhciBuild_PRDT()\n");
914
915 sgl = &SrbExtension->Sgl;
916 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
917 AdapterExtension = PortExtension->AdapterExtension;
918
919 NT_ASSERT(sgl != NULL);
920 NT_ASSERT(sgl->NumberOfElements < MAXIMUM_AHCI_PRDT_ENTRIES);
921
922 for (index = 0; index < sgl->NumberOfElements; index++)
923 {
924 NT_ASSERT(sgl->List[index].Length <= MAXIMUM_TRANSFER_LENGTH);
925
926 cmdTable->PRDT[index].DBA = sgl->List[index].PhysicalAddress.LowPart;
927 if (IsAdapterCAPS64(AdapterExtension->CAP))
928 {
929 cmdTable->PRDT[index].DBAU = sgl->List[index].PhysicalAddress.HighPart;
930 }
931 }
932
933 return sgl->NumberOfElements;
934 }// -- AhciBuild_PRDT();
935
936 /**
937 * @name AhciProcessSrb
938 * @implemented
939 *
940 * Prepare Srb for IO processing
941 *
942 * @param PortExtension
943 * @param Srb
944 * @param SlotIndex
945 *
946 */
947 VOID
948 AhciProcessSrb (
949 __in PAHCI_PORT_EXTENSION PortExtension,
950 __in PSCSI_REQUEST_BLOCK Srb,
951 __in ULONG SlotIndex
952 )
953 {
954 ULONG prdtlen, sig, length;
955 PAHCI_SRB_EXTENSION SrbExtension;
956 PAHCI_COMMAND_HEADER CommandHeader;
957 PAHCI_ADAPTER_EXTENSION AdapterExtension;
958 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress;
959
960 DebugPrint("AhciProcessSrb()\n");
961
962 NT_ASSERT(Srb->PathId == PortExtension->PortNumber);
963
964 SrbExtension = GetSrbExtension(Srb);
965 AdapterExtension = PortExtension->AdapterExtension;
966
967 NT_ASSERT(SrbExtension != NULL);
968 NT_ASSERT(SrbExtension->AtaFunction != 0);
969
970 if ((SrbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) &&
971 (SrbExtension->CommandReg == IDE_COMMAND_NOT_VALID))
972 {
973 // Here we are safe to check SIG register
974 sig = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SIG);
975 if (sig == 0x101)
976 {
977 SrbExtension->CommandReg = IDE_COMMAND_IDENTIFY;
978 }
979 else
980 {
981 SrbExtension->CommandReg = IDE_COMMAND_ATAPI_IDENTIFY;
982 }
983 }
984
985 NT_ASSERT(SlotIndex < AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
986 SrbExtension->SlotIndex = SlotIndex;
987
988 // program the CFIS in the CommandTable
989 CommandHeader = &PortExtension->CommandList[SlotIndex];
990
991 if (IsAtaCommand(SrbExtension->AtaFunction))
992 {
993 AhciATA_CFIS(PortExtension, SrbExtension);
994 }
995 else if (IsAtapiCommand(SrbExtension->AtaFunction))
996 {
997 AhciATAPI_CFIS(PortExtension, SrbExtension);
998 }
999
1000 prdtlen = 0;
1001 if (IsDataTransferNeeded(SrbExtension))
1002 {
1003 prdtlen = AhciBuild_PRDT(PortExtension, SrbExtension);
1004 NT_ASSERT(prdtlen != -1);
1005 }
1006
1007 // Program the command header
1008 CommandHeader->DI.PRDTL = prdtlen; // number of entries in PRD table
1009 CommandHeader->DI.CFL = 5;
1010 CommandHeader->DI.W = (SrbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0;
1011 CommandHeader->DI.P = 0; // ATA Specifications says so
1012 CommandHeader->DI.PMP = 0; // Port Multiplier
1013
1014 // Reset -- Manual Configuation
1015 CommandHeader->DI.R = 0;
1016 CommandHeader->DI.B = 0;
1017 CommandHeader->DI.C = 0;
1018
1019 CommandHeader->PRDBC = 0;
1020
1021 CommandHeader->Reserved[0] = 0;
1022 CommandHeader->Reserved[1] = 0;
1023 CommandHeader->Reserved[2] = 0;
1024 CommandHeader->Reserved[3] = 0;
1025
1026 // set CommandHeader CTBA
1027 // I am really not sure if SrbExtension is 128 byte aligned or not
1028 // Command FIS will not work if it is not so.
1029 CommandTablePhysicalAddress = StorPortGetPhysicalAddress(AdapterExtension,
1030 NULL,
1031 SrbExtension,
1032 &length);
1033
1034 // command table alignment
1035 NT_ASSERT((CommandTablePhysicalAddress.LowPart % 128) == 0);
1036
1037 CommandHeader->CTBA0 = CommandTablePhysicalAddress.LowPart;
1038
1039 if (IsAdapterCAPS64(AdapterExtension->CAP))
1040 {
1041 CommandHeader->CTBA_U0 = CommandTablePhysicalAddress.HighPart;
1042 }
1043
1044 // mark this slot
1045 PortExtension->Slot[SlotIndex] = Srb;
1046 PortExtension->QueueSlots |= SlotIndex;
1047 return;
1048 }// -- AhciProcessSrb();
1049
1050 /**
1051 * @name AhciActivatePort
1052 * @implemented
1053 *
1054 * Program Port and populate command list
1055 *
1056 * @param PortExtension
1057 *
1058 */
1059 VOID
1060 AhciActivatePort (
1061 __in PAHCI_PORT_EXTENSION PortExtension
1062 )
1063 {
1064 ULONG cmd, QueueSlots, slotToActivate, tmp;
1065 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1066
1067 DebugPrint("AhciActivatePort()\n");
1068
1069 AdapterExtension = PortExtension->AdapterExtension;
1070 QueueSlots = PortExtension->QueueSlots;
1071
1072 if (QueueSlots == 0)
1073 return;
1074
1075 // section 3.3.14
1076 // Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’
1077 cmd = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
1078
1079 if ((cmd&1) == 0) // PxCMD.ST == 0
1080 return;
1081
1082 // get the lowest set bit
1083 tmp = QueueSlots & (QueueSlots - 1);
1084
1085 if (tmp == 0)
1086 slotToActivate = QueueSlots;
1087 else
1088 slotToActivate = (QueueSlots & (~tmp));
1089
1090 // mark that bit off in QueueSlots
1091 // so we can know we it is really needed to activate port or not
1092 PortExtension->QueueSlots &= ~slotToActivate;
1093 // mark this CommandIssuedSlots
1094 // to validate in completeIssuedCommand
1095 PortExtension->CommandIssuedSlots |= slotToActivate;
1096
1097 // tell the HBA to issue this Command Slot to the given port
1098 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CI, slotToActivate);
1099
1100 return;
1101 }// -- AhciActivatePort();
1102
1103 /**
1104 * @name AhciProcessIO
1105 * @implemented
1106 *
1107 * Acquire Exclusive lock to port, populate pending commands to command List
1108 * program controller's port to process new commands in command list.
1109 *
1110 * @param AdapterExtension
1111 * @param PathId
1112 * @param Srb
1113 *
1114 */
1115 VOID
1116 AhciProcessIO (
1117 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1118 __in UCHAR PathId,
1119 __in PSCSI_REQUEST_BLOCK Srb
1120 )
1121 {
1122 STOR_LOCK_HANDLE lockhandle;
1123 PSCSI_REQUEST_BLOCK tmpSrb;
1124 PAHCI_PORT_EXTENSION PortExtension;
1125 ULONG commandSlotMask, occupiedSlots, slotIndex, NCS;
1126
1127 DebugPrint("AhciProcessIO()\n");
1128 DebugPrint("\tPathId: %d\n", PathId);
1129
1130 PortExtension = &AdapterExtension->PortExtension[PathId];
1131
1132 NT_ASSERT(PathId < AdapterExtension->PortCount);
1133
1134 // add Srb to queue
1135 AddQueue(&PortExtension->SrbQueue, Srb);
1136
1137 if (PortExtension->IsActive == FALSE)
1138 return; // we should wait for device to get active
1139
1140 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
1141
1142 // Acquire Lock
1143 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
1144
1145 occupiedSlots = (PortExtension->QueueSlots | PortExtension->CommandIssuedSlots); // Busy command slots for given port
1146 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
1147 commandSlotMask = (1 << NCS) - 1; // available slots mask
1148
1149 commandSlotMask = (commandSlotMask & ~occupiedSlots);
1150 if(commandSlotMask != 0)
1151 {
1152 // iterate over HBA port slots
1153 for (slotIndex = 0; slotIndex < NCS; slotIndex++)
1154 {
1155 // find first free slot
1156 if ((commandSlotMask & (1 << slotIndex)) != 0)
1157 {
1158 tmpSrb = RemoveQueue(&PortExtension->SrbQueue);
1159 if (tmpSrb != NULL)
1160 {
1161 NT_ASSERT(tmpSrb->PathId == PathId);
1162 AhciProcessSrb(PortExtension, tmpSrb, slotIndex);
1163 }
1164 else
1165 {
1166 break;
1167 }
1168 }
1169 else
1170 {
1171 break;
1172 }
1173 }
1174 }
1175
1176 // program HBA port
1177 AhciActivatePort(PortExtension);
1178
1179 // Release Lock
1180 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1181
1182 return;
1183 }// -- AhciProcessIO();
1184
1185 /**
1186 * @name InquiryCompletion
1187 * @not_implemented
1188 *
1189 * InquiryCompletion routine should be called after device signals
1190 * for device inquiry request is completed (through interrupt)
1191 *
1192 * @param PortExtension
1193 * @param Srb
1194 *
1195 */
1196 VOID
1197 InquiryCompletion (
1198 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1199 __in PAHCI_PORT_EXTENSION PortExtension,
1200 __in PSCSI_REQUEST_BLOCK Srb
1201 )
1202 {
1203 ULONG SrbStatus;
1204 PAHCI_SRB_EXTENSION SrbExtension;
1205
1206 DebugPrint("InquiryCompletion()\n");
1207
1208 NT_ASSERT(PortExtension != NULL);
1209 NT_ASSERT(Srb != NULL);
1210
1211 SrbStatus = Srb->SrbStatus;
1212 SrbExtension = GetSrbExtension(Srb);
1213
1214 if (SrbStatus == SRB_STATUS_SUCCESS)
1215 {
1216 if (SrbExtension->CommandReg == IDE_COMMAND_IDENTIFY)
1217 {
1218 AdapterExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATA;
1219 }
1220 else
1221 {
1222 AdapterExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATAPI;
1223 }
1224 // TODO: Set Device Paramters
1225 }
1226 else if (SrbStatus == SRB_STATUS_NO_DEVICE)
1227 {
1228 AdapterExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_NODEVICE;
1229 }
1230 else
1231 {
1232 return;
1233 }
1234
1235 return;
1236 }// -- InquiryCompletion();
1237
1238 /**
1239 * @name DeviceInquiryRequest
1240 * @implemented
1241 *
1242 * Tells wheather given port is implemented or not
1243 *
1244 * @param AdapterExtension
1245 * @param Srb
1246 * @param Cdb
1247 *
1248 * @return
1249 * return STOR status for DeviceInquiryRequest
1250 *
1251 * @remark
1252 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
1253 */
1254 ULONG
1255 DeviceInquiryRequest (
1256 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1257 __in PSCSI_REQUEST_BLOCK Srb,
1258 __in PCDB Cdb
1259 )
1260 {
1261 PVOID DataBuffer;
1262 ULONG DataBufferLength;
1263 PAHCI_PORT_EXTENSION PortExtension;
1264 PAHCI_SRB_EXTENSION SrbExtension;
1265
1266 DebugPrint("DeviceInquiryRequest()\n");
1267
1268 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
1269
1270 SrbExtension = GetSrbExtension(Srb);
1271 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1272
1273 // 3.6.1
1274 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
1275 if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0)
1276 {
1277 DebugPrint("\tEVPD Inquired\n");
1278 NT_ASSERT(SrbExtension != NULL);
1279
1280 SrbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY;
1281 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
1282 SrbExtension->CompletionRoutine = InquiryCompletion;
1283 SrbExtension->CommandReg = IDE_COMMAND_NOT_VALID;
1284
1285 // TODO: Should use AhciZeroMemory
1286 SrbExtension->FeaturesLow = 0;
1287 SrbExtension->LBA0 = 0;
1288 SrbExtension->LBA1 = 0;
1289 SrbExtension->LBA2 = 0;
1290 SrbExtension->Device = 0;
1291 SrbExtension->LBA3 = 0;
1292 SrbExtension->LBA4 = 0;
1293 SrbExtension->LBA5 = 0;
1294 SrbExtension->FeaturesHigh = 0;
1295 SrbExtension->SectorCountLow = 0;
1296 SrbExtension->SectorCountHigh = 0;
1297
1298 SrbExtension->Sgl.NumberOfElements = 1;
1299 SrbExtension->Sgl.List[0].PhysicalAddress.LowPart = PortExtension->IdentifyDeviceDataPhysicalAddress.LowPart;
1300 SrbExtension->Sgl.List[0].PhysicalAddress.HighPart = PortExtension->IdentifyDeviceDataPhysicalAddress.HighPart;
1301 SrbExtension->Sgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA);
1302 }
1303 else
1304 {
1305 DebugPrint("\tVPD Inquired\n");
1306
1307 DataBuffer = Srb->DataBuffer;
1308 DataBufferLength = Srb->DataTransferLength;
1309
1310 if (DataBuffer == NULL)
1311 {
1312 return SRB_STATUS_INVALID_REQUEST;
1313 }
1314
1315 AhciZeroMemory(DataBuffer, DataBufferLength);
1316
1317 // not supported
1318 return SRB_STATUS_BAD_FUNCTION;
1319 }
1320
1321 AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
1322 return SRB_STATUS_PENDING;
1323 }// -- DeviceInquiryRequest();
1324
1325 /**
1326 * @name AhciAdapterReset
1327 * @implemented
1328 *
1329 * 10.4.3 HBA Reset
1330 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
1331 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
1332 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
1333 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
1334 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
1335 * the HBA reset has completed.
1336 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
1337 * a hung or locked state.
1338 *
1339 * @param AdapterExtension
1340 *
1341 * @return
1342 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
1343 */
1344 BOOLEAN
1345 AhciAdapterReset (
1346 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
1347 )
1348 {
1349 ULONG ghc, ticks, ghcStatus;
1350 PAHCI_MEMORY_REGISTERS abar = NULL;
1351
1352 DebugPrint("AhciAdapterReset()\n");
1353
1354 abar = AdapterExtension->ABAR_Address;
1355 if (abar == NULL) // basic sanity
1356 {
1357 return FALSE;
1358 }
1359
1360 // HR -- Very first bit (lowest significant)
1361 ghc = AHCI_Global_HBA_CONTROL_HR;
1362 StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc);
1363
1364 for (ticks = 0; ticks < 50; ++ticks)
1365 {
1366 ghcStatus = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC);
1367 if ((ghcStatus & AHCI_Global_HBA_CONTROL_HR) == 0)
1368 {
1369 break;
1370 }
1371 StorPortStallExecution(20000);
1372 }
1373
1374 if (ticks == 50)// 1 second
1375 {
1376 DebugPrint("\tDevice Timeout\n");
1377 return FALSE;
1378 }
1379
1380 return TRUE;
1381 }// -- AhciAdapterReset();
1382
1383 /**
1384 * @name AhciZeroMemory
1385 * @implemented
1386 *
1387 * Clear buffer by filling zeros
1388 *
1389 * @param Buffer
1390 * @param BufferSize
1391 */
1392 __inline
1393 VOID
1394 AhciZeroMemory (
1395 __out PCHAR Buffer,
1396 __in ULONG BufferSize
1397 )
1398 {
1399 ULONG i;
1400 for (i = 0; i < BufferSize; i++)
1401 {
1402 Buffer[i] = 0;
1403 }
1404
1405 return;
1406 }// -- AhciZeroMemory();
1407
1408 /**
1409 * @name IsPortValid
1410 * @implemented
1411 *
1412 * Tells wheather given port is implemented or not
1413 *
1414 * @param AdapterExtension
1415 * @param PathId
1416 *
1417 * @return
1418 * return TRUE if provided port is valid (implemented) or not
1419 */
1420 __inline
1421 BOOLEAN
1422 IsPortValid (
1423 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1424 __in UCHAR pathId
1425 )
1426 {
1427 NT_ASSERT(pathId >= 0);
1428
1429 if (pathId >= AdapterExtension->PortCount)
1430 {
1431 return FALSE;
1432 }
1433
1434 return AdapterExtension->PortExtension[pathId].IsActive;
1435 }// -- IsPortValid()
1436
1437 /**
1438 * @name AddQueue
1439 * @implemented
1440 *
1441 * Add Srb to Queue
1442 *
1443 * @param Queue
1444 * @param Srb
1445 *
1446 * @return
1447 * return TRUE if Srb is successfully added to Queue
1448 *
1449 */
1450 __inline
1451 BOOLEAN
1452 AddQueue (
1453 __inout PAHCI_QUEUE Queue,
1454 __in PVOID Srb
1455 )
1456 {
1457 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1458 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1459
1460 if (Queue->Tail == ((Queue->Head + 1) % MAXIMUM_QUEUE_BUFFER_SIZE))
1461 return FALSE;
1462
1463 Queue->Buffer[Queue->Head++] = Srb;
1464 Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE;
1465
1466 return TRUE;
1467 }// -- AddQueue();
1468
1469 /**
1470 * @name RemoveQueue
1471 * @implemented
1472 *
1473 * Remove and return Srb from Queue
1474 *
1475 * @param Queue
1476 *
1477 * @return
1478 * return Srb
1479 *
1480 */
1481 __inline
1482 PVOID
1483 RemoveQueue (
1484 __inout PAHCI_QUEUE Queue
1485 )
1486 {
1487 PVOID Srb;
1488
1489 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1490 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1491
1492 if (Queue->Head == Queue->Tail)
1493 return NULL;
1494
1495 Srb = Queue->Buffer[Queue->Tail++];
1496 Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE;
1497
1498 return Srb;
1499 }// -- RemoveQueue();
1500
1501 /**
1502 * @name GetSrbExtension
1503 * @implemented
1504 *
1505 * GetSrbExtension from Srb make sure It is properly aligned
1506 *
1507 * @param Srb
1508 *
1509 * @return
1510 * return SrbExtension
1511 *
1512 */
1513 __inline
1514 PAHCI_SRB_EXTENSION
1515 GetSrbExtension (
1516 __in PSCSI_REQUEST_BLOCK Srb
1517 )
1518 {
1519 ULONG Offset;
1520 ULONG_PTR SrbExtension;
1521
1522 SrbExtension = Srb->SrbExtension;
1523 Offset = SrbExtension % 128;
1524
1525 // CommandTable should be 128 byte aligned
1526 if (Offset != 0)
1527 Offset = 128 - Offset;
1528
1529 return (PAHCI_SRB_EXTENSION)(SrbExtension + Offset);
1530 }// -- PAHCI_SRB_EXTENSION();