5cfd7e0295a3ad2491ff550dbff10589f03a668e
[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 AHCI_PORT_CMD cmd;
27 ULONG mappedLength, portNumber;
28 PAHCI_MEMORY_REGISTERS abar;
29 PAHCI_ADAPTER_EXTENSION adapterExtension;
30 STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical;
31
32 DebugPrint("AhciPortInitialize()\n");
33
34 adapterExtension = PortExtension->AdapterExtension;
35 abar = adapterExtension->ABAR_Address;
36 portNumber = PortExtension->PortNumber;
37
38 NT_ASSERT(abar != NULL);
39 NT_ASSERT(portNumber < adapterExtension->PortCount);
40
41 PortExtension->Port = &abar->PortList[portNumber];
42
43 commandListPhysical = StorPortGetPhysicalAddress(adapterExtension,
44 NULL,
45 PortExtension->CommandList,
46 &mappedLength);
47
48 if ((mappedLength == 0) || ((commandListPhysical.LowPart % 1024) != 0))
49 {
50 DebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength);
51 return FALSE;
52 }
53
54 receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension,
55 NULL,
56 PortExtension->ReceivedFIS,
57 &mappedLength);
58
59 if ((mappedLength == 0) || ((receivedFISPhysical.LowPart % 256) != 0))
60 {
61 DebugPrint("\treceivedFISPhysical mappedLength:%d\n", mappedLength);
62 return FALSE;
63 }
64
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
74
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))
78 {
79 DebugPrint("\tPort is not idle: %x\n", cmd);
80 }
81
82 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
83 //  PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
84 //  PxFB and PxFBU (if CAP.S64A is set to ‘1’)
85 // Note: Assuming 32bit support only
86 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLB, commandListPhysical.LowPart);
87 if (IsAdapterCAPS64(adapterExtension->CAP))
88 {
89 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLBU, commandListPhysical.HighPart);
90 }
91
92 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FB, receivedFISPhysical.LowPart);
93 if (IsAdapterCAPS64(adapterExtension->CAP))
94 {
95 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FBU, receivedFISPhysical.HighPart);
96 }
97
98 PortExtension->IdentifyDeviceDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension,
99 NULL,
100 PortExtension->IdentifyDeviceData,
101 &mappedLength);
102
103 // set device power state flag to D0
104 PortExtension->DevicePowerState = StorPowerDeviceD0;
105
106 // clear pending interrupts
107 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->SERR, (ULONG)~0);
108 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->IS, (ULONG)~0);
109 StorPortWriteRegisterUlong(adapterExtension, adapterExtension->IS, (1 << PortExtension->PortNumber));
110
111 return TRUE;
112 }// -- AhciPortInitialize();
113
114 /**
115 * @name AhciAllocateResourceForAdapter
116 * @implemented
117 *
118 * Allocate memory from poll for required pointers
119 *
120 * @param AdapterExtension
121 * @param ConfigInfo
122 *
123 * @return
124 * return TRUE if allocation was successful
125 */
126 BOOLEAN
127 AhciAllocateResourceForAdapter (
128 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
129 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
130 )
131 {
132 PCHAR nonCachedExtension, tmp;
133 ULONG index, NCS, AlignedNCS;
134 ULONG portCount, portImplemented, nonCachedExtensionSize;
135 PAHCI_PORT_EXTENSION PortExtension;
136
137 DebugPrint("AhciAllocateResourceForAdapter()\n");
138
139 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
140 AlignedNCS = ROUND_UP(NCS, 8);
141
142 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
143 portCount = 0;
144 portImplemented = AdapterExtension->PortImplemented;
145
146 NT_ASSERT(portImplemented != 0);
147 for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--)
148 if ((portImplemented & (1 << index)) != 0)
149 break;
150
151 portCount = index + 1;
152 DebugPrint("\tPort Count: %d\n", portCount);
153
154 AdapterExtension->PortCount = portCount;
155 nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
156 sizeof(AHCI_RECEIVED_FIS) +
157 sizeof(IDENTIFY_DEVICE_DATA);
158
159 // align nonCachedExtensionSize to 1024
160 nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024);
161
162 AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension,
163 ConfigInfo,
164 nonCachedExtensionSize * portCount);
165
166 if (AdapterExtension->NonCachedExtension == NULL)
167 {
168 DebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
169 return FALSE;
170 }
171
172 nonCachedExtension = AdapterExtension->NonCachedExtension;
173 AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount);
174
175 for (index = 0; index < portCount; index++)
176 {
177 PortExtension = &AdapterExtension->PortExtension[index];
178
179 PortExtension->DeviceParams.IsActive = FALSE;
180 if ((AdapterExtension->PortImplemented & (1 << index)) != 0)
181 {
182 PortExtension->PortNumber = index;
183 PortExtension->DeviceParams.IsActive = TRUE;
184 PortExtension->AdapterExtension = AdapterExtension;
185 PortExtension->CommandList = (PAHCI_COMMAND_HEADER)nonCachedExtension;
186
187 tmp = (PCHAR)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
188
189 PortExtension->ReceivedFIS = (PAHCI_RECEIVED_FIS)tmp;
190 PortExtension->IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)(tmp + sizeof(AHCI_RECEIVED_FIS));
191 PortExtension->MaxPortQueueDepth = NCS;
192 nonCachedExtension += nonCachedExtensionSize;
193 }
194 }
195
196 return TRUE;
197 }// -- AhciAllocateResourceForAdapter();
198
199 /**
200 * @name AhciStartPort
201 * @implemented
202 *
203 * Try to start the port device
204 *
205 * @param AdapterExtension
206 * @param PortExtension
207 *
208 */
209 BOOLEAN
210 AhciStartPort (
211 __in PAHCI_PORT_EXTENSION PortExtension
212 )
213 {
214 ULONG index;
215 AHCI_PORT_CMD cmd;
216 AHCI_TASK_FILE_DATA tfd;
217 AHCI_INTERRUPT_ENABLE ie;
218 AHCI_SERIAL_ATA_STATUS ssts;
219 AHCI_SERIAL_ATA_CONTROL sctl;
220 PAHCI_ADAPTER_EXTENSION AdapterExtension;
221
222 DebugPrint("AhciStartPort()\n");
223
224 AdapterExtension = PortExtension->AdapterExtension;
225 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
226
227 if ((cmd.FR == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.ST == 1))
228 {
229 // Already Running
230 return TRUE;
231 }
232
233 cmd.SUD = 1;
234 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
235
236 if (((cmd.FR == 1) && (cmd.FRE == 0)) ||
237 ((cmd.CR == 1) && (cmd.ST == 0)))
238 {
239 DebugPrint("\tCOMRESET\n");
240 // perform COMRESET
241 // section 10.4.2
242
243 // Software causes a port reset (COMRESET) by writing 1h to the PxSCTL.DET field to invoke a
244 // COMRESET on the interface and start a re-establishment of Phy layer communications. Software shall
245 // wait at least 1 millisecond before clearing PxSCTL.DET to 0h; this ensures that at least one COMRESET
246 // signal is sent over the interface. After clearing PxSCTL.DET to 0h, software should wait for
247 // communication to be re-established as indicated by PxSSTS.DET being set to 3h. Then software should
248 // write all 1s to the PxSERR register to clear any bits that were set as part of the port reset.
249
250 sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
251 sctl.DET = 1;
252 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
253
254 StorPortStallExecution(1000);
255
256 sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
257 sctl.DET = 0;
258 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
259
260 // Poll DET to verify if a device is attached to the port
261 index = 0;
262 do
263 {
264 StorPortStallExecution(1000);
265 ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
266
267 index++;
268 if (ssts.DET != 0)
269 {
270 break;
271 }
272 }
273 while(index < 30);
274 }
275
276 ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
277 switch (ssts.DET)
278 {
279 case 0x3:
280 {
281 NT_ASSERT(cmd.ST == 0);
282
283 // make sure FIS Recieve is enabled (cmd.FRE)
284 index = 0;
285 do
286 {
287 StorPortStallExecution(10000);
288 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
289 cmd.FRE = 1;
290 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
291 index++;
292 }
293 while((cmd.FR != 1) && (index < 3));
294
295 if (cmd.FR != 1)
296 {
297 // failed to start FIS DMA engine
298 // it can crash the driver later
299 // so better to turn this port off
300 return FALSE;
301 }
302
303 // start port channel
304 // set cmd.ST
305
306 NT_ASSERT(cmd.FRE == 1);
307 NT_ASSERT(cmd.CR == 0);
308
309 // why assert? well If we face such condition on DET = 0x3
310 // then we don't have port in idle state and hence before executing this part of code
311 // we must have restarted it.
312 tfd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->TFD);
313
314 if ((tfd.STS.BSY) || (tfd.STS.DRQ))
315 {
316 DebugPrint("\tUnhandled Case BSY-DRQ\n");
317 }
318
319 // clear pending interrupts
320 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SERR, (ULONG)~0);
321 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, (ULONG)~0);
322 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, (1 << PortExtension->PortNumber));
323
324 // set IE
325 ie.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IE);
326 /* Device to Host Register FIS Interrupt Enable */
327 ie.DHRE = 1;
328 /* PIO Setup FIS Interrupt Enable */
329 ie.PSE = 1;
330 /* DMA Setup FIS Interrupt Enable */
331 ie.DSE = 1;
332 /* Set Device Bits FIS Interrupt Enable */
333 ie.SDBE = 1;
334 /* Unknown FIS Interrupt Enable */
335 ie.UFE = 0;
336 /* Descriptor Processed Interrupt Enable */
337 ie.DPE = 0;
338 /* Port Change Interrupt Enable */
339 ie.PCE = 1;
340 /* Device Mechanical Presence Enable */
341 ie.DMPE = 0;
342 /* PhyRdy Change Interrupt Enable */
343 ie.PRCE = 1;
344 /* Incorrect Port Multiplier Enable */
345 ie.IPME = 0;
346 /* Overflow Enable */
347 ie.OFE = 1;
348 /* Interface Non-fatal Error Enable */
349 ie.INFE = 1;
350 /* Interface Fatal Error Enable */
351 ie.IFE = 1;
352 /* Host Bus Data Error Enable */
353 ie.HBDE = 1;
354 /* Host Bus Fatal Error Enable */
355 ie.HBFE = 1;
356 /* Task File Error Enable */
357 ie.TFEE = 1;
358
359 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
360 /* Cold Presence Detect Enable */
361 if (cmd.CPD) // does it support CPD?
362 {
363 // disable it for now
364 ie.CPDE = 0;
365 }
366
367 // should I replace this to single line?
368 // by directly setting ie.Status?
369
370 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IE, ie.Status);
371
372 cmd.ST = 1;
373 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
374 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
375
376 if (cmd.ST != 1)
377 {
378 DebugPrint("\tFailed to start Port\n");
379 return FALSE;
380 }
381
382 return TRUE;
383 }
384 default:
385 // unhandled case
386 DebugPrint("\tDET == %x Unsupported\n", ssts.DET);
387 return FALSE;
388 }
389
390 }// -- AhciStartPort();
391
392 /**
393 * @name AhciCommandCompletionDpcRoutine
394 * @implemented
395 *
396 * Handles Completed Commands
397 *
398 * @param Dpc
399 * @param AdapterExtension
400 * @param SystemArgument1
401 * @param SystemArgument2
402 */
403 VOID
404 AhciCommandCompletionDpcRoutine (
405 __in PSTOR_DPC Dpc,
406 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
407 __in PAHCI_PORT_EXTENSION PortExtension,
408 __in PVOID SystemArgument2
409 )
410 {
411 PSCSI_REQUEST_BLOCK Srb;
412 STOR_LOCK_HANDLE lockhandle;
413 PAHCI_SRB_EXTENSION SrbExtension;
414 PAHCI_COMPLETION_ROUTINE CompletionRoutine;
415
416 UNREFERENCED_PARAMETER(Dpc);
417 UNREFERENCED_PARAMETER(SystemArgument2);
418
419 DebugPrint("AhciCommandCompletionDpcRoutine()\n");
420
421 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
422
423 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
424 Srb = RemoveQueue(&PortExtension->CompletionQueue);
425
426 NT_ASSERT(Srb != NULL);
427
428 if (Srb->SrbStatus == SRB_STATUS_PENDING)
429 {
430 Srb->SrbStatus = SRB_STATUS_SUCCESS;
431 }
432
433 SrbExtension = GetSrbExtension(Srb);
434 CompletionRoutine = SrbExtension->CompletionRoutine;
435
436 if (CompletionRoutine != NULL)
437 {
438 // now it's completion routine responsibility to set SrbStatus
439 CompletionRoutine(PortExtension, Srb);
440 }
441 else
442 {
443 Srb->SrbStatus = SRB_STATUS_SUCCESS;
444 StorPortNotification(RequestComplete, AdapterExtension, Srb);
445 }
446
447 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
448 return;
449 }// -- AhciCommandCompletionDpcRoutine();
450
451 /**
452 * @name AhciHwPassiveInitialize
453 * @implemented
454 *
455 * initializes the HBA and finds all devices that are of interest to the miniport driver. (at PASSIVE LEVEL)
456 *
457 * @param adapterExtension
458 *
459 * @return
460 * return TRUE if intialization was successful
461 */
462 BOOLEAN
463 AhciHwPassiveInitialize (
464 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
465 )
466 {
467 ULONG index;
468 PAHCI_PORT_EXTENSION PortExtension;
469
470 DebugPrint("AhciHwPassiveInitialize()\n");
471
472 for (index = 0; index < AdapterExtension->PortCount; index++)
473 {
474 if ((AdapterExtension->PortImplemented & (0x1 << index)) != 0)
475 {
476 PortExtension = &AdapterExtension->PortExtension[index];
477 PortExtension->DeviceParams.IsActive = AhciStartPort(PortExtension);
478 StorPortInitializeDpc(AdapterExtension, &PortExtension->CommandCompletion, AhciCommandCompletionDpcRoutine);
479 }
480 }
481
482 return TRUE;
483 }// -- AhciHwPassiveInitialize();
484
485 /**
486 * @name AhciHwInitialize
487 * @implemented
488 *
489 * initializes the HBA and finds all devices that are of interest to the miniport driver.
490 *
491 * @param adapterExtension
492 *
493 * @return
494 * return TRUE if intialization was successful
495 */
496 BOOLEAN
497 AhciHwInitialize (
498 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
499 )
500 {
501 AHCI_GHC ghc;
502 MESSAGE_INTERRUPT_INFORMATION messageInfo;
503
504 DebugPrint("AhciHwInitialize()\n");
505
506 AdapterExtension->StateFlags.MessagePerPort = FALSE;
507
508 // First check what type of interrupt/synchronization device is using
509 ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &AdapterExtension->ABAR_Address->GHC);
510
511 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
512 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
513 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
514 // software has allocated the number of messages requested
515 if (ghc.MRSM == 0)
516 {
517 AdapterExtension->StateFlags.MessagePerPort = TRUE;
518 DebugPrint("\tMultiple MSI based message not supported\n");
519 }
520
521 StorPortEnablePassiveInitialization(AdapterExtension, AhciHwPassiveInitialize);
522
523 return TRUE;
524 }// -- AhciHwInitialize();
525
526 /**
527 * @name AhciCompleteIssuedSrb
528 * @implemented
529 *
530 * Complete issued Srbs
531 *
532 * @param PortExtension
533 *
534 */
535 VOID
536 AhciCompleteIssuedSrb (
537 __in PAHCI_PORT_EXTENSION PortExtension,
538 __in ULONG CommandsToComplete
539 )
540 {
541 ULONG NCS, i;
542 PSCSI_REQUEST_BLOCK Srb;
543 PAHCI_ADAPTER_EXTENSION AdapterExtension;
544
545 DebugPrint("AhciCompleteIssuedSrb()\n");
546
547 NT_ASSERT(CommandsToComplete != 0);
548
549 DebugPrint("\tCompleted Commands: %d\n", CommandsToComplete);
550
551 AdapterExtension = PortExtension->AdapterExtension;
552 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
553
554 for (i = 0; i < NCS; i++)
555 {
556 if (((1 << i) & CommandsToComplete) != 0)
557 {
558 Srb = PortExtension->Slot[i];
559 NT_ASSERT(Srb != NULL);
560
561 AddQueue(&PortExtension->CompletionQueue, Srb);
562 StorPortIssueDpc(AdapterExtension, &PortExtension->CommandCompletion, PortExtension, Srb);
563 }
564 }
565
566 return;
567 }// -- AhciCompleteIssuedSrb();
568
569 /**
570 * @name AhciInterruptHandler
571 * @not_implemented
572 *
573 * Interrupt Handler for PortExtension
574 *
575 * @param PortExtension
576 *
577 */
578 VOID
579 AhciInterruptHandler (
580 __in PAHCI_PORT_EXTENSION PortExtension
581 )
582 {
583 ULONG is, ci, sact, outstanding;
584 AHCI_INTERRUPT_STATUS PxIS;
585 AHCI_INTERRUPT_STATUS PxISMasked;
586 PAHCI_ADAPTER_EXTENSION AdapterExtension;
587
588 DebugPrint("AhciInterruptHandler()\n");
589 DebugPrint("\tPort Number: %d\n", PortExtension->PortNumber);
590
591 AdapterExtension = PortExtension->AdapterExtension;
592 NT_ASSERT(IsPortValid(AdapterExtension, PortExtension->PortNumber));
593
594 // 5.5.3
595 // 1. Software determines the cause of the interrupt by reading the PxIS register.
596 // It is possible for multiple bits to be set
597 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
598 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
599 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
600 // the list of commands previously issued by software that are still outstanding.
601 // If executing native queued commands, software reads the PxSACT register and compares the current
602 // value to the list of commands previously issued by software.
603 // Software completes with success any outstanding command whose corresponding bit has been cleared in
604 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
605 // to determine commands that have completed, not to determine which commands have previously been issued.
606 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
607 PxISMasked.Status = 0;
608 PxIS.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IS);
609
610 // 6.2.2
611 // Fatal Error
612 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
613 if (PxIS.HBFS || PxIS.HBDS || PxIS.IFS || PxIS.TFES)
614 {
615 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
616 // any native command queuing commands. To recover, the port must be restarted
617 // To detect an error that requires software recovery actions to be performed,
618 // software should check whether any of the following status bits are set on an interrupt:
619 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
620 // software should perform the appropriate error recovery actions based on whether
621 // non-queued commands were being issued or native command queuing commands were being issued.
622
623 DebugPrint("\tFatal Error: %x\n", PxIS.Status);
624 }
625
626 // Normal Command Completion
627 // 3.3.5
628 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
629 PxISMasked.DHRS = PxIS.DHRS;
630 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
631 PxISMasked.PSS = PxIS.PSS;
632 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
633 PxISMasked.DSS = PxIS.DSS;
634 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
635 PxISMasked.SDBS = PxIS.SDBS;
636 // A PRD with the ‘I’ bit set has transferred all of its data.
637 PxISMasked.DPS = PxIS.DPS;
638
639 if (PxISMasked.Status != 0)
640 {
641 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, PxISMasked.Status);
642 }
643
644 // 10.7.1.1
645 // Clear port interrupt
646 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
647 is = (1 << PortExtension->PortNumber);
648 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, is);
649
650 ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI);
651 sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT);
652
653 outstanding = ci | sact; // NOTE: Including both non-NCQ and NCQ based commands
654 if ((PortExtension->CommandIssuedSlots & (~outstanding)) != 0)
655 {
656 AhciCompleteIssuedSrb(PortExtension, (PortExtension->CommandIssuedSlots & (~outstanding)));
657 PortExtension->CommandIssuedSlots &= outstanding;
658 }
659
660 return;
661 }// -- AhciInterruptHandler();
662
663 /**
664 * @name AhciHwInterrupt
665 * @implemented
666 *
667 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
668 *
669 * @param AdapterExtension
670 *
671 * @return
672 * return TRUE Indicates that an interrupt was pending on adapter.
673 * return FALSE Indicates the interrupt was not ours.
674 */
675 BOOLEAN
676 AhciHwInterrupt (
677 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
678 )
679 {
680 ULONG portPending, nextPort, i, portCount;
681
682 if (AdapterExtension->StateFlags.Removed)
683 {
684 return FALSE;
685 }
686
687 portPending = StorPortReadRegisterUlong(AdapterExtension, AdapterExtension->IS);
688
689 // we process interrupt for implemented ports only
690 portCount = AdapterExtension->PortCount;
691 portPending = portPending & AdapterExtension->PortImplemented;
692
693 if (portPending == 0)
694 {
695 return FALSE;
696 }
697
698 for (i = 1; i <= portCount; i++)
699 {
700 nextPort = (AdapterExtension->LastInterruptPort + i) % portCount;
701 if ((portPending & (0x1 << nextPort)) == 0)
702 continue;
703
704 NT_ASSERT(IsPortValid(AdapterExtension, nextPort));
705
706 if (AdapterExtension->PortExtension[nextPort].DeviceParams.IsActive == FALSE)
707 {
708 continue;
709 }
710
711 // we can assign this interrupt to this port
712 AdapterExtension->LastInterruptPort = nextPort;
713 AhciInterruptHandler(&AdapterExtension->PortExtension[nextPort]);
714
715 portPending &= ~(1 << nextPort);
716
717 // interrupt belongs to this device
718 // should always return TRUE
719 return TRUE;
720 }
721
722 DebugPrint("\tSomething went wrong");
723 return FALSE;
724 }// -- AhciHwInterrupt();
725
726 /**
727 * @name AhciHwStartIo
728 * @not_implemented
729 *
730 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
731 *
732 * @param adapterExtension
733 * @param Srb
734 *
735 * @return
736 * return TRUE if the request was accepted
737 * return FALSE if the request must be submitted later
738 */
739 BOOLEAN
740 AhciHwStartIo (
741 __in PVOID AdapterExtension,
742 __in PSCSI_REQUEST_BLOCK Srb
743 )
744 {
745 UCHAR function, pathId;
746 PAHCI_ADAPTER_EXTENSION adapterExtension;
747
748 DebugPrint("AhciHwStartIo()\n");
749
750 pathId = Srb->PathId;
751 function = Srb->Function;
752 adapterExtension = AdapterExtension;
753
754 DebugPrint("\tFunction: %x\n", function);
755
756 if (!IsPortValid(adapterExtension, pathId))
757 {
758 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
759 StorPortNotification(RequestComplete, adapterExtension, Srb);
760 return TRUE;
761 }
762
763 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
764 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
765 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
766 if (function == SRB_FUNCTION_PNP)
767 {
768 PSCSI_PNP_REQUEST_BLOCK pnpRequest;
769
770 pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb;
771 if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0)
772 {
773 if ((pnpRequest->PnPAction == StorRemoveDevice) ||
774 (pnpRequest->PnPAction == StorSurpriseRemoval))
775 {
776 Srb->SrbStatus = SRB_STATUS_SUCCESS;
777 adapterExtension->StateFlags.Removed = 1;
778 DebugPrint("\tAdapter removed\n");
779 }
780 else if (pnpRequest->PnPAction == StorStopDevice)
781 {
782 Srb->SrbStatus = SRB_STATUS_SUCCESS;
783 DebugPrint("\tRequested to Stop the adapter\n");
784 }
785 else
786 {
787 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
788 }
789
790 StorPortNotification(RequestComplete, adapterExtension, Srb);
791 return TRUE;
792 }
793 }
794
795 if (function == SRB_FUNCTION_EXECUTE_SCSI)
796 {
797 DebugPrint("\tSRB_FUNCTION_EXECUTE_SCSI\n");
798 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
799 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
800 // routine does the following:
801 //
802 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
803 // logical unit, and/or SRB extensions
804 // For example, a miniport driver might set up a logical unit extension with pointers
805 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
806 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
807 // carried out on the HBA.
808 //
809 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
810 // for the requested operation
811 // For a device I/O operation, such an internal routine generally selects the target device
812 // and sends the CDB over the bus to the target logical unit.
813 if (Srb->CdbLength > 0)
814 {
815 PCDB cdb = (PCDB)&Srb->Cdb;
816 NT_ASSERT(cdb != NULL);
817
818 switch(cdb->CDB10.OperationCode)
819 {
820 case SCSIOP_INQUIRY:
821 Srb->SrbStatus = DeviceInquiryRequest(adapterExtension, Srb, cdb, TRUE);
822 break;
823 case SCSIOP_REPORT_LUNS:
824 Srb->SrbStatus = DeviceInquiryRequest(adapterExtension, Srb, cdb, FALSE);
825 break;
826 default:
827 {
828 DebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode);
829 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
830 }
831 break;
832 }
833 }
834 else
835 {
836 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
837 }
838
839 StorPortNotification(RequestComplete, adapterExtension, Srb);
840 return TRUE;
841 }
842
843 DebugPrint("\tUnknown function code recieved: %x\n", function);
844 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
845 StorPortNotification(RequestComplete, adapterExtension, Srb);
846 return TRUE;
847 }// -- AhciHwStartIo();
848
849 /**
850 * @name AhciHwResetBus
851 * @not_implemented
852 *
853 * The HwStorResetBus routine is called by the port driver to clear error conditions.
854 *
855 * @param adapterExtension
856 * @param PathId
857 *
858 * @return
859 * return TRUE if bus was successfully reset
860 */
861 BOOLEAN
862 AhciHwResetBus (
863 __in PVOID AdapterExtension,
864 __in ULONG PathId
865 )
866 {
867 STOR_LOCK_HANDLE lockhandle;
868 PAHCI_ADAPTER_EXTENSION adapterExtension;
869
870 DebugPrint("AhciHwResetBus()\n");
871
872 adapterExtension = AdapterExtension;
873
874 if (IsPortValid(AdapterExtension, PathId))
875 {
876 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
877
878 // Acquire Lock
879 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
880
881 // TODO: Perform port reset
882
883 // Release lock
884 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
885 }
886
887 return FALSE;
888 }// -- AhciHwResetBus();
889
890 /**
891 * @name AhciHwFindAdapter
892 * @implemented
893 *
894 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
895 * HBA is supported and, if it is, to return configuration information about that adapter.
896 *
897 * 10.1 Platform Communication
898 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
899
900 * @param DeviceExtension
901 * @param HwContext
902 * @param BusInformation
903 * @param ArgumentString
904 * @param ConfigInfo
905 * @param Reserved3
906 *
907 * @return
908 * SP_RETURN_FOUND
909 * 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.
910 *
911 * SP_RETURN_ERROR
912 * 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.
913 *
914 * SP_RETURN_BAD_CONFIG
915 * Indicates that the supplied configuration information was invalid for the adapter.
916 *
917 * SP_RETURN_NOT_FOUND
918 * Indicates that no supported HBA was found for the supplied configuration information.
919 *
920 * @remarks Called by Storport.
921 */
922 ULONG
923 AhciHwFindAdapter (
924 __in PVOID AdapterExtension,
925 __in PVOID HwContext,
926 __in PVOID BusInformation,
927 __in PVOID ArgumentString,
928 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
929 __in PBOOLEAN Reserved3
930 )
931 {
932 AHCI_GHC ghc;
933 ULONG index, pci_cfg_len;
934 PACCESS_RANGE accessRange;
935 UCHAR pci_cfg_buf[sizeof(PCI_COMMON_CONFIG)];
936
937 PAHCI_MEMORY_REGISTERS abar;
938 PPCI_COMMON_CONFIG pciConfigData;
939 PAHCI_ADAPTER_EXTENSION adapterExtension;
940
941 DebugPrint("AhciHwFindAdapter()\n");
942
943 UNREFERENCED_PARAMETER(HwContext);
944 UNREFERENCED_PARAMETER(BusInformation);
945 UNREFERENCED_PARAMETER(ArgumentString);
946 UNREFERENCED_PARAMETER(Reserved3);
947
948 adapterExtension = AdapterExtension;
949 adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
950 adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
951
952 // get PCI configuration header
953 pci_cfg_len = StorPortGetBusData(
954 adapterExtension,
955 PCIConfiguration,
956 adapterExtension->SystemIoBusNumber,
957 adapterExtension->SlotNumber,
958 pci_cfg_buf,
959 sizeof(PCI_COMMON_CONFIG));
960
961 if (pci_cfg_len != sizeof(PCI_COMMON_CONFIG))
962 {
963 DebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG), pci_cfg_len);
964 return SP_RETURN_ERROR;//Not a valid device at the given bus number
965 }
966
967 pciConfigData = pci_cfg_buf;
968 adapterExtension->VendorID = pciConfigData->VendorID;
969 adapterExtension->DeviceID = pciConfigData->DeviceID;
970 adapterExtension->RevisionID = pciConfigData->RevisionID;
971 // 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).
972 adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
973
974 DebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension->VendorID,
975 adapterExtension->DeviceID,
976 adapterExtension->RevisionID);
977
978 // 2.1.11
979 abar = NULL;
980 if (ConfigInfo->NumberOfAccessRanges > 0)
981 {
982 accessRange = *(ConfigInfo->AccessRanges);
983 for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
984 {
985 if (accessRange[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
986 {
987 abar = StorPortGetDeviceBase(adapterExtension,
988 ConfigInfo->AdapterInterfaceType,
989 ConfigInfo->SystemIoBusNumber,
990 accessRange[index].RangeStart,
991 accessRange[index].RangeLength,
992 !accessRange[index].RangeInMemory);
993 break;
994 }
995 }
996 }
997
998 if (abar == NULL)
999 {
1000 DebugPrint("\tabar == NULL\n");
1001 return SP_RETURN_ERROR; // corrupted information supplied
1002 }
1003
1004 adapterExtension->ABAR_Address = abar;
1005 adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
1006 adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
1007 adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
1008 adapterExtension->LastInterruptPort = -1;
1009
1010 // 10.1.2
1011 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
1012 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
1013 ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
1014 // AE := Highest Significant bit of GHC
1015 if (ghc.AE != 0)// Hmm, controller was already in power state
1016 {
1017 // reset controller to have it in known state
1018 DebugPrint("\tAE Already set, Reset()\n");
1019 if (!AhciAdapterReset(adapterExtension))
1020 {
1021 DebugPrint("\tReset Failed!\n");
1022 return SP_RETURN_ERROR;// reset failed
1023 }
1024 }
1025
1026 ghc.Status = 0;
1027 ghc.AE = 1;// only AE=1
1028 // tell the controller that we know about AHCI
1029 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status);
1030
1031 adapterExtension->IS = &abar->IS;
1032 adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
1033
1034 if (adapterExtension->PortImplemented == 0)
1035 {
1036 DebugPrint("\tadapterExtension->PortImplemented == 0\n");
1037 return SP_RETURN_ERROR;
1038 }
1039
1040 ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;//128 KB
1041 ConfigInfo->NumberOfPhysicalBreaks = 0x21;
1042 ConfigInfo->MaximumNumberOfTargets = 1;
1043 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
1044 ConfigInfo->ResetTargetSupported = TRUE;
1045 ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT;
1046 ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
1047 ConfigInfo->ScatterGather = TRUE;
1048
1049 // Turn IE -- Interrupt Enabled
1050 ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
1051 ghc.IE = 1;
1052 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status);
1053
1054 // allocate necessary resource for each port
1055 if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
1056 {
1057 DebugPrint("\tAhciAllocateResourceForAdapter() == FALSE\n");
1058 return SP_RETURN_ERROR;
1059 }
1060
1061 for (index = 0; index < adapterExtension->PortCount; index++)
1062 {
1063 if ((adapterExtension->PortImplemented & (0x1 << index)) != 0)
1064 AhciPortInitialize(&adapterExtension->PortExtension[index]);
1065 }
1066
1067 return SP_RETURN_FOUND;
1068 }// -- AhciHwFindAdapter();
1069
1070 /**
1071 * @name DriverEntry
1072 * @implemented
1073 *
1074 * Initial Entrypoint for storahci miniport driver
1075 *
1076 * @param DriverObject
1077 * @param RegistryPath
1078 *
1079 * @return
1080 * NT_STATUS in case of driver loaded successfully.
1081 */
1082 ULONG
1083 DriverEntry (
1084 __in PVOID DriverObject,
1085 __in PVOID RegistryPath
1086 )
1087 {
1088 ULONG status;
1089 HW_INITIALIZATION_DATA hwInitializationData;
1090
1091 DebugPrint("Storahci Loaded\n");
1092
1093 // initialize the hardware data structure
1094 AhciZeroMemory(&hwInitializationData, sizeof(HW_INITIALIZATION_DATA));
1095
1096 // set size of hardware initialization structure
1097 hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
1098
1099 // identity required miniport entry point routines
1100 hwInitializationData.HwStartIo = AhciHwStartIo;
1101 hwInitializationData.HwResetBus = AhciHwResetBus;
1102 hwInitializationData.HwInterrupt = AhciHwInterrupt;
1103 hwInitializationData.HwInitialize = AhciHwInitialize;
1104 hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
1105
1106 // adapter specific information
1107 hwInitializationData.NeedPhysicalAddresses = TRUE;
1108 hwInitializationData.TaggedQueuing = TRUE;
1109 hwInitializationData.AutoRequestSense = TRUE;
1110 hwInitializationData.MultipleRequestPerLu = TRUE;
1111
1112 hwInitializationData.NumberOfAccessRanges = 6;
1113 hwInitializationData.AdapterInterfaceType = PCIBus;
1114 hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
1115
1116 // set required extension sizes
1117 hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
1118 hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
1119
1120 // register our hw init data
1121 status = StorPortInitialize(DriverObject,
1122 RegistryPath,
1123 &hwInitializationData,
1124 NULL);
1125
1126 DebugPrint("\tstatus: %x\n", status);
1127 return status;
1128 }// -- DriverEntry();
1129
1130 /**
1131 * @name AhciATA_CFIS
1132 * @implemented
1133 *
1134 * create ATA CFIS from Srb
1135 *
1136 * @param PortExtension
1137 * @param Srb
1138 *
1139 * @return
1140 * Number of CFIS fields used in DWORD
1141 */
1142 ULONG
1143 AhciATA_CFIS (
1144 __in PAHCI_PORT_EXTENSION PortExtension,
1145 __in PAHCI_SRB_EXTENSION SrbExtension
1146 )
1147 {
1148 PAHCI_COMMAND_TABLE cmdTable;
1149
1150 UNREFERENCED_PARAMETER(PortExtension);
1151
1152 DebugPrint("AhciATA_CFIS()\n");
1153
1154 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
1155
1156 AhciZeroMemory(&cmdTable->CFIS, sizeof(cmdTable->CFIS));
1157
1158 cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = FIS_TYPE_REG_H2D; // FIS Type
1159 cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C
1160 cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg;
1161
1162 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow;
1163 cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0;
1164 cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1;
1165 cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2;
1166 cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device;
1167 cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3;
1168 cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4;
1169 cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5;
1170 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh;
1171 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow;
1172 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh;
1173
1174 return 5;
1175 }// -- AhciATA_CFIS();
1176
1177 /**
1178 * @name AhciATAPI_CFIS
1179 * @not_implemented
1180 *
1181 * create ATAPI CFIS from Srb
1182 *
1183 * @param PortExtension
1184 * @param Srb
1185 *
1186 * @return
1187 * Number of CFIS fields used in DWORD
1188 */
1189 ULONG
1190 AhciATAPI_CFIS (
1191 __in PAHCI_PORT_EXTENSION PortExtension,
1192 __in PAHCI_SRB_EXTENSION SrbExtension
1193 )
1194 {
1195 UNREFERENCED_PARAMETER(PortExtension);
1196 UNREFERENCED_PARAMETER(SrbExtension);
1197
1198 DebugPrint("AhciATAPI_CFIS()\n");
1199
1200 return 2;
1201 }// -- AhciATAPI_CFIS();
1202
1203 /**
1204 * @name AhciBuild_PRDT
1205 * @implemented
1206 *
1207 * Build PRDT for data transfer
1208 *
1209 * @param PortExtension
1210 * @param Srb
1211 *
1212 * @return
1213 * Return number of entries in PRDT.
1214 */
1215 ULONG
1216 AhciBuild_PRDT (
1217 __in PAHCI_PORT_EXTENSION PortExtension,
1218 __in PAHCI_SRB_EXTENSION SrbExtension
1219 )
1220 {
1221 ULONG index;
1222 PAHCI_COMMAND_TABLE cmdTable;
1223 PLOCAL_SCATTER_GATHER_LIST sgl;
1224 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1225
1226 DebugPrint("AhciBuild_PRDT()\n");
1227
1228 sgl = &SrbExtension->Sgl;
1229 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
1230 AdapterExtension = PortExtension->AdapterExtension;
1231
1232 NT_ASSERT(sgl != NULL);
1233 NT_ASSERT(sgl->NumberOfElements < MAXIMUM_AHCI_PRDT_ENTRIES);
1234
1235 for (index = 0; index < sgl->NumberOfElements; index++)
1236 {
1237 NT_ASSERT(sgl->List[index].Length <= MAXIMUM_TRANSFER_LENGTH);
1238
1239 cmdTable->PRDT[index].DBA = sgl->List[index].PhysicalAddress.LowPart;
1240 if (IsAdapterCAPS64(AdapterExtension->CAP))
1241 {
1242 cmdTable->PRDT[index].DBAU = sgl->List[index].PhysicalAddress.HighPart;
1243 }
1244
1245 // Data Byte Count (DBC): A ‘0’ based value that Indicates the length, in bytes, of the data block.
1246 // A maximum of length of 4MB may exist for any entry. Bit ‘0’ of this field must always be ‘1’ to
1247 // indicate an even byte count. A value of ‘1’ indicates 2 bytes, ‘3’ indicates 4 bytes, etc.
1248 cmdTable->PRDT[index].DBC = sgl->List[index].Length - 1;
1249 }
1250
1251 return sgl->NumberOfElements;
1252 }// -- AhciBuild_PRDT();
1253
1254 /**
1255 * @name AhciProcessSrb
1256 * @implemented
1257 *
1258 * Prepare Srb for IO processing
1259 *
1260 * @param PortExtension
1261 * @param Srb
1262 * @param SlotIndex
1263 *
1264 */
1265 VOID
1266 AhciProcessSrb (
1267 __in PAHCI_PORT_EXTENSION PortExtension,
1268 __in PSCSI_REQUEST_BLOCK Srb,
1269 __in ULONG SlotIndex
1270 )
1271 {
1272 ULONG prdtlen, sig, length, cfl;
1273 PAHCI_SRB_EXTENSION SrbExtension;
1274 PAHCI_COMMAND_HEADER CommandHeader;
1275 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1276 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress;
1277
1278 DebugPrint("AhciProcessSrb()\n");
1279
1280 NT_ASSERT(Srb->PathId == PortExtension->PortNumber);
1281
1282 SrbExtension = GetSrbExtension(Srb);
1283 AdapterExtension = PortExtension->AdapterExtension;
1284
1285 NT_ASSERT(SrbExtension != NULL);
1286 NT_ASSERT(SrbExtension->AtaFunction != 0);
1287
1288 if ((SrbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) &&
1289 (SrbExtension->CommandReg == IDE_COMMAND_NOT_VALID))
1290 {
1291 // Here we are safe to check SIG register
1292 sig = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SIG);
1293 if (sig == 0x101)
1294 {
1295 DebugPrint("\tATA Device Found!\n");
1296 SrbExtension->CommandReg = IDE_COMMAND_IDENTIFY;
1297 }
1298 else
1299 {
1300 DebugPrint("\tATAPI Device Found!\n");
1301 SrbExtension->CommandReg = IDE_COMMAND_ATAPI_IDENTIFY;
1302 }
1303 }
1304
1305 NT_ASSERT(SlotIndex < AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
1306 SrbExtension->SlotIndex = SlotIndex;
1307
1308 // program the CFIS in the CommandTable
1309 CommandHeader = &PortExtension->CommandList[SlotIndex];
1310
1311 cfl = 0;
1312 if (IsAtaCommand(SrbExtension->AtaFunction))
1313 {
1314 cfl = AhciATA_CFIS(PortExtension, SrbExtension);
1315 }
1316 else if (IsAtapiCommand(SrbExtension->AtaFunction))
1317 {
1318 cfl = AhciATAPI_CFIS(PortExtension, SrbExtension);
1319 }
1320
1321 prdtlen = 0;
1322 if (IsDataTransferNeeded(SrbExtension))
1323 {
1324 prdtlen = AhciBuild_PRDT(PortExtension, SrbExtension);
1325 NT_ASSERT(prdtlen != -1);
1326 }
1327
1328 // Program the command header
1329 CommandHeader->DI.PRDTL = prdtlen; // number of entries in PRD table
1330 CommandHeader->DI.CFL = cfl;
1331 CommandHeader->DI.A = (SrbExtension->AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) ? 1 : 0;
1332 CommandHeader->DI.W = (SrbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0;
1333 CommandHeader->DI.P = 0; // ATA Specifications says so
1334 CommandHeader->DI.PMP = 0; // Port Multiplier
1335
1336 // Reset -- Manual Configuation
1337 CommandHeader->DI.R = 0;
1338 CommandHeader->DI.B = 0;
1339 CommandHeader->DI.C = 0;
1340
1341 CommandHeader->PRDBC = 0;
1342
1343 CommandHeader->Reserved[0] = 0;
1344 CommandHeader->Reserved[1] = 0;
1345 CommandHeader->Reserved[2] = 0;
1346 CommandHeader->Reserved[3] = 0;
1347
1348 // set CommandHeader CTBA
1349 CommandTablePhysicalAddress = StorPortGetPhysicalAddress(AdapterExtension,
1350 NULL,
1351 SrbExtension,
1352 &length);
1353
1354 NT_ASSERT(length != 0);
1355
1356 // command table alignment
1357 NT_ASSERT((CommandTablePhysicalAddress.LowPart % 128) == 0);
1358
1359 CommandHeader->CTBA = CommandTablePhysicalAddress.LowPart;
1360
1361 if (IsAdapterCAPS64(AdapterExtension->CAP))
1362 {
1363 CommandHeader->CTBA_U = CommandTablePhysicalAddress.HighPart;
1364 }
1365
1366 // mark this slot
1367 PortExtension->Slot[SlotIndex] = Srb;
1368 PortExtension->QueueSlots |= 1 << SlotIndex;
1369 return;
1370 }// -- AhciProcessSrb();
1371
1372 /**
1373 * @name AhciActivatePort
1374 * @implemented
1375 *
1376 * Program Port and populate command list
1377 *
1378 * @param PortExtension
1379 *
1380 */
1381 VOID
1382 AhciActivatePort (
1383 __in PAHCI_PORT_EXTENSION PortExtension
1384 )
1385 {
1386 AHCI_PORT_CMD cmd;
1387 ULONG sact, ci;
1388 ULONG QueueSlots, slotToActivate, tmp;
1389 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1390
1391 DebugPrint("AhciActivatePort()\n");
1392
1393 AdapterExtension = PortExtension->AdapterExtension;
1394 QueueSlots = PortExtension->QueueSlots;
1395
1396 if (QueueSlots == 0)
1397 {
1398 return;
1399 }
1400
1401 // section 3.3.14
1402 // Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’
1403 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
1404
1405 if (cmd.ST == 0) // PxCMD.ST == 0
1406 {
1407 return;
1408 }
1409
1410 sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT);
1411 ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI);
1412
1413 // get the lowest set bit
1414 tmp = QueueSlots & (QueueSlots - 1);
1415
1416 if (tmp == 0)
1417 slotToActivate = QueueSlots;
1418 else
1419 slotToActivate = (QueueSlots & (~tmp));
1420
1421 // mark that bit off in QueueSlots
1422 // so we can know we it is really needed to activate port or not
1423 PortExtension->QueueSlots &= ~slotToActivate;
1424 // mark this CommandIssuedSlots
1425 // to validate in completeIssuedCommand
1426 PortExtension->CommandIssuedSlots |= slotToActivate;
1427
1428 // tell the HBA to issue this Command Slot to the given port
1429 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CI, slotToActivate);
1430
1431 return;
1432 }// -- AhciActivatePort();
1433
1434 /**
1435 * @name AhciProcessIO
1436 * @implemented
1437 *
1438 * Acquire Exclusive lock to port, populate pending commands to command List
1439 * program controller's port to process new commands in command list.
1440 *
1441 * @param AdapterExtension
1442 * @param PathId
1443 * @param Srb
1444 *
1445 */
1446 VOID
1447 AhciProcessIO (
1448 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1449 __in UCHAR PathId,
1450 __in PSCSI_REQUEST_BLOCK Srb
1451 )
1452 {
1453 STOR_LOCK_HANDLE lockhandle;
1454 PSCSI_REQUEST_BLOCK tmpSrb;
1455 PAHCI_PORT_EXTENSION PortExtension;
1456 ULONG commandSlotMask, occupiedSlots, slotIndex, NCS;
1457
1458 DebugPrint("AhciProcessIO()\n");
1459 DebugPrint("\tPathId: %d\n", PathId);
1460
1461 PortExtension = &AdapterExtension->PortExtension[PathId];
1462
1463 NT_ASSERT(PathId < AdapterExtension->PortCount);
1464
1465 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
1466
1467 // Acquire Lock
1468 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
1469
1470 // add Srb to queue
1471 AddQueue(&PortExtension->SrbQueue, Srb);
1472
1473 if (PortExtension->DeviceParams.IsActive == FALSE)
1474 {
1475 // Release Lock
1476 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1477 return; // we should wait for device to get active
1478 }
1479
1480 occupiedSlots = (PortExtension->QueueSlots | PortExtension->CommandIssuedSlots); // Busy command slots for given port
1481 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
1482 commandSlotMask = (1 << NCS) - 1; // available slots mask
1483
1484 commandSlotMask = (commandSlotMask & ~occupiedSlots);
1485 if(commandSlotMask != 0)
1486 {
1487 // iterate over HBA port slots
1488 for (slotIndex = 0; slotIndex < NCS; slotIndex++)
1489 {
1490 // find first free slot
1491 if ((commandSlotMask & (1 << slotIndex)) != 0)
1492 {
1493 tmpSrb = RemoveQueue(&PortExtension->SrbQueue);
1494 if (tmpSrb != NULL)
1495 {
1496 NT_ASSERT(tmpSrb->PathId == PathId);
1497 AhciProcessSrb(PortExtension, tmpSrb, slotIndex);
1498 }
1499 else
1500 {
1501 break;
1502 }
1503 }
1504 else
1505 {
1506 break;
1507 }
1508 }
1509 }
1510
1511 // program HBA port
1512 AhciActivatePort(PortExtension);
1513
1514 // Release Lock
1515 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1516
1517 return;
1518 }// -- AhciProcessIO();
1519
1520 /**
1521 * @name InquiryCompletion
1522 * @not_implemented
1523 *
1524 * InquiryCompletion routine should be called after device signals
1525 * for device inquiry request is completed (through interrupt)
1526 *
1527 * @param PortExtension
1528 * @param Srb
1529 *
1530 */
1531 VOID
1532 InquiryCompletion (
1533 __in PAHCI_PORT_EXTENSION PortExtension,
1534 __in PSCSI_REQUEST_BLOCK Srb
1535 )
1536 {
1537 PCDB cdb;
1538 PLUN_LIST LunList;
1539 PAHCI_SRB_EXTENSION SrbExtension;
1540 PIDENTIFY_DEVICE_DATA IdentifyDeviceData;
1541 ULONG SrbStatus, LunCount, DataBufferLength;
1542
1543 DebugPrint("InquiryCompletion()\n");
1544
1545 NT_ASSERT(PortExtension != NULL);
1546 NT_ASSERT(Srb != NULL);
1547
1548 cdb = (PCDB)&Srb->Cdb;
1549 SrbStatus = Srb->SrbStatus;
1550 SrbExtension = GetSrbExtension(Srb);
1551
1552 if (SrbStatus == SRB_STATUS_SUCCESS)
1553 {
1554 IdentifyDeviceData = PortExtension->IdentifyDeviceData;
1555
1556 if (SrbExtension->CommandReg == IDE_COMMAND_IDENTIFY)
1557 {
1558 DebugPrint("Device: ATA\n");
1559 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATA;
1560 if (IdentifyDeviceData->GeneralConfiguration.RemovableMedia)
1561 {
1562 PortExtension->DeviceParams.RemovableDevice = 1;
1563 }
1564
1565 if (IdentifyDeviceData->CommandSetSupport.BigLba && IdentifyDeviceData->CommandSetActive.BigLba)
1566 {
1567 PortExtension->DeviceParams.Lba48BitMode = 1;
1568 }
1569
1570 PortExtension->DeviceParams.AccessType = DIRECT_ACCESS_DEVICE;
1571
1572 // TODO: Add other device params
1573 }
1574 else
1575 {
1576 DebugPrint("Device: ATAPI\n");
1577 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATAPI;
1578
1579 PortExtension->DeviceParams.AccessType = READ_ONLY_DIRECT_ACCESS_DEVICE;
1580 }
1581 }
1582 else if (SrbStatus == SRB_STATUS_NO_DEVICE)
1583 {
1584 DebugPrint("Device: No Device\n");
1585 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_NODEVICE;
1586 }
1587 else
1588 {
1589 return;
1590 }
1591
1592 if ((cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS))
1593 {
1594 Srb->SrbStatus = SRB_STATUS_SUCCESS;
1595 Srb->ScsiStatus = SCSISTAT_GOOD;
1596
1597 SrbExtension->AtaFunction = 0;
1598 DataBufferLength = Srb->DataTransferLength;
1599
1600 LunList = (PLUN_LIST)Srb->DataBuffer;
1601 if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_NODEVICE)
1602 {
1603 LunCount = 0;
1604 }
1605 else
1606 {
1607 LunCount = 1;
1608 }
1609
1610 if (DataBufferLength < sizeof(LUN_LIST))
1611 {
1612 DebugPrint("\tSRB_STATUS_DATA_OVERRUN\n");
1613 Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
1614 }
1615 else
1616 {
1617 LunList->LunListLength[0] = 0;
1618 LunList->LunListLength[1] = 0;
1619 LunList->LunListLength[2] = 0;
1620 LunList->LunListLength[3] = 8;
1621
1622 // followed by 8 entries
1623 LunList->Lun[0][0] = 0;
1624 LunList->Lun[0][1] = 0;
1625 LunList->Lun[0][2] = 0;
1626 LunList->Lun[0][3] = 0;
1627 LunList->Lun[0][4] = 0;
1628 LunList->Lun[0][5] = 0;
1629 LunList->Lun[0][6] = 0;
1630 LunList->Lun[0][7] = 0;
1631 }
1632 }
1633
1634 return;
1635 }// -- InquiryCompletion();
1636
1637 /**
1638 * @name DeviceInquiryRequest
1639 * @implemented
1640 *
1641 * Tells wheather given port is implemented or not
1642 *
1643 * @param AdapterExtension
1644 * @param Srb
1645 * @param Cdb
1646 *
1647 * @return
1648 * return STOR status for DeviceInquiryRequest
1649 *
1650 * @remark
1651 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
1652 */
1653 UCHAR
1654 DeviceInquiryRequest (
1655 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1656 __in PSCSI_REQUEST_BLOCK Srb,
1657 __in PCDB Cdb,
1658 __in BOOLEAN HasProductDataRequest
1659 )
1660 {
1661 PVOID DataBuffer;
1662 PAHCI_PORT_EXTENSION PortExtension;
1663 PAHCI_SRB_EXTENSION SrbExtension;
1664 PVPD_SUPPORTED_PAGES_PAGE VpdOutputBuffer;
1665 ULONG DataBufferLength, RequiredDataBufferLength;
1666
1667 DebugPrint("DeviceInquiryRequest()\n");
1668
1669 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
1670
1671 SrbExtension = GetSrbExtension(Srb);
1672 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1673
1674 if (Srb->Lun != 0)
1675 {
1676 return SRB_STATUS_SELECTION_TIMEOUT;
1677 }
1678 else if ((HasProductDataRequest == FALSE) || (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0))
1679 {
1680 // 3.6.1
1681 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
1682 DebugPrint("\tEVPD Inquired\n");
1683 NT_ASSERT(SrbExtension != NULL);
1684
1685 SrbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY;
1686 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
1687 SrbExtension->CompletionRoutine = InquiryCompletion;
1688 SrbExtension->CommandReg = IDE_COMMAND_NOT_VALID;
1689
1690 // TODO: Should use AhciZeroMemory
1691 SrbExtension->FeaturesLow = 0;
1692 SrbExtension->LBA0 = 0;
1693 SrbExtension->LBA1 = 0;
1694 SrbExtension->LBA2 = 0;
1695 SrbExtension->Device = 0xA0;
1696 SrbExtension->LBA3 = 0;
1697 SrbExtension->LBA4 = 0;
1698 SrbExtension->LBA5 = 0;
1699 SrbExtension->FeaturesHigh = 0;
1700 SrbExtension->SectorCountLow = 0;
1701 SrbExtension->SectorCountHigh = 0;
1702
1703 SrbExtension->Sgl.NumberOfElements = 1;
1704 SrbExtension->Sgl.List[0].PhysicalAddress.LowPart = PortExtension->IdentifyDeviceDataPhysicalAddress.LowPart;
1705 SrbExtension->Sgl.List[0].PhysicalAddress.HighPart = PortExtension->IdentifyDeviceDataPhysicalAddress.HighPart;
1706 SrbExtension->Sgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA);
1707
1708 AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
1709 return SRB_STATUS_PENDING;
1710 }
1711 else if (HasProductDataRequest == TRUE)
1712 {
1713 DebugPrint("\tVPD Inquired\n");
1714
1715 DataBuffer = Srb->DataBuffer;
1716 DataBufferLength = Srb->DataTransferLength;
1717
1718 if (DataBuffer == NULL)
1719 {
1720 return SRB_STATUS_INVALID_REQUEST;
1721 }
1722
1723 AhciZeroMemory(DataBuffer, DataBufferLength);
1724
1725 switch(Cdb->CDB6INQUIRY3.PageCode)
1726 {
1727 case VPD_SUPPORTED_PAGES:
1728 {
1729 DebugPrint("\tVPD_SUPPORTED_PAGES\n");
1730 RequiredDataBufferLength = sizeof(VPD_SUPPORTED_PAGES_PAGE);
1731 if (DataBufferLength < RequiredDataBufferLength)
1732 {
1733 DebugPrint("\tDataBufferLength: %d Required: %d\n", DataBufferLength, RequiredDataBufferLength);
1734 return SRB_STATUS_INVALID_REQUEST;
1735 }
1736 else
1737 {
1738 VpdOutputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)DataBuffer;
1739 VpdOutputBuffer->DeviceType = PortExtension->DeviceParams.DeviceType;
1740 VpdOutputBuffer->DeviceTypeQualifier = 0;
1741 VpdOutputBuffer->PageCode = VPD_SUPPORTED_PAGES;
1742 VpdOutputBuffer->PageLength = 1;
1743 VpdOutputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES;
1744
1745 return SRB_STATUS_SUCCESS;
1746 }
1747 }
1748 break;
1749 default:
1750 DebugPrint("\tPageCode: %x\n", Cdb->CDB6INQUIRY3.PageCode);
1751 break;
1752 }
1753 }
1754
1755 return SRB_STATUS_INVALID_REQUEST;
1756 }// -- DeviceInquiryRequest();
1757
1758 /**
1759 * @name AhciAdapterReset
1760 * @implemented
1761 *
1762 * 10.4.3 HBA Reset
1763 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
1764 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
1765 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
1766 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
1767 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
1768 * the HBA reset has completed.
1769 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
1770 * a hung or locked state.
1771 *
1772 * @param AdapterExtension
1773 *
1774 * @return
1775 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
1776 */
1777 BOOLEAN
1778 AhciAdapterReset (
1779 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
1780 )
1781 {
1782 ULONG ticks;
1783 AHCI_GHC ghc;
1784 PAHCI_MEMORY_REGISTERS abar = NULL;
1785
1786 DebugPrint("AhciAdapterReset()\n");
1787
1788 abar = AdapterExtension->ABAR_Address;
1789 if (abar == NULL) // basic sanity
1790 {
1791 return FALSE;
1792 }
1793
1794 // HR -- Very first bit (lowest significant)
1795 ghc.HR = 1;
1796 StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc.Status);
1797
1798 for (ticks = 0; ticks < 50; ++ticks)
1799 {
1800 ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC);
1801 if (ghc.HR == 0)
1802 {
1803 break;
1804 }
1805 StorPortStallExecution(20000);
1806 }
1807
1808 if (ticks == 50)// 1 second
1809 {
1810 DebugPrint("\tDevice Timeout\n");
1811 return FALSE;
1812 }
1813
1814 return TRUE;
1815 }// -- AhciAdapterReset();
1816
1817 /**
1818 * @name AhciZeroMemory
1819 * @implemented
1820 *
1821 * Clear buffer by filling zeros
1822 *
1823 * @param Buffer
1824 * @param BufferSize
1825 */
1826 __inline
1827 VOID
1828 AhciZeroMemory (
1829 __out PCHAR Buffer,
1830 __in ULONG BufferSize
1831 )
1832 {
1833 ULONG i;
1834 for (i = 0; i < BufferSize; i++)
1835 {
1836 Buffer[i] = 0;
1837 }
1838
1839 return;
1840 }// -- AhciZeroMemory();
1841
1842 /**
1843 * @name IsPortValid
1844 * @implemented
1845 *
1846 * Tells wheather given port is implemented or not
1847 *
1848 * @param AdapterExtension
1849 * @param PathId
1850 *
1851 * @return
1852 * return TRUE if provided port is valid (implemented) or not
1853 */
1854 __inline
1855 BOOLEAN
1856 IsPortValid (
1857 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1858 __in ULONG pathId
1859 )
1860 {
1861 NT_ASSERT(pathId < MAXIMUM_AHCI_PORT_COUNT);
1862
1863 if (pathId >= AdapterExtension->PortCount)
1864 {
1865 return FALSE;
1866 }
1867
1868 return AdapterExtension->PortExtension[pathId].DeviceParams.IsActive;
1869 }// -- IsPortValid()
1870
1871 /**
1872 * @name AddQueue
1873 * @implemented
1874 *
1875 * Add Srb to Queue
1876 *
1877 * @param Queue
1878 * @param Srb
1879 *
1880 * @return
1881 * return TRUE if Srb is successfully added to Queue
1882 *
1883 */
1884 __inline
1885 BOOLEAN
1886 AddQueue (
1887 __inout PAHCI_QUEUE Queue,
1888 __in PVOID Srb
1889 )
1890 {
1891 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1892 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1893
1894 if (Queue->Tail == ((Queue->Head + 1) % MAXIMUM_QUEUE_BUFFER_SIZE))
1895 return FALSE;
1896
1897 Queue->Buffer[Queue->Head++] = Srb;
1898 Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE;
1899
1900 return TRUE;
1901 }// -- AddQueue();
1902
1903 /**
1904 * @name RemoveQueue
1905 * @implemented
1906 *
1907 * Remove and return Srb from Queue
1908 *
1909 * @param Queue
1910 *
1911 * @return
1912 * return Srb
1913 *
1914 */
1915 __inline
1916 PVOID
1917 RemoveQueue (
1918 __inout PAHCI_QUEUE Queue
1919 )
1920 {
1921 PVOID Srb;
1922
1923 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1924 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1925
1926 if (Queue->Head == Queue->Tail)
1927 return NULL;
1928
1929 Srb = Queue->Buffer[Queue->Tail++];
1930 Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE;
1931
1932 return Srb;
1933 }// -- RemoveQueue();
1934
1935 /**
1936 * @name GetSrbExtension
1937 * @implemented
1938 *
1939 * GetSrbExtension from Srb make sure It is properly aligned
1940 *
1941 * @param Srb
1942 *
1943 * @return
1944 * return SrbExtension
1945 *
1946 */
1947 __inline
1948 PAHCI_SRB_EXTENSION
1949 GetSrbExtension (
1950 __in PSCSI_REQUEST_BLOCK Srb
1951 )
1952 {
1953 ULONG Offset;
1954 ULONG_PTR SrbExtension;
1955
1956 SrbExtension = (ULONG_PTR)Srb->SrbExtension;
1957 Offset = SrbExtension % 128;
1958
1959 // CommandTable should be 128 byte aligned
1960 if (Offset != 0)
1961 Offset = 128 - Offset;
1962
1963 return (PAHCI_SRB_EXTENSION)(SrbExtension + Offset);
1964 }// -- PAHCI_SRB_EXTENSION();