xref: /reactos/drivers/storage/port/scsiport/fdo.c (revision 53221834)
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
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
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                 // try to find an existing device
255                 PSCSI_PORT_LUN_EXTENSION lunExt = GetLunByPath(PortExtension,
256                                                                pathId,
257                                                                targetId,
258                                                                lun);
259 
260                 if (lunExt)
261                 {
262                     // check if the device still exists
263                     status = FdoSendInquiry(lunExt->Common.DeviceObject);
264                     if (!NT_SUCCESS(status))
265                     {
266                         // remove the device
267                         UNIMPLEMENTED;
268                         __debugbreak();
269                     }
270 
271                     if (lunExt->InquiryData.DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED)
272                     {
273                         // remove the device
274                         UNIMPLEMENTED;
275                         __debugbreak();
276                     }
277 
278                     /* Decide whether we are continuing or not */
279                     if (status == STATUS_INVALID_DEVICE_REQUEST)
280                         continue;
281                     else
282                         break;
283                 }
284 
285                 // create a new LUN device
286                 PDEVICE_OBJECT lunPDO = PdoCreateLunDevice(PortExtension);
287                 if (!lunPDO)
288                 {
289                     continue;
290                 }
291 
292                 lunExt = lunPDO->DeviceExtension;
293 
294                 lunExt->PathId = pathId;
295                 lunExt->TargetId = targetId;
296                 lunExt->Lun = lun;
297 
298                 DPRINT("Add PDO to list: PDO: %p, FDOExt: %p, PDOExt: %p\n", lunPDO, PortExtension, lunExt);
299 
300                 /* Set flag to prevent race conditions */
301                 lunExt->Flags |= SCSI_PORT_SCAN_IN_PROGRESS;
302 
303                 /* Finally send the inquiry command */
304                 status = FdoSendInquiry(lunPDO);
305 
306                 if (NT_SUCCESS(status))
307                 {
308                     /* Let's see if we really found a device */
309                     PINQUIRYDATA InquiryData = &lunExt->InquiryData;
310 
311                     /* Check if this device is unsupported */
312                     if (InquiryData->DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED)
313                     {
314                         IoDeleteDevice(lunPDO);
315                         continue;
316                     }
317 
318                     /* Clear the "in scan" flag */
319                     lunExt->Flags &= ~SCSI_PORT_SCAN_IN_PROGRESS;
320 
321                     DPRINT("FdoScanAdapter(): Found device of type %d at bus %d tid %d lun %d\n",
322                         InquiryData->DeviceType, pathId, targetId, lun);
323 
324                     InsertTailList(&currentBus->LunsListHead, &lunExt->LunEntry);
325 
326                     DPRINT1("SCSIPORT: created lun device: %p Status: %x\n", lunPDO, status);
327 
328                     totalLUNs++;
329                     currentBus->LogicalUnitsCount++;
330                     targetFound = TRUE;
331                 }
332                 else
333                 {
334                     /* Decide whether we are continuing or not */
335                     if (status == STATUS_INVALID_DEVICE_REQUEST)
336                         continue;
337                     else
338                         break;
339                 }
340             }
341 
342             if (targetFound)
343             {
344                 currentBus->TargetsCount++;
345             }
346         }
347     }
348 
349     PortExtension->TotalLUCount = totalLUNs;
350 }
351 
352 /**
353  * @brief      Calls HwInitialize routine of the miniport and sets up interrupts
354  *             Should be called inside ScsiPortInitialize (for legacy drivers)
355  *             or inside IRP_MN_START_DEVICE for pnp drivers
356  *
357  * @param[in]  DeviceExtension  The device extension
358  *
359  * @return     NTSTATUS of the operation
360  */
361 NTSTATUS
362 FdoCallHWInitialize(
363     _In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
364 {
365     PPORT_CONFIGURATION_INFORMATION PortConfig = DeviceExtension->PortConfig;
366     NTSTATUS Status;
367     KIRQL OldIrql;
368 
369     /* Deal with interrupts */
370     if (DeviceExtension->HwInterrupt == NULL ||
371         (PortConfig->BusInterruptLevel == 0 && PortConfig->BusInterruptVector == 0))
372     {
373         /* No interrupts */
374         DeviceExtension->InterruptCount = 0;
375 
376         DPRINT1("Interrupt Count: 0\n");
377 
378         UNIMPLEMENTED;
379 
380         /* This code path will ALWAYS crash so stop it now */
381         __debugbreak();
382     }
383     else
384     {
385         BOOLEAN InterruptShareable;
386         KINTERRUPT_MODE InterruptMode[2];
387         ULONG InterruptVector[2], i, MappedIrq[2];
388         KIRQL Dirql[2], MaxDirql;
389         KAFFINITY Affinity[2];
390 
391         DeviceExtension->InterruptLevel[0] = PortConfig->BusInterruptLevel;
392         DeviceExtension->InterruptLevel[1] = PortConfig->BusInterruptLevel2;
393 
394         InterruptVector[0] = PortConfig->BusInterruptVector;
395         InterruptVector[1] = PortConfig->BusInterruptVector2;
396 
397         InterruptMode[0] = PortConfig->InterruptMode;
398         InterruptMode[1] = PortConfig->InterruptMode2;
399 
400         DeviceExtension->InterruptCount =
401             (PortConfig->BusInterruptLevel2 != 0 ||
402              PortConfig->BusInterruptVector2 != 0) ? 2 : 1;
403 
404         for (i = 0; i < DeviceExtension->InterruptCount; i++)
405         {
406             /* Register an interrupt handler for this device */
407             MappedIrq[i] = HalGetInterruptVector(
408                 PortConfig->AdapterInterfaceType, PortConfig->SystemIoBusNumber,
409                 DeviceExtension->InterruptLevel[i], InterruptVector[i], &Dirql[i],
410                 &Affinity[i]);
411         }
412 
413         if (DeviceExtension->InterruptCount == 1 || Dirql[0] > Dirql[1])
414         {
415             MaxDirql = Dirql[0];
416         }
417         else
418         {
419             MaxDirql = Dirql[1];
420         }
421 
422         for (i = 0; i < DeviceExtension->InterruptCount; i++)
423         {
424             /* Determine IRQ sharability as usual */
425             if (PortConfig->AdapterInterfaceType == MicroChannel ||
426                 InterruptMode[i] == LevelSensitive)
427             {
428                 InterruptShareable = TRUE;
429             }
430             else
431             {
432                 InterruptShareable = FALSE;
433             }
434 
435             Status = IoConnectInterrupt(&DeviceExtension->Interrupt[i],
436                                         ScsiPortIsr,
437                                         DeviceExtension,
438                                         &DeviceExtension->IrqLock,
439                                         MappedIrq[i], Dirql[i],
440                                         MaxDirql,
441                                         InterruptMode[i],
442                                         InterruptShareable,
443                                         Affinity[i],
444                                         FALSE);
445 
446             if (!(NT_SUCCESS(Status)))
447             {
448                 DPRINT1("Could not connect interrupt %d\n", InterruptVector[i]);
449                 DeviceExtension->Interrupt[i] = NULL;
450                 return Status;
451             }
452         }
453     }
454 
455     /* Save IoAddress (from access ranges) */
456     if (PortConfig->NumberOfAccessRanges != 0)
457     {
458         DeviceExtension->IoAddress = ((*(PortConfig->AccessRanges))[0]).RangeStart.LowPart;
459 
460         DPRINT("Io Address %x\n", DeviceExtension->IoAddress);
461     }
462 
463     /* Set flag that it's allowed to disconnect during this command */
464     DeviceExtension->Flags |= SCSI_PORT_DISCONNECT_ALLOWED;
465 
466     /* Initialize counter of active requests (-1 means there are none) */
467     DeviceExtension->ActiveRequestCounter = -1;
468 
469     /* Analyze what we have about DMA */
470     if (DeviceExtension->AdapterObject != NULL && PortConfig->Master &&
471         PortConfig->NeedPhysicalAddresses)
472     {
473         DeviceExtension->MapRegisters = TRUE;
474     }
475     else
476     {
477         DeviceExtension->MapRegisters = FALSE;
478     }
479 
480     /* Call HwInitialize at DISPATCH_LEVEL */
481     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
482 
483     if (!KeSynchronizeExecution(
484             DeviceExtension->Interrupt[0], DeviceExtension->HwInitialize,
485             DeviceExtension->MiniPortDeviceExtension))
486     {
487         DPRINT1("HwInitialize() failed!\n");
488         KeLowerIrql(OldIrql);
489         return STATUS_ADAPTER_HARDWARE_ERROR;
490     }
491 
492     /* Check if a notification is needed */
493     if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
494     {
495         /* Call DPC right away, because we're already at DISPATCH_LEVEL */
496         ScsiPortDpcForIsr(NULL, DeviceExtension->Common.DeviceObject, NULL, NULL);
497     }
498 
499     /* Lower irql back to what it was */
500     KeLowerIrql(OldIrql);
501 
502     return STATUS_SUCCESS;
503 }
504 
505 NTSTATUS
506 FdoRemoveAdapter(
507     _In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
508 {
509     IoStopTimer(DeviceExtension->Common.DeviceObject);
510 
511     // release device interface
512     if (DeviceExtension->InterfaceName.Buffer)
513     {
514         IoSetDeviceInterfaceState(&DeviceExtension->InterfaceName, FALSE);
515 
516         RtlFreeUnicodeString(&DeviceExtension->InterfaceName);
517         RtlInitUnicodeString(&DeviceExtension->InterfaceName, NULL);
518     }
519 
520     // remove the dos device link
521     WCHAR dosNameBuffer[12];
522     UNICODE_STRING dosDeviceName;
523 
524     swprintf(dosNameBuffer, L"\\??\\Scsi%lu:", DeviceExtension->PortNumber);
525     RtlInitUnicodeString(&dosDeviceName, dosNameBuffer);
526 
527     IoDeleteSymbolicLink(&dosDeviceName); // don't check the result
528 
529     // decrease the port count
530     if (DeviceExtension->DeviceStarted)
531     {
532         PCONFIGURATION_INFORMATION sysConfig = IoGetConfigurationInformation();
533         sysConfig->ScsiPortCount--;
534     }
535 
536     // disconnect the interrupts
537     while (DeviceExtension->InterruptCount)
538     {
539         if (DeviceExtension->Interrupt[--DeviceExtension->InterruptCount])
540             IoDisconnectInterrupt(DeviceExtension->Interrupt[DeviceExtension->InterruptCount]);
541     }
542 
543     // FIXME: delete LUNs
544     if (DeviceExtension->Buses)
545     {
546         for (UINT8 pathId = 0; pathId < DeviceExtension->NumberOfBuses; pathId++)
547         {
548             PSCSI_BUS_INFO bus = &DeviceExtension->Buses[pathId];
549             if (bus->RegistryMapKey)
550             {
551                 ZwDeleteKey(bus->RegistryMapKey);
552                 ZwClose(bus->RegistryMapKey);
553                 bus->RegistryMapKey = NULL;
554             }
555         }
556 
557         ExFreePoolWithTag(DeviceExtension->Buses, TAG_SCSIPORT);
558     }
559 
560     /* Free PortConfig */
561     if (DeviceExtension->PortConfig)
562     {
563         ExFreePoolWithTag(DeviceExtension->PortConfig, TAG_SCSIPORT);
564     }
565 
566     /* Free common buffer (if it exists) */
567     if (DeviceExtension->SrbExtensionBuffer != NULL && DeviceExtension->CommonBufferLength != 0)
568     {
569         if (!DeviceExtension->AdapterObject)
570         {
571             ExFreePoolWithTag(DeviceExtension->SrbExtensionBuffer, TAG_SCSIPORT);
572         }
573         else
574         {
575             HalFreeCommonBuffer(DeviceExtension->AdapterObject,
576                                 DeviceExtension->CommonBufferLength,
577                                 DeviceExtension->PhysicalAddress,
578                                 DeviceExtension->SrbExtensionBuffer,
579                                 FALSE);
580         }
581     }
582 
583     /* Free SRB info */
584     if (DeviceExtension->SrbInfo != NULL)
585         ExFreePoolWithTag(DeviceExtension->SrbInfo, TAG_SCSIPORT);
586 
587     /* Unmap mapped addresses */
588     while (DeviceExtension->MappedAddressList != NULL)
589     {
590         MmUnmapIoSpace(DeviceExtension->MappedAddressList->MappedAddress,
591                        DeviceExtension->MappedAddressList->NumberOfBytes);
592 
593         PVOID ptr = DeviceExtension->MappedAddressList;
594         DeviceExtension->MappedAddressList = DeviceExtension->MappedAddressList->NextMappedAddress;
595 
596         ExFreePoolWithTag(ptr, TAG_SCSIPORT);
597     }
598 
599     IoDeleteDevice(DeviceExtension->Common.DeviceObject);
600 
601     return STATUS_SUCCESS;
602 }
603 
604 NTSTATUS
605 FdoStartAdapter(
606     _In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension)
607 {
608     WCHAR dosNameBuffer[12];
609     UNICODE_STRING dosDeviceName;
610     NTSTATUS status;
611 
612     // Start our timer
613     IoStartTimer(PortExtension->Common.DeviceObject);
614 
615     // Create the dos device link
616     swprintf(dosNameBuffer, L"\\??\\Scsi%u:", PortExtension->PortNumber);
617     RtlInitUnicodeString(&dosDeviceName, dosNameBuffer);
618     status = IoCreateSymbolicLink(&dosDeviceName, &PortExtension->DeviceName);
619     if (!NT_SUCCESS(status))
620     {
621         return status;
622     }
623 
624     // start building a device map
625     RegistryInitAdapterKey(PortExtension);
626 
627     // increase the port count
628     PCONFIGURATION_INFORMATION sysConfig = IoGetConfigurationInformation();
629     sysConfig->ScsiPortCount++;
630 
631     // Register and enable the device interface
632     status = IoRegisterDeviceInterface(PortExtension->Common.DeviceObject,
633                                        &StoragePortClassGuid,
634                                        NULL,
635                                        &PortExtension->InterfaceName);
636     DPRINT("IoRegisterDeviceInterface status: %x, InterfaceName: %wZ\n",
637         status, &PortExtension->InterfaceName);
638 
639     if (NT_SUCCESS(status))
640     {
641         IoSetDeviceInterfaceState(&PortExtension->InterfaceName, TRUE);
642     }
643 
644     PortExtension->DeviceStarted = TRUE;
645 
646     return STATUS_SUCCESS;
647 }
648 
649 static
650 NTSTATUS
651 FdoHandleDeviceRelations(
652     _In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension,
653     _Inout_ PIRP Irp)
654 {
655     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
656 
657     // FDO always only handles bus relations
658     if (ioStack->Parameters.QueryDeviceRelations.Type == BusRelations)
659     {
660         FdoScanAdapter(PortExtension);
661         DPRINT("Found %u PD objects, FDOExt: %p\n", PortExtension->TotalLUCount, PortExtension);
662 
663         // check that no filter driver has messed up this
664         ASSERT(Irp->IoStatus.Information == 0);
665 
666         PDEVICE_RELATIONS deviceRelations =
667             ExAllocatePoolWithTag(PagedPool,
668                                   (sizeof(DEVICE_RELATIONS) +
669                                    sizeof(PDEVICE_OBJECT) * (PortExtension->TotalLUCount - 1)),
670                                   TAG_SCSIPORT);
671 
672         if (!deviceRelations)
673         {
674             Irp->IoStatus.Information = 0;
675             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
676             IoCompleteRequest(Irp, IO_NO_INCREMENT);
677             return STATUS_INSUFFICIENT_RESOURCES;
678         }
679 
680         deviceRelations->Count = 0;
681 
682         for (UINT8 pathId = 0; pathId < PortExtension->NumberOfBuses; pathId++)
683         {
684             PSCSI_BUS_INFO bus = &PortExtension->Buses[pathId];
685 
686             for (PLIST_ENTRY lunEntry = bus->LunsListHead.Flink;
687                  lunEntry != &bus->LunsListHead;
688                  lunEntry = lunEntry->Flink)
689             {
690                 PSCSI_PORT_LUN_EXTENSION lunExt =
691                     CONTAINING_RECORD(lunEntry, SCSI_PORT_LUN_EXTENSION, LunEntry);
692 
693                 deviceRelations->Objects[deviceRelations->Count++] = lunExt->Common.DeviceObject;
694                 ObReferenceObject(lunExt->Common.DeviceObject);
695             }
696         }
697 
698         ASSERT(deviceRelations->Count == PortExtension->TotalLUCount);
699 
700         Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
701         Irp->IoStatus.Status = STATUS_SUCCESS;
702     }
703 
704     IoSkipCurrentIrpStackLocation(Irp);
705     return IoCallDriver(PortExtension->Common.LowerDevice, Irp);
706 }
707 
708 static
709 NTSTATUS
710 FdoHandleQueryCompatibleId(
711     _Inout_ PZZWSTR* PwIds)
712 {
713     static WCHAR GenScsiAdapterId[] = L"GEN_SCSIADAPTER";
714     PWCHAR Ids = *PwIds, NewIds;
715     ULONG Length = 0;
716 
717     if (Ids)
718     {
719         /* Calculate the length of existing MULTI_SZ value line by line */
720         while (*Ids)
721         {
722             Ids += wcslen(Ids) + 1;
723         }
724         Length = Ids - *PwIds;
725         Ids = *PwIds;
726     }
727 
728     /* New MULTI_SZ with added identifier and finalizing zeros */
729     NewIds = ExAllocatePoolZero(PagedPool,
730                                 Length * sizeof(WCHAR) + sizeof(GenScsiAdapterId) + sizeof(UNICODE_NULL),
731                                 TAG_SCSIPORT);
732     if (!NewIds)
733     {
734         return STATUS_INSUFFICIENT_RESOURCES;
735     }
736 
737     if (Length)
738     {
739         RtlCopyMemory(NewIds, Ids, Length * sizeof(WCHAR));
740     }
741     RtlCopyMemory(&NewIds[Length], GenScsiAdapterId, sizeof(GenScsiAdapterId));
742 
743     /* Finally replace identifiers */
744     if (Ids)
745     {
746         ExFreePool(Ids);
747     }
748     *PwIds = NewIds;
749 
750     return STATUS_SUCCESS;
751 }
752 
753 NTSTATUS
754 FdoDispatchPnp(
755     _In_ PDEVICE_OBJECT DeviceObject,
756     _Inout_ PIRP Irp)
757 {
758     PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
759     PSCSI_PORT_DEVICE_EXTENSION portExt = DeviceObject->DeviceExtension;
760     NTSTATUS status;
761 
762     ASSERT(portExt->Common.IsFDO);
763 
764     DPRINT("FDO PnP request %s\n", GetIRPMinorFunctionString(ioStack->MinorFunction));
765 
766     switch (ioStack->MinorFunction)
767     {
768         case IRP_MN_START_DEVICE:
769         {
770             // as we don't support PnP yet, this is a no-op for us
771             // (FdoStartAdapter is being called during initialization for legacy miniports)
772             status = STATUS_SUCCESS;
773             // status = FdoStartAdapter(DeviceExtension);
774             break;
775         }
776         case IRP_MN_QUERY_DEVICE_RELATIONS:
777         {
778             return FdoHandleDeviceRelations(portExt, Irp);
779         }
780         case IRP_MN_QUERY_ID:
781         {
782             if (ioStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs)
783             {
784                 Irp->IoStatus.Information = 0;
785                 IoForwardIrpSynchronously(portExt->Common.LowerDevice, Irp);
786                 status = FdoHandleQueryCompatibleId((PZZWSTR*)&Irp->IoStatus.Information);
787                 break;
788             }
789             // otherwise fall through the default case
790         }
791         default:
792         {
793             // forward irp to next device object
794             IoCopyCurrentIrpStackLocationToNext(Irp);
795             return IoCallDriver(portExt->Common.LowerDevice, Irp);
796         }
797     }
798 
799     if (status != STATUS_PENDING)
800     {
801         Irp->IoStatus.Status = status;
802         IoCompleteRequest(Irp, IO_NO_INCREMENT);
803     }
804 
805     return status;
806 }
807