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 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 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 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 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 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 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 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