reactos/drivers/storage/port/scsiport/fdo.c
Jérôme Gardou 58ad5d9e02 [SCSIPORT] Fix locking the device extension.
From MSDN:
It is an error to call KeReleaseSpinLockFromDpcLevel if the specified spin lock was acquired by calling KeAcquireSpinLock because the caller's original IRQL is not restored, which can cause deadlocks or fatal page faults.
2020-12-09 12:24:23 +01:00

751 lines
24 KiB
C

/*
* PROJECT: ReactOS Storage Stack / SCSIPORT storage port library
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Adapter device object (FDO) support routines
* COPYRIGHT: Eric Kohl (eric.kohl@reactos.org)
* Aleksey Bragin (aleksey@reactos.org)
* 2020 Victor Perevertkin (victor.perevertkin@reactos.org)
*/
#include "scsiport.h"
#define NDEBUG
#include <debug.h>
static
NTSTATUS
FdoSendInquiry(
_In_ PDEVICE_OBJECT DeviceObject)
{
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION IrpStack;
KEVENT Event;
KIRQL Irql;
PIRP Irp;
NTSTATUS Status;
PINQUIRYDATA InquiryBuffer;
PSENSE_DATA SenseBuffer;
BOOLEAN KeepTrying = TRUE;
ULONG RetryCount = 0;
SCSI_REQUEST_BLOCK Srb;
PCDB Cdb;
DPRINT("FdoSendInquiry() called\n");
PSCSI_PORT_LUN_EXTENSION LunExtension = DeviceObject->DeviceExtension;
PSCSI_PORT_DEVICE_EXTENSION DeviceExtension =
LunExtension->Common.LowerDevice->DeviceExtension;
InquiryBuffer = ExAllocatePoolWithTag(NonPagedPool, INQUIRYDATABUFFERSIZE, TAG_SCSIPORT);
if (InquiryBuffer == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
SenseBuffer = ExAllocatePoolWithTag(NonPagedPool, SENSE_BUFFER_SIZE, TAG_SCSIPORT);
if (SenseBuffer == NULL)
{
ExFreePoolWithTag(InquiryBuffer, TAG_SCSIPORT);
return STATUS_INSUFFICIENT_RESOURCES;
}
while (KeepTrying)
{
/* Initialize event for waiting */
KeInitializeEvent(&Event,
NotificationEvent,
FALSE);
/* Create an IRP */
Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_IN,
DeviceObject,
NULL,
0,
InquiryBuffer,
INQUIRYDATABUFFERSIZE,
TRUE,
&Event,
&IoStatusBlock);
if (Irp == NULL)
{
DPRINT("IoBuildDeviceIoControlRequest() failed\n");
/* Quit the loop */
Status = STATUS_INSUFFICIENT_RESOURCES;
KeepTrying = FALSE;
continue;
}
/* Prepare SRB */
RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
Srb.OriginalRequest = Irp;
Srb.PathId = LunExtension->PathId;
Srb.TargetId = LunExtension->TargetId;
Srb.Lun = LunExtension->Lun;
Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
Srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
Srb.TimeOutValue = 4;
Srb.CdbLength = 6;
Srb.SenseInfoBuffer = SenseBuffer;
Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
Srb.DataBuffer = InquiryBuffer;
Srb.DataTransferLength = INQUIRYDATABUFFERSIZE;
/* Attach Srb to the Irp */
IrpStack = IoGetNextIrpStackLocation (Irp);
IrpStack->Parameters.Scsi.Srb = &Srb;
/* Fill in CDB */
Cdb = (PCDB)Srb.Cdb;
Cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
Cdb->CDB6INQUIRY.LogicalUnitNumber = LunExtension->Lun;
Cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
/* Call the driver */
Status = IoCallDriver(DeviceObject, Irp);
/* Wait for it to complete */
if (Status == STATUS_PENDING)
{
DPRINT("FdoSendInquiry(): Waiting for the driver to process request...\n");
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
DPRINT("FdoSendInquiry(): Request processed by driver, status = 0x%08X\n", Status);
if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_SUCCESS)
{
/* All fine, copy data over */
RtlCopyMemory(&LunExtension->InquiryData,
InquiryBuffer,
INQUIRYDATABUFFERSIZE);
/* Quit the loop */
Status = STATUS_SUCCESS;
KeepTrying = FALSE;
continue;
}
DPRINT("Inquiry SRB failed with SrbStatus 0x%08X\n", Srb.SrbStatus);
/* Check if the queue is frozen */
if (Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
{
/* Something weird happened, deal with it (unfreeze the queue) */
KeepTrying = FALSE;
DPRINT("FdoSendInquiry(): the queue is frozen at TargetId %d\n", Srb.TargetId);
/* Clear frozen flag */
LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE;
/* Acquire the spinlock */
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
/* Process the request. SpiGetNextRequestFromLun will unlock for us */
SpiGetNextRequestFromLun(DeviceExtension, LunExtension, &Irql);
}
/* Check if data overrun happened */
if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN)
{
DPRINT("Data overrun at TargetId %d\n", LunExtension->TargetId);
/* Nothing dramatic, just copy data, but limiting the size */
RtlCopyMemory(&LunExtension->InquiryData,
InquiryBuffer,
(Srb.DataTransferLength > INQUIRYDATABUFFERSIZE) ?
INQUIRYDATABUFFERSIZE : Srb.DataTransferLength);
/* Quit the loop */
Status = STATUS_SUCCESS;
KeepTrying = FALSE;
}
else if ((Srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
SenseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST)
{
/* LUN is not valid, but some device responds there.
Mark it as invalid anyway */
/* Quit the loop */
Status = STATUS_INVALID_DEVICE_REQUEST;
KeepTrying = FALSE;
}
else
{
/* Retry a couple of times if no timeout happened */
if ((RetryCount < 2) &&
(SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_NO_DEVICE) &&
(SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT))
{
RetryCount++;
KeepTrying = TRUE;
}
else
{
/* That's all, quit the loop */
KeepTrying = FALSE;
/* Set status according to SRB status */
if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_FUNCTION ||
SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_SRB_BLOCK_LENGTH)
{
Status = STATUS_INVALID_DEVICE_REQUEST;
}
else
{
Status = STATUS_IO_DEVICE_ERROR;
}
}
}
}
/* Free buffers */
ExFreePoolWithTag(InquiryBuffer, TAG_SCSIPORT);
ExFreePoolWithTag(SenseBuffer, TAG_SCSIPORT);
DPRINT("FdoSendInquiry() done with Status 0x%08X\n", Status);
return Status;
}
/* Scans all SCSI buses */
VOID
FdoScanAdapter(
_In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension)
{
NTSTATUS status;
UINT32 totalLUNs = PortExtension->TotalLUCount;
DPRINT("FdoScanAdapter() called\n");
/* Scan all buses */
for (UINT8 pathId = 0; pathId < PortExtension->NumberOfBuses; pathId++)
{
DPRINT(" Scanning bus/pathID %u\n", pathId);
/* Get pointer to the scan information */
PSCSI_BUS_INFO currentBus = &PortExtension->Buses[pathId];
/* And send INQUIRY to every target */
for (UINT8 targetId = 0;
targetId < PortExtension->PortConfig->MaximumNumberOfTargets;
targetId++)
{
BOOLEAN targetFound = FALSE;
/* TODO: Support scan bottom-up */
/* Skip if it's the same address */
if (targetId == currentBus->BusIdentifier)
continue;
/* Scan all logical units */
for (UINT8 lun = 0; lun < PortExtension->MaxLunCount; lun++)
{
// try to find an existing device
PSCSI_PORT_LUN_EXTENSION lunExt = GetLunByPath(PortExtension,
pathId,
targetId,
lun);
if (lunExt)
{
// check if the device still exists
status = FdoSendInquiry(lunExt->Common.DeviceObject);
if (!NT_SUCCESS(status))
{
// remove the device
UNIMPLEMENTED;
__debugbreak();
}
if (lunExt->InquiryData.DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED)
{
// remove the device
UNIMPLEMENTED;
__debugbreak();
}
/* Decide whether we are continuing or not */
if (status == STATUS_INVALID_DEVICE_REQUEST)
continue;
else
break;
}
// create a new LUN device
PDEVICE_OBJECT lunPDO = PdoCreateLunDevice(PortExtension);
if (!lunPDO)
{
continue;
}
lunExt = lunPDO->DeviceExtension;
lunExt->PathId = pathId;
lunExt->TargetId = targetId;
lunExt->Lun = lun;
DPRINT("Add PDO to list: PDO: %p, FDOExt: %p, PDOExt: %p\n", lunPDO, PortExtension, lunExt);
/* Set flag to prevent race conditions */
lunExt->Flags |= SCSI_PORT_SCAN_IN_PROGRESS;
/* Finally send the inquiry command */
status = FdoSendInquiry(lunPDO);
if (NT_SUCCESS(status))
{
/* Let's see if we really found a device */
PINQUIRYDATA InquiryData = &lunExt->InquiryData;
/* Check if this device is unsupported */
if (InquiryData->DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED)
{
IoDeleteDevice(lunPDO);
continue;
}
/* Clear the "in scan" flag */
lunExt->Flags &= ~SCSI_PORT_SCAN_IN_PROGRESS;
DPRINT("FdoScanAdapter(): Found device of type %d at bus %d tid %d lun %d\n",
InquiryData->DeviceType, pathId, targetId, lun);
InsertTailList(&currentBus->LunsListHead, &lunExt->LunEntry);
DPRINT1("SCSIPORT: created lun device: %p Status: %x\n", lunPDO, status);
totalLUNs++;
currentBus->LogicalUnitsCount++;
targetFound = TRUE;
}
else
{
/* Decide whether we are continuing or not */
if (status == STATUS_INVALID_DEVICE_REQUEST)
continue;
else
break;
}
}
if (targetFound)
{
currentBus->TargetsCount++;
}
}
}
PortExtension->TotalLUCount = totalLUNs;
}
/**
* @brief Calls HwInitialize routine of the miniport and sets up interrupts
* Should be called inside ScsiPortInitialize (for legacy drivers)
* or inside IRP_MN_START_DEVICE for pnp drivers
*
* @param[in] DeviceExtension The device extension
*
* @return NTSTATUS of the operation
*/
NTSTATUS
FdoCallHWInitialize(
_In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
{
PPORT_CONFIGURATION_INFORMATION PortConfig = DeviceExtension->PortConfig;
NTSTATUS Status;
KIRQL OldIrql;
/* Deal with interrupts */
if (DeviceExtension->HwInterrupt == NULL ||
(PortConfig->BusInterruptLevel == 0 && PortConfig->BusInterruptVector == 0))
{
/* No interrupts */
DeviceExtension->InterruptCount = 0;
DPRINT1("Interrupt Count: 0\n");
UNIMPLEMENTED;
/* This code path will ALWAYS crash so stop it now */
__debugbreak();
}
else
{
BOOLEAN InterruptShareable;
KINTERRUPT_MODE InterruptMode[2];
ULONG InterruptVector[2], i, MappedIrq[2];
KIRQL Dirql[2], MaxDirql;
KAFFINITY Affinity[2];
DeviceExtension->InterruptLevel[0] = PortConfig->BusInterruptLevel;
DeviceExtension->InterruptLevel[1] = PortConfig->BusInterruptLevel2;
InterruptVector[0] = PortConfig->BusInterruptVector;
InterruptVector[1] = PortConfig->BusInterruptVector2;
InterruptMode[0] = PortConfig->InterruptMode;
InterruptMode[1] = PortConfig->InterruptMode2;
DeviceExtension->InterruptCount =
(PortConfig->BusInterruptLevel2 != 0 ||
PortConfig->BusInterruptVector2 != 0) ? 2 : 1;
for (i = 0; i < DeviceExtension->InterruptCount; i++)
{
/* Register an interrupt handler for this device */
MappedIrq[i] = HalGetInterruptVector(
PortConfig->AdapterInterfaceType, PortConfig->SystemIoBusNumber,
DeviceExtension->InterruptLevel[i], InterruptVector[i], &Dirql[i],
&Affinity[i]);
}
if (DeviceExtension->InterruptCount == 1 || Dirql[0] > Dirql[1])
{
MaxDirql = Dirql[0];
}
else
{
MaxDirql = Dirql[1];
}
for (i = 0; i < DeviceExtension->InterruptCount; i++)
{
/* Determine IRQ sharability as usual */
if (PortConfig->AdapterInterfaceType == MicroChannel ||
InterruptMode[i] == LevelSensitive)
{
InterruptShareable = TRUE;
}
else
{
InterruptShareable = FALSE;
}
Status = IoConnectInterrupt(&DeviceExtension->Interrupt[i],
ScsiPortIsr,
DeviceExtension,
&DeviceExtension->IrqLock,
MappedIrq[i], Dirql[i],
MaxDirql,
InterruptMode[i],
InterruptShareable,
Affinity[i],
FALSE);
if (!(NT_SUCCESS(Status)))
{
DPRINT1("Could not connect interrupt %d\n", InterruptVector[i]);
DeviceExtension->Interrupt[i] = NULL;
return Status;
}
}
}
/* Save IoAddress (from access ranges) */
if (PortConfig->NumberOfAccessRanges != 0)
{
DeviceExtension->IoAddress = ((*(PortConfig->AccessRanges))[0]).RangeStart.LowPart;
DPRINT("Io Address %x\n", DeviceExtension->IoAddress);
}
/* Set flag that it's allowed to disconnect during this command */
DeviceExtension->Flags |= SCSI_PORT_DISCONNECT_ALLOWED;
/* Initialize counter of active requests (-1 means there are none) */
DeviceExtension->ActiveRequestCounter = -1;
/* Analyze what we have about DMA */
if (DeviceExtension->AdapterObject != NULL && PortConfig->Master &&
PortConfig->NeedPhysicalAddresses)
{
DeviceExtension->MapRegisters = TRUE;
}
else
{
DeviceExtension->MapRegisters = FALSE;
}
/* Call HwInitialize at DISPATCH_LEVEL */
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
if (!KeSynchronizeExecution(
DeviceExtension->Interrupt[0], DeviceExtension->HwInitialize,
DeviceExtension->MiniPortDeviceExtension))
{
DPRINT1("HwInitialize() failed!\n");
KeLowerIrql(OldIrql);
return STATUS_ADAPTER_HARDWARE_ERROR;
}
/* Check if a notification is needed */
if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
{
/* Call DPC right away, because we're already at DISPATCH_LEVEL */
ScsiPortDpcForIsr(NULL, DeviceExtension->Common.DeviceObject, NULL, NULL);
}
/* Lower irql back to what it was */
KeLowerIrql(OldIrql);
return STATUS_SUCCESS;
}
NTSTATUS
FdoRemoveAdapter(
_In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
{
IoStopTimer(DeviceExtension->Common.DeviceObject);
// release device interface
if (DeviceExtension->InterfaceName.Buffer)
{
IoSetDeviceInterfaceState(&DeviceExtension->InterfaceName, FALSE);
RtlFreeUnicodeString(&DeviceExtension->InterfaceName);
RtlInitUnicodeString(&DeviceExtension->InterfaceName, NULL);
}
// remove the dos device link
WCHAR dosNameBuffer[12];
UNICODE_STRING dosDeviceName;
swprintf(dosNameBuffer, L"\\??\\Scsi%lu:", DeviceExtension->PortNumber);
RtlInitUnicodeString(&dosDeviceName, dosNameBuffer);
IoDeleteSymbolicLink(&dosDeviceName); // don't check the result
// decrease the port count
if (DeviceExtension->DeviceStarted)
{
PCONFIGURATION_INFORMATION sysConfig = IoGetConfigurationInformation();
sysConfig->ScsiPortCount--;
}
// disconnect the interrupts
while (DeviceExtension->InterruptCount)
{
if (DeviceExtension->Interrupt[--DeviceExtension->InterruptCount])
IoDisconnectInterrupt(DeviceExtension->Interrupt[DeviceExtension->InterruptCount]);
}
// FIXME: delete LUNs
if (DeviceExtension->Buses)
{
for (UINT8 pathId = 0; pathId < DeviceExtension->NumberOfBuses; pathId++)
{
PSCSI_BUS_INFO bus = &DeviceExtension->Buses[pathId];
if (bus->RegistryMapKey)
{
ZwDeleteKey(bus->RegistryMapKey);
ZwClose(bus->RegistryMapKey);
bus->RegistryMapKey = NULL;
}
}
ExFreePoolWithTag(DeviceExtension->Buses, TAG_SCSIPORT);
}
/* Free PortConfig */
if (DeviceExtension->PortConfig)
{
ExFreePoolWithTag(DeviceExtension->PortConfig, TAG_SCSIPORT);
}
/* Free common buffer (if it exists) */
if (DeviceExtension->SrbExtensionBuffer != NULL && DeviceExtension->CommonBufferLength != 0)
{
if (!DeviceExtension->AdapterObject)
{
ExFreePoolWithTag(DeviceExtension->SrbExtensionBuffer, TAG_SCSIPORT);
}
else
{
HalFreeCommonBuffer(DeviceExtension->AdapterObject,
DeviceExtension->CommonBufferLength,
DeviceExtension->PhysicalAddress,
DeviceExtension->SrbExtensionBuffer,
FALSE);
}
}
/* Free SRB info */
if (DeviceExtension->SrbInfo != NULL)
ExFreePoolWithTag(DeviceExtension->SrbInfo, TAG_SCSIPORT);
/* Unmap mapped addresses */
while (DeviceExtension->MappedAddressList != NULL)
{
MmUnmapIoSpace(DeviceExtension->MappedAddressList->MappedAddress,
DeviceExtension->MappedAddressList->NumberOfBytes);
PVOID ptr = DeviceExtension->MappedAddressList;
DeviceExtension->MappedAddressList = DeviceExtension->MappedAddressList->NextMappedAddress;
ExFreePoolWithTag(ptr, TAG_SCSIPORT);
}
IoDeleteDevice(DeviceExtension->Common.DeviceObject);
return STATUS_SUCCESS;
}
NTSTATUS
FdoStartAdapter(
_In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension)
{
WCHAR dosNameBuffer[12];
UNICODE_STRING dosDeviceName;
NTSTATUS status;
// Start our timer
IoStartTimer(PortExtension->Common.DeviceObject);
// Create the dos device link
swprintf(dosNameBuffer, L"\\??\\Scsi%u:", PortExtension->PortNumber);
RtlInitUnicodeString(&dosDeviceName, dosNameBuffer);
status = IoCreateSymbolicLink(&dosDeviceName, &PortExtension->DeviceName);
if (!NT_SUCCESS(status))
{
return status;
}
// start building a device map
RegistryInitAdapterKey(PortExtension);
// increase the port count
PCONFIGURATION_INFORMATION sysConfig = IoGetConfigurationInformation();
sysConfig->ScsiPortCount++;
// Register and enable the device interface
status = IoRegisterDeviceInterface(PortExtension->Common.DeviceObject,
&StoragePortClassGuid,
NULL,
&PortExtension->InterfaceName);
DPRINT("IoRegisterDeviceInterface status: %x, InterfaceName: %wZ\n",
status, &PortExtension->InterfaceName);
if (NT_SUCCESS(status))
{
IoSetDeviceInterfaceState(&PortExtension->InterfaceName, TRUE);
}
PortExtension->DeviceStarted = TRUE;
return STATUS_SUCCESS;
}
static
NTSTATUS
FdoHandleDeviceRelations(
_In_ PSCSI_PORT_DEVICE_EXTENSION PortExtension,
_Inout_ PIRP Irp)
{
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
// FDO always only handles bus relations
if (ioStack->Parameters.QueryDeviceRelations.Type == BusRelations)
{
FdoScanAdapter(PortExtension);
DPRINT("Found %u PD objects, FDOExt: %p\n", PortExtension->TotalLUCount, PortExtension);
// check that no filter driver has messed up this
ASSERT(Irp->IoStatus.Information == 0);
PDEVICE_RELATIONS deviceRelations =
ExAllocatePoolWithTag(PagedPool,
(sizeof(DEVICE_RELATIONS) +
sizeof(PDEVICE_OBJECT) * (PortExtension->TotalLUCount - 1)),
TAG_SCSIPORT);
if (!deviceRelations)
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES;
}
deviceRelations->Count = 0;
for (UINT8 pathId = 0; pathId < PortExtension->NumberOfBuses; pathId++)
{
PSCSI_BUS_INFO bus = &PortExtension->Buses[pathId];
for (PLIST_ENTRY lunEntry = bus->LunsListHead.Flink;
lunEntry != &bus->LunsListHead;
lunEntry = lunEntry->Flink)
{
PSCSI_PORT_LUN_EXTENSION lunExt =
CONTAINING_RECORD(lunEntry, SCSI_PORT_LUN_EXTENSION, LunEntry);
deviceRelations->Objects[deviceRelations->Count++] = lunExt->Common.DeviceObject;
ObReferenceObject(lunExt->Common.DeviceObject);
}
}
ASSERT(deviceRelations->Count == PortExtension->TotalLUCount);
Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
Irp->IoStatus.Status = STATUS_SUCCESS;
}
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(PortExtension->Common.LowerDevice, Irp);
}
NTSTATUS
FdoDispatchPnp(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
PSCSI_PORT_DEVICE_EXTENSION portExt = DeviceObject->DeviceExtension;
NTSTATUS status;
ASSERT(portExt->Common.IsFDO);
DPRINT("FDO PnP request %s\n", GetIRPMinorFunctionString(ioStack->MinorFunction));
switch (ioStack->MinorFunction)
{
case IRP_MN_START_DEVICE:
{
// as we don't support PnP yet, this is a no-op for us
// (FdoStartAdapter is being called during initialization for legacy miniports)
status = STATUS_SUCCESS;
// status = FdoStartAdapter(DeviceExtension);
break;
}
case IRP_MN_QUERY_DEVICE_RELATIONS:
{
return FdoHandleDeviceRelations(portExt, Irp);
}
default:
{
// forward irp to next device object
IoCopyCurrentIrpStackLocationToNext(Irp);
return IoCallDriver(portExt->Common.LowerDevice, Irp);
}
}
if (status != STATUS_PENDING)
{
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return status;
}