reactos/drivers/storage/port/scsiport/scsi.c
2020-12-09 12:40:51 +01:00

1777 lines
55 KiB
C

/*
* PROJECT: ReactOS Storage Stack
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: SCSI Port driver SCSI requests handling
* 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
SpiStatusSrbToNt(
_In_ UCHAR SrbStatus)
{
switch (SRB_STATUS(SrbStatus))
{
case SRB_STATUS_TIMEOUT:
case SRB_STATUS_COMMAND_TIMEOUT:
return STATUS_IO_TIMEOUT;
case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
case SRB_STATUS_BAD_FUNCTION:
return STATUS_INVALID_DEVICE_REQUEST;
case SRB_STATUS_NO_DEVICE:
case SRB_STATUS_INVALID_LUN:
case SRB_STATUS_INVALID_TARGET_ID:
case SRB_STATUS_NO_HBA:
return STATUS_DEVICE_DOES_NOT_EXIST;
case SRB_STATUS_DATA_OVERRUN:
return STATUS_BUFFER_OVERFLOW;
case SRB_STATUS_SELECTION_TIMEOUT:
return STATUS_DEVICE_NOT_CONNECTED;
default:
return STATUS_IO_DEVICE_ERROR;
}
return STATUS_IO_DEVICE_ERROR;
}
static
NTSTATUS
SpiHandleAttachRelease(
_In_ PSCSI_PORT_LUN_EXTENSION LunExtension,
_Inout_ PIRP Irp)
{
PSCSI_PORT_DEVICE_EXTENSION DeviceExtension =
LunExtension->Common.LowerDevice->DeviceExtension;
PDEVICE_OBJECT DeviceObject;
KIRQL Irql;
/* Get pointer to the SRB */
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PSCSI_REQUEST_BLOCK Srb = IrpStack->Parameters.Scsi.Srb;
/* Get spinlock */
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
/* Release, if asked */
if (Srb->Function == SRB_FUNCTION_RELEASE_DEVICE)
{
LunExtension->DeviceClaimed = FALSE;
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
Srb->SrbStatus = SRB_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
/* Attach, if not already claimed */
if (LunExtension->DeviceClaimed)
{
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
Srb->SrbStatus = SRB_STATUS_BUSY;
return STATUS_DEVICE_BUSY;
}
/* Save the device object */
DeviceObject = LunExtension->Common.DeviceObject;
if (Srb->Function == SRB_FUNCTION_CLAIM_DEVICE)
LunExtension->DeviceClaimed = TRUE;
if (Srb->Function == SRB_FUNCTION_ATTACH_DEVICE)
LunExtension->Common.DeviceObject = Srb->DataBuffer;
Srb->DataBuffer = DeviceObject;
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
Srb->SrbStatus = SRB_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
/**********************************************************************
* NAME INTERNAL
* ScsiPortDispatchScsi
*
* DESCRIPTION
* Answer requests for SCSI calls
*
* RUN LEVEL
* PASSIVE_LEVEL
*
* ARGUMENTS
* Standard dispatch arguments
*
* RETURNS
* NTSTATUS
*/
NTSTATUS
NTAPI
ScsiPortDispatchScsi(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
PSCSI_PORT_DEVICE_EXTENSION portExt;
PSCSI_PORT_LUN_EXTENSION lunExt;
PIO_STACK_LOCATION Stack;
PSCSI_REQUEST_BLOCK Srb;
KIRQL Irql;
NTSTATUS Status = STATUS_SUCCESS;
PIRP NextIrp, IrpList;
PKDEVICE_QUEUE_ENTRY Entry;
DPRINT("ScsiPortDispatchScsi(DeviceObject %p Irp %p)\n", DeviceObject, Irp);
Stack = IoGetCurrentIrpStackLocation(Irp);
Srb = Stack->Parameters.Scsi.Srb;
lunExt = DeviceObject->DeviceExtension;
ASSERT(!lunExt->Common.IsFDO);
portExt = lunExt->Common.LowerDevice->DeviceExtension;
if (Srb == NULL)
{
DPRINT1("ScsiPortDispatchScsi() called with Srb = NULL!\n");
Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
DPRINT("Srb: %p, Srb->Function: %lu\n", Srb, Srb->Function);
Srb->PathId = lunExt->PathId;
Srb->TargetId = lunExt->TargetId;
Srb->Lun = lunExt->Lun;
if (lunExt == NULL)
{
DPRINT("ScsiPortDispatchScsi() called with an invalid LUN\n");
Status = STATUS_NO_SUCH_DEVICE;
Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
switch (Srb->Function)
{
case SRB_FUNCTION_SHUTDOWN:
case SRB_FUNCTION_FLUSH:
DPRINT(" SRB_FUNCTION_SHUTDOWN or FLUSH\n");
if (portExt->CachesData == FALSE)
{
/* All success here */
Srb->SrbStatus = SRB_STATUS_SUCCESS;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
/* Fall through to a usual execute operation */
case SRB_FUNCTION_EXECUTE_SCSI:
case SRB_FUNCTION_IO_CONTROL:
DPRINT(" SRB_FUNCTION_EXECUTE_SCSI or SRB_FUNCTION_IO_CONTROL\n");
/* Mark IRP as pending in all cases */
IoMarkIrpPending(Irp);
if (Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)
{
/* Start IO directly */
IoStartPacket(portExt->Common.DeviceObject, Irp, NULL, NULL);
}
else
{
KIRQL oldIrql;
/* We need to be at DISPATCH_LEVEL */
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
/* Insert IRP into the queue */
if (!KeInsertByKeyDeviceQueue(&lunExt->DeviceQueue,
&Irp->Tail.Overlay.DeviceQueueEntry,
Srb->QueueSortKey))
{
/* It means the queue is empty, and we just start this request */
IoStartPacket(portExt->Common.DeviceObject, Irp, NULL, NULL);
}
/* Back to the old IRQL */
KeLowerIrql(oldIrql);
}
return STATUS_PENDING;
case SRB_FUNCTION_CLAIM_DEVICE:
case SRB_FUNCTION_ATTACH_DEVICE:
DPRINT(" SRB_FUNCTION_CLAIM_DEVICE or ATTACH\n");
/* Reference device object and keep the device object */
Status = SpiHandleAttachRelease(lunExt, Irp);
break;
case SRB_FUNCTION_RELEASE_DEVICE:
DPRINT(" SRB_FUNCTION_RELEASE_DEVICE\n");
/* Dereference device object and clear the device object */
Status = SpiHandleAttachRelease(lunExt, Irp);
break;
case SRB_FUNCTION_RELEASE_QUEUE:
DPRINT(" SRB_FUNCTION_RELEASE_QUEUE\n");
/* Guard with the spinlock */
KeAcquireSpinLock(&portExt->SpinLock, &Irql);
if (!(lunExt->Flags & LUNEX_FROZEN_QUEUE))
{
DPRINT("Queue is not frozen really\n");
KeReleaseSpinLock(&portExt->SpinLock, Irql);
Srb->SrbStatus = SRB_STATUS_SUCCESS;
Status = STATUS_SUCCESS;
break;
}
/* Unfreeze the queue */
lunExt->Flags &= ~LUNEX_FROZEN_QUEUE;
if (lunExt->SrbInfo.Srb == NULL)
{
/* Get next logical unit request. SpiGetNextRequestFromLun releases the lock. */
SpiGetNextRequestFromLun(portExt, lunExt, &Irql);
}
else
{
DPRINT("The queue has active request\n");
KeReleaseSpinLock(&portExt->SpinLock, Irql);
}
Srb->SrbStatus = SRB_STATUS_SUCCESS;
Status = STATUS_SUCCESS;
break;
case SRB_FUNCTION_FLUSH_QUEUE:
DPRINT(" SRB_FUNCTION_FLUSH_QUEUE\n");
/* Guard with the spinlock */
KeAcquireSpinLock(&portExt->SpinLock, &Irql);
if (!(lunExt->Flags & LUNEX_FROZEN_QUEUE))
{
DPRINT("Queue is not frozen really\n");
KeReleaseSpinLock(&portExt->SpinLock, Irql);
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
/* Make sure there is no active request */
ASSERT(lunExt->SrbInfo.Srb == NULL);
/* Compile a list from the device queue */
IrpList = NULL;
while ((Entry = KeRemoveDeviceQueue(&lunExt->DeviceQueue)) != NULL)
{
NextIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);
/* Get the Srb */
Stack = IoGetCurrentIrpStackLocation(NextIrp);
Srb = Stack->Parameters.Scsi.Srb;
/* Set statuse */
Srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
NextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
/* Add then to the list */
NextIrp->Tail.Overlay.ListEntry.Flink = (PLIST_ENTRY)IrpList;
IrpList = NextIrp;
}
/* Unfreeze the queue */
lunExt->Flags &= ~LUNEX_FROZEN_QUEUE;
/* Release the spinlock */
KeReleaseSpinLock(&portExt->SpinLock, Irql);
/* Complete those requests */
while (IrpList)
{
NextIrp = IrpList;
IrpList = (PIRP)NextIrp->Tail.Overlay.ListEntry.Flink;
IoCompleteRequest(NextIrp, 0);
}
Status = STATUS_SUCCESS;
break;
default:
DPRINT1("SRB function not implemented (Function %lu)\n", Srb->Function);
Status = STATUS_NOT_IMPLEMENTED;
break;
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
VOID
SpiGetNextRequestFromLun(
_In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
_Inout_ PSCSI_PORT_LUN_EXTENSION LunExtension,
_Inout_opt_ PKIRQL OldIrql
)
{
PIO_STACK_LOCATION IrpStack;
PIRP NextIrp;
PKDEVICE_QUEUE_ENTRY Entry;
PSCSI_REQUEST_BLOCK Srb;
/* If LUN is not active or queue is more than maximum allowed */
if (LunExtension->QueueCount >= LunExtension->MaxQueueCount ||
!(LunExtension->Flags & SCSI_PORT_LU_ACTIVE))
{
/* Release the spinlock and exit */
if (OldIrql != NULL)
KeReleaseSpinLock(&DeviceExtension->SpinLock, *OldIrql);
else
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
return;
}
/* Check if we can get a next request */
if (LunExtension->Flags &
(LUNEX_NEED_REQUEST_SENSE | LUNEX_BUSY |
LUNEX_FULL_QUEUE | LUNEX_FROZEN_QUEUE | LUNEX_REQUEST_PENDING))
{
/* Pending requests can only be started if the queue is empty */
if (IsListEmpty(&LunExtension->SrbInfo.Requests) &&
!(LunExtension->Flags &
(LUNEX_BUSY | LUNEX_FROZEN_QUEUE | LUNEX_FULL_QUEUE | LUNEX_NEED_REQUEST_SENSE)))
{
/* Make sure we have SRB */
ASSERT(LunExtension->SrbInfo.Srb == NULL);
/* Clear active and pending flags */
LunExtension->Flags &= ~(LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE);
/* Get next Irp, and clear pending requests list */
NextIrp = LunExtension->PendingRequest;
LunExtension->PendingRequest = NULL;
/* Set attempt counter to zero */
LunExtension->AttemptCount = 0;
/* Release the spinlock */
if (OldIrql != NULL)
KeReleaseSpinLock(&DeviceExtension->SpinLock, *OldIrql);
else
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
/* Start the next pending request */
IoStartPacket(DeviceExtension->Common.DeviceObject, NextIrp, (PULONG)NULL, NULL);
return;
}
else
{
/* Release the spinlock, without clearing any flags and exit */
if (OldIrql != NULL)
KeReleaseSpinLock(&DeviceExtension->SpinLock, *OldIrql);
else
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
return;
}
}
/* Reset active flag */
LunExtension->Flags &= ~SCSI_PORT_LU_ACTIVE;
/* Set attempt counter to zero */
LunExtension->AttemptCount = 0;
/* Remove packet from the device queue */
Entry = KeRemoveByKeyDeviceQueue(&LunExtension->DeviceQueue, LunExtension->SortKey);
if (Entry != NULL)
{
/* Get pointer to the next irp */
NextIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);
/* Get point to the SRB */
IrpStack = IoGetCurrentIrpStackLocation(NextIrp);
Srb = (PSCSI_REQUEST_BLOCK)IrpStack->Parameters.Others.Argument1;
/* Set new key*/
LunExtension->SortKey = Srb->QueueSortKey;
LunExtension->SortKey++;
/* Release the spinlock */
if (OldIrql != NULL)
KeReleaseSpinLock(&DeviceExtension->SpinLock, *OldIrql);
else
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
/* Start the next pending request */
IoStartPacket(DeviceExtension->Common.DeviceObject, NextIrp, (PULONG)NULL, NULL);
}
else
{
/* Release the spinlock */
if (OldIrql != NULL)
KeReleaseSpinLock(&DeviceExtension->SpinLock, *OldIrql);
else
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
}
IO_COMPLETION_ROUTINE SpiSenseCompletionRoutine;
NTSTATUS
NTAPI
SpiSenseCompletionRoutine(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_opt_ PVOID Context)
{
PIO_STACK_LOCATION ioStack = IoGetNextIrpStackLocation(Irp);
PSCSI_PORT_LUN_EXTENSION lunExt = ioStack->DeviceObject->DeviceExtension;
PSCSI_PORT_DEVICE_EXTENSION portExt = lunExt->Common.LowerDevice->DeviceExtension;
PSCSI_REQUEST_BLOCK Srb = (PSCSI_REQUEST_BLOCK)Context;
PSCSI_REQUEST_BLOCK InitialSrb;
PIRP InitialIrp;
DPRINT("SpiCompletionRoutine() entered, IRP %p \n", Irp);
if ((Srb->Function == SRB_FUNCTION_RESET_BUS) ||
(Srb->Function == SRB_FUNCTION_ABORT_COMMAND))
{
/* Deallocate SRB and IRP and exit */
ExFreePool(Srb);
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
/* Get a pointer to the SRB and IRP which were initially sent */
InitialSrb = *((PVOID *)(Srb+1));
InitialIrp = InitialSrb->OriginalRequest;
if ((SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) ||
(SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN))
{
/* Sense data is OK */
InitialSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
/* Set length to be the same */
InitialSrb->SenseInfoBufferLength = (UCHAR)Srb->DataTransferLength;
}
/* Make sure initial SRB's queue is frozen */
ASSERT(InitialSrb->SrbStatus & SRB_STATUS_QUEUE_FROZEN);
// The queue is frozen, but the SRB had a SRB_FLAGS_NO_QUEUE_FREEZE => unfreeze the queue
if ((InitialSrb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE) &&
(InitialSrb->SrbStatus & SRB_STATUS_QUEUE_FROZEN))
{
KIRQL irql;
KeAcquireSpinLock(&portExt->SpinLock, &irql);
ASSERT(lunExt->Flags & LUNEX_FROZEN_QUEUE);
lunExt->Flags &= ~LUNEX_FROZEN_QUEUE;
lunExt->Flags &= ~LUNEX_NEED_REQUEST_SENSE;
// SpiGetNextRequestFromLun releases the lock
SpiGetNextRequestFromLun(portExt, lunExt, &irql);
InitialSrb->SrbStatus &= ~SRB_STATUS_QUEUE_FROZEN;
}
/* Complete this request */
IoCompleteRequest(InitialIrp, IO_DISK_INCREMENT);
/* Deallocate everything (internal) */
ExFreePool(Srb);
if (Irp->MdlAddress != NULL)
{
MmUnlockPages(Irp->MdlAddress);
IoFreeMdl(Irp->MdlAddress);
Irp->MdlAddress = NULL;
}
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
static
VOID
SpiSendRequestSense(
_In_ PSCSI_PORT_LUN_EXTENSION LunExtension,
_In_ PSCSI_REQUEST_BLOCK InitialSrb)
{
PSCSI_REQUEST_BLOCK Srb;
PCDB Cdb;
PIRP Irp;
PIO_STACK_LOCATION IrpStack;
LARGE_INTEGER LargeInt;
PVOID *Ptr;
DPRINT("SpiSendRequestSense() entered, InitialSrb %p\n", InitialSrb);
/* Allocate Srb */
Srb = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK) + sizeof(PVOID), TAG_SCSIPORT);
RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
/* Allocate IRP */
LargeInt.QuadPart = (LONGLONG) 1;
Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ,
LunExtension->Common.DeviceObject,
InitialSrb->SenseInfoBuffer,
InitialSrb->SenseInfoBufferLength,
&LargeInt,
NULL);
IoSetCompletionRoutine(Irp,
SpiSenseCompletionRoutine,
Srb,
TRUE,
TRUE,
TRUE);
if (!Srb)
{
DPRINT("SpiSendRequestSense() failed, Srb %p\n", Srb);
return;
}
IrpStack = IoGetNextIrpStackLocation(Irp);
IrpStack->MajorFunction = IRP_MJ_SCSI;
/* Put Srb address into Irp... */
IrpStack->Parameters.Others.Argument1 = (PVOID)Srb;
/* ...and vice versa */
Srb->OriginalRequest = Irp;
/* Save Srb */
Ptr = (PVOID *)(Srb+1);
*Ptr = InitialSrb;
/* Build CDB for REQUEST SENSE */
Srb->CdbLength = 6;
Cdb = (PCDB)Srb->Cdb;
Cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
Cdb->CDB6INQUIRY.LogicalUnitNumber = 0;
Cdb->CDB6INQUIRY.Reserved1 = 0;
Cdb->CDB6INQUIRY.PageCode = 0;
Cdb->CDB6INQUIRY.IReserved = 0;
Cdb->CDB6INQUIRY.AllocationLength = (UCHAR)InitialSrb->SenseInfoBufferLength;
Cdb->CDB6INQUIRY.Control = 0;
/* Set address */
Srb->TargetId = InitialSrb->TargetId;
Srb->Lun = InitialSrb->Lun;
Srb->PathId = InitialSrb->PathId;
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
/* Timeout will be 2 seconds */
Srb->TimeOutValue = 2;
/* No auto request sense */
Srb->SenseInfoBufferLength = 0;
Srb->SenseInfoBuffer = NULL;
/* Set necessary flags */
Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_BYPASS_FROZEN_QUEUE |
SRB_FLAGS_DISABLE_DISCONNECT;
// pass some InitialSrb flags
if (InitialSrb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER)
Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
if (InitialSrb->SrbFlags & SRB_FLAGS_BYPASS_LOCKED_QUEUE)
Srb->SrbFlags |= SRB_FLAGS_BYPASS_LOCKED_QUEUE;
if (InitialSrb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE)
Srb->SrbFlags |= SRB_FLAGS_NO_QUEUE_FREEZE;
Srb->DataBuffer = InitialSrb->SenseInfoBuffer;
/* Fill the transfer length */
Srb->DataTransferLength = InitialSrb->SenseInfoBufferLength;
/* Clear statuses */
Srb->ScsiStatus = Srb->SrbStatus = 0;
Srb->NextSrb = 0;
/* Call the driver */
(VOID)IoCallDriver(LunExtension->Common.DeviceObject, Irp);
DPRINT("SpiSendRequestSense() done\n");
}
static
VOID
SpiProcessCompletedRequest(
_In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
_Inout_ PSCSI_REQUEST_BLOCK_INFO SrbInfo,
_Out_ PBOOLEAN NeedToCallStartIo)
{
PSCSI_REQUEST_BLOCK Srb;
PSCSI_PORT_LUN_EXTENSION LunExtension;
LONG Result;
PIRP Irp;
//ULONG SequenceNumber;
Srb = SrbInfo->Srb;
Irp = Srb->OriginalRequest;
PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
/* Get Lun extension */
LunExtension = IoStack->DeviceObject->DeviceExtension;
ASSERT(LunExtension && !LunExtension->Common.IsFDO);
if (Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION &&
DeviceExtension->MapBuffers &&
Irp->MdlAddress)
{
/* MDL is shared if transfer is broken into smaller parts */
Srb->DataBuffer = (PCCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress) +
((PCCHAR)Srb->DataBuffer - SrbInfo->DataOffset);
/* In case of data going in, flush the buffers */
if (Srb->SrbFlags & SRB_FLAGS_DATA_IN)
{
KeFlushIoBuffers(Irp->MdlAddress,
TRUE,
FALSE);
}
}
/* Flush adapter if needed */
if (SrbInfo->BaseOfMapRegister)
{
/* TODO: Implement */
ASSERT(FALSE);
}
/* Clear the request */
SrbInfo->Srb = NULL;
/* If disconnect is disabled... */
if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
{
/* Acquire the spinlock since we mess with flags */
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
/* Set corresponding flag */
DeviceExtension->Flags |= SCSI_PORT_DISCONNECT_ALLOWED;
/* Clear the timer if needed */
if (!(DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET))
DeviceExtension->TimerCount = -1;
/* Spinlock is not needed anymore */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
if (!(DeviceExtension->Flags & SCSI_PORT_REQUEST_PENDING) &&
!(DeviceExtension->Flags & SCSI_PORT_DEVICE_BUSY) &&
!(*NeedToCallStartIo))
{
/* We're not busy, but we have a request pending */
IoStartNextPacket(DeviceExtension->Common.DeviceObject, FALSE);
}
}
/* Scatter/gather */
if (Srb->SrbFlags & SRB_FLAGS_SGLIST_FROM_POOL)
{
/* TODO: Implement */
ASSERT(FALSE);
}
/* Acquire spinlock (we're freeing SrbExtension) */
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
/* Free it (if needed) */
if (Srb->SrbExtension)
{
if (Srb->SenseInfoBuffer != NULL && DeviceExtension->SupportsAutoSense)
{
ASSERT(Srb->SenseInfoBuffer == NULL || SrbInfo->SaveSenseRequest != NULL);
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)
{
/* Copy sense data to the buffer */
RtlCopyMemory(SrbInfo->SaveSenseRequest,
Srb->SenseInfoBuffer,
Srb->SenseInfoBufferLength);
}
/* And restore the pointer */
Srb->SenseInfoBuffer = SrbInfo->SaveSenseRequest;
}
/* Put it into the free srb extensions list */
*((PVOID *)Srb->SrbExtension) = DeviceExtension->FreeSrbExtensions;
DeviceExtension->FreeSrbExtensions = Srb->SrbExtension;
}
/* Save transfer length in the IRP */
Irp->IoStatus.Information = Srb->DataTransferLength;
//SequenceNumber = SrbInfo->SequenceNumber;
SrbInfo->SequenceNumber = 0;
/* Decrement the queue count */
LunExtension->QueueCount--;
/* Free Srb, if needed*/
if (Srb->QueueTag != SP_UNTAGGED)
{
/* Put it into the free list */
SrbInfo->Requests.Blink = NULL;
SrbInfo->Requests.Flink = (PLIST_ENTRY)DeviceExtension->FreeSrbInfo;
DeviceExtension->FreeSrbInfo = SrbInfo;
}
/* SrbInfo is not used anymore */
SrbInfo = NULL;
if (DeviceExtension->Flags & SCSI_PORT_REQUEST_PENDING)
{
/* Clear the flag */
DeviceExtension->Flags &= ~SCSI_PORT_REQUEST_PENDING;
/* Note the caller about StartIo */
*NeedToCallStartIo = TRUE;
}
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
{
/* Start the packet */
Irp->IoStatus.Status = STATUS_SUCCESS;
if (!(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) &&
LunExtension->RequestTimeout == -1)
{
/* Start the next packet. SpiGetNextRequestFromLun will release the lock for us */
SpiGetNextRequestFromLun(DeviceExtension, LunExtension, NULL);
}
else
{
/* Release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
DPRINT("IoCompleting request IRP 0x%p\n", Irp);
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
/* Decrement number of active requests, and analyze the result */
Result = InterlockedDecrement(&DeviceExtension->ActiveRequestCounter);
if (Result < 0 &&
!DeviceExtension->MapRegisters &&
DeviceExtension->AdapterObject != NULL)
{
/* Nullify map registers */
DeviceExtension->MapRegisterBase = NULL;
IoFreeAdapterChannel(DeviceExtension->AdapterObject);
}
/* Exit, we're done */
return;
}
/* Decrement number of active requests, and analyze the result */
Result = InterlockedDecrement(&DeviceExtension->ActiveRequestCounter);
if (Result < 0 &&
!DeviceExtension->MapRegisters &&
DeviceExtension->AdapterObject != NULL)
{
/* Result is negative, so this is a slave, free map registers */
DeviceExtension->MapRegisterBase = NULL;
IoFreeAdapterChannel(DeviceExtension->AdapterObject);
}
/* Convert status */
Irp->IoStatus.Status = SpiStatusSrbToNt(Srb->SrbStatus);
/* It's not a bypass, it's busy or the queue is full? */
if ((Srb->ScsiStatus == SCSISTAT_BUSY ||
Srb->SrbStatus == SRB_STATUS_BUSY ||
Srb->ScsiStatus == SCSISTAT_QUEUE_FULL) &&
!(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE))
{
DPRINT("Busy SRB status %x\n", Srb->SrbStatus);
/* Requeue, if needed */
if (LunExtension->Flags & (LUNEX_FROZEN_QUEUE | LUNEX_BUSY))
{
DPRINT("it's being requeued\n");
Srb->SrbStatus = SRB_STATUS_PENDING;
Srb->ScsiStatus = 0;
if (!KeInsertByKeyDeviceQueue(&LunExtension->DeviceQueue,
&Irp->Tail.Overlay.DeviceQueueEntry,
Srb->QueueSortKey))
{
/* It's a big f.ck up if we got here */
Srb->SrbStatus = SRB_STATUS_ERROR;
Srb->ScsiStatus = SCSISTAT_BUSY;
ASSERT(FALSE);
goto Error;
}
/* Release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
else if (LunExtension->AttemptCount++ < 20)
{
/* LUN is still busy */
Srb->ScsiStatus = 0;
Srb->SrbStatus = SRB_STATUS_PENDING;
LunExtension->BusyRequest = Irp;
LunExtension->Flags |= LUNEX_BUSY;
/* Release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
else
{
Error:
/* Freeze the queue*/
Srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN;
LunExtension->Flags |= LUNEX_FROZEN_QUEUE;
/* "Unfull" the queue */
LunExtension->Flags &= ~LUNEX_FULL_QUEUE;
/* Release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
/* Return status that the device is not ready */
Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}
return;
}
/* Start the next request, if LUN is idle, and this is sense request */
if (((Srb->ScsiStatus != SCSISTAT_CHECK_CONDITION) ||
(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) ||
!Srb->SenseInfoBuffer || !Srb->SenseInfoBufferLength)
&& (Srb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE))
{
if (LunExtension->RequestTimeout == -1)
SpiGetNextRequestFromLun(DeviceExtension, LunExtension, NULL);
else
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
else
{
/* Freeze the queue */
Srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN;
LunExtension->Flags |= LUNEX_FROZEN_QUEUE;
/* Do we need a request sense? */
if (Srb->ScsiStatus == SCSISTAT_CHECK_CONDITION &&
!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
Srb->SenseInfoBuffer && Srb->SenseInfoBufferLength)
{
/* If LUN is busy, we have to requeue it in order to allow request sense */
if (LunExtension->Flags & LUNEX_BUSY)
{
DPRINT("Requeuing busy request to allow request sense\n");
if (!KeInsertByKeyDeviceQueue(&LunExtension->DeviceQueue,
&LunExtension->BusyRequest->Tail.Overlay.DeviceQueueEntry,
Srb->QueueSortKey))
{
/* We should never get here */
ASSERT(FALSE);
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return;
}
/* Clear busy flags */
LunExtension->Flags &= ~(LUNEX_FULL_QUEUE | LUNEX_BUSY);
}
/* Release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
/* Send RequestSense */
SpiSendRequestSense(LunExtension, Srb);
/* Exit */
return;
}
/* Release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
/* Complete the request */
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}
BOOLEAN
NTAPI
ScsiPortStartPacket(
_In_ PVOID Context)
{
PIO_STACK_LOCATION IrpStack;
PSCSI_REQUEST_BLOCK Srb;
PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context;
PSCSI_PORT_COMMON_EXTENSION CommonExtension = DeviceObject->DeviceExtension;
PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
PSCSI_PORT_LUN_EXTENSION LunExtension;
PSCSI_REQUEST_BLOCK_INFO SrbInfo;
BOOLEAN Result;
BOOLEAN StartTimer;
DPRINT("ScsiPortStartPacket() called\n");
IrpStack = IoGetCurrentIrpStackLocation(DeviceObject->CurrentIrp);
Srb = IrpStack->Parameters.Scsi.Srb;
if (CommonExtension->IsFDO) // IsFDO
{
DeviceExtension = DeviceObject->DeviceExtension;
LunExtension = IrpStack->DeviceObject->DeviceExtension;
ASSERT(LunExtension && !LunExtension->Common.IsFDO);
}
else
{
LunExtension = DeviceObject->DeviceExtension;
DeviceExtension = LunExtension->Common.LowerDevice->DeviceExtension;
}
/* Check if we are in a reset state */
if (DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET)
{
/* Mark the we've got requests while being in the reset state */
DeviceExtension->InterruptData.Flags |= SCSI_PORT_RESET_REQUEST;
return TRUE;
}
/* Set the time out value */
DeviceExtension->TimerCount = Srb->TimeOutValue;
/* We are busy */
DeviceExtension->Flags |= SCSI_PORT_DEVICE_BUSY;
if (LunExtension->RequestTimeout != -1)
{
/* Timer already active */
StartTimer = FALSE;
}
else
{
/* It hasn't been initialized yet */
LunExtension->RequestTimeout = Srb->TimeOutValue;
StartTimer = TRUE;
}
if (Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)
{
/* Handle bypass-requests */
/* Is this an abort request? */
if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND)
{
/* Get pointer to SRB info structure */
SrbInfo = SpiGetSrbData(DeviceExtension, LunExtension, Srb->QueueTag);
/* Check if the request is still "active" */
if (SrbInfo == NULL ||
SrbInfo->Srb == NULL ||
!(SrbInfo->Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE))
{
/* It's not, mark it as active then */
Srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE;
if (StartTimer)
LunExtension->RequestTimeout = -1;
DPRINT("Request has been already completed, but abort request came\n");
Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
/* Notify about request complete */
ScsiPortNotification(RequestComplete,
DeviceExtension->MiniPortDeviceExtension,
Srb);
/* and about readiness for the next request */
ScsiPortNotification(NextRequest,
DeviceExtension->MiniPortDeviceExtension);
/* They might ask for some work, so queue the DPC for them */
IoRequestDpc(DeviceExtension->Common.DeviceObject, NULL, NULL);
/* We're done in this branch */
return TRUE;
}
}
else
{
/* Add number of queued requests */
LunExtension->QueueCount++;
}
/* Bypass requests don't need request sense */
LunExtension->Flags &= ~LUNEX_NEED_REQUEST_SENSE;
/* Is disconnect disabled for this request? */
if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
{
/* Set the corresponding flag */
DeviceExtension->Flags &= ~SCSI_PORT_DISCONNECT_ALLOWED;
}
/* Transfer timeout value from Srb to Lun */
LunExtension->RequestTimeout = Srb->TimeOutValue;
}
else
{
if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
{
/* It's a disconnect, so no more requests can go */
DeviceExtension->Flags &= ~SCSI_PORT_DISCONNECT_ALLOWED;
}
LunExtension->Flags |= SCSI_PORT_LU_ACTIVE;
/* Increment queue count */
LunExtension->QueueCount++;
/* If it's tagged - special thing */
if (Srb->QueueTag != SP_UNTAGGED)
{
SrbInfo = &DeviceExtension->SrbInfo[Srb->QueueTag - 1];
/* Chek for consistency */
ASSERT(SrbInfo->Requests.Blink == NULL);
/* Insert it into the list of requests */
InsertTailList(&LunExtension->SrbInfo.Requests, &SrbInfo->Requests);
}
}
/* Mark this Srb active */
Srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE;
/* Call HwStartIo routine */
Result = DeviceExtension->HwStartIo(&DeviceExtension->MiniPortDeviceExtension,
Srb);
/* If notification is needed, then request a DPC */
if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
IoRequestDpc(DeviceExtension->Common.DeviceObject, NULL, NULL);
return Result;
}
BOOLEAN
NTAPI
SpiSaveInterruptData(IN PVOID Context)
{
PSCSI_PORT_SAVE_INTERRUPT InterruptContext = Context;
PSCSI_PORT_LUN_EXTENSION LunExtension;
PSCSI_REQUEST_BLOCK Srb;
PSCSI_REQUEST_BLOCK_INFO SrbInfo, NextSrbInfo;
PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
BOOLEAN IsTimed;
/* Get pointer to the device extension */
DeviceExtension = InterruptContext->DeviceExtension;
/* If we don't have anything pending - return */
if (!(DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED))
return FALSE;
/* Actually save the interrupt data */
*InterruptContext->InterruptData = DeviceExtension->InterruptData;
/* Clear the data stored in the device extension */
DeviceExtension->InterruptData.Flags &=
(SCSI_PORT_RESET | SCSI_PORT_RESET_REQUEST | SCSI_PORT_DISABLE_INTERRUPTS);
DeviceExtension->InterruptData.CompletedAbort = NULL;
DeviceExtension->InterruptData.ReadyLun = NULL;
DeviceExtension->InterruptData.CompletedRequests = NULL;
/* Loop through the list of completed requests */
SrbInfo = InterruptContext->InterruptData->CompletedRequests;
while (SrbInfo)
{
/* Make sure we have SRV */
ASSERT(SrbInfo->Srb);
/* Get SRB and LunExtension */
Srb = SrbInfo->Srb;
PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Srb->OriginalRequest);
LunExtension = IoStack->DeviceObject->DeviceExtension;
ASSERT(LunExtension && !LunExtension->Common.IsFDO);
/* We have to check special cases if request is unsuccessful*/
if (Srb->SrbStatus != SRB_STATUS_SUCCESS)
{
/* Check if we need request sense by a few conditions */
if (Srb->SenseInfoBuffer && Srb->SenseInfoBufferLength &&
Srb->ScsiStatus == SCSISTAT_CHECK_CONDITION &&
!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID))
{
if (LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE)
{
/* It means: we tried to send REQUEST SENSE, but failed */
Srb->ScsiStatus = 0;
Srb->SrbStatus = SRB_STATUS_REQUEST_SENSE_FAILED;
}
else
{
/* Set the corresponding flag, so that REQUEST SENSE
will be sent */
LunExtension->Flags |= LUNEX_NEED_REQUEST_SENSE;
}
}
/* Check for a full queue */
if (Srb->ScsiStatus == SCSISTAT_QUEUE_FULL)
{
/* TODO: Implement when it's encountered */
ASSERT(FALSE);
}
}
/* Let's decide if we need to watch timeout or not */
if (Srb->QueueTag == SP_UNTAGGED)
{
IsTimed = TRUE;
}
else
{
if (LunExtension->SrbInfo.Requests.Flink == &SrbInfo->Requests)
IsTimed = TRUE;
else
IsTimed = FALSE;
/* Remove it from the queue */
RemoveEntryList(&SrbInfo->Requests);
}
if (IsTimed)
{
/* We have to maintain timeout counter */
if (IsListEmpty(&LunExtension->SrbInfo.Requests))
{
LunExtension->RequestTimeout = -1;
}
else
{
NextSrbInfo = CONTAINING_RECORD(LunExtension->SrbInfo.Requests.Flink,
SCSI_REQUEST_BLOCK_INFO,
Requests);
Srb = NextSrbInfo->Srb;
/* Update timeout counter */
LunExtension->RequestTimeout = Srb->TimeOutValue;
}
}
SrbInfo = SrbInfo->CompletedRequests;
}
return TRUE;
}
VOID
NTAPI
ScsiPortDpcForIsr(
_In_ PKDPC Dpc,
_In_ PDEVICE_OBJECT DpcDeviceObject,
_Inout_ PIRP DpcIrp,
_In_opt_ PVOID DpcContext)
{
PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = DpcDeviceObject->DeviceExtension;
SCSI_PORT_INTERRUPT_DATA InterruptData;
SCSI_PORT_SAVE_INTERRUPT Context;
PSCSI_PORT_LUN_EXTENSION LunExtension;
BOOLEAN NeedToStartIo;
PSCSI_REQUEST_BLOCK_INFO SrbInfo;
LARGE_INTEGER TimerValue;
DPRINT("ScsiPortDpcForIsr(Dpc %p DpcDeviceObject %p DpcIrp %p DpcContext %p)\n",
Dpc, DpcDeviceObject, DpcIrp, DpcContext);
/* We need to acquire spinlock */
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
RtlZeroMemory(&InterruptData, sizeof(SCSI_PORT_INTERRUPT_DATA));
TryAgain:
/* Interrupt structure must be snapshotted, and only then analyzed */
Context.InterruptData = &InterruptData;
Context.DeviceExtension = DeviceExtension;
if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0],
SpiSaveInterruptData,
&Context))
{
/* Nothing - just return (don't forget to release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
DPRINT("ScsiPortDpcForIsr() done\n");
return;
}
/* If flush of adapters is needed - do it */
if (InterruptData.Flags & SCSI_PORT_FLUSH_ADAPTERS)
{
/* TODO: Implement */
ASSERT(FALSE);
}
/* Check for IoMapTransfer */
if (InterruptData.Flags & SCSI_PORT_MAP_TRANSFER)
{
/* TODO: Implement */
ASSERT(FALSE);
}
/* Check if timer is needed */
if (InterruptData.Flags & SCSI_PORT_TIMER_NEEDED)
{
/* Save the timer routine */
DeviceExtension->HwScsiTimer = InterruptData.HwScsiTimer;
if (InterruptData.MiniportTimerValue == 0)
{
/* Cancel the timer */
KeCancelTimer(&DeviceExtension->MiniportTimer);
}
else
{
/* Convert timer value */
TimerValue.QuadPart = Int32x32To64(InterruptData.MiniportTimerValue, -10);
/* Set the timer */
KeSetTimer(&DeviceExtension->MiniportTimer,
TimerValue,
&DeviceExtension->MiniportTimerDpc);
}
}
/* If it's ready for the next request */
if (InterruptData.Flags & SCSI_PORT_NEXT_REQUEST_READY)
{
/* Check for a duplicate request (NextRequest+NextLuRequest) */
if ((DeviceExtension->Flags &
(SCSI_PORT_DEVICE_BUSY | SCSI_PORT_DISCONNECT_ALLOWED)) ==
(SCSI_PORT_DEVICE_BUSY | SCSI_PORT_DISCONNECT_ALLOWED))
{
/* Clear busy flag set by ScsiPortStartPacket() */
DeviceExtension->Flags &= ~SCSI_PORT_DEVICE_BUSY;
if (!(InterruptData.Flags & SCSI_PORT_RESET))
{
/* Ready for next, and no reset is happening */
DeviceExtension->TimerCount = -1;
}
}
else
{
/* Not busy, but not ready for the next request */
DeviceExtension->Flags &= ~SCSI_PORT_DEVICE_BUSY;
InterruptData.Flags &= ~SCSI_PORT_NEXT_REQUEST_READY;
}
}
/* Any resets? */
if (InterruptData.Flags & SCSI_PORT_RESET_REPORTED)
{
/* Hold for a bit */
DeviceExtension->TimerCount = 4;
}
/* Any ready LUN? */
if (InterruptData.ReadyLun != NULL)
{
/* Process all LUNs from the list*/
while (TRUE)
{
/* Remove it from the list first (as processed) */
LunExtension = InterruptData.ReadyLun;
InterruptData.ReadyLun = LunExtension->ReadyLun;
LunExtension->ReadyLun = NULL;
/* Get next request for this LUN */
SpiGetNextRequestFromLun(DeviceExtension, LunExtension, NULL);
/* Still ready requests exist?
If yes - get spinlock, if no - stop here */
if (InterruptData.ReadyLun != NULL)
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
else
break;
}
}
else
{
/* Release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
/* If we ready for next packet, start it */
if (InterruptData.Flags & SCSI_PORT_NEXT_REQUEST_READY)
IoStartNextPacket(DeviceExtension->Common.DeviceObject, FALSE);
NeedToStartIo = FALSE;
/* Loop the completed request list */
while (InterruptData.CompletedRequests)
{
/* Remove the request */
SrbInfo = InterruptData.CompletedRequests;
InterruptData.CompletedRequests = SrbInfo->CompletedRequests;
SrbInfo->CompletedRequests = NULL;
/* Process it */
SpiProcessCompletedRequest(DeviceExtension,
SrbInfo,
&NeedToStartIo);
}
/* Loop abort request list */
while (InterruptData.CompletedAbort)
{
LunExtension = InterruptData.CompletedAbort;
/* Remove the request */
InterruptData.CompletedAbort = LunExtension->CompletedAbortRequests;
/* Get spinlock since we're going to change flags */
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
/* TODO: Put SrbExtension to the list of free extensions */
ASSERT(FALSE);
}
/* If we need - call StartIo routine */
if (NeedToStartIo)
{
/* Make sure CurrentIrp is not null! */
ASSERT(DpcDeviceObject->CurrentIrp != NULL);
ScsiPortStartIo(DpcDeviceObject, DpcDeviceObject->CurrentIrp);
}
/* Everything has been done, check */
if (InterruptData.Flags & SCSI_PORT_ENABLE_INT_REQUEST)
{
/* Synchronize using spinlock */
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
/* Request an interrupt */
DeviceExtension->HwInterrupt(DeviceExtension->MiniPortDeviceExtension);
ASSERT(DeviceExtension->Flags & SCSI_PORT_DISABLE_INT_REQUESET);
/* Should interrupts be enabled again? */
if (DeviceExtension->Flags & SCSI_PORT_DISABLE_INT_REQUESET)
{
/* Clear this flag */
DeviceExtension->Flags &= ~SCSI_PORT_DISABLE_INT_REQUESET;
/* Call a special routine to do this */
ASSERT(FALSE);
#if 0
KeSynchronizeExecution(DeviceExtension->Interrupt,
SpiEnableInterrupts,
DeviceExtension);
#endif
}
/* If we need a notification again - loop */
if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
goto TryAgain;
/* Release the spinlock */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
DPRINT("ScsiPortDpcForIsr() done\n");
}
static
PSCSI_REQUEST_BLOCK_INFO
SpiAllocateSrbStructures(
_Inout_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
_Inout_ PSCSI_PORT_LUN_EXTENSION LunExtension,
_Inout_ PSCSI_REQUEST_BLOCK Srb)
{
PCHAR SrbExtension;
PSCSI_REQUEST_BLOCK_INFO SrbInfo;
/* Spinlock must be held while this function executes */
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
/* Allocate SRB data structure */
if (DeviceExtension->NeedSrbDataAlloc)
{
/* Treat the abort request in a special way */
if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND)
{
SrbInfo = SpiGetSrbData(DeviceExtension, LunExtension, Srb->QueueTag);
}
else if (Srb->SrbFlags &
(SRB_FLAGS_QUEUE_ACTION_ENABLE | SRB_FLAGS_NO_QUEUE_FREEZE) &&
!(Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
)
{
/* Do not process tagged commands if need request sense is set */
if (LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE)
{
ASSERT(!(LunExtension->Flags & LUNEX_REQUEST_PENDING));
LunExtension->PendingRequest = Srb->OriginalRequest;
LunExtension->Flags |= LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE;
/* Release the spinlock and return */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
return NULL;
}
ASSERT(LunExtension->SrbInfo.Srb == NULL);
SrbInfo = DeviceExtension->FreeSrbInfo;
if (SrbInfo == NULL)
{
/* No SRB structures left in the list. We have to leave
and wait while we are called again */
DeviceExtension->Flags |= SCSI_PORT_REQUEST_PENDING;
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
return NULL;
}
DeviceExtension->FreeSrbInfo = (PSCSI_REQUEST_BLOCK_INFO)SrbInfo->Requests.Flink;
/* QueueTag must never be 0, so +1 to it */
Srb->QueueTag = (UCHAR)(SrbInfo - DeviceExtension->SrbInfo) + 1;
}
else
{
/* Usual untagged command */
if (
(!IsListEmpty(&LunExtension->SrbInfo.Requests) ||
LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE) &&
!(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)
)
{
/* Mark it as pending and leave */
ASSERT(!(LunExtension->Flags & LUNEX_REQUEST_PENDING));
LunExtension->Flags |= LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE;
LunExtension->PendingRequest = Srb->OriginalRequest;
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
return(NULL);
}
Srb->QueueTag = SP_UNTAGGED;
SrbInfo = &LunExtension->SrbInfo;
}
}
else
{
Srb->QueueTag = SP_UNTAGGED;
SrbInfo = &LunExtension->SrbInfo;
}
/* Allocate SRB extension structure */
if (DeviceExtension->NeedSrbExtensionAlloc)
{
/* Check the list of free extensions */
SrbExtension = DeviceExtension->FreeSrbExtensions;
/* If no free extensions... */
if (SrbExtension == NULL)
{
/* Free SRB data */
if (Srb->Function != SRB_FUNCTION_ABORT_COMMAND &&
Srb->QueueTag != SP_UNTAGGED)
{
SrbInfo->Requests.Blink = NULL;
SrbInfo->Requests.Flink = (PLIST_ENTRY)DeviceExtension->FreeSrbInfo;
DeviceExtension->FreeSrbInfo = SrbInfo;
}
/* Return, in order to be called again later */
DeviceExtension->Flags |= SCSI_PORT_REQUEST_PENDING;
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
return NULL;
}
/* Remove that free SRB extension from the list (since
we're going to use it) */
DeviceExtension->FreeSrbExtensions = *((PVOID *)SrbExtension);
/* Spinlock can be released now */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
Srb->SrbExtension = SrbExtension;
if (Srb->SenseInfoBuffer != NULL &&
DeviceExtension->SupportsAutoSense)
{
/* Store pointer to the SenseInfo buffer */
SrbInfo->SaveSenseRequest = Srb->SenseInfoBuffer;
/* Does data fit the buffer? */
if (Srb->SenseInfoBufferLength > sizeof(SENSE_DATA))
{
/* No, disabling autosense at all */
Srb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE;
}
else
{
/* Yes, update the buffer pointer */
Srb->SenseInfoBuffer = SrbExtension + DeviceExtension->SrbExtensionSize;
}
}
}
else
{
/* Cleanup... */
Srb->SrbExtension = NULL;
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
return SrbInfo;
}
VOID
NTAPI
ScsiPortStartIo(
_Inout_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
PSCSI_PORT_LUN_EXTENSION LunExtension;
PIO_STACK_LOCATION IrpStack;
PSCSI_REQUEST_BLOCK Srb;
PSCSI_REQUEST_BLOCK_INFO SrbInfo;
LONG CounterResult;
NTSTATUS Status;
DPRINT("ScsiPortStartIo() called!\n");
DeviceExtension = DeviceObject->DeviceExtension;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
LunExtension = IrpStack->DeviceObject->DeviceExtension;
ASSERT(DeviceExtension->Common.IsFDO);
ASSERT(!LunExtension->Common.IsFDO);
DPRINT("LunExtension %p DeviceExtension %p\n", LunExtension, DeviceExtension);
Srb = IrpStack->Parameters.Scsi.Srb;
/* Apply "default" flags */
Srb->SrbFlags |= DeviceExtension->SrbFlags;
if (DeviceExtension->NeedSrbDataAlloc ||
DeviceExtension->NeedSrbExtensionAlloc)
{
/* Allocate them */
SrbInfo = SpiAllocateSrbStructures(DeviceExtension,
LunExtension,
Srb);
/* Couldn't alloc one or both data structures, return */
if (SrbInfo == NULL)
{
/* We have to call IoStartNextPacket, because this request
was not started */
if (LunExtension->Flags & LUNEX_REQUEST_PENDING)
IoStartNextPacket(DeviceObject, FALSE);
return;
}
}
else
{
/* No allocations are needed */
SrbInfo = &LunExtension->SrbInfo;
Srb->SrbExtension = NULL;
Srb->QueueTag = SP_UNTAGGED;
}
/* Increase sequence number of SRB */
if (!SrbInfo->SequenceNumber)
{
/* Increase global sequence number */
DeviceExtension->SequenceNumber++;
/* Assign it */
SrbInfo->SequenceNumber = DeviceExtension->SequenceNumber;
}
/* Check some special SRBs */
if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND)
{
/* Some special handling */
DPRINT1("Abort command! Unimplemented now\n");
}
else
{
SrbInfo->Srb = Srb;
}
if (Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION)
{
// Store the MDL virtual address in SrbInfo structure
SrbInfo->DataOffset = MmGetMdlVirtualAddress(Irp->MdlAddress);
if (DeviceExtension->MapBuffers)
{
/* Calculate offset within DataBuffer */
SrbInfo->DataOffset = MmGetSystemAddressForMdl(Irp->MdlAddress);
Srb->DataBuffer = SrbInfo->DataOffset +
(ULONG)((PUCHAR)Srb->DataBuffer -
(PUCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress));
}
if (DeviceExtension->AdapterObject)
{
/* Flush buffers */
KeFlushIoBuffers(Irp->MdlAddress,
Srb->SrbFlags & SRB_FLAGS_DATA_IN ? TRUE : FALSE,
TRUE);
}
if (DeviceExtension->MapRegisters)
{
/* Calculate number of needed map registers */
SrbInfo->NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
Srb->DataBuffer,
Srb->DataTransferLength);
/* Allocate adapter channel */
Status = IoAllocateAdapterChannel(DeviceExtension->AdapterObject,
DeviceExtension->Common.DeviceObject,
SrbInfo->NumberOfMapRegisters,
SpiAdapterControl,
SrbInfo);
if (!NT_SUCCESS(Status))
{
DPRINT1("IoAllocateAdapterChannel() failed!\n");
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
ScsiPortNotification(RequestComplete,
DeviceExtension + 1,
Srb);
ScsiPortNotification(NextRequest,
DeviceExtension + 1);
/* Request DPC for that work */
IoRequestDpc(DeviceExtension->Common.DeviceObject, NULL, NULL);
}
/* Control goes to SpiAdapterControl */
return;
}
}
/* Increase active request counter */
CounterResult = InterlockedIncrement(&DeviceExtension->ActiveRequestCounter);
if (CounterResult == 0 &&
DeviceExtension->AdapterObject != NULL &&
!DeviceExtension->MapRegisters)
{
IoAllocateAdapterChannel(
DeviceExtension->AdapterObject,
DeviceObject,
DeviceExtension->PortCapabilities.MaximumPhysicalPages,
ScsiPortAllocateAdapterChannel,
LunExtension
);
return;
}
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0],
ScsiPortStartPacket,
DeviceObject))
{
DPRINT("Synchronization failed!\n");
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
else
{
/* Release the spinlock only */
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}
DPRINT("ScsiPortStartIo() done\n");
}