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