xref: /reactos/drivers/storage/port/scsiport/fdo.c (revision 1a6bd270)
1 /*
2  * PROJECT:     ReactOS Storage Stack / SCSIPORT storage port library
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Adapter device object (FDO) support routines
5  * COPYRIGHT:   Eric Kohl (eric.kohl@reactos.org)
6  *              Aleksey Bragin (aleksey@reactos.org)
7  *              2020 Victor Perevertkin (victor.perevertkin@reactos.org)
8  */
9 
10 #include "scsiport.h"
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 
16 static
17 NTSTATUS
FdoSendInquiry(_In_ PDEVICE_OBJECT DeviceObject)18 FdoSendInquiry(
19     _In_ PDEVICE_OBJECT DeviceObject)
20 {
21     IO_STATUS_BLOCK IoStatusBlock;
22     PIO_STACK_LOCATION IrpStack;
23     KEVENT Event;
24     KIRQL Irql;
25     PIRP Irp;
26     NTSTATUS Status;
27     PINQUIRYDATA InquiryBuffer;
28     PSENSE_DATA SenseBuffer;
29     BOOLEAN KeepTrying = TRUE;
30     ULONG RetryCount = 0;
31     SCSI_REQUEST_BLOCK Srb;
32     PCDB Cdb;
33 
34     DPRINT("FdoSendInquiry() called\n");
35 
36     PSCSI_PORT_LUN_EXTENSION LunExtension = DeviceObject->DeviceExtension;
37     PSCSI_PORT_DEVICE_EXTENSION DeviceExtension =
38         LunExtension->Common.LowerDevice->DeviceExtension;
39 
40     InquiryBuffer = ExAllocatePoolWithTag(NonPagedPool, INQUIRYDATABUFFERSIZE, TAG_SCSIPORT);
41     if (InquiryBuffer == NULL)
42         return STATUS_INSUFFICIENT_RESOURCES;
43 
44     SenseBuffer = ExAllocatePoolWithTag(NonPagedPool, SENSE_BUFFER_SIZE, TAG_SCSIPORT);
45     if (SenseBuffer == NULL)
46     {
47         ExFreePoolWithTag(InquiryBuffer, TAG_SCSIPORT);
48         return STATUS_INSUFFICIENT_RESOURCES;
49     }
50 
51     while (KeepTrying)
52     {
53         /* Initialize event for waiting */
54         KeInitializeEvent(&Event,
55                           NotificationEvent,
56                           FALSE);
57 
58         /* Create an IRP */
59         Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_IN,
60                                             DeviceObject,
61                                             NULL,
62                                             0,
63                                             InquiryBuffer,
64                                             INQUIRYDATABUFFERSIZE,
65                                             TRUE,
66                                             &Event,
67                                             &IoStatusBlock);
68         if (Irp == NULL)
69         {
70             DPRINT("IoBuildDeviceIoControlRequest() failed\n");
71 
72             /* Quit the loop */
73             Status = STATUS_INSUFFICIENT_RESOURCES;
74             KeepTrying = FALSE;
75             continue;
76         }
77 
78         /* Prepare SRB */
79         RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
80 
81         Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
82         Srb.OriginalRequest = Irp;
83         Srb.PathId = LunExtension->PathId;
84         Srb.TargetId = LunExtension->TargetId;
85         Srb.Lun = LunExtension->Lun;
86         Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
87         Srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
88         Srb.TimeOutValue = 4;
89         Srb.CdbLength = 6;
90 
91         Srb.SenseInfoBuffer = SenseBuffer;
92         Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
93 
94         Srb.DataBuffer = InquiryBuffer;
95         Srb.DataTransferLength = INQUIRYDATABUFFERSIZE;
96 
97         /* Attach Srb to the Irp */
98         IrpStack = IoGetNextIrpStackLocation (Irp);
99         IrpStack->Parameters.Scsi.Srb = &Srb;
100 
101         /* Fill in CDB */
102         Cdb = (PCDB)Srb.Cdb;
103         Cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
104         Cdb->CDB6INQUIRY.LogicalUnitNumber = LunExtension->Lun;
105         Cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
106 
107         /* Call the driver */
108         Status = IoCallDriver(DeviceObject, Irp);
109 
110         /* Wait for it to complete */
111         if (Status == STATUS_PENDING)
112         {
113             DPRINT("FdoSendInquiry(): Waiting for the driver to process request...\n");
114             KeWaitForSingleObject(&Event,
115                                   Executive,
116                                   KernelMode,
117                                   FALSE,
118                                   NULL);
119             Status = IoStatusBlock.Status;
120         }
121 
122         DPRINT("FdoSendInquiry(): Request processed by driver, status = 0x%08X\n", Status);
123 
124         if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_SUCCESS)
125         {
126             /* All fine, copy data over */
127             RtlCopyMemory(&LunExtension->InquiryData,
128                           InquiryBuffer,
129                           INQUIRYDATABUFFERSIZE);
130 
131             /* Quit the loop */
132             Status = STATUS_SUCCESS;
133             KeepTrying = FALSE;
134             continue;
135         }
136 
137         DPRINT("Inquiry SRB failed with SrbStatus 0x%08X\n", Srb.SrbStatus);
138 
139         /* Check if the queue is frozen */
140         if (Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
141         {
142             /* Something weird happened, deal with it (unfreeze the queue) */
143             KeepTrying = FALSE;
144 
145             DPRINT("FdoSendInquiry(): the queue is frozen at TargetId %d\n", Srb.TargetId);
146 
147             /* Clear frozen flag */
148             LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE;
149 
150             /* Acquire the spinlock */
151             KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
152 
153             /* Process the request. SpiGetNextRequestFromLun will unlock for us */
154             SpiGetNextRequestFromLun(DeviceExtension, LunExtension, &Irql);
155         }
156 
157         /* Check if data overrun happened */
158         if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN)
159         {
160             DPRINT("Data overrun at TargetId %d\n", LunExtension->TargetId);
161 
162             /* Nothing dramatic, just copy data, but limiting the size */
163             RtlCopyMemory(&LunExtension->InquiryData,
164                             InquiryBuffer,
165                             (Srb.DataTransferLength > INQUIRYDATABUFFERSIZE) ?
166                             INQUIRYDATABUFFERSIZE : Srb.DataTransferLength);
167 
168             /* Quit the loop */
169             Status = STATUS_SUCCESS;
170             KeepTrying = FALSE;
171         }
172         else if ((Srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
173                  SenseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST)
174         {
175             /* LUN is not valid, but some device responds there.
176                 Mark it as invalid anyway */
177 
178             /* Quit the loop */
179             Status = STATUS_INVALID_DEVICE_REQUEST;
180             KeepTrying = FALSE;
181         }
182         else
183         {
184             /* Retry a couple of times if no timeout happened */
185             if ((RetryCount < 2) &&
186                 (SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_NO_DEVICE) &&
187                 (SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT))
188             {
189                 RetryCount++;
190                 KeepTrying = TRUE;
191             }
192             else
193             {
194                 /* That's all, quit the loop */
195                 KeepTrying = FALSE;
196 
197                 /* Set status according to SRB status */
198                 if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_FUNCTION ||
199                     SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_SRB_BLOCK_LENGTH)
200                 {
201                     Status = STATUS_INVALID_DEVICE_REQUEST;
202                 }
203                 else
204                 {
205                     Status = STATUS_IO_DEVICE_ERROR;
206                 }
207             }
208         }
209     }
210 
211     /* Free buffers */
212     ExFreePoolWithTag(InquiryBuffer, TAG_SCSIPORT);
213     ExFreePoolWithTag(SenseBuffer, TAG_SCSIPORT);
214 
215     DPRINT("FdoSendInquiry() done with Status 0x%08X\n", Status);
216 
217     return Status;
218 }
219 
220 /* Scans all SCSI buses */
221 VOID
FdoScanAdapter(_In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension)222 FdoScanAdapter(
223     _In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension)
224 {
225     NTSTATUS status;
226     UINT32 totalLUNs = PortExtension->TotalLUCount;
227 
228     DPRINT("FdoScanAdapter() called\n");
229 
230     /* Scan all buses */
231     for (UINT8 pathId = 0; pathId < PortExtension->NumberOfBuses; pathId++)
232     {
233         DPRINT("    Scanning bus/pathID %u\n", pathId);
234 
235         /* Get pointer to the scan information */
236         PSCSI_BUS_INFO currentBus = &PortExtension->Buses[pathId];
237 
238         /* And send INQUIRY to every target */
239         for (UINT8 targetId = 0;
240              targetId < PortExtension->PortConfig->MaximumNumberOfTargets;
241              targetId++)
242         {
243             BOOLEAN targetFound = FALSE;
244 
245             /* TODO: Support scan bottom-up */
246 
247             /* Skip if it's the same address */
248             if (targetId == currentBus->BusIdentifier)
249                 continue;
250 
251             /* Scan all logical units */
252             for (UINT8 lun = 0; lun < PortExtension->MaxLunCount; lun++)
253             {
254                 PSCSI_PORT_LUN_EXTENSION lunExt;
255 
256                 /* Skip invalid lun values */
257                 if (lun >= PortExtension->PortConfig->MaximumNumberOfLogicalUnits)
258                     continue;
259 
260                 // try to find an existing device
261                 lunExt = GetLunByPath(PortExtension,
262                                       pathId,
263                                       targetId,
264                                       lun);
265 
266                 if (lunExt)
267                 {
268                     // check if the device still exists
269                     status = FdoSendInquiry(lunExt->Common.DeviceObject);
270                     if (!NT_SUCCESS(status))
271                     {
272                         // remove the device
273                         UNIMPLEMENTED;
274                         __debugbreak();
275                     }
276 
277                     if (lunExt->InquiryData.DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED)
278                     {
279                         // remove the device
280                         UNIMPLEMENTED;
281                         __debugbreak();
282                     }
283 
284                     /* Decide whether we are continuing or not */
285                     if (status == STATUS_INVALID_DEVICE_REQUEST)
286                         continue;
287                     else
288                         break;
289                 }
290 
291                 // create a new LUN device
292                 PDEVICE_OBJECT lunPDO = PdoCreateLunDevice(PortExtension);
293                 if (!lunPDO)
294                 {
295                     continue;
296                 }
297 
298                 lunExt = lunPDO->DeviceExtension;
299 
300                 lunExt->PathId = pathId;
301                 lunExt->TargetId = targetId;
302                 lunExt->Lun = lun;
303 
304                 DPRINT("Add PDO to list: PDO: %p, FDOExt: %p, PDOExt: %p\n", lunPDO, PortExtension, lunExt);
305 
306                 /* Set flag to prevent race conditions */
307                 lunExt->Flags |= SCSI_PORT_SCAN_IN_PROGRESS;
308 
309                 /* Finally send the inquiry command */
310                 status = FdoSendInquiry(lunPDO);
311 
312                 if (NT_SUCCESS(status))
313                 {
314                     /* Let's see if we really found a device */
315                     PINQUIRYDATA InquiryData = &lunExt->InquiryData;
316 
317                     /* Check if this device is unsupported */
318                     if (InquiryData->DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED)
319                     {
320                         IoDeleteDevice(lunPDO);
321                         continue;
322                     }
323 
324                     /* Clear the "in scan" flag */
325                     lunExt->Flags &= ~SCSI_PORT_SCAN_IN_PROGRESS;
326 
327                     DPRINT1("Found device of type %d at controller %d bus %d tid %d lun %d, PDO: %p\n",
328                         InquiryData->DeviceType, PortExtension->PortNumber, pathId, targetId, lun, lunPDO);
329 
330                     InsertTailList(&currentBus->LunsListHead, &lunExt->LunEntry);
331 
332                     totalLUNs++;
333                     currentBus->LogicalUnitsCount++;
334                     targetFound = TRUE;
335                 }
336                 else
337                 {
338                     /* Decide whether we are continuing or not */
339                     if (status == STATUS_INVALID_DEVICE_REQUEST)
340                         continue;
341                     else
342                         break;
343                 }
344             }
345 
346             if (targetFound)
347             {
348                 currentBus->TargetsCount++;
349             }
350         }
351     }
352 
353     PortExtension->TotalLUCount = totalLUNs;
354 }
355 
356 /**
357  * @brief      Calls HwInitialize routine of the miniport and sets up interrupts
358  *             Should be called inside ScsiPortInitialize (for legacy drivers)
359  *             or inside IRP_MN_START_DEVICE for pnp drivers
360  *
361  * @param[in]  DeviceExtension  The device extension
362  *
363  * @return     NTSTATUS of the operation
364  */
365 NTSTATUS
FdoCallHWInitialize(_In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)366 FdoCallHWInitialize(
367     _In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
368 {
369     PPORT_CONFIGURATION_INFORMATION PortConfig = DeviceExtension->PortConfig;
370     NTSTATUS Status;
371     KIRQL OldIrql;
372 
373     /* Deal with interrupts */
374     if (DeviceExtension->HwInterrupt == NULL ||
375         (PortConfig->BusInterruptLevel == 0 && PortConfig->BusInterruptVector == 0))
376     {
377         /* No interrupts */
378         DeviceExtension->InterruptCount = 0;
379 
380         DPRINT1("Interrupt Count: 0\n");
381 
382         UNIMPLEMENTED;
383 
384         /* This code path will ALWAYS crash so stop it now */
385         __debugbreak();
386     }
387     else
388     {
389         BOOLEAN InterruptShareable;
390         KINTERRUPT_MODE InterruptMode[2];
391         ULONG InterruptVector[2], i, MappedIrq[2];
392         KIRQL Dirql[2], MaxDirql;
393         KAFFINITY Affinity[2];
394 
395         DeviceExtension->InterruptLevel[0] = PortConfig->BusInterruptLevel;
396         DeviceExtension->InterruptLevel[1] = PortConfig->BusInterruptLevel2;
397 
398         InterruptVector[0] = PortConfig->BusInterruptVector;
399         InterruptVector[1] = PortConfig->BusInterruptVector2;
400 
401         InterruptMode[0] = PortConfig->InterruptMode;
402         InterruptMode[1] = PortConfig->InterruptMode2;
403 
404         DeviceExtension->InterruptCount =
405             (PortConfig->BusInterruptLevel2 != 0 ||
406              PortConfig->BusInterruptVector2 != 0) ? 2 : 1;
407 
408         for (i = 0; i < DeviceExtension->InterruptCount; i++)
409         {
410             /* Register an interrupt handler for this device */
411             MappedIrq[i] = HalGetInterruptVector(
412                 PortConfig->AdapterInterfaceType, PortConfig->SystemIoBusNumber,
413                 DeviceExtension->InterruptLevel[i], InterruptVector[i], &Dirql[i],
414                 &Affinity[i]);
415         }
416 
417         if (DeviceExtension->InterruptCount == 1 || Dirql[0] > Dirql[1])
418         {
419             MaxDirql = Dirql[0];
420         }
421         else
422         {
423             MaxDirql = Dirql[1];
424         }
425 
426         for (i = 0; i < DeviceExtension->InterruptCount; i++)
427         {
428             /* Determine IRQ sharability as usual */
429             if (PortConfig->AdapterInterfaceType == MicroChannel ||
430                 InterruptMode[i] == LevelSensitive)
431             {
432                 InterruptShareable = TRUE;
433             }
434             else
435             {
436                 InterruptShareable = FALSE;
437             }
438 
439             Status = IoConnectInterrupt(&DeviceExtension->Interrupt[i],
440                                         ScsiPortIsr,
441                                         DeviceExtension,
442                                         &DeviceExtension->IrqLock,
443                                         MappedIrq[i], Dirql[i],
444                                         MaxDirql,
445                                         InterruptMode[i],
446                                         InterruptShareable,
447                                         Affinity[i],
448                                         FALSE);
449 
450             if (!(NT_SUCCESS(Status)))
451             {
452                 DPRINT1("Could not connect interrupt %d\n", InterruptVector[i]);
453                 DeviceExtension->Interrupt[i] = NULL;
454                 return Status;
455             }
456         }
457     }
458 
459     /* Save IoAddress (from access ranges) */
460     if (PortConfig->NumberOfAccessRanges != 0)
461     {
462         DeviceExtension->IoAddress = ((*(PortConfig->AccessRanges))[0]).RangeStart.LowPart;
463 
464         DPRINT("Io Address %x\n", DeviceExtension->IoAddress);
465     }
466 
467     /* Set flag that it's allowed to disconnect during this command */
468     DeviceExtension->Flags |= SCSI_PORT_DISCONNECT_ALLOWED;
469 
470     /* Initialize counter of active requests (-1 means there are none) */
471     DeviceExtension->ActiveRequestCounter = -1;
472 
473     /* Analyze what we have about DMA */
474     if (DeviceExtension->AdapterObject != NULL && PortConfig->Master &&
475         PortConfig->NeedPhysicalAddresses)
476     {
477         DeviceExtension->MapRegisters = TRUE;
478     }
479     else
480     {
481         DeviceExtension->MapRegisters = FALSE;
482     }
483 
484     /* Call HwInitialize at DISPATCH_LEVEL */
485     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
486 
487     if (!KeSynchronizeExecution(
488             DeviceExtension->Interrupt[0], DeviceExtension->HwInitialize,
489             DeviceExtension->MiniPortDeviceExtension))
490     {
491         DPRINT1("HwInitialize() failed!\n");
492         KeLowerIrql(OldIrql);
493         return STATUS_ADAPTER_HARDWARE_ERROR;
494     }
495 
496     /* Check if a notification is needed */
497     if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
498     {
499         /* Call DPC right away, because we're already at DISPATCH_LEVEL */
500         ScsiPortDpcForIsr(NULL, DeviceExtension->Common.DeviceObject, NULL, NULL);
501     }
502 
503     /* Lower irql back to what it was */
504     KeLowerIrql(OldIrql);
505 
506     return STATUS_SUCCESS;
507 }
508 
509 NTSTATUS
FdoRemoveAdapter(_In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)510 FdoRemoveAdapter(
511     _In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
512 {
513     IoStopTimer(DeviceExtension->Common.DeviceObject);
514 
515     // release device interface
516     if (DeviceExtension->InterfaceName.Buffer)
517     {
518         IoSetDeviceInterfaceState(&DeviceExtension->InterfaceName, FALSE);
519 
520         RtlFreeUnicodeString(&DeviceExtension->InterfaceName);
521         RtlInitUnicodeString(&DeviceExtension->InterfaceName, NULL);
522     }
523 
524     // remove the dos device link
525     WCHAR dosNameBuffer[12];
526     UNICODE_STRING dosDeviceName;
527 
528     swprintf(dosNameBuffer, L"\\??\\Scsi%lu:", DeviceExtension->PortNumber);
529     RtlInitUnicodeString(&dosDeviceName, dosNameBuffer);
530 
531     IoDeleteSymbolicLink(&dosDeviceName); // don't check the result
532 
533     // decrease the port count
534     if (DeviceExtension->DeviceStarted)
535     {
536         PCONFIGURATION_INFORMATION sysConfig = IoGetConfigurationInformation();
537         sysConfig->ScsiPortCount--;
538     }
539 
540     // disconnect the interrupts
541     while (DeviceExtension->InterruptCount)
542     {
543         if (DeviceExtension->Interrupt[--DeviceExtension->InterruptCount])
544             IoDisconnectInterrupt(DeviceExtension->Interrupt[DeviceExtension->InterruptCount]);
545     }
546 
547     // FIXME: delete LUNs
548     if (DeviceExtension->Buses)
549     {
550         for (UINT8 pathId = 0; pathId < DeviceExtension->NumberOfBuses; pathId++)
551         {
552             PSCSI_BUS_INFO bus = &DeviceExtension->Buses[pathId];
553             if (bus->RegistryMapKey)
554             {
555                 ZwDeleteKey(bus->RegistryMapKey);
556                 ZwClose(bus->RegistryMapKey);
557                 bus->RegistryMapKey = NULL;
558             }
559         }
560 
561         ExFreePoolWithTag(DeviceExtension->Buses, TAG_SCSIPORT);
562     }
563 
564     /* Free PortConfig */
565     if (DeviceExtension->PortConfig)
566     {
567         ExFreePoolWithTag(DeviceExtension->PortConfig, TAG_SCSIPORT);
568     }
569 
570     /* Free common buffer (if it exists) */
571     if (DeviceExtension->SrbExtensionBuffer != NULL && DeviceExtension->CommonBufferLength != 0)
572     {
573         if (!DeviceExtension->AdapterObject)
574         {
575             ExFreePoolWithTag(DeviceExtension->SrbExtensionBuffer, TAG_SCSIPORT);
576         }
577         else
578         {
579             HalFreeCommonBuffer(DeviceExtension->AdapterObject,
580                                 DeviceExtension->CommonBufferLength,
581                                 DeviceExtension->PhysicalAddress,
582                                 DeviceExtension->SrbExtensionBuffer,
583                                 FALSE);
584         }
585     }
586 
587     /* Free SRB info */
588     if (DeviceExtension->SrbInfo != NULL)
589         ExFreePoolWithTag(DeviceExtension->SrbInfo, TAG_SCSIPORT);
590 
591     /* Unmap mapped addresses */
592     while (DeviceExtension->MappedAddressList != NULL)
593     {
594         MmUnmapIoSpace(DeviceExtension->MappedAddressList->MappedAddress,
595                        DeviceExtension->MappedAddressList->NumberOfBytes);
596 
597         PVOID ptr = DeviceExtension->MappedAddressList;
598         DeviceExtension->MappedAddressList = DeviceExtension->MappedAddressList->NextMappedAddress;
599 
600         ExFreePoolWithTag(ptr, TAG_SCSIPORT);
601     }
602 
603     IoDeleteDevice(DeviceExtension->Common.DeviceObject);
604 
605     return STATUS_SUCCESS;
606 }
607 
608 NTSTATUS
FdoStartAdapter(_In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension)609 FdoStartAdapter(
610     _In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension)
611 {
612     WCHAR dosNameBuffer[12];
613     UNICODE_STRING dosDeviceName;
614     NTSTATUS status;
615 
616     // Start our timer
617     IoStartTimer(PortExtension->Common.DeviceObject);
618 
619     // Create the dos device link
620     swprintf(dosNameBuffer, L"\\??\\Scsi%u:", PortExtension->PortNumber);
621     RtlInitUnicodeString(&dosDeviceName, dosNameBuffer);
622     status = IoCreateSymbolicLink(&dosDeviceName, &PortExtension->DeviceName);
623     if (!NT_SUCCESS(status))
624     {
625         return status;
626     }
627 
628     // start building a device map
629     RegistryInitAdapterKey(PortExtension);
630 
631     // increase the port count
632     PCONFIGURATION_INFORMATION sysConfig = IoGetConfigurationInformation();
633     sysConfig->ScsiPortCount++;
634 
635     // Register and enable the device interface
636     status = IoRegisterDeviceInterface(PortExtension->Common.DeviceObject,
637                                        &StoragePortClassGuid,
638                                        NULL,
639                                        &PortExtension->InterfaceName);
640     DPRINT("IoRegisterDeviceInterface status: %x, InterfaceName: %wZ\n",
641         status, &PortExtension->InterfaceName);
642 
643     if (NT_SUCCESS(status))
644     {
645         IoSetDeviceInterfaceState(&PortExtension->InterfaceName, TRUE);
646     }
647 
648     PortExtension->DeviceStarted = TRUE;
649 
650     return STATUS_SUCCESS;
651 }
652 
653 static
654 NTSTATUS
FdoHandleDeviceRelations(_In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension,_Inout_ PIRP Irp)655 FdoHandleDeviceRelations(
656     _In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension,
657     _Inout_ PIRP Irp)
658 {
659     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
660 
661     // FDO always only handles bus relations
662     if (ioStack->Parameters.QueryDeviceRelations.Type == BusRelations)
663     {
664         FdoScanAdapter(PortExtension);
665         DPRINT("Found %u PD objects, FDOExt: %p\n", PortExtension->TotalLUCount, PortExtension);
666 
667         // check that no filter driver has messed up this
668         ASSERT(Irp->IoStatus.Information == 0);
669 
670         PDEVICE_RELATIONS deviceRelations =
671             ExAllocatePoolWithTag(PagedPool,
672                                   (sizeof(DEVICE_RELATIONS) +
673                                    sizeof(PDEVICE_OBJECT) * (PortExtension->TotalLUCount - 1)),
674                                   TAG_SCSIPORT);
675 
676         if (!deviceRelations)
677         {
678             Irp->IoStatus.Information = 0;
679             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
680             IoCompleteRequest(Irp, IO_NO_INCREMENT);
681             return STATUS_INSUFFICIENT_RESOURCES;
682         }
683 
684         deviceRelations->Count = 0;
685 
686         for (UINT8 pathId = 0; pathId < PortExtension->NumberOfBuses; pathId++)
687         {
688             PSCSI_BUS_INFO bus = &PortExtension->Buses[pathId];
689 
690             for (PLIST_ENTRY lunEntry = bus->LunsListHead.Flink;
691                  lunEntry != &bus->LunsListHead;
692                  lunEntry = lunEntry->Flink)
693             {
694                 PSCSI_PORT_LUN_EXTENSION lunExt =
695                     CONTAINING_RECORD(lunEntry, SCSI_PORT_LUN_EXTENSION, LunEntry);
696 
697                 deviceRelations->Objects[deviceRelations->Count++] = lunExt->Common.DeviceObject;
698                 ObReferenceObject(lunExt->Common.DeviceObject);
699             }
700         }
701 
702         ASSERT(deviceRelations->Count == PortExtension->TotalLUCount);
703 
704         Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
705         Irp->IoStatus.Status = STATUS_SUCCESS;
706     }
707 
708     IoSkipCurrentIrpStackLocation(Irp);
709     return IoCallDriver(PortExtension->Common.LowerDevice, Irp);
710 }
711 
712 static
713 NTSTATUS
FdoHandleQueryCompatibleId(_Inout_ PZZWSTR * PwIds)714 FdoHandleQueryCompatibleId(
715     _Inout_ PZZWSTR* PwIds)
716 {
717     static WCHAR GenScsiAdapterId[] = L"GEN_SCSIADAPTER";
718     PWCHAR Ids = *PwIds, NewIds;
719     ULONG Length = 0;
720 
721     if (Ids)
722     {
723         /* Calculate the length of existing MULTI_SZ value line by line */
724         while (*Ids)
725         {
726             Ids += wcslen(Ids) + 1;
727         }
728         Length = Ids - *PwIds;
729         Ids = *PwIds;
730     }
731 
732     /* New MULTI_SZ with added identifier and finalizing zeros */
733     NewIds = ExAllocatePoolZero(PagedPool,
734                                 Length * sizeof(WCHAR) + sizeof(GenScsiAdapterId) + sizeof(UNICODE_NULL),
735                                 TAG_SCSIPORT);
736     if (!NewIds)
737     {
738         return STATUS_INSUFFICIENT_RESOURCES;
739     }
740 
741     if (Length)
742     {
743         RtlCopyMemory(NewIds, Ids, Length * sizeof(WCHAR));
744     }
745     RtlCopyMemory(&NewIds[Length], GenScsiAdapterId, sizeof(GenScsiAdapterId));
746 
747     /* Finally replace identifiers */
748     if (Ids)
749     {
750         ExFreePool(Ids);
751     }
752     *PwIds = NewIds;
753 
754     return STATUS_SUCCESS;
755 }
756 
757 NTSTATUS
FdoDispatchPnp(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp)758 FdoDispatchPnp(
759     _In_ PDEVICE_OBJECT DeviceObject,
760     _Inout_ PIRP Irp)
761 {
762     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
763     PSCSI_PORT_DEVICE_EXTENSION portExt = DeviceObject->DeviceExtension;
764     NTSTATUS status;
765 
766     ASSERT(portExt->Common.IsFDO);
767 
768     DPRINT("FDO PnP request %s\n", GetIRPMinorFunctionString(ioStack->MinorFunction));
769 
770     switch (ioStack->MinorFunction)
771     {
772         case IRP_MN_START_DEVICE:
773         {
774             // as we don't support PnP yet, this is a no-op for us
775             // (FdoStartAdapter is being called during initialization for legacy miniports)
776             status = STATUS_SUCCESS;
777             // status = FdoStartAdapter(DeviceExtension);
778             break;
779         }
780         case IRP_MN_QUERY_DEVICE_RELATIONS:
781         {
782             return FdoHandleDeviceRelations(portExt, Irp);
783         }
784         case IRP_MN_QUERY_ID:
785         {
786             if (ioStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs)
787             {
788                 Irp->IoStatus.Information = 0;
789                 IoForwardIrpSynchronously(portExt->Common.LowerDevice, Irp);
790                 status = FdoHandleQueryCompatibleId((PZZWSTR*)&Irp->IoStatus.Information);
791                 break;
792             }
793             // otherwise fall through the default case
794         }
795         default:
796         {
797             // forward irp to next device object
798             IoCopyCurrentIrpStackLocationToNext(Irp);
799             return IoCallDriver(portExt->Common.LowerDevice, Irp);
800         }
801     }
802 
803     if (status != STATUS_PENDING)
804     {
805         Irp->IoStatus.Status = status;
806         IoCompleteRequest(Irp, IO_NO_INCREMENT);
807     }
808 
809     return status;
810 }
811