/* * PROJECT: ReactOS Storport Driver * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Storport FDO code * COPYRIGHT: Copyright 2017 Eric Kohl (eric.kohl@reactos.org) */ /* INCLUDES *******************************************************************/ #include "precomp.h" #define NDEBUG #include /* FUNCTIONS ******************************************************************/ static BOOLEAN NTAPI PortFdoInterruptRoutine( _In_ PKINTERRUPT Interrupt, _In_ PVOID ServiceContext) { PFDO_DEVICE_EXTENSION DeviceExtension; DPRINT1("PortFdoInterruptRoutine(%p %p)\n", Interrupt, ServiceContext); DeviceExtension = (PFDO_DEVICE_EXTENSION)ServiceContext; return MiniportHwInterrupt(&DeviceExtension->Miniport); } static NTSTATUS PortFdoConnectInterrupt( _In_ PFDO_DEVICE_EXTENSION DeviceExtension) { ULONG Vector; KIRQL Irql; KINTERRUPT_MODE InterruptMode; BOOLEAN ShareVector; KAFFINITY Affinity; NTSTATUS Status; DPRINT1("PortFdoConnectInterrupt(%p)\n", DeviceExtension); /* No resources, no interrupt. Done! */ if (DeviceExtension->AllocatedResources == NULL || DeviceExtension->TranslatedResources == NULL) { DPRINT1("Checkpoint\n"); return STATUS_SUCCESS; } /* Get the interrupt data from the resource list */ Status = GetResourceListInterrupt(DeviceExtension, &Vector, &Irql, &InterruptMode, &ShareVector, &Affinity); if (!NT_SUCCESS(Status)) { DPRINT1("GetResourceListInterrupt() failed (Status 0x%08lx)\n", Status); return Status; } DPRINT1("Vector: %lu\n", Vector); DPRINT1("Irql: %lu\n", Irql); DPRINT1("Affinity: 0x%08lx\n", Affinity); /* Connect the interrupt */ Status = IoConnectInterrupt(&DeviceExtension->Interrupt, PortFdoInterruptRoutine, DeviceExtension, NULL, Vector, Irql, Irql, InterruptMode, ShareVector, Affinity, FALSE); if (NT_SUCCESS(Status)) { DeviceExtension->InterruptIrql = Irql; } else { DPRINT1("IoConnectInterrupt() failed (Status 0x%08lx)\n", Status); } return Status; } static NTSTATUS PortFdoStartMiniport( _In_ PFDO_DEVICE_EXTENSION DeviceExtension) { PHW_INITIALIZATION_DATA InitData; INTERFACE_TYPE InterfaceType; NTSTATUS Status; DPRINT1("PortFdoStartDevice(%p)\n", DeviceExtension); /* Get the interface type of the lower device */ InterfaceType = GetBusInterface(DeviceExtension->LowerDevice); if (InterfaceType == InterfaceTypeUndefined) return STATUS_NO_SUCH_DEVICE; /* Get the driver init data for the given interface type */ InitData = PortGetDriverInitData(DeviceExtension->DriverExtension, InterfaceType); if (InitData == NULL) return STATUS_NO_SUCH_DEVICE; /* Initialize the miniport */ Status = MiniportInitialize(&DeviceExtension->Miniport, DeviceExtension, InitData); if (!NT_SUCCESS(Status)) { DPRINT1("MiniportInitialize() failed (Status 0x%08lx)\n", Status); return Status; } /* Call the miniports FindAdapter function */ Status = MiniportFindAdapter(&DeviceExtension->Miniport); if (!NT_SUCCESS(Status)) { DPRINT1("MiniportFindAdapter() failed (Status 0x%08lx)\n", Status); return Status; } /* Connect the configured interrupt */ Status = PortFdoConnectInterrupt(DeviceExtension); if (!NT_SUCCESS(Status)) { DPRINT1("PortFdoConnectInterrupt() failed (Status 0x%08lx)\n", Status); return Status; } /* Call the miniports HwInitialize function */ Status = MiniportHwInitialize(&DeviceExtension->Miniport); if (!NT_SUCCESS(Status)) { DPRINT1("MiniportHwInitialize() failed (Status 0x%08lx)\n", Status); return Status; } /* Call the HwPassiveInitRoutine function, if available */ if (DeviceExtension->HwPassiveInitRoutine != NULL) { DPRINT1("Calling HwPassiveInitRoutine()\n"); if (!DeviceExtension->HwPassiveInitRoutine(&DeviceExtension->Miniport.MiniportExtension->HwDeviceExtension)) { DPRINT1("HwPassiveInitRoutine() failed\n"); return STATUS_UNSUCCESSFUL; } } return STATUS_SUCCESS; } static NTSTATUS NTAPI PortFdoStartDevice( _In_ PFDO_DEVICE_EXTENSION DeviceExtension, _In_ PIRP Irp) { PIO_STACK_LOCATION Stack; NTSTATUS Status; DPRINT1("PortFdoStartDevice(%p %p)\n", DeviceExtension, Irp); ASSERT(DeviceExtension->ExtensionType == FdoExtension); /* Get the current stack location */ Stack = IoGetCurrentIrpStackLocation(Irp); /* Start the lower device if the FDO is in 'stopped' state */ if (DeviceExtension->PnpState == dsStopped) { if (IoForwardIrpSynchronously(DeviceExtension->LowerDevice, Irp)) { Status = Irp->IoStatus.Status; } else { Status = STATUS_UNSUCCESSFUL; } if (!NT_SUCCESS(Status)) { DPRINT1("Lower device failed the IRP (Status 0x%08lx)\n", Status); return Status; } } /* Change to the 'started' state */ DeviceExtension->PnpState = dsStarted; /* Copy the raw and translated resource lists into the device extension */ if (Stack->Parameters.StartDevice.AllocatedResources != NULL && Stack->Parameters.StartDevice.AllocatedResourcesTranslated != NULL) { DeviceExtension->AllocatedResources = CopyResourceList(NonPagedPool, Stack->Parameters.StartDevice.AllocatedResources); if (DeviceExtension->AllocatedResources == NULL) return STATUS_NO_MEMORY; DeviceExtension->TranslatedResources = CopyResourceList(NonPagedPool, Stack->Parameters.StartDevice.AllocatedResourcesTranslated); if (DeviceExtension->TranslatedResources == NULL) return STATUS_NO_MEMORY; } /* Get the bus interface of the lower (bus) device */ Status = QueryBusInterface(DeviceExtension->LowerDevice, (PGUID)&GUID_BUS_INTERFACE_STANDARD, sizeof(BUS_INTERFACE_STANDARD), 1, &DeviceExtension->BusInterface, NULL); DPRINT1("Status: 0x%08lx\n", Status); if (NT_SUCCESS(Status)) { DPRINT1("Context: %p\n", DeviceExtension->BusInterface.Context); DeviceExtension->BusInitialized = TRUE; } /* Start the miniport (FindAdapter & Initialize) */ Status = PortFdoStartMiniport(DeviceExtension); if (!NT_SUCCESS(Status)) { DPRINT1("FdoStartMiniport() failed (Status 0x%08lx)\n", Status); DeviceExtension->PnpState = dsStopped; } return Status; } static NTSTATUS PortSendInquiry( _In_ PPDO_DEVICE_EXTENSION PdoExtension) { IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION IrpStack; KEVENT Event; // KIRQL Irql; PIRP Irp; NTSTATUS Status; PSENSE_DATA SenseBuffer; BOOLEAN KeepTrying = TRUE; ULONG RetryCount = 0; SCSI_REQUEST_BLOCK Srb; PCDB Cdb; // PSCSI_PORT_LUN_EXTENSION LunExtension; // PFDO_DEVICE_EXTENSION DeviceExtension; DPRINT("PortSendInquiry(%p)\n", PdoExtension); if (PdoExtension->InquiryBuffer == NULL) { PdoExtension->InquiryBuffer = ExAllocatePoolWithTag(NonPagedPool, INQUIRYDATABUFFERSIZE, TAG_INQUIRY_DATA); if (PdoExtension->InquiryBuffer == NULL) return STATUS_INSUFFICIENT_RESOURCES; } SenseBuffer = ExAllocatePoolWithTag(NonPagedPool, SENSE_BUFFER_SIZE, TAG_SENSE_DATA); if (SenseBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } while (KeepTrying) { /* Initialize event for waiting */ KeInitializeEvent(&Event, NotificationEvent, FALSE); /* Create an IRP */ Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_IN, PdoExtension->Device, NULL, 0, PdoExtension->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 = PdoExtension->Bus; Srb.TargetId = PdoExtension->Target; Srb.Lun = PdoExtension->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 = PdoExtension->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 = PdoExtension->Lun; Cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE; /* Call the driver */ Status = IoCallDriver(PdoExtension->Device, Irp); /* Wait for it to complete */ if (Status == STATUS_PENDING) { DPRINT1("PortSendInquiry(): Waiting for the driver to process request...\n"); KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } DPRINT("PortSendInquiry(): Request processed by driver, status = 0x%08X\n", Status); if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_SUCCESS) { DPRINT("Found a device!\n"); /* 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("SpiSendInquiry(): the queue is frozen at TargetId %d\n", Srb.TargetId); // LunExtension = SpiGetLunExtension(DeviceExtension, // LunInfo->PathId, // LunInfo->TargetId, // LunInfo->Lun); /* Clear frozen flag */ // LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE; /* Acquire the spinlock */ // KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql); /* Process the request */ // SpiGetNextRequestFromLun(DeviceObject->DeviceExtension, LunExtension); /* SpiGetNextRequestFromLun() releases the spinlock, so we just lower irql back to what it was before */ // KeLowerIrql(Irql); } /* Check if data overrun happened */ if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) { DPRINT("Data overrun at TargetId %d\n", PdoExtension->Target); /* 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 the sense buffer */ ExFreePoolWithTag(SenseBuffer, TAG_SENSE_DATA); DPRINT("PortSendInquiry() done with Status 0x%08X\n", Status); return Status; } static NTSTATUS PortFdoScanBus( _In_ PFDO_DEVICE_EXTENSION DeviceExtension) { PPDO_DEVICE_EXTENSION PdoExtension; ULONG Bus, Target; //, Lun; NTSTATUS Status; DPRINT("PortFdoScanBus(%p)\n", DeviceExtension); DPRINT("NumberOfBuses: %lu\n", DeviceExtension->Miniport.PortConfig.NumberOfBuses); DPRINT("MaximumNumberOfTargets: %lu\n", DeviceExtension->Miniport.PortConfig.MaximumNumberOfTargets); DPRINT("MaximumNumberOfLogicalUnits: %lu\n", DeviceExtension->Miniport.PortConfig.MaximumNumberOfLogicalUnits); /* Scan all buses */ for (Bus = 0; Bus < DeviceExtension->Miniport.PortConfig.NumberOfBuses; Bus++) { DPRINT("Scanning bus %ld\n", Bus); /* Scan all targets */ for (Target = 0; Target < DeviceExtension->Miniport.PortConfig.MaximumNumberOfTargets; Target++) { DPRINT(" Scanning target %ld:%ld\n", Bus, Target); DPRINT(" Scanning logical unit %ld:%ld:%ld\n", Bus, Target, 0); Status = PortCreatePdo(DeviceExtension, Bus, Target, 0, &PdoExtension); if (NT_SUCCESS(Status)) { /* Scan LUN 0 */ Status = PortSendInquiry(PdoExtension); DPRINT("PortSendInquiry returned 0x%08lx\n", Status); if (!NT_SUCCESS(Status)) { PortDeletePdo(PdoExtension); } else { DPRINT("VendorId: %.8s\n", PdoExtension->InquiryBuffer->VendorId); DPRINT("ProductId: %.16s\n", PdoExtension->InquiryBuffer->ProductId); DPRINT("ProductRevisionLevel: %.4s\n", PdoExtension->InquiryBuffer->ProductRevisionLevel); DPRINT("VendorSpecific: %.20s\n", PdoExtension->InquiryBuffer->VendorSpecific); } } #if 0 /* Scan all logical units */ for (Lun = 1; Lun < DeviceExtension->Miniport.PortConfig.MaximumNumberOfLogicalUnits; Lun++) { DPRINT(" Scanning logical unit %ld:%ld:%ld\n", Bus, Target, Lun); Status = PortSendInquiry(DeviceExtension->Device, Bus, Target, Lun); DPRINT("PortSendInquiry returned 0x%08lx\n", Status); if (!NT_SUCCESS(Status)) break; } #endif } } DPRINT("PortFdoScanBus() done!\n"); return STATUS_SUCCESS; } static NTSTATUS PortFdoQueryBusRelations( _In_ PFDO_DEVICE_EXTENSION DeviceExtension, _Out_ PULONG_PTR Information) { NTSTATUS Status = STATUS_SUCCESS;; DPRINT1("PortFdoQueryBusRelations(%p %p)\n", DeviceExtension, Information); Status = PortFdoScanBus(DeviceExtension); DPRINT1("Units found: %lu\n", DeviceExtension->PdoCount); *Information = 0; return Status; } static NTSTATUS PortFdoFilterRequirements( PFDO_DEVICE_EXTENSION DeviceExtension, PIRP Irp) { PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList; DPRINT1("PortFdoFilterRequirements(%p %p)\n", DeviceExtension, Irp); /* Get the bus number and the slot number */ RequirementsList =(PIO_RESOURCE_REQUIREMENTS_LIST)Irp->IoStatus.Information; if (RequirementsList != NULL) { DeviceExtension->BusNumber = RequirementsList->BusNumber; DeviceExtension->SlotNumber = RequirementsList->SlotNumber; } return STATUS_SUCCESS; } NTSTATUS NTAPI PortFdoScsi( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) { PFDO_DEVICE_EXTENSION DeviceExtension; // PIO_STACK_LOCATION Stack; ULONG_PTR Information = 0; NTSTATUS Status = STATUS_NOT_SUPPORTED; DPRINT("PortFdoScsi(%p %p)\n", DeviceObject, Irp); DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ASSERT(DeviceExtension); ASSERT(DeviceExtension->ExtensionType == FdoExtension); // Stack = IoGetCurrentIrpStackLocation(Irp); Irp->IoStatus.Information = Information; Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS NTAPI PortFdoPnp( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) { PFDO_DEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION Stack; ULONG_PTR Information = 0; NTSTATUS Status = STATUS_NOT_SUPPORTED; DPRINT1("PortFdoPnp(%p %p)\n", DeviceObject, Irp); DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ASSERT(DeviceExtension); ASSERT(DeviceExtension->ExtensionType == FdoExtension); Stack = IoGetCurrentIrpStackLocation(Irp); switch (Stack->MinorFunction) { case IRP_MN_START_DEVICE: /* 0x00 */ DPRINT1("IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); Status = PortFdoStartDevice(DeviceExtension, Irp); break; case IRP_MN_QUERY_REMOVE_DEVICE: /* 0x01 */ DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_REMOVE_DEVICE\n"); break; case IRP_MN_REMOVE_DEVICE: /* 0x02 */ DPRINT1("IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n"); break; case IRP_MN_CANCEL_REMOVE_DEVICE: /* 0x03 */ DPRINT1("IRP_MJ_PNP / IRP_MN_CANCEL_REMOVE_DEVICE\n"); break; case IRP_MN_STOP_DEVICE: /* 0x04 */ DPRINT1("IRP_MJ_PNP / IRP_MN_STOP_DEVICE\n"); break; case IRP_MN_QUERY_STOP_DEVICE: /* 0x05 */ DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_STOP_DEVICE\n"); break; case IRP_MN_CANCEL_STOP_DEVICE: /* 0x06 */ DPRINT1("IRP_MJ_PNP / IRP_MN_CANCEL_STOP_DEVICE\n"); break; case IRP_MN_QUERY_DEVICE_RELATIONS: /* 0x07 */ DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS\n"); switch (Stack->Parameters.QueryDeviceRelations.Type) { case BusRelations: DPRINT1(" IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n"); Status = PortFdoQueryBusRelations(DeviceExtension, &Information); break; case RemovalRelations: DPRINT1(" IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n"); return ForwardIrpAndForget(DeviceExtension->LowerDevice, Irp); default: DPRINT1(" IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n", Stack->Parameters.QueryDeviceRelations.Type); return ForwardIrpAndForget(DeviceExtension->LowerDevice, Irp); } break; case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* 0x0d */ DPRINT1("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n"); PortFdoFilterRequirements(DeviceExtension, Irp); return ForwardIrpAndForget(DeviceExtension->LowerDevice, Irp); case IRP_MN_QUERY_PNP_DEVICE_STATE: /* 0x14 */ DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_PNP_DEVICE_STATE\n"); break; case IRP_MN_DEVICE_USAGE_NOTIFICATION: /* 0x16 */ DPRINT1("IRP_MJ_PNP / IRP_MN_DEVICE_USAGE_NOTIFICATION\n"); break; case IRP_MN_SURPRISE_REMOVAL: /* 0x17 */ DPRINT1("IRP_MJ_PNP / IRP_MN_SURPRISE_REMOVAL\n"); break; default: DPRINT1("IRP_MJ_PNP / Unknown IOCTL 0x%lx\n", Stack->MinorFunction); return ForwardIrpAndForget(DeviceExtension->LowerDevice, Irp); } Irp->IoStatus.Information = Information; Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } /* EOF */