[STORAHCI] Merge Storport Miniport driver by Aman Priyadarshi in GSoC.
[reactos.git] / reactos / 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 PAHCI_MEMORY_REGISTERS abar;
28 ULONG mappedLength, portNumber, ticks;
29 PAHCI_ADAPTER_EXTENSION adapterExtension;
30 STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical;
31
32 AhciDebugPrint("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 AhciDebugPrint("\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 AhciDebugPrint("\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 cmd.ST = 0;
80 cmd.FRE = 0;
81
82 ticks = 3;
83 do
84 {
85 StorPortStallExecution(50000);
86 cmd.Status = StorPortReadRegisterUlong(adapterExtension, &PortExtension->Port->CMD);
87 if (ticks == 0)
88 {
89 AhciDebugPrint("\tAttempt to reset port failed: %x\n", cmd);
90 return FALSE;
91 }
92 ticks--;
93 }
94 while(cmd.CR != 0 || cmd.FR != 0);
95 }
96
97 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
98 //  PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
99 //  PxFB and PxFBU (if CAP.S64A is set to ‘1’)
100 // Note: Assuming 32bit support only
101 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLB, commandListPhysical.LowPart);
102 if (IsAdapterCAPS64(adapterExtension->CAP))
103 {
104 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLBU, commandListPhysical.HighPart);
105 }
106
107 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FB, receivedFISPhysical.LowPart);
108 if (IsAdapterCAPS64(adapterExtension->CAP))
109 {
110 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FBU, receivedFISPhysical.HighPart);
111 }
112
113 PortExtension->IdentifyDeviceDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension,
114 NULL,
115 PortExtension->IdentifyDeviceData,
116 &mappedLength);
117
118 // set device power state flag to D0
119 PortExtension->DevicePowerState = StorPowerDeviceD0;
120
121 // clear pending interrupts
122 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->SERR, (ULONG)~0);
123 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->IS, (ULONG)~0);
124 StorPortWriteRegisterUlong(adapterExtension, adapterExtension->IS, (1 << PortExtension->PortNumber));
125
126 return TRUE;
127 }// -- AhciPortInitialize();
128
129 /**
130 * @name AhciAllocateResourceForAdapter
131 * @implemented
132 *
133 * Allocate memory from poll for required pointers
134 *
135 * @param AdapterExtension
136 * @param ConfigInfo
137 *
138 * @return
139 * return TRUE if allocation was successful
140 */
141 BOOLEAN
142 AhciAllocateResourceForAdapter (
143 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
144 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
145 )
146 {
147 PCHAR nonCachedExtension, tmp;
148 ULONG index, NCS, AlignedNCS;
149 ULONG portCount, portImplemented, nonCachedExtensionSize;
150 PAHCI_PORT_EXTENSION PortExtension;
151
152 AhciDebugPrint("AhciAllocateResourceForAdapter()\n");
153
154 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
155 AlignedNCS = ROUND_UP(NCS, 8);
156
157 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
158 portCount = 0;
159 portImplemented = AdapterExtension->PortImplemented;
160
161 NT_ASSERT(portImplemented != 0);
162 for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--)
163 if ((portImplemented & (1 << index)) != 0)
164 break;
165
166 portCount = index + 1;
167 AhciDebugPrint("\tPort Count: %d\n", portCount);
168
169 AdapterExtension->PortCount = portCount;
170 nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
171 sizeof(AHCI_RECEIVED_FIS) +
172 sizeof(IDENTIFY_DEVICE_DATA);
173
174 // align nonCachedExtensionSize to 1024
175 nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024);
176
177 AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension,
178 ConfigInfo,
179 nonCachedExtensionSize * portCount);
180
181 if (AdapterExtension->NonCachedExtension == NULL)
182 {
183 AhciDebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
184 return FALSE;
185 }
186
187 nonCachedExtension = AdapterExtension->NonCachedExtension;
188 AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount);
189
190 for (index = 0; index < portCount; index++)
191 {
192 PortExtension = &AdapterExtension->PortExtension[index];
193
194 PortExtension->DeviceParams.IsActive = FALSE;
195 if ((AdapterExtension->PortImplemented & (1 << index)) != 0)
196 {
197 PortExtension->PortNumber = index;
198 PortExtension->DeviceParams.IsActive = TRUE;
199 PortExtension->AdapterExtension = AdapterExtension;
200 PortExtension->CommandList = (PAHCI_COMMAND_HEADER)nonCachedExtension;
201
202 tmp = (PCHAR)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
203
204 PortExtension->ReceivedFIS = (PAHCI_RECEIVED_FIS)tmp;
205 PortExtension->IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)(tmp + sizeof(AHCI_RECEIVED_FIS));
206 PortExtension->MaxPortQueueDepth = NCS;
207 nonCachedExtension += nonCachedExtensionSize;
208 }
209 }
210
211 return TRUE;
212 }// -- AhciAllocateResourceForAdapter();
213
214 /**
215 * @name AhciStartPort
216 * @implemented
217 *
218 * Try to start the port device
219 *
220 * @param AdapterExtension
221 * @param PortExtension
222 *
223 */
224 BOOLEAN
225 AhciStartPort (
226 __in PAHCI_PORT_EXTENSION PortExtension
227 )
228 {
229 ULONG index;
230 AHCI_PORT_CMD cmd;
231 AHCI_TASK_FILE_DATA tfd;
232 AHCI_INTERRUPT_ENABLE ie;
233 AHCI_SERIAL_ATA_STATUS ssts;
234 AHCI_SERIAL_ATA_CONTROL sctl;
235 PAHCI_ADAPTER_EXTENSION AdapterExtension;
236
237 AhciDebugPrint("AhciStartPort()\n");
238
239 AdapterExtension = PortExtension->AdapterExtension;
240 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
241
242 if ((cmd.FR == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.ST == 1))
243 {
244 // Already Running
245 return TRUE;
246 }
247
248 cmd.SUD = 1;
249 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
250
251 if (((cmd.FR == 1) && (cmd.FRE == 0)) ||
252 ((cmd.CR == 1) && (cmd.ST == 0)))
253 {
254 AhciDebugPrint("\tCOMRESET\n");
255 // perform COMRESET
256 // section 10.4.2
257
258 // Software causes a port reset (COMRESET) by writing 1h to the PxSCTL.DET field to invoke a
259 // COMRESET on the interface and start a re-establishment of Phy layer communications. Software shall
260 // wait at least 1 millisecond before clearing PxSCTL.DET to 0h; this ensures that at least one COMRESET
261 // signal is sent over the interface. After clearing PxSCTL.DET to 0h, software should wait for
262 // communication to be re-established as indicated by PxSSTS.DET being set to 3h. Then software should
263 // write all 1s to the PxSERR register to clear any bits that were set as part of the port reset.
264
265 sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
266 sctl.DET = 1;
267 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
268
269 StorPortStallExecution(1000);
270
271 sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
272 sctl.DET = 0;
273 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
274
275 // Poll DET to verify if a device is attached to the port
276 index = 0;
277 do
278 {
279 StorPortStallExecution(1000);
280 ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
281
282 index++;
283 if (ssts.DET != 0)
284 {
285 break;
286 }
287 }
288 while(index < 30);
289 }
290
291 ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
292 switch (ssts.DET)
293 {
294 case 0x3:
295 {
296 NT_ASSERT(cmd.ST == 0);
297
298 // make sure FIS Recieve is enabled (cmd.FRE)
299 index = 0;
300 do
301 {
302 StorPortStallExecution(10000);
303 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
304 cmd.FRE = 1;
305 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
306 index++;
307 }
308 while((cmd.FR != 1) && (index < 3));
309
310 if (cmd.FR != 1)
311 {
312 // failed to start FIS DMA engine
313 // it can crash the driver later
314 // so better to turn this port off
315 return FALSE;
316 }
317
318 // start port channel
319 // set cmd.ST
320
321 NT_ASSERT(cmd.FRE == 1);
322 NT_ASSERT(cmd.CR == 0);
323
324 // why assert? well If we face such condition on DET = 0x3
325 // then we don't have port in idle state and hence before executing this part of code
326 // we must have restarted it.
327 tfd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->TFD);
328
329 if ((tfd.STS.BSY) || (tfd.STS.DRQ))
330 {
331 AhciDebugPrint("\tUnhandled Case BSY-DRQ\n");
332 }
333
334 // clear pending interrupts
335 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SERR, (ULONG)~0);
336 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, (ULONG)~0);
337 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, (1 << PortExtension->PortNumber));
338
339 // set IE
340 ie.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IE);
341 /* Device to Host Register FIS Interrupt Enable */
342 ie.DHRE = 1;
343 /* PIO Setup FIS Interrupt Enable */
344 ie.PSE = 1;
345 /* DMA Setup FIS Interrupt Enable */
346 ie.DSE = 1;
347 /* Set Device Bits FIS Interrupt Enable */
348 ie.SDBE = 1;
349 /* Unknown FIS Interrupt Enable */
350 ie.UFE = 0;
351 /* Descriptor Processed Interrupt Enable */
352 ie.DPE = 0;
353 /* Port Change Interrupt Enable */
354 ie.PCE = 1;
355 /* Device Mechanical Presence Enable */
356 ie.DMPE = 0;
357 /* PhyRdy Change Interrupt Enable */
358 ie.PRCE = 1;
359 /* Incorrect Port Multiplier Enable */
360 ie.IPME = 0;
361 /* Overflow Enable */
362 ie.OFE = 1;
363 /* Interface Non-fatal Error Enable */
364 ie.INFE = 1;
365 /* Interface Fatal Error Enable */
366 ie.IFE = 1;
367 /* Host Bus Data Error Enable */
368 ie.HBDE = 1;
369 /* Host Bus Fatal Error Enable */
370 ie.HBFE = 1;
371 /* Task File Error Enable */
372 ie.TFEE = 1;
373
374 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
375 /* Cold Presence Detect Enable */
376 if (cmd.CPD) // does it support CPD?
377 {
378 // disable it for now
379 ie.CPDE = 0;
380 }
381
382 // should I replace this to single line?
383 // by directly setting ie.Status?
384
385 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IE, ie.Status);
386
387 cmd.ST = 1;
388 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
389 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
390
391 if (cmd.ST != 1)
392 {
393 AhciDebugPrint("\tFailed to start Port\n");
394 return FALSE;
395 }
396
397 return TRUE;
398 }
399 default:
400 // unhandled case
401 AhciDebugPrint("\tDET == %x Unsupported\n", ssts.DET);
402 return FALSE;
403 }
404 }// -- AhciStartPort();
405
406 /**
407 * @name AhciCommandCompletionDpcRoutine
408 * @implemented
409 *
410 * Handles Completed Commands
411 *
412 * @param Dpc
413 * @param AdapterExtension
414 * @param SystemArgument1
415 * @param SystemArgument2
416 */
417 VOID
418 AhciCommandCompletionDpcRoutine (
419 __in PSTOR_DPC Dpc,
420 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
421 __in PAHCI_PORT_EXTENSION PortExtension,
422 __in PVOID SystemArgument2
423 )
424 {
425 PSCSI_REQUEST_BLOCK Srb;
426 PAHCI_SRB_EXTENSION SrbExtension;
427 STOR_LOCK_HANDLE lockhandle = {0};
428 PAHCI_COMPLETION_ROUTINE CompletionRoutine;
429
430 UNREFERENCED_PARAMETER(Dpc);
431 UNREFERENCED_PARAMETER(SystemArgument2);
432
433 AhciDebugPrint("AhciCommandCompletionDpcRoutine()\n");
434
435 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
436 Srb = RemoveQueue(&PortExtension->CompletionQueue);
437 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
438
439 NT_ASSERT(Srb != NULL);
440
441 if (Srb->SrbStatus == SRB_STATUS_PENDING)
442 {
443 Srb->SrbStatus = SRB_STATUS_SUCCESS;
444 }
445 else
446 {
447 return;
448 }
449
450 SrbExtension = GetSrbExtension(Srb);
451
452 CompletionRoutine = SrbExtension->CompletionRoutine;
453 NT_ASSERT(CompletionRoutine != NULL);
454
455 // now it's completion routine responsibility to set SrbStatus
456 CompletionRoutine(PortExtension, Srb);
457
458 StorPortNotification(RequestComplete, AdapterExtension, Srb);
459
460 return;
461 }// -- AhciCommandCompletionDpcRoutine();
462
463 /**
464 * @name AhciHwPassiveInitialize
465 * @implemented
466 *
467 * initializes the HBA and finds all devices that are of interest to the miniport driver. (at PASSIVE LEVEL)
468 *
469 * @param adapterExtension
470 *
471 * @return
472 * return TRUE if intialization was successful
473 */
474 BOOLEAN
475 AhciHwPassiveInitialize (
476 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
477 )
478 {
479 ULONG index;
480 PAHCI_PORT_EXTENSION PortExtension;
481
482 AhciDebugPrint("AhciHwPassiveInitialize()\n");
483
484 for (index = 0; index < AdapterExtension->PortCount; index++)
485 {
486 if ((AdapterExtension->PortImplemented & (0x1 << index)) != 0)
487 {
488 PortExtension = &AdapterExtension->PortExtension[index];
489 PortExtension->DeviceParams.IsActive = AhciStartPort(PortExtension);
490 StorPortInitializeDpc(AdapterExtension, &PortExtension->CommandCompletion, AhciCommandCompletionDpcRoutine);
491 }
492 }
493
494 return TRUE;
495 }// -- AhciHwPassiveInitialize();
496
497 /**
498 * @name AhciHwInitialize
499 * @implemented
500 *
501 * initializes the HBA and finds all devices that are of interest to the miniport driver.
502 *
503 * @param adapterExtension
504 *
505 * @return
506 * return TRUE if intialization was successful
507 */
508 BOOLEAN
509 AhciHwInitialize (
510 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
511 )
512 {
513 AHCI_GHC ghc;
514
515 AhciDebugPrint("AhciHwInitialize()\n");
516
517 AdapterExtension->StateFlags.MessagePerPort = FALSE;
518
519 // First check what type of interrupt/synchronization device is using
520 ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &AdapterExtension->ABAR_Address->GHC);
521
522 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
523 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
524 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
525 // software has allocated the number of messages requested
526 if (ghc.MRSM == 0)
527 {
528 AdapterExtension->StateFlags.MessagePerPort = TRUE;
529 AhciDebugPrint("\tMultiple MSI based message not supported\n");
530 }
531
532 StorPortEnablePassiveInitialization(AdapterExtension, AhciHwPassiveInitialize);
533
534 return TRUE;
535 }// -- AhciHwInitialize();
536
537 /**
538 * @name AhciCompleteIssuedSrb
539 * @implemented
540 *
541 * Complete issued Srbs
542 *
543 * @param PortExtension
544 *
545 */
546 VOID
547 AhciCompleteIssuedSrb (
548 __in PAHCI_PORT_EXTENSION PortExtension,
549 __in ULONG CommandsToComplete
550 )
551 {
552 ULONG NCS, i;
553 PSCSI_REQUEST_BLOCK Srb;
554 PAHCI_SRB_EXTENSION SrbExtension;
555 PAHCI_ADAPTER_EXTENSION AdapterExtension;
556
557 AhciDebugPrint("AhciCompleteIssuedSrb()\n");
558
559 NT_ASSERT(CommandsToComplete != 0);
560
561 AhciDebugPrint("\tCompleted Commands: %d\n", CommandsToComplete);
562
563 AdapterExtension = PortExtension->AdapterExtension;
564 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
565
566 for (i = 0; i < NCS; i++)
567 {
568 if (((1 << i) & CommandsToComplete) != 0)
569 {
570 Srb = PortExtension->Slot[i];
571
572 if (Srb == NULL)
573 {
574 continue;
575 }
576
577 SrbExtension = GetSrbExtension(Srb);
578 NT_ASSERT(SrbExtension != NULL);
579
580 if (SrbExtension->CompletionRoutine != NULL)
581 {
582 AddQueue(&PortExtension->CompletionQueue, Srb);
583 StorPortIssueDpc(AdapterExtension, &PortExtension->CommandCompletion, PortExtension, Srb);
584 }
585 else
586 {
587 NT_ASSERT(Srb->SrbStatus == SRB_STATUS_PENDING);
588 Srb->SrbStatus = SRB_STATUS_SUCCESS;
589 StorPortNotification(RequestComplete, AdapterExtension, Srb);
590 }
591 }
592 }
593
594 return;
595 }// -- AhciCompleteIssuedSrb();
596
597 /**
598 * @name AhciInterruptHandler
599 * @not_implemented
600 *
601 * Interrupt Handler for PortExtension
602 *
603 * @param PortExtension
604 *
605 */
606 VOID
607 AhciInterruptHandler (
608 __in PAHCI_PORT_EXTENSION PortExtension
609 )
610 {
611 ULONG is, ci, sact, outstanding;
612 AHCI_INTERRUPT_STATUS PxIS;
613 AHCI_INTERRUPT_STATUS PxISMasked;
614 PAHCI_ADAPTER_EXTENSION AdapterExtension;
615
616 AhciDebugPrint("AhciInterruptHandler()\n");
617 AhciDebugPrint("\tPort Number: %d\n", PortExtension->PortNumber);
618
619 AdapterExtension = PortExtension->AdapterExtension;
620 NT_ASSERT(IsPortValid(AdapterExtension, PortExtension->PortNumber));
621
622 // 5.5.3
623 // 1. Software determines the cause of the interrupt by reading the PxIS register.
624 // It is possible for multiple bits to be set
625 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
626 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
627 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
628 // the list of commands previously issued by software that are still outstanding.
629 // If executing native queued commands, software reads the PxSACT register and compares the current
630 // value to the list of commands previously issued by software.
631 // Software completes with success any outstanding command whose corresponding bit has been cleared in
632 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
633 // to determine commands that have completed, not to determine which commands have previously been issued.
634 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
635 PxISMasked.Status = 0;
636 PxIS.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IS);
637
638 // 6.2.2
639 // Fatal Error
640 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
641 if (PxIS.HBFS || PxIS.HBDS || PxIS.IFS || PxIS.TFES)
642 {
643 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
644 // any native command queuing commands. To recover, the port must be restarted
645 // To detect an error that requires software recovery actions to be performed,
646 // software should check whether any of the following status bits are set on an interrupt:
647 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
648 // software should perform the appropriate error recovery actions based on whether
649 // non-queued commands were being issued or native command queuing commands were being issued.
650
651 AhciDebugPrint("\tFatal Error: %x\n", PxIS.Status);
652 }
653
654 // Normal Command Completion
655 // 3.3.5
656 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
657 PxISMasked.DHRS = PxIS.DHRS;
658 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
659 PxISMasked.PSS = PxIS.PSS;
660 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
661 PxISMasked.DSS = PxIS.DSS;
662 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
663 PxISMasked.SDBS = PxIS.SDBS;
664 // A PRD with the ‘I’ bit set has transferred all of its data.
665 PxISMasked.DPS = PxIS.DPS;
666
667 if (PxISMasked.Status != 0)
668 {
669 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, PxISMasked.Status);
670 }
671
672 // 10.7.1.1
673 // Clear port interrupt
674 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
675 is = (1 << PortExtension->PortNumber);
676 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, is);
677
678 ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI);
679 sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT);
680
681 outstanding = ci | sact; // NOTE: Including both non-NCQ and NCQ based commands
682 if ((PortExtension->CommandIssuedSlots & (~outstanding)) != 0)
683 {
684 AhciCompleteIssuedSrb(PortExtension, (PortExtension->CommandIssuedSlots & (~outstanding)));
685 PortExtension->CommandIssuedSlots &= outstanding;
686 }
687
688 return;
689 }// -- AhciInterruptHandler();
690
691 /**
692 * @name AhciHwInterrupt
693 * @implemented
694 *
695 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
696 *
697 * @param AdapterExtension
698 *
699 * @return
700 * return TRUE Indicates that an interrupt was pending on adapter.
701 * return FALSE Indicates the interrupt was not ours.
702 */
703 BOOLEAN
704 AhciHwInterrupt (
705 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
706 )
707 {
708 ULONG portPending, nextPort, i, portCount;
709
710 if (AdapterExtension->StateFlags.Removed)
711 {
712 return FALSE;
713 }
714
715 portPending = StorPortReadRegisterUlong(AdapterExtension, AdapterExtension->IS);
716
717 // we process interrupt for implemented ports only
718 portCount = AdapterExtension->PortCount;
719 portPending = portPending & AdapterExtension->PortImplemented;
720
721 if (portPending == 0)
722 {
723 return FALSE;
724 }
725
726 for (i = 1; i <= portCount; i++)
727 {
728 nextPort = (AdapterExtension->LastInterruptPort + i) % portCount;
729 if ((portPending & (0x1 << nextPort)) == 0)
730 continue;
731
732 NT_ASSERT(IsPortValid(AdapterExtension, nextPort));
733
734 if (AdapterExtension->PortExtension[nextPort].DeviceParams.IsActive == FALSE)
735 {
736 continue;
737 }
738
739 // we can assign this interrupt to this port
740 AdapterExtension->LastInterruptPort = nextPort;
741 AhciInterruptHandler(&AdapterExtension->PortExtension[nextPort]);
742
743 portPending &= ~(1 << nextPort);
744
745 // interrupt belongs to this device
746 // should always return TRUE
747 return TRUE;
748 }
749
750 AhciDebugPrint("\tSomething went wrong");
751 return FALSE;
752 }// -- AhciHwInterrupt();
753
754 /**
755 * @name AhciHwStartIo
756 * @not_implemented
757 *
758 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
759 *
760 * @param adapterExtension
761 * @param Srb
762 *
763 * @return
764 * return TRUE if the request was accepted
765 * return FALSE if the request must be submitted later
766 */
767 BOOLEAN
768 AhciHwStartIo (
769 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
770 __in PSCSI_REQUEST_BLOCK Srb
771 )
772 {
773 AhciDebugPrint("AhciHwStartIo()\n");
774
775 if (!IsPortValid(AdapterExtension, Srb->PathId))
776 {
777 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
778 StorPortNotification(RequestComplete, AdapterExtension, Srb);
779 return TRUE;
780 }
781
782 switch(Srb->Function)
783 {
784 case SRB_FUNCTION_PNP:
785 {
786 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
787 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
788 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
789
790 PSCSI_PNP_REQUEST_BLOCK pnpRequest;
791 pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb;
792 if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0)
793 {
794 switch(pnpRequest->PnPAction)
795 {
796 case StorRemoveDevice:
797 case StorSurpriseRemoval:
798 {
799 Srb->SrbStatus = SRB_STATUS_SUCCESS;
800 AdapterExtension->StateFlags.Removed = 1;
801 AhciDebugPrint("\tAdapter removed\n");
802 }
803 break;
804 case StorStopDevice:
805 {
806 Srb->SrbStatus = SRB_STATUS_SUCCESS;
807 AhciDebugPrint("\tRequested to Stop the adapter\n");
808 }
809 break;
810 default:
811 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
812 break;
813 }
814 }
815 else
816 {
817 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
818 }
819 }
820 break;
821 case SRB_FUNCTION_EXECUTE_SCSI:
822 {
823 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
824 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
825 // routine does the following:
826 //
827 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
828 // logical unit, and/or SRB extensions
829 // For example, a miniport driver might set up a logical unit extension with pointers
830 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
831 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
832 // carried out on the HBA.
833 //
834 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
835 // for the requested operation
836 // For a device I/O operation, such an internal routine generally selects the target device
837 // and sends the CDB over the bus to the target logical unit.
838 PCDB cdb = (PCDB)&Srb->Cdb;
839 if (Srb->CdbLength == 0)
840 {
841 AhciDebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode);
842 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
843 break;
844 }
845
846 NT_ASSERT(cdb != NULL);
847
848 switch(cdb->CDB10.OperationCode)
849 {
850 case SCSIOP_INQUIRY:
851 Srb->SrbStatus = DeviceInquiryRequest(AdapterExtension, Srb, cdb);
852 break;
853 case SCSIOP_REPORT_LUNS:
854 Srb->SrbStatus = DeviceReportLuns(AdapterExtension, Srb, cdb);
855 break;
856 case SCSIOP_READ_CAPACITY:
857 Srb->SrbStatus = DeviceRequestCapacity(AdapterExtension, Srb, cdb);
858 break;
859 case SCSIOP_TEST_UNIT_READY:
860 Srb->SrbStatus = DeviceRequestComplete(AdapterExtension, Srb, cdb);
861 break;
862 case SCSIOP_MODE_SENSE:
863 Srb->SrbStatus = DeviceRequestSense(AdapterExtension, Srb, cdb);
864 break;
865 case SCSIOP_READ:
866 case SCSIOP_WRITE:
867 Srb->SrbStatus = DeviceRequestReadWrite(AdapterExtension, Srb, cdb);
868 break;
869 default:
870 AhciDebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode);
871 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
872 break;
873 }
874 }
875 break;
876 default:
877 AhciDebugPrint("\tUnknown function code recieved: %x\n", Srb->Function);
878 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
879 break;
880 }
881
882 if (Srb->SrbStatus != SRB_STATUS_PENDING)
883 {
884 StorPortNotification(RequestComplete, AdapterExtension, Srb);
885 }
886 else
887 {
888 AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
889 }
890 return TRUE;
891 }// -- AhciHwStartIo();
892
893 /**
894 * @name AhciHwResetBus
895 * @not_implemented
896 *
897 * The HwStorResetBus routine is called by the port driver to clear error conditions.
898 *
899 * @param adapterExtension
900 * @param PathId
901 *
902 * @return
903 * return TRUE if bus was successfully reset
904 */
905 BOOLEAN
906 AhciHwResetBus (
907 __in PVOID AdapterExtension,
908 __in ULONG PathId
909 )
910 {
911 STOR_LOCK_HANDLE lockhandle = {0};
912 PAHCI_ADAPTER_EXTENSION adapterExtension;
913
914 AhciDebugPrint("AhciHwResetBus()\n");
915
916 adapterExtension = AdapterExtension;
917
918 if (IsPortValid(AdapterExtension, PathId))
919 {
920 // Acquire Lock
921 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
922
923 // TODO: Perform port reset
924
925 // Release lock
926 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
927 }
928
929 return FALSE;
930 }// -- AhciHwResetBus();
931
932 /**
933 * @name AhciHwFindAdapter
934 * @implemented
935 *
936 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
937 * HBA is supported and, if it is, to return configuration information about that adapter.
938 *
939 * 10.1 Platform Communication
940 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
941
942 * @param DeviceExtension
943 * @param HwContext
944 * @param BusInformation
945 * @param ArgumentString
946 * @param ConfigInfo
947 * @param Reserved3
948 *
949 * @return
950 * SP_RETURN_FOUND
951 * Indicates that a supported HBA was found and that the HBA-relevant configuration information was successfully determined and set in the PORT_CONFIGURATION_INFORMATION structure.
952 *
953 * SP_RETURN_ERROR
954 * Indicates that an HBA was found but there was an error obtaining the configuration information. If possible, such an error should be logged with StorPortLogError.
955 *
956 * SP_RETURN_BAD_CONFIG
957 * Indicates that the supplied configuration information was invalid for the adapter.
958 *
959 * SP_RETURN_NOT_FOUND
960 * Indicates that no supported HBA was found for the supplied configuration information.
961 *
962 * @remarks Called by Storport.
963 */
964 ULONG
965 AhciHwFindAdapter (
966 __in PVOID AdapterExtension,
967 __in PVOID HwContext,
968 __in PVOID BusInformation,
969 __in PVOID ArgumentString,
970 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
971 __in PBOOLEAN Reserved3
972 )
973 {
974 AHCI_GHC ghc;
975 ULONG index, pci_cfg_len;
976 PACCESS_RANGE accessRange;
977 UCHAR pci_cfg_buf[sizeof(PCI_COMMON_CONFIG)];
978
979 PAHCI_MEMORY_REGISTERS abar;
980 PPCI_COMMON_CONFIG pciConfigData;
981 PAHCI_ADAPTER_EXTENSION adapterExtension;
982
983 AhciDebugPrint("AhciHwFindAdapter()\n");
984
985 UNREFERENCED_PARAMETER(HwContext);
986 UNREFERENCED_PARAMETER(BusInformation);
987 UNREFERENCED_PARAMETER(ArgumentString);
988 UNREFERENCED_PARAMETER(Reserved3);
989
990 adapterExtension = AdapterExtension;
991 adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
992 adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
993
994 // get PCI configuration header
995 pci_cfg_len = StorPortGetBusData(
996 adapterExtension,
997 PCIConfiguration,
998 adapterExtension->SystemIoBusNumber,
999 adapterExtension->SlotNumber,
1000 pci_cfg_buf,
1001 sizeof(PCI_COMMON_CONFIG));
1002
1003 if (pci_cfg_len != sizeof(PCI_COMMON_CONFIG))
1004 {
1005 AhciDebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG), pci_cfg_len);
1006 return SP_RETURN_ERROR;//Not a valid device at the given bus number
1007 }
1008
1009 pciConfigData = (PPCI_COMMON_CONFIG)pci_cfg_buf;
1010 adapterExtension->VendorID = pciConfigData->VendorID;
1011 adapterExtension->DeviceID = pciConfigData->DeviceID;
1012 adapterExtension->RevisionID = pciConfigData->RevisionID;
1013 // The last PCI base address register (BAR[5], header offset 0x24) points to the AHCI base memory, it’s called ABAR (AHCI Base Memory Register).
1014 adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
1015
1016 AhciDebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension->VendorID,
1017 adapterExtension->DeviceID,
1018 adapterExtension->RevisionID);
1019
1020 // 2.1.11
1021 abar = NULL;
1022 if (ConfigInfo->NumberOfAccessRanges > 0)
1023 {
1024 accessRange = *(ConfigInfo->AccessRanges);
1025 for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
1026 {
1027 if (accessRange[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
1028 {
1029 abar = StorPortGetDeviceBase(adapterExtension,
1030 ConfigInfo->AdapterInterfaceType,
1031 ConfigInfo->SystemIoBusNumber,
1032 accessRange[index].RangeStart,
1033 accessRange[index].RangeLength,
1034 !accessRange[index].RangeInMemory);
1035 break;
1036 }
1037 }
1038 }
1039
1040 if (abar == NULL)
1041 {
1042 AhciDebugPrint("\tabar == NULL\n");
1043 return SP_RETURN_ERROR; // corrupted information supplied
1044 }
1045
1046 adapterExtension->ABAR_Address = abar;
1047 adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
1048 adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
1049 adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
1050 adapterExtension->LastInterruptPort = (ULONG)-1;
1051
1052 // 10.1.2
1053 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
1054 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
1055 ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
1056 // AE := Highest Significant bit of GHC
1057 if (ghc.AE != 0)// Hmm, controller was already in power state
1058 {
1059 // reset controller to have it in known state
1060 AhciDebugPrint("\tAE Already set, Reset()\n");
1061 if (!AhciAdapterReset(adapterExtension))
1062 {
1063 AhciDebugPrint("\tReset Failed!\n");
1064 return SP_RETURN_ERROR;// reset failed
1065 }
1066 }
1067
1068 ghc.Status = 0;
1069 ghc.AE = 1;// only AE=1
1070 // tell the controller that we know about AHCI
1071 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status);
1072
1073 adapterExtension->IS = &abar->IS;
1074 adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
1075
1076 if (adapterExtension->PortImplemented == 0)
1077 {
1078 AhciDebugPrint("\tadapterExtension->PortImplemented == 0\n");
1079 return SP_RETURN_ERROR;
1080 }
1081
1082 ConfigInfo->Master = TRUE;
1083 ConfigInfo->AlignmentMask = 0x3;
1084 ConfigInfo->ScatterGather = TRUE;
1085 ConfigInfo->DmaWidth = Width32Bits;
1086 ConfigInfo->WmiDataProvider = FALSE;
1087 ConfigInfo->Dma32BitAddresses = TRUE;
1088
1089 if (IsAdapterCAPS64(adapterExtension->CAP))
1090 {
1091 ConfigInfo->Dma64BitAddresses = TRUE;
1092 }
1093
1094 ConfigInfo->MaximumNumberOfTargets = 1;
1095 ConfigInfo->ResetTargetSupported = TRUE;
1096 ConfigInfo->NumberOfPhysicalBreaks = 0x21;
1097 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
1098 ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT;
1099 ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;
1100 ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
1101
1102 // Turn IE -- Interrupt Enabled
1103 ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
1104 ghc.IE = 1;
1105 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status);
1106
1107 // allocate necessary resource for each port
1108 if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
1109 {
1110 NT_ASSERT(FALSE);
1111 return SP_RETURN_ERROR;
1112 }
1113
1114 for (index = 0; index < adapterExtension->PortCount; index++)
1115 {
1116 if ((adapterExtension->PortImplemented & (0x1 << index)) != 0)
1117 AhciPortInitialize(&adapterExtension->PortExtension[index]);
1118 }
1119
1120 return SP_RETURN_FOUND;
1121 }// -- AhciHwFindAdapter();
1122
1123 /**
1124 * @name DriverEntry
1125 * @implemented
1126 *
1127 * Initial Entrypoint for storahci miniport driver
1128 *
1129 * @param DriverObject
1130 * @param RegistryPath
1131 *
1132 * @return
1133 * NT_STATUS in case of driver loaded successfully.
1134 */
1135 ULONG
1136 DriverEntry (
1137 __in PVOID DriverObject,
1138 __in PVOID RegistryPath
1139 )
1140 {
1141 ULONG status;
1142 // initialize the hardware data structure
1143 HW_INITIALIZATION_DATA hwInitializationData = {0};
1144
1145 // set size of hardware initialization structure
1146 hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
1147
1148 // identity required miniport entry point routines
1149 hwInitializationData.HwStartIo = AhciHwStartIo;
1150 hwInitializationData.HwResetBus = AhciHwResetBus;
1151 hwInitializationData.HwInterrupt = AhciHwInterrupt;
1152 hwInitializationData.HwInitialize = AhciHwInitialize;
1153 hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
1154
1155 // adapter specific information
1156 hwInitializationData.TaggedQueuing = TRUE;
1157 hwInitializationData.AutoRequestSense = TRUE;
1158 hwInitializationData.MultipleRequestPerLu = TRUE;
1159 hwInitializationData.NeedPhysicalAddresses = TRUE;
1160
1161 hwInitializationData.NumberOfAccessRanges = 6;
1162 hwInitializationData.AdapterInterfaceType = PCIBus;
1163 hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
1164
1165 // set required extension sizes
1166 hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
1167 hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
1168
1169 // register our hw init data
1170 status = StorPortInitialize(DriverObject,
1171 RegistryPath,
1172 &hwInitializationData,
1173 NULL);
1174
1175 NT_ASSERT(status == STATUS_SUCCESS);
1176 return status;
1177 }// -- DriverEntry();
1178
1179 /**
1180 * @name AhciATA_CFIS
1181 * @implemented
1182 *
1183 * create ATA CFIS from Srb
1184 *
1185 * @param PortExtension
1186 * @param Srb
1187 *
1188 * @return
1189 * Number of CFIS fields used in DWORD
1190 */
1191 ULONG
1192 AhciATA_CFIS (
1193 __in PAHCI_PORT_EXTENSION PortExtension,
1194 __in PAHCI_SRB_EXTENSION SrbExtension
1195 )
1196 {
1197 PAHCI_COMMAND_TABLE cmdTable;
1198
1199 UNREFERENCED_PARAMETER(PortExtension);
1200
1201 AhciDebugPrint("AhciATA_CFIS()\n");
1202
1203 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
1204
1205 AhciZeroMemory((PCHAR)cmdTable->CFIS, sizeof(cmdTable->CFIS));
1206
1207 cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = FIS_TYPE_REG_H2D; // FIS Type
1208 cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C
1209 cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg;
1210
1211 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow;
1212 cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0;
1213 cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1;
1214 cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2;
1215 cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device;
1216 cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3;
1217 cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4;
1218 cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5;
1219 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh;
1220 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow;
1221 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh;
1222
1223 return 5;
1224 }// -- AhciATA_CFIS();
1225
1226 /**
1227 * @name AhciATAPI_CFIS
1228 * @not_implemented
1229 *
1230 * create ATAPI CFIS from Srb
1231 *
1232 * @param PortExtension
1233 * @param Srb
1234 *
1235 * @return
1236 * Number of CFIS fields used in DWORD
1237 */
1238 ULONG
1239 AhciATAPI_CFIS (
1240 __in PAHCI_PORT_EXTENSION PortExtension,
1241 __in PAHCI_SRB_EXTENSION SrbExtension
1242 )
1243 {
1244 PAHCI_COMMAND_TABLE cmdTable;
1245 UNREFERENCED_PARAMETER(PortExtension);
1246
1247 AhciDebugPrint("AhciATAPI_CFIS()\n");
1248
1249 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
1250
1251 NT_ASSERT(SrbExtension->CommandReg == IDE_COMMAND_ATAPI_PACKET);
1252
1253 AhciZeroMemory((PCHAR)cmdTable->CFIS, sizeof(cmdTable->CFIS));
1254
1255 cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = FIS_TYPE_REG_H2D; // FIS Type
1256 cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C
1257 cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg;
1258
1259 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow;
1260 cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0;
1261 cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1;
1262 cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2;
1263 cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device;
1264 cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3;
1265 cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4;
1266 cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5;
1267 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh;
1268 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow;
1269 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh;
1270
1271 return 5;
1272 }// -- AhciATAPI_CFIS();
1273
1274 /**
1275 * @name AhciBuild_PRDT
1276 * @implemented
1277 *
1278 * Build PRDT for data transfer
1279 *
1280 * @param PortExtension
1281 * @param Srb
1282 *
1283 * @return
1284 * Return number of entries in PRDT.
1285 */
1286 ULONG
1287 AhciBuild_PRDT (
1288 __in PAHCI_PORT_EXTENSION PortExtension,
1289 __in PAHCI_SRB_EXTENSION SrbExtension
1290 )
1291 {
1292 ULONG index;
1293 PAHCI_COMMAND_TABLE cmdTable;
1294 PLOCAL_SCATTER_GATHER_LIST sgl;
1295 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1296
1297 AhciDebugPrint("AhciBuild_PRDT()\n");
1298
1299 sgl = SrbExtension->pSgl;
1300 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
1301 AdapterExtension = PortExtension->AdapterExtension;
1302
1303 NT_ASSERT(sgl != NULL);
1304 NT_ASSERT(sgl->NumberOfElements < MAXIMUM_AHCI_PRDT_ENTRIES);
1305
1306 for (index = 0; index < sgl->NumberOfElements; index++)
1307 {
1308 NT_ASSERT(sgl->List[index].Length <= MAXIMUM_TRANSFER_LENGTH);
1309
1310 cmdTable->PRDT[index].DBA = sgl->List[index].PhysicalAddress.LowPart;
1311 if (IsAdapterCAPS64(AdapterExtension->CAP))
1312 {
1313 cmdTable->PRDT[index].DBAU = sgl->List[index].PhysicalAddress.HighPart;
1314 }
1315
1316 // Data Byte Count (DBC): A ‘0’ based value that Indicates the length, in bytes, of the data block.
1317 // A maximum of length of 4MB may exist for any entry. Bit ‘0’ of this field must always be ‘1’ to
1318 // indicate an even byte count. A value of ‘1’ indicates 2 bytes, ‘3’ indicates 4 bytes, etc.
1319 cmdTable->PRDT[index].DBC = sgl->List[index].Length - 1;
1320 }
1321
1322 return sgl->NumberOfElements;
1323 }// -- AhciBuild_PRDT();
1324
1325 /**
1326 * @name AhciProcessSrb
1327 * @implemented
1328 *
1329 * Prepare Srb for IO processing
1330 *
1331 * @param PortExtension
1332 * @param Srb
1333 * @param SlotIndex
1334 *
1335 */
1336 VOID
1337 AhciProcessSrb (
1338 __in PAHCI_PORT_EXTENSION PortExtension,
1339 __in PSCSI_REQUEST_BLOCK Srb,
1340 __in ULONG SlotIndex
1341 )
1342 {
1343 ULONG prdtlen, sig, length, cfl;
1344 PAHCI_SRB_EXTENSION SrbExtension;
1345 PAHCI_COMMAND_HEADER CommandHeader;
1346 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1347 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress;
1348
1349 AhciDebugPrint("AhciProcessSrb()\n");
1350
1351 NT_ASSERT(Srb->PathId == PortExtension->PortNumber);
1352
1353 SrbExtension = GetSrbExtension(Srb);
1354 AdapterExtension = PortExtension->AdapterExtension;
1355
1356 NT_ASSERT(SrbExtension != NULL);
1357 NT_ASSERT(SrbExtension->AtaFunction != 0);
1358
1359 if ((SrbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) &&
1360 (SrbExtension->CommandReg == IDE_COMMAND_NOT_VALID))
1361 {
1362 // Here we are safe to check SIG register
1363 sig = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SIG);
1364 if (sig == 0x101)
1365 {
1366 AhciDebugPrint("\tATA Device Found!\n");
1367 SrbExtension->CommandReg = IDE_COMMAND_IDENTIFY;
1368 }
1369 else
1370 {
1371 AhciDebugPrint("\tATAPI Device Found!\n");
1372 SrbExtension->CommandReg = IDE_COMMAND_ATAPI_IDENTIFY;
1373 }
1374 }
1375
1376 NT_ASSERT(SlotIndex < AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
1377 SrbExtension->SlotIndex = SlotIndex;
1378
1379 // program the CFIS in the CommandTable
1380 CommandHeader = &PortExtension->CommandList[SlotIndex];
1381
1382 cfl = 0;
1383 if (IsAtapiCommand(SrbExtension->AtaFunction))
1384 {
1385 cfl = AhciATAPI_CFIS(PortExtension, SrbExtension);
1386 }
1387 else if (IsAtaCommand(SrbExtension->AtaFunction))
1388 {
1389 cfl = AhciATA_CFIS(PortExtension, SrbExtension);
1390 }
1391 else
1392 {
1393 NT_ASSERT(FALSE);
1394 }
1395
1396 prdtlen = 0;
1397 if (IsDataTransferNeeded(SrbExtension))
1398 {
1399 prdtlen = AhciBuild_PRDT(PortExtension, SrbExtension);
1400 NT_ASSERT(prdtlen != -1);
1401 }
1402
1403 // Program the command header
1404 CommandHeader->DI.PRDTL = prdtlen; // number of entries in PRD table
1405 CommandHeader->DI.CFL = cfl;
1406 CommandHeader->DI.A = (SrbExtension->AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) ? 1 : 0;
1407 CommandHeader->DI.W = (SrbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0;
1408 CommandHeader->DI.P = 0; // ATA Specifications says so
1409 CommandHeader->DI.PMP = 0; // Port Multiplier
1410
1411 // Reset -- Manual Configuation
1412 CommandHeader->DI.R = 0;
1413 CommandHeader->DI.B = 0;
1414 CommandHeader->DI.C = 0;
1415
1416 CommandHeader->PRDBC = 0;
1417
1418 CommandHeader->Reserved[0] = 0;
1419 CommandHeader->Reserved[1] = 0;
1420 CommandHeader->Reserved[2] = 0;
1421 CommandHeader->Reserved[3] = 0;
1422
1423 // set CommandHeader CTBA
1424 CommandTablePhysicalAddress = StorPortGetPhysicalAddress(AdapterExtension,
1425 NULL,
1426 SrbExtension,
1427 &length);
1428
1429 NT_ASSERT(length != 0);
1430
1431 // command table alignment
1432 NT_ASSERT((CommandTablePhysicalAddress.LowPart % 128) == 0);
1433
1434 CommandHeader->CTBA = CommandTablePhysicalAddress.LowPart;
1435
1436 if (IsAdapterCAPS64(AdapterExtension->CAP))
1437 {
1438 CommandHeader->CTBA_U = CommandTablePhysicalAddress.HighPart;
1439 }
1440
1441 // mark this slot
1442 PortExtension->Slot[SlotIndex] = Srb;
1443 PortExtension->QueueSlots |= 1 << SlotIndex;
1444 return;
1445 }// -- AhciProcessSrb();
1446
1447 /**
1448 * @name AhciActivatePort
1449 * @implemented
1450 *
1451 * Program Port and populate command list
1452 *
1453 * @param PortExtension
1454 *
1455 */
1456 VOID
1457 AhciActivatePort (
1458 __in PAHCI_PORT_EXTENSION PortExtension
1459 )
1460 {
1461 AHCI_PORT_CMD cmd;
1462 ULONG QueueSlots, slotToActivate, tmp;
1463 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1464
1465 AhciDebugPrint("AhciActivatePort()\n");
1466
1467 AdapterExtension = PortExtension->AdapterExtension;
1468 QueueSlots = PortExtension->QueueSlots;
1469
1470 if (QueueSlots == 0)
1471 {
1472 return;
1473 }
1474
1475 // section 3.3.14
1476 // Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’
1477 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
1478
1479 if (cmd.ST == 0) // PxCMD.ST == 0
1480 {
1481 return;
1482 }
1483
1484 // get the lowest set bit
1485 tmp = QueueSlots & (QueueSlots - 1);
1486
1487 if (tmp == 0)
1488 slotToActivate = QueueSlots;
1489 else
1490 slotToActivate = (QueueSlots & (~tmp));
1491
1492 // mark that bit off in QueueSlots
1493 // so we can know we it is really needed to activate port or not
1494 PortExtension->QueueSlots &= ~slotToActivate;
1495 // mark this CommandIssuedSlots
1496 // to validate in completeIssuedCommand
1497 PortExtension->CommandIssuedSlots |= slotToActivate;
1498
1499 // tell the HBA to issue this Command Slot to the given port
1500 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CI, slotToActivate);
1501
1502 return;
1503 }// -- AhciActivatePort();
1504
1505 /**
1506 * @name AhciProcessIO
1507 * @implemented
1508 *
1509 * Acquire Exclusive lock to port, populate pending commands to command List
1510 * program controller's port to process new commands in command list.
1511 *
1512 * @param AdapterExtension
1513 * @param PathId
1514 * @param Srb
1515 *
1516 */
1517 VOID
1518 AhciProcessIO (
1519 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1520 __in UCHAR PathId,
1521 __in PSCSI_REQUEST_BLOCK Srb
1522 )
1523 {
1524 PSCSI_REQUEST_BLOCK tmpSrb;
1525 STOR_LOCK_HANDLE lockhandle = {0};
1526 PAHCI_PORT_EXTENSION PortExtension;
1527 ULONG commandSlotMask, occupiedSlots, slotIndex, NCS;
1528
1529 AhciDebugPrint("AhciProcessIO()\n");
1530 AhciDebugPrint("\tPathId: %d\n", PathId);
1531
1532 PortExtension = &AdapterExtension->PortExtension[PathId];
1533
1534 NT_ASSERT(PathId < AdapterExtension->PortCount);
1535
1536 // Acquire Lock
1537 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
1538
1539 // add Srb to queue
1540 AddQueue(&PortExtension->SrbQueue, Srb);
1541
1542 if (PortExtension->DeviceParams.IsActive == FALSE)
1543 {
1544 // Release Lock
1545 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1546 return; // we should wait for device to get active
1547 }
1548
1549 occupiedSlots = (PortExtension->QueueSlots | PortExtension->CommandIssuedSlots); // Busy command slots for given port
1550 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
1551 commandSlotMask = (1 << NCS) - 1; // available slots mask
1552
1553 commandSlotMask = (commandSlotMask & ~occupiedSlots);
1554 if(commandSlotMask != 0)
1555 {
1556 // iterate over HBA port slots
1557 for (slotIndex = 0; slotIndex < NCS; slotIndex++)
1558 {
1559 // find first free slot
1560 if ((commandSlotMask & (1 << slotIndex)) != 0)
1561 {
1562 tmpSrb = RemoveQueue(&PortExtension->SrbQueue);
1563 if (tmpSrb != NULL)
1564 {
1565 NT_ASSERT(tmpSrb->PathId == PathId);
1566 AhciProcessSrb(PortExtension, tmpSrb, slotIndex);
1567 }
1568 else
1569 {
1570 break;
1571 }
1572 }
1573 else
1574 {
1575 break;
1576 }
1577 }
1578 }
1579
1580 // program HBA port
1581 AhciActivatePort(PortExtension);
1582
1583 // Release Lock
1584 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1585
1586 return;
1587 }// -- AhciProcessIO();
1588
1589 /**
1590 * @name AtapiInquiryCompletion
1591 * @implemented
1592 *
1593 * AtapiInquiryCompletion routine should be called after device signals
1594 * for device inquiry request is completed (through interrupt) -- ATAPI Device only
1595 *
1596 * @param PortExtension
1597 * @param Srb
1598 *
1599 */
1600 VOID
1601 AtapiInquiryCompletion (
1602 __in PAHCI_PORT_EXTENSION PortExtension,
1603 __in PSCSI_REQUEST_BLOCK Srb
1604 )
1605 {
1606 BOOLEAN status;
1607 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1608
1609 AhciDebugPrint("AtapiInquiryCompletion()\n");
1610
1611 NT_ASSERT(Srb != NULL);
1612 NT_ASSERT(PortExtension != NULL);
1613
1614 AdapterExtension = PortExtension->AdapterExtension;
1615
1616 // send queue depth
1617 status = StorPortSetDeviceQueueDepth(PortExtension->AdapterExtension,
1618 Srb->PathId,
1619 Srb->TargetId,
1620 Srb->Lun,
1621 AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
1622
1623 NT_ASSERT(status == TRUE);
1624 return;
1625 }// -- AtapiInquiryCompletion();
1626
1627 /**
1628 * @name InquiryCompletion
1629 * @implemented
1630 *
1631 * InquiryCompletion routine should be called after device signals
1632 * for device inquiry request is completed (through interrupt)
1633 *
1634 * @param PortExtension
1635 * @param Srb
1636 *
1637 */
1638 VOID
1639 InquiryCompletion (
1640 __in PAHCI_PORT_EXTENSION PortExtension,
1641 __in PSCSI_REQUEST_BLOCK Srb
1642 )
1643 {
1644 PCDB cdb;
1645 BOOLEAN status;
1646 PINQUIRYDATA InquiryData;
1647 PAHCI_SRB_EXTENSION SrbExtension;
1648 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1649 PIDENTIFY_DEVICE_DATA IdentifyDeviceData;
1650
1651 AhciDebugPrint("InquiryCompletion()\n");
1652
1653 NT_ASSERT(Srb != NULL);
1654 NT_ASSERT(PortExtension != NULL);
1655
1656 cdb = (PCDB)&Srb->Cdb;
1657 InquiryData = Srb->DataBuffer;
1658 SrbExtension = GetSrbExtension(Srb);
1659 AdapterExtension = PortExtension->AdapterExtension;
1660 IdentifyDeviceData = PortExtension->IdentifyDeviceData;
1661
1662 if (Srb->SrbStatus != SRB_STATUS_SUCCESS)
1663 {
1664 if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE)
1665 {
1666 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_NODEVICE;
1667 }
1668 return;
1669 }
1670
1671 NT_ASSERT(InquiryData != NULL);
1672 NT_ASSERT(Srb->SrbStatus == SRB_STATUS_SUCCESS);
1673
1674 // Device specific data
1675 PortExtension->DeviceParams.MaxLba.QuadPart = 0;
1676
1677 if (SrbExtension->CommandReg == IDE_COMMAND_IDENTIFY)
1678 {
1679 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATA;
1680 if (IdentifyDeviceData->GeneralConfiguration.RemovableMedia)
1681 {
1682 PortExtension->DeviceParams.RemovableDevice = 1;
1683 }
1684
1685 if ((IdentifyDeviceData->CommandSetSupport.BigLba) && (IdentifyDeviceData->CommandSetActive.BigLba))
1686 {
1687 PortExtension->DeviceParams.Lba48BitMode = 1;
1688 }
1689
1690 PortExtension->DeviceParams.AccessType = DIRECT_ACCESS_DEVICE;
1691
1692 /* Device max address lba */
1693 if (PortExtension->DeviceParams.Lba48BitMode)
1694 {
1695 PortExtension->DeviceParams.MaxLba.LowPart = IdentifyDeviceData->Max48BitLBA[0];
1696 PortExtension->DeviceParams.MaxLba.HighPart = IdentifyDeviceData->Max48BitLBA[1];
1697 }
1698 else
1699 {
1700 PortExtension->DeviceParams.MaxLba.LowPart = IdentifyDeviceData->UserAddressableSectors;
1701 }
1702
1703 /* Bytes Per Logical Sector */
1704 if (IdentifyDeviceData->PhysicalLogicalSectorSize.LogicalSectorLongerThan256Words)
1705 {
1706 AhciDebugPrint("\tBytesPerLogicalSector != DEVICE_ATA_BLOCK_SIZE\n");
1707 NT_ASSERT(FALSE);
1708 }
1709
1710 PortExtension->DeviceParams.BytesPerLogicalSector = DEVICE_ATA_BLOCK_SIZE;
1711
1712 /* Bytes Per Physical Sector */
1713 if (IdentifyDeviceData->PhysicalLogicalSectorSize.MultipleLogicalSectorsPerPhysicalSector)
1714 {
1715 AhciDebugPrint("\tBytesPerPhysicalSector != DEVICE_ATA_BLOCK_SIZE\n");
1716 NT_ASSERT(FALSE);
1717 }
1718
1719 PortExtension->DeviceParams.BytesPerPhysicalSector = DEVICE_ATA_BLOCK_SIZE;
1720
1721 // last byte should be NULL
1722 StorPortCopyMemory(PortExtension->DeviceParams.VendorId, IdentifyDeviceData->ModelNumber, sizeof(PortExtension->DeviceParams.VendorId) - 1);
1723 StorPortCopyMemory(PortExtension->DeviceParams.RevisionID, IdentifyDeviceData->FirmwareRevision, sizeof(PortExtension->DeviceParams.RevisionID) - 1);
1724 StorPortCopyMemory(PortExtension->DeviceParams.SerialNumber, IdentifyDeviceData->SerialNumber, sizeof(PortExtension->DeviceParams.SerialNumber) - 1);
1725
1726 PortExtension->DeviceParams.VendorId[sizeof(PortExtension->DeviceParams.VendorId) - 1] = '\0';
1727 PortExtension->DeviceParams.RevisionID[sizeof(PortExtension->DeviceParams.RevisionID) - 1] = '\0';
1728 PortExtension->DeviceParams.SerialNumber[sizeof(PortExtension->DeviceParams.SerialNumber) - 1] = '\0';
1729
1730 // TODO: Add other device params
1731 AhciDebugPrint("\tATA Device\n");
1732 }
1733 else
1734 {
1735 AhciDebugPrint("\tATAPI Device\n");
1736 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATAPI;
1737 PortExtension->DeviceParams.AccessType = READ_ONLY_DIRECT_ACCESS_DEVICE;
1738 }
1739
1740 // INQUIRYDATABUFFERSIZE = 36 ; Defined in storport.h
1741 if (Srb->DataTransferLength < INQUIRYDATABUFFERSIZE)
1742 {
1743 AhciDebugPrint("\tDataBufferLength < sizeof(INQUIRYDATA), Could crash the driver.\n");
1744 NT_ASSERT(FALSE);
1745 }
1746
1747 // update data transfer length
1748 Srb->DataTransferLength = INQUIRYDATABUFFERSIZE;
1749
1750 // prepare data to send
1751 InquiryData->Versions = 2;
1752 InquiryData->Wide32Bit = 1;
1753 InquiryData->CommandQueue = 0; // NCQ not supported
1754 InquiryData->ResponseDataFormat = 0x2;
1755 InquiryData->DeviceTypeModifier = 0;
1756 InquiryData->DeviceTypeQualifier = DEVICE_CONNECTED;
1757 InquiryData->AdditionalLength = INQUIRYDATABUFFERSIZE - 5;
1758 InquiryData->DeviceType = PortExtension->DeviceParams.AccessType;
1759 InquiryData->RemovableMedia = PortExtension->DeviceParams.RemovableDevice;
1760
1761 // Fill VendorID, Product Revision Level and other string fields
1762 StorPortCopyMemory(InquiryData->VendorId, PortExtension->DeviceParams.VendorId, sizeof(InquiryData->VendorId) - 1);
1763 StorPortCopyMemory(InquiryData->ProductId, PortExtension->DeviceParams.RevisionID, sizeof(PortExtension->DeviceParams.RevisionID));
1764 StorPortCopyMemory(InquiryData->ProductRevisionLevel, PortExtension->DeviceParams.SerialNumber, sizeof(InquiryData->ProductRevisionLevel) - 1);
1765
1766 InquiryData->VendorId[sizeof(InquiryData->VendorId) - 1] = '\0';
1767 InquiryData->ProductId[sizeof(InquiryData->ProductId) - 1] = '\0';
1768 InquiryData->ProductRevisionLevel[sizeof(InquiryData->ProductRevisionLevel) - 1] = '\0';
1769
1770 // send queue depth
1771 status = StorPortSetDeviceQueueDepth(PortExtension->AdapterExtension,
1772 Srb->PathId,
1773 Srb->TargetId,
1774 Srb->Lun,
1775 AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
1776
1777 NT_ASSERT(status == TRUE);
1778 return;
1779 }// -- InquiryCompletion();
1780
1781 /**
1782 * @name AhciATAPICommand
1783 * @implemented
1784 *
1785 * Handles ATAPI Requests commands
1786 *
1787 * @param AdapterExtension
1788 * @param Srb
1789 * @param Cdb
1790 *
1791 * @return
1792 * return STOR status for AhciATAPICommand
1793 */
1794 UCHAR
1795 AhciATAPICommand (
1796 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1797 __in PSCSI_REQUEST_BLOCK Srb,
1798 __in PCDB Cdb
1799 )
1800 {
1801 ULONG SrbFlags, DataBufferLength;
1802 PAHCI_SRB_EXTENSION SrbExtension;
1803 PAHCI_PORT_EXTENSION PortExtension;
1804
1805 AhciDebugPrint("AhciATAPICommand()\n");
1806
1807 SrbFlags = Srb->SrbFlags;
1808 SrbExtension = GetSrbExtension(Srb);
1809 DataBufferLength = Srb->DataTransferLength;
1810 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1811
1812 NT_ASSERT(PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI);
1813
1814 NT_ASSERT(SrbExtension != NULL);
1815
1816 SrbExtension->AtaFunction = ATA_FUNCTION_ATAPI_COMMAND;
1817 SrbExtension->Flags = 0;
1818
1819 if (SrbFlags & SRB_FLAGS_DATA_IN)
1820 {
1821 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
1822 }
1823
1824 if (SrbFlags & SRB_FLAGS_DATA_OUT)
1825 {
1826 SrbExtension->Flags |= ATA_FLAGS_DATA_OUT;
1827 }
1828
1829 SrbExtension->FeaturesLow = 0;
1830
1831 SrbExtension->CompletionRoutine = NULL;
1832
1833 NT_ASSERT(Cdb != NULL);
1834 switch(Cdb->CDB10.OperationCode)
1835 {
1836 case SCSIOP_INQUIRY:
1837 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
1838 SrbExtension->CompletionRoutine = AtapiInquiryCompletion;
1839 break;
1840 case SCSIOP_READ:
1841 SrbExtension->Flags |= ATA_FLAGS_USE_DMA;
1842 SrbExtension->FeaturesLow = 0x5;
1843 break;
1844 case SCSIOP_WRITE:
1845 SrbExtension->Flags |= ATA_FLAGS_USE_DMA;
1846 SrbExtension->FeaturesLow = 0x1;
1847 break;
1848 }
1849
1850 SrbExtension->CommandReg = IDE_COMMAND_ATAPI_PACKET;
1851
1852 SrbExtension->LBA0 = 0;
1853 SrbExtension->LBA1 = (UCHAR)(DataBufferLength >> 0);
1854 SrbExtension->LBA2 = (UCHAR)(DataBufferLength >> 8);
1855 SrbExtension->Device = 0;
1856 SrbExtension->LBA3 = 0;
1857 SrbExtension->LBA4 = 0;
1858 SrbExtension->LBA5 = 0;
1859 SrbExtension->FeaturesHigh = 0;
1860 SrbExtension->SectorCountLow = 0;
1861 SrbExtension->SectorCountHigh = 0;
1862
1863 if ((SrbExtension->Flags & ATA_FLAGS_DATA_IN) || (SrbExtension->Flags & ATA_FLAGS_DATA_OUT))
1864 {
1865 SrbExtension->pSgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(AdapterExtension, Srb);
1866 }
1867
1868 return SRB_STATUS_PENDING;
1869 }// -- AhciATAPICommand();
1870
1871 /**
1872 * @name DeviceRequestSense
1873 * @implemented
1874 *
1875 * Handle SCSIOP_MODE_SENSE OperationCode
1876 *
1877 * @param AdapterExtension
1878 * @param Srb
1879 * @param Cdb
1880 *
1881 * @return
1882 * return STOR status for DeviceRequestSense
1883 */
1884 UCHAR
1885 DeviceRequestSense (
1886 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1887 __in PSCSI_REQUEST_BLOCK Srb,
1888 __in PCDB Cdb
1889 )
1890 {
1891 PMODE_PARAMETER_HEADER ModeHeader;
1892 PAHCI_PORT_EXTENSION PortExtension;
1893
1894 AhciDebugPrint("DeviceRequestSense()\n");
1895
1896 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
1897 NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_MODE_SENSE);
1898
1899 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1900
1901 if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
1902 {
1903 return AhciATAPICommand(AdapterExtension, Srb, Cdb);
1904 }
1905
1906 ModeHeader = (PMODE_PARAMETER_HEADER)Srb->DataBuffer;
1907
1908 NT_ASSERT(ModeHeader != NULL);
1909
1910 AhciZeroMemory((PCHAR)ModeHeader, Srb->DataTransferLength);
1911
1912 ModeHeader->ModeDataLength = sizeof(MODE_PARAMETER_HEADER);
1913 ModeHeader->MediumType = 0;
1914 ModeHeader->DeviceSpecificParameter = 0;
1915 ModeHeader->BlockDescriptorLength = 0;
1916
1917 if (Cdb->MODE_SENSE.PageCode == MODE_SENSE_CURRENT_VALUES)
1918 {
1919 ModeHeader->ModeDataLength = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
1920 ModeHeader->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
1921 }
1922
1923 return SRB_STATUS_SUCCESS;
1924 }// -- DeviceRequestSense();
1925
1926 /**
1927 * @name DeviceRequestReadWrite
1928 * @implemented
1929 *
1930 * Handle SCSIOP_READ SCSIOP_WRITE OperationCode
1931 *
1932 * @param AdapterExtension
1933 * @param Srb
1934 * @param Cdb
1935 *
1936 * @return
1937 * return STOR status for DeviceRequestReadWrite
1938 */
1939 UCHAR
1940 DeviceRequestReadWrite (
1941 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1942 __in PSCSI_REQUEST_BLOCK Srb,
1943 __in PCDB Cdb
1944 )
1945 {
1946 BOOLEAN IsReading;
1947 ULONG64 StartOffset;
1948 PAHCI_SRB_EXTENSION SrbExtension;
1949 PAHCI_PORT_EXTENSION PortExtension;
1950 ULONG DataTransferLength, BytesPerSector, SectorCount;
1951
1952 AhciDebugPrint("DeviceRequestReadWrite()\n");
1953
1954 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
1955 NT_ASSERT((Cdb->CDB10.OperationCode == SCSIOP_READ) || (Cdb->CDB10.OperationCode == SCSIOP_WRITE));
1956
1957 SrbExtension = GetSrbExtension(Srb);
1958 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1959
1960 if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
1961 {
1962 return AhciATAPICommand(AdapterExtension, Srb, Cdb);
1963 }
1964
1965 DataTransferLength = Srb->DataTransferLength;
1966 BytesPerSector = PortExtension->DeviceParams.BytesPerLogicalSector;
1967
1968 NT_ASSERT(BytesPerSector > 0);
1969
1970 //ROUND_UP(DataTransferLength, BytesPerSector);
1971
1972 SectorCount = DataTransferLength / BytesPerSector;
1973
1974 Srb->DataTransferLength = SectorCount * BytesPerSector;
1975
1976 StartOffset = AhciGetLba(Cdb, Srb->CdbLength);
1977 IsReading = (Cdb->CDB10.OperationCode == SCSIOP_READ);
1978
1979 NT_ASSERT(SectorCount > 0);
1980
1981 SrbExtension->AtaFunction = ATA_FUNCTION_ATA_READ;
1982 SrbExtension->Flags |= ATA_FLAGS_USE_DMA;
1983 SrbExtension->CompletionRoutine = NULL;
1984
1985 if (IsReading)
1986 {
1987 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
1988 SrbExtension->CommandReg = IDE_COMMAND_READ_DMA;
1989 }
1990 else
1991 {
1992 SrbExtension->Flags |= ATA_FLAGS_DATA_OUT;
1993 SrbExtension->CommandReg = IDE_COMMAND_WRITE_DMA;
1994 }
1995
1996 SrbExtension->FeaturesLow = 0;
1997 SrbExtension->LBA0 = (StartOffset >> 0) & 0xFF;
1998 SrbExtension->LBA1 = (StartOffset >> 8) & 0xFF;
1999 SrbExtension->LBA2 = (StartOffset >> 16) & 0xFF;
2000
2001 SrbExtension->Device = (0xA0 | IDE_LBA_MODE);
2002
2003 if (PortExtension->DeviceParams.Lba48BitMode)
2004 {
2005 SrbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND;
2006
2007 if (IsReading)
2008 {
2009 SrbExtension->CommandReg = IDE_COMMAND_READ_DMA_EXT;
2010 }
2011 else
2012 {
2013 SrbExtension->CommandReg = IDE_COMMAND_WRITE_DMA_EXT;
2014 }
2015
2016 SrbExtension->LBA3 = (StartOffset >> 24) & 0xFF;
2017 SrbExtension->LBA4 = (StartOffset >> 32) & 0xFF;
2018 SrbExtension->LBA5 = (StartOffset >> 40) & 0xFF;
2019 }
2020 else
2021 {
2022 NT_ASSERT(FALSE);
2023 }
2024
2025 SrbExtension->FeaturesHigh = 0;
2026 SrbExtension->SectorCountLow = (SectorCount >> 0) & 0xFF;
2027 SrbExtension->SectorCountHigh = (SectorCount >> 8) & 0xFF;
2028
2029 NT_ASSERT(SectorCount < 0x100);
2030
2031 SrbExtension->pSgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(AdapterExtension, Srb);
2032
2033 return SRB_STATUS_PENDING;
2034 }// -- DeviceRequestReadWrite();
2035
2036 /**
2037 * @name DeviceRequestCapacity
2038 * @implemented
2039 *
2040 * Handle SCSIOP_READ_CAPACITY OperationCode
2041 *
2042 * @param AdapterExtension
2043 * @param Srb
2044 * @param Cdb
2045 *
2046 * @return
2047 * return STOR status for DeviceRequestCapacity
2048 */
2049 UCHAR
2050 DeviceRequestCapacity (
2051 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
2052 __in PSCSI_REQUEST_BLOCK Srb,
2053 __in PCDB Cdb
2054 )
2055 {
2056 ULONG MaxLba, BytesPerLogicalSector;
2057 PREAD_CAPACITY_DATA ReadCapacity;
2058 PAHCI_PORT_EXTENSION PortExtension;
2059
2060 AhciDebugPrint("DeviceRequestCapacity()\n");
2061
2062 UNREFERENCED_PARAMETER(AdapterExtension);
2063 UNREFERENCED_PARAMETER(Cdb);
2064
2065 NT_ASSERT(Srb->DataBuffer != NULL);
2066 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
2067
2068
2069 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
2070
2071 if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
2072 {
2073 return AhciATAPICommand(AdapterExtension, Srb, Cdb);
2074 }
2075
2076 if (Cdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY)
2077 {
2078 ReadCapacity = (PREAD_CAPACITY_DATA)Srb->DataBuffer;
2079
2080 BytesPerLogicalSector = PortExtension->DeviceParams.BytesPerLogicalSector;
2081 MaxLba = (ULONG)PortExtension->DeviceParams.MaxLba.QuadPart - 1;
2082
2083 // I trust you windows :D
2084 NT_ASSERT(Srb->DataTransferLength >= sizeof(READ_CAPACITY_DATA));
2085
2086 // I trust you user :D
2087 NT_ASSERT(PortExtension->DeviceParams.MaxLba.QuadPart < (ULONG)-1);
2088
2089 // Actually I don't trust anyone :p
2090 Srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
2091
2092 REVERSE_BYTES(&ReadCapacity->BytesPerBlock, &BytesPerLogicalSector);
2093 REVERSE_BYTES(&ReadCapacity->LogicalBlockAddress, &MaxLba);
2094 }
2095 else
2096 {
2097 AhciDebugPrint("\tSCSIOP_READ_CAPACITY16 not supported\n");
2098 NT_ASSERT(FALSE);
2099 }
2100
2101 return SRB_STATUS_SUCCESS;
2102 }// -- DeviceRequestCapacity();
2103
2104 /**
2105 * @name DeviceRequestComplete
2106 * @implemented
2107 *
2108 * Handle UnHandled Requests
2109 *
2110 * @param AdapterExtension
2111 * @param Srb
2112 * @param Cdb
2113 *
2114 * @return
2115 * return STOR status for DeviceRequestComplete
2116 */
2117 UCHAR
2118 DeviceRequestComplete (
2119 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
2120 __in PSCSI_REQUEST_BLOCK Srb,
2121 __in PCDB Cdb
2122 )
2123 {
2124 AhciDebugPrint("DeviceRequestComplete()\n");
2125
2126 UNREFERENCED_PARAMETER(AdapterExtension);
2127 UNREFERENCED_PARAMETER(Cdb);
2128
2129 Srb->ScsiStatus = SCSISTAT_GOOD;
2130
2131 return SRB_STATUS_SUCCESS;
2132 }// -- DeviceRequestComplete();
2133
2134 /**
2135 * @name DeviceReportLuns
2136 * @implemented
2137 *
2138 * Handle SCSIOP_REPORT_LUNS OperationCode
2139 *
2140 * @param AdapterExtension
2141 * @param Srb
2142 * @param Cdb
2143 *
2144 * @return
2145 * return STOR status for DeviceReportLuns
2146 */
2147 UCHAR
2148 DeviceReportLuns (
2149 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
2150 __in PSCSI_REQUEST_BLOCK Srb,
2151 __in PCDB Cdb
2152 )
2153 {
2154 PLUN_LIST LunList;
2155 PAHCI_PORT_EXTENSION PortExtension;
2156
2157 AhciDebugPrint("DeviceReportLuns()\n");
2158
2159 UNREFERENCED_PARAMETER(Cdb);
2160
2161 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
2162
2163 NT_ASSERT(Srb->DataTransferLength >= sizeof(LUN_LIST));
2164 NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS);
2165
2166 if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
2167 {
2168 return AhciATAPICommand(AdapterExtension, Srb, Cdb);
2169 }
2170
2171 LunList = (PLUN_LIST)Srb->DataBuffer;
2172
2173 NT_ASSERT(LunList != NULL);
2174
2175 AhciZeroMemory((PCHAR)LunList, sizeof(LUN_LIST));
2176
2177 LunList->LunListLength[3] = 8;
2178
2179 Srb->ScsiStatus = SCSISTAT_GOOD;
2180 Srb->DataTransferLength = sizeof(LUN_LIST);
2181
2182 return SRB_STATUS_SUCCESS;
2183 }// -- DeviceReportLuns();
2184
2185 /**
2186 * @name DeviceInquiryRequest
2187 * @implemented
2188 *
2189 * Tells wheather given port is implemented or not
2190 *
2191 * @param AdapterExtension
2192 * @param Srb
2193 * @param Cdb
2194 *
2195 * @return
2196 * return STOR status for DeviceInquiryRequest
2197 *
2198 * @remark
2199 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
2200 */
2201 UCHAR
2202 DeviceInquiryRequest (
2203 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
2204 __in PSCSI_REQUEST_BLOCK Srb,
2205 __in PCDB Cdb
2206 )
2207 {
2208 PVOID DataBuffer;
2209 PAHCI_SRB_EXTENSION SrbExtension;
2210 PAHCI_PORT_EXTENSION PortExtension;
2211 PVPD_SUPPORTED_PAGES_PAGE VpdOutputBuffer;
2212 ULONG DataBufferLength, RequiredDataBufferLength;
2213
2214 AhciDebugPrint("DeviceInquiryRequest()\n");
2215
2216 NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_INQUIRY);
2217 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
2218
2219 SrbExtension = GetSrbExtension(Srb);
2220 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
2221
2222 if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI)
2223 {
2224 return AhciATAPICommand(AdapterExtension, Srb, Cdb);
2225 }
2226
2227 if (Srb->Lun != 0)
2228 {
2229 return SRB_STATUS_SELECTION_TIMEOUT;
2230 }
2231 else if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0)
2232 {
2233 // 3.6.1
2234 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
2235 AhciDebugPrint("\tEVPD Inquired\n");
2236 NT_ASSERT(SrbExtension != NULL);
2237
2238 SrbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY;
2239 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
2240 SrbExtension->CompletionRoutine = InquiryCompletion;
2241 SrbExtension->CommandReg = IDE_COMMAND_NOT_VALID;
2242
2243 // TODO: Should use AhciZeroMemory
2244 SrbExtension->FeaturesLow = 0;
2245 SrbExtension->LBA0 = 0;
2246 SrbExtension->LBA1 = 0;
2247 SrbExtension->LBA2 = 0;
2248 SrbExtension->Device = 0xA0;
2249 SrbExtension->LBA3 = 0;
2250 SrbExtension->LBA4 = 0;
2251 SrbExtension->LBA5 = 0;
2252 SrbExtension->FeaturesHigh = 0;
2253 SrbExtension->SectorCountLow = 0;
2254 SrbExtension->SectorCountHigh = 0;
2255
2256 SrbExtension->Sgl.NumberOfElements = 1;
2257 SrbExtension->Sgl.List[0].PhysicalAddress.LowPart = PortExtension->IdentifyDeviceDataPhysicalAddress.LowPart;
2258 SrbExtension->Sgl.List[0].PhysicalAddress.HighPart = PortExtension->IdentifyDeviceDataPhysicalAddress.HighPart;
2259 SrbExtension->Sgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA);
2260
2261 SrbExtension->pSgl = &SrbExtension->Sgl;
2262 return SRB_STATUS_PENDING;
2263 }
2264 else
2265 {
2266 AhciDebugPrint("\tVPD Inquired\n");
2267
2268 DataBuffer = Srb->DataBuffer;
2269 DataBufferLength = Srb->DataTransferLength;
2270 RequiredDataBufferLength = DataBufferLength; // make the compiler happy :p
2271
2272 if (DataBuffer == NULL)
2273 {
2274 return SRB_STATUS_INVALID_REQUEST;
2275 }
2276
2277 AhciZeroMemory(DataBuffer, DataBufferLength);
2278
2279 switch(Cdb->CDB6INQUIRY3.PageCode)
2280 {
2281 case VPD_SUPPORTED_PAGES:
2282 {
2283 AhciDebugPrint("\tVPD_SUPPORTED_PAGES\n");
2284 RequiredDataBufferLength = sizeof(VPD_SUPPORTED_PAGES_PAGE) + 1;
2285
2286 if (DataBufferLength < RequiredDataBufferLength)
2287 {
2288 AhciDebugPrint("\tDataBufferLength: %d Required: %d\n", DataBufferLength, RequiredDataBufferLength);
2289 return SRB_STATUS_INVALID_REQUEST;
2290 }
2291
2292 VpdOutputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)DataBuffer;
2293
2294 VpdOutputBuffer->DeviceType = PortExtension->DeviceParams.AccessType;
2295 VpdOutputBuffer->DeviceTypeQualifier = 0;
2296 VpdOutputBuffer->PageCode = VPD_SUPPORTED_PAGES;
2297 VpdOutputBuffer->PageLength = 1;
2298 VpdOutputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES;
2299 //VpdOutputBuffer->SupportedPageList[1] = VPD_SERIAL_NUMBER;
2300 //VpdOutputBuffer->SupportedPageList[2] = VPD_DEVICE_IDENTIFIERS;
2301
2302 NT_ASSERT(VpdOutputBuffer->DeviceType == DIRECT_ACCESS_DEVICE);
2303 }
2304 break;
2305 case VPD_SERIAL_NUMBER:
2306 {
2307 AhciDebugPrint("\tVPD_SERIAL_NUMBER\n");
2308 }
2309 break;
2310 case VPD_DEVICE_IDENTIFIERS:
2311 {
2312 AhciDebugPrint("\tVPD_DEVICE_IDENTIFIERS\n");
2313 }
2314 break;
2315 default:
2316 AhciDebugPrint("\tPageCode: %x\n", Cdb->CDB6INQUIRY3.PageCode);
2317 return SRB_STATUS_INVALID_REQUEST;
2318 }
2319
2320 Srb->DataTransferLength = RequiredDataBufferLength;
2321 return SRB_STATUS_SUCCESS;
2322 }
2323 }// -- DeviceInquiryRequest();
2324
2325 /**
2326 * @name AhciAdapterReset
2327 * @implemented
2328 *
2329 * 10.4.3 HBA Reset
2330 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
2331 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
2332 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
2333 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
2334 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
2335 * the HBA reset has completed.
2336 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
2337 * a hung or locked state.
2338 *
2339 * @param AdapterExtension
2340 *
2341 * @return
2342 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
2343 */
2344 BOOLEAN
2345 AhciAdapterReset (
2346 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
2347 )
2348 {
2349 ULONG ticks;
2350 AHCI_GHC ghc;
2351 PAHCI_MEMORY_REGISTERS abar = NULL;
2352
2353 AhciDebugPrint("AhciAdapterReset()\n");
2354
2355 abar = AdapterExtension->ABAR_Address;
2356 if (abar == NULL) // basic sanity
2357 {
2358 return FALSE;
2359 }
2360
2361 // HR -- Very first bit (lowest significant)
2362 ghc.HR = 1;
2363 StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc.Status);
2364
2365 for (ticks = 0; ticks < 50; ++ticks)
2366 {
2367 ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC);
2368 if (ghc.HR == 0)
2369 {
2370 break;
2371 }
2372 StorPortStallExecution(20000);
2373 }
2374
2375 if (ticks == 50)// 1 second
2376 {
2377 AhciDebugPrint("\tDevice Timeout\n");
2378 return FALSE;
2379 }
2380
2381 return TRUE;
2382 }// -- AhciAdapterReset();
2383
2384 /**
2385 * @name AhciZeroMemory
2386 * @implemented
2387 *
2388 * Clear buffer by filling zeros
2389 *
2390 * @param Buffer
2391 * @param BufferSize
2392 */
2393 __inline
2394 VOID
2395 AhciZeroMemory (
2396 __out PCHAR Buffer,
2397 __in ULONG BufferSize
2398 )
2399 {
2400 ULONG i;
2401 for (i = 0; i < BufferSize; i++)
2402 {
2403 Buffer[i] = 0;
2404 }
2405
2406 return;
2407 }// -- AhciZeroMemory();
2408
2409 /**
2410 * @name IsPortValid
2411 * @implemented
2412 *
2413 * Tells wheather given port is implemented or not
2414 *
2415 * @param AdapterExtension
2416 * @param PathId
2417 *
2418 * @return
2419 * return TRUE if provided port is valid (implemented) or not
2420 */
2421 __inline
2422 BOOLEAN
2423 IsPortValid (
2424 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
2425 __in ULONG pathId
2426 )
2427 {
2428 NT_ASSERT(pathId < MAXIMUM_AHCI_PORT_COUNT);
2429
2430 if (pathId >= AdapterExtension->PortCount)
2431 {
2432 return FALSE;
2433 }
2434
2435 return AdapterExtension->PortExtension[pathId].DeviceParams.IsActive;
2436 }// -- IsPortValid()
2437
2438 /**
2439 * @name AddQueue
2440 * @implemented
2441 *
2442 * Add Srb to Queue
2443 *
2444 * @param Queue
2445 * @param Srb
2446 *
2447 * @return
2448 * return TRUE if Srb is successfully added to Queue
2449 *
2450 */
2451 __inline
2452 BOOLEAN
2453 AddQueue (
2454 __inout PAHCI_QUEUE Queue,
2455 __in PVOID Srb
2456 )
2457 {
2458 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
2459 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
2460
2461 if (Queue->Tail == ((Queue->Head + 1) % MAXIMUM_QUEUE_BUFFER_SIZE))
2462 return FALSE;
2463
2464 Queue->Buffer[Queue->Head++] = Srb;
2465 Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE;
2466
2467 return TRUE;
2468 }// -- AddQueue();
2469
2470 /**
2471 * @name RemoveQueue
2472 * @implemented
2473 *
2474 * Remove and return Srb from Queue
2475 *
2476 * @param Queue
2477 *
2478 * @return
2479 * return Srb
2480 *
2481 */
2482 __inline
2483 PVOID
2484 RemoveQueue (
2485 __inout PAHCI_QUEUE Queue
2486 )
2487 {
2488 PVOID Srb;
2489
2490 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
2491 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
2492
2493 if (Queue->Head == Queue->Tail)
2494 return NULL;
2495
2496 Srb = Queue->Buffer[Queue->Tail++];
2497 Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE;
2498
2499 return Srb;
2500 }// -- RemoveQueue();
2501
2502 /**
2503 * @name GetSrbExtension
2504 * @implemented
2505 *
2506 * GetSrbExtension from Srb make sure It is properly aligned
2507 *
2508 * @param Srb
2509 *
2510 * @return
2511 * return SrbExtension
2512 *
2513 */
2514 __inline
2515 PAHCI_SRB_EXTENSION
2516 GetSrbExtension (
2517 __in PSCSI_REQUEST_BLOCK Srb
2518 )
2519 {
2520 ULONG Offset;
2521 ULONG_PTR SrbExtension;
2522
2523 SrbExtension = (ULONG_PTR)Srb->SrbExtension;
2524 Offset = SrbExtension % 128;
2525
2526 // CommandTable should be 128 byte aligned
2527 if (Offset != 0)
2528 Offset = 128 - Offset;
2529
2530 return (PAHCI_SRB_EXTENSION)(SrbExtension + Offset);
2531 }// -- PAHCI_SRB_EXTENSION();
2532
2533 /**
2534 * @name AhciGetLba
2535 * @implemented
2536 *
2537 * Find the logical address of demand block from Cdb
2538 *
2539 * @param Srb
2540 *
2541 * @return
2542 * return Logical Address of the block
2543 *
2544 */
2545 __inline
2546 ULONG64
2547 AhciGetLba (
2548 __in PCDB Cdb,
2549 __in ULONG CdbLength
2550 )
2551 {
2552 ULONG64 lba = 0;
2553
2554 NT_ASSERT(Cdb != NULL);
2555 NT_ASSERT(CdbLength != 0);
2556
2557 if (CdbLength == 0x10)
2558 {
2559 REVERSE_BYTES_QUAD(&lba, Cdb->CDB16.LogicalBlock);
2560 }
2561 else
2562 {
2563 lba |= Cdb->CDB10.LogicalBlockByte3 << 0;
2564 lba |= Cdb->CDB10.LogicalBlockByte2 << 8;
2565 lba |= Cdb->CDB10.LogicalBlockByte1 << 16;
2566 lba |= Cdb->CDB10.LogicalBlockByte0 << 24;
2567 }
2568
2569 return lba;
2570 }// -- AhciGetLba();