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(¤tBus->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