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