reactos/drivers/usb/usbstor/scsi.c
Victor Perevertkin c452f7dab4 [USBSTOR] Refactor request issue code.
Pipe handle selection now made more correctly.
Simplified an MDL allocation for a request.
2019-06-11 04:39:43 +03:00

569 lines
18 KiB
C

/*
* PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: USB block storage device driver.
* COPYRIGHT: 2005-2006 James Tabor
* 2011-2012 Michael Martin (michael.martin@reactos.org)
* 2011-2013 Johannes Anderwald (johannes.anderwald@reactos.org)
* 2017 Vadim Galyant
* 2019 Victor Perevertkin (victor.perevertkin@reactos.org)
*/
#include "usbstor.h"
#define NDEBUG
#include <debug.h>
static
NTSTATUS
USBSTOR_SrbStatusToNtStatus(
IN PSCSI_REQUEST_BLOCK Srb)
{
UCHAR SrbStatus;
SrbStatus = SRB_STATUS(Srb->SrbStatus);
switch (SrbStatus)
{
case SRB_STATUS_SUCCESS:
return STATUS_SUCCESS;
case SRB_STATUS_DATA_OVERRUN:
return STATUS_BUFFER_OVERFLOW;
case SRB_STATUS_BAD_FUNCTION:
case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
return STATUS_INVALID_DEVICE_REQUEST;
case SRB_STATUS_INVALID_LUN:
case SRB_STATUS_INVALID_TARGET_ID:
case SRB_STATUS_NO_HBA:
case SRB_STATUS_NO_DEVICE:
return STATUS_DEVICE_DOES_NOT_EXIST;
case SRB_STATUS_TIMEOUT:
return STATUS_IO_TIMEOUT;
case SRB_STATUS_BUS_RESET:
case SRB_STATUS_COMMAND_TIMEOUT:
case SRB_STATUS_SELECTION_TIMEOUT:
return STATUS_DEVICE_NOT_CONNECTED;
default:
return STATUS_IO_DEVICE_ERROR;
}
}
static
NTSTATUS
USBSTOR_IssueBulkOrInterruptRequest(
IN PFDO_DEVICE_EXTENSION FDODeviceExtension,
IN PIRP Irp,
IN USBD_PIPE_HANDLE PipeHandle,
IN ULONG TransferFlags,
IN ULONG TransferBufferLength,
IN PVOID TransferBuffer,
IN PMDL TransferBufferMDL,
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
IN PIRP_CONTEXT Context)
{
PIO_STACK_LOCATION NextStack;
RtlZeroMemory(&Context->Urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
Context->Urb.UrbHeader.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
Context->Urb.UrbHeader.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
Context->Urb.UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;
Context->Urb.UrbBulkOrInterruptTransfer.TransferFlags = TransferFlags;
Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength = TransferBufferLength;
Context->Urb.UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;
Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferMDL = TransferBufferMDL;
NextStack = IoGetNextIrpStackLocation(Irp);
NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
NextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
NextStack->Parameters.Others.Argument1 = &Context->Urb;
IoSetCompletionRoutine(Irp,
CompletionRoutine,
Context,
TRUE,
TRUE,
TRUE);
return IoCallDriver(FDODeviceExtension->LowerDeviceObject, Irp);
}
PIRP_CONTEXT
USBSTOR_AllocateIrpContext()
{
PIRP_CONTEXT Context;
Context = (PIRP_CONTEXT)AllocateItem(NonPagedPool, sizeof(IRP_CONTEXT));
if (!Context)
{
return NULL;
}
Context->cbw = (PCBW)AllocateItem(NonPagedPool, 512);
if (!Context->cbw)
{
FreeItem(Context);
return NULL;
}
return Context;
}
static
BOOLEAN
USBSTOR_IsCSWValid(
PIRP_CONTEXT Context)
{
if (Context->csw->Signature != CSW_SIGNATURE)
{
DPRINT1("[USBSTOR] Expected Signature %x but got %x\n", CSW_SIGNATURE, Context->csw->Signature);
return FALSE;
}
if (Context->csw->Tag != (ULONG_PTR)Context->csw)
{
DPRINT1("[USBSTOR] Expected Tag %Ix but got %x\n", (ULONG_PTR)Context->csw, Context->csw->Tag);
return FALSE;
}
return TRUE;
}
NTSTATUS
USBSTOR_QueueWorkItem(
PIRP_CONTEXT Context,
PIRP Irp)
{
PERRORHANDLER_WORKITEM_DATA ErrorHandlerWorkItemData;
ErrorHandlerWorkItemData = ExAllocatePoolWithTag(NonPagedPool, sizeof(ERRORHANDLER_WORKITEM_DATA), USB_STOR_TAG);
if (!ErrorHandlerWorkItemData)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
// error handling started
Context->FDODeviceExtension->SrbErrorHandlingActive = TRUE;
// srb error handling finished
Context->FDODeviceExtension->TimerWorkQueueEnabled = FALSE;
// Initialize and queue the work item to handle the error
ExInitializeWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem,
ErrorHandlerWorkItemRoutine,
ErrorHandlerWorkItemData);
ErrorHandlerWorkItemData->DeviceObject = Context->FDODeviceExtension->FunctionalDeviceObject;
ErrorHandlerWorkItemData->Context = Context;
ErrorHandlerWorkItemData->Irp = Irp;
ErrorHandlerWorkItemData->DeviceObject = Context->FDODeviceExtension->FunctionalDeviceObject;
DPRINT1("Queuing WorkItemROutine\n");
ExQueueWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem, DelayedWorkQueue);
return STATUS_MORE_PROCESSING_REQUIRED;
}
IO_COMPLETION_ROUTINE USBSTOR_CSWCompletionRoutine;
NTSTATUS
NTAPI
USBSTOR_CSWCompletionRoutine(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Ctx)
{
PIRP_CONTEXT Context;
PIO_STACK_LOCATION IoStack;
PSCSI_REQUEST_BLOCK Request;
PUFI_CAPACITY_RESPONSE Response;
NTSTATUS Status;
Context = (PIRP_CONTEXT)Ctx;
DPRINT("USBSTOR_CSWCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
// first check for Irp errors
if (!NT_SUCCESS(Irp->IoStatus.Status))
{
if (USBD_STATUS(Context->Urb.UrbHeader.Status) == USBD_STATUS(USBD_STATUS_STALL_PID))
{
if (Context->RetryCount < 2)
{
++Context->RetryCount;
// clear stall and resend cbw
Context->ErrorIndex = 1;
Status = USBSTOR_QueueWorkItem(Context, Irp);
ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
return STATUS_MORE_PROCESSING_REQUIRED;
}
}
else
{
DPRINT1("USBSTOR_CSWCompletionRoutine: Urb.Hdr.Status - %x\n", Context->Urb.UrbHeader.Status);
}
// perform reset recovery
Context->ErrorIndex = 2;
Status = USBSTOR_QueueWorkItem(Context, NULL);
ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
return STATUS_MORE_PROCESSING_REQUIRED;
}
// now check the CSW packet validity
if (!USBSTOR_IsCSWValid(Context) || Context->csw->Status == CSW_STATUS_PHASE_ERROR)
{
// perform reset recovery
Context->ErrorIndex = 2;
Status = USBSTOR_QueueWorkItem(Context, NULL);
ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
return STATUS_MORE_PROCESSING_REQUIRED;
}
IoStack = IoGetCurrentIrpStackLocation(Irp);
Request = IoStack->Parameters.Scsi.Srb;
ASSERT(Request);
// finally check for CSW errors
if (Context->csw->Status == CSW_STATUS_COMMAND_PASSED)
{
// read capacity needs special work
if (Request->Cdb[0] == SCSIOP_READ_CAPACITY)
{
// get output buffer
Response = (PUFI_CAPACITY_RESPONSE)Context->TransferData;
// store in pdo
Context->PDODeviceExtension->BlockLength = NTOHL(Response->BlockLength);
Context->PDODeviceExtension->LastLogicBlockAddress = NTOHL(Response->LastLogicalBlockAddress);
}
Status = USBSTOR_SrbStatusToNtStatus(Request);
}
else if (Context->csw->Status == CSW_STATUS_COMMAND_FAILED)
{
// the command is correct but with failed status - issue request sense
DPRINT("USBSTOR_CSWCompletionRoutine: CSW_STATUS_COMMAND_FAILED\n");
ASSERT(Context->FDODeviceExtension->ActiveSrb == Request);
// setting a generic error status, additional information
// should be read by higher-level driver from SenseInfoBuffer
Request->SrbStatus = SRB_STATUS_ERROR;
Request->ScsiStatus = 2;
Request->DataTransferLength = 0;
DPRINT("Flags: %x SBL: %x, buf: %p\n", Request->SrbFlags, Request->SenseInfoBufferLength, Request->SenseInfoBuffer);
if (!(Request->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
Request->SenseInfoBufferLength &&
Request->SenseInfoBuffer)
{
// TODO: issue request sense
}
Status = STATUS_IO_DEVICE_ERROR;
}
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = Request->DataTransferLength;
FreeItem(Context->cbw);
// terminate current request
USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Irp);
USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject);
FreeItem(Context);
return Status;
}
NTSTATUS
USBSTOR_SendCSWRequest(
PIRP_CONTEXT Context,
PIRP Irp)
{
return USBSTOR_IssueBulkOrInterruptRequest(Context->FDODeviceExtension,
Irp,
Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle,
USBD_TRANSFER_DIRECTION_IN,
sizeof(CSW),
Context->csw,
NULL,
USBSTOR_CSWCompletionRoutine,
Context);
}
IO_COMPLETION_ROUTINE USBSTOR_DataCompletionRoutine;
NTSTATUS
NTAPI
USBSTOR_DataCompletionRoutine(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Ctx)
{
PIRP_CONTEXT Context;
NTSTATUS Status;
PIO_STACK_LOCATION IoStack;
PSCSI_REQUEST_BLOCK Request;
DPRINT("USBSTOR_DataCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
Context = (PIRP_CONTEXT)Ctx;
IoStack = IoGetCurrentIrpStackLocation(Irp);
Request = IoStack->Parameters.Scsi.Srb;
if (Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferMDL != Irp->MdlAddress)
{
IoFreeMdl(Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferMDL);
}
if (NT_SUCCESS(Irp->IoStatus.Status))
{
if (Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength < Request->DataTransferLength)
{
Request->SrbStatus = SRB_STATUS_DATA_OVERRUN;
}
else
{
Request->SrbStatus = SRB_STATUS_SUCCESS;
}
Request->DataTransferLength = Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength;
USBSTOR_SendCSWRequest(Context, Irp);
}
else if (USBD_STATUS(Context->Urb.UrbHeader.Status) == USBD_STATUS(USBD_STATUS_STALL_PID))
{
++Context->RetryCount;
Request->SrbStatus = SRB_STATUS_DATA_OVERRUN;
Request->DataTransferLength = Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength;
// clear stall and resend cbw
Context->ErrorIndex = 1;
Status = USBSTOR_QueueWorkItem(Context, Irp);
ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
}
else
{
// perform reset recovery
Context->ErrorIndex = 2;
Status = USBSTOR_QueueWorkItem(Context, NULL);
ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
IO_COMPLETION_ROUTINE USBSTOR_CBWCompletionRoutine;
NTSTATUS
NTAPI
USBSTOR_CBWCompletionRoutine(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Ctx)
{
PIRP_CONTEXT Context;
PIO_STACK_LOCATION IoStack;
PSCSI_REQUEST_BLOCK Request;
USBD_PIPE_HANDLE PipeHandle;
ULONG TransferFlags;
PMDL Mdl = NULL;
PVOID TransferBuffer = NULL;
NTSTATUS Status;
DPRINT("USBSTOR_CBWCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
Context = (PIRP_CONTEXT)Ctx;
IoStack = IoGetCurrentIrpStackLocation(Irp);
Request = IoStack->Parameters.Scsi.Srb;
if (!NT_SUCCESS(Irp->IoStatus.Status))
{
goto ResetRecovery;
}
// a request without the buffer
if (!Irp->MdlAddress)
{
Request->SrbStatus = SRB_STATUS_SUCCESS;
USBSTOR_SendCSWRequest(Context, Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
// a request with the data buffer
if ((Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_IN)
{
PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle;
TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
}
else if ((Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_OUT)
{
PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle;
TransferFlags = USBD_TRANSFER_DIRECTION_OUT;
}
else
{
// we check the validity of a request in disk.c so we should never be here
DPRINT1("Warning: shouldn't be here\n");
goto ResetRecovery;
}
if (MmGetMdlVirtualAddress(Irp->MdlAddress) == Request->DataBuffer)
{
Mdl = Irp->MdlAddress;
}
else
{
Mdl = IoAllocateMdl(Request->DataBuffer,
Request->DataTransferLength,
FALSE,
FALSE,
NULL);
if (Mdl)
{
IoBuildPartialMdl(Irp->MdlAddress,
Mdl,
Request->DataBuffer,
Request->DataTransferLength);
}
}
if (!Mdl)
{
DPRINT1("USBSTOR_DataTransfer: Mdl - %p\n", Mdl);
goto ResetRecovery;
}
USBSTOR_IssueBulkOrInterruptRequest(Context->FDODeviceExtension,
Irp,
PipeHandle,
TransferFlags,
Request->DataTransferLength,
TransferBuffer,
Mdl,
USBSTOR_DataCompletionRoutine,
Context);
return STATUS_MORE_PROCESSING_REQUIRED;
ResetRecovery:
Context->ErrorIndex = 2;
Status = USBSTOR_QueueWorkItem(Context, NULL);
ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
DumpCBW(
PUCHAR Block)
{
DPRINT("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
Block[0] & 0xFF, Block[1] & 0xFF, Block[2] & 0xFF, Block[3] & 0xFF, Block[4] & 0xFF, Block[5] & 0xFF, Block[6] & 0xFF, Block[7] & 0xFF, Block[8] & 0xFF, Block[9] & 0xFF,
Block[10] & 0xFF, Block[11] & 0xFF, Block[12] & 0xFF, Block[13] & 0xFF, Block[14] & 0xFF, Block[15] & 0xFF, Block[16] & 0xFF, Block[17] & 0xFF, Block[18] & 0xFF, Block[19] & 0xFF,
Block[20] & 0xFF, Block[21] & 0xFF, Block[22] & 0xFF, Block[23] & 0xFF, Block[24] & 0xFF, Block[25] & 0xFF, Block[26] & 0xFF, Block[27] & 0xFF, Block[28] & 0xFF, Block[29] & 0xFF,
Block[30] & 0xFF);
}
static
NTSTATUS
USBSTOR_SendCBWRequest(
IN PFDO_DEVICE_EXTENSION FDODeviceExtension,
IN PIRP Irp,
IN PIRP_CONTEXT Context)
{
PPDO_DEVICE_EXTENSION PDODeviceExtension;
PIO_STACK_LOCATION IoStack;
PSCSI_REQUEST_BLOCK Request;
if (!Context)
{
Context = USBSTOR_AllocateIrpContext();
if (!Context)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
RtlZeroMemory(Context->cbw, sizeof(CBW));
RtlZeroMemory(&Context->Urb, sizeof(URB));
}
IoStack = IoGetCurrentIrpStackLocation(Irp);
PDODeviceExtension = IoStack->DeviceObject->DeviceExtension;
Request = IoStack->Parameters.Scsi.Srb;
Context->cbw->Signature = CBW_SIGNATURE;
Context->cbw->Tag = PtrToUlong(Context->cbw);
Context->cbw->DataTransferLength = Request->DataTransferLength;
Context->cbw->Flags = ((UCHAR)Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) << 1;
Context->cbw->LUN = PDODeviceExtension->LUN;
Context->cbw->CommandBlockLength = Request->CdbLength;
RtlCopyMemory(Context->cbw->CommandBlock, Request->Cdb, Request->CdbLength);
DPRINT("CBW %p\n", Context->cbw);
DumpCBW((PUCHAR)Context->cbw);
// initialize rest of context
Context->Irp = Irp;
Context->TransferData = Request->DataBuffer;
Context->TransferDataLength = Request->DataTransferLength;
Context->FDODeviceExtension = FDODeviceExtension;
Context->PDODeviceExtension = PDODeviceExtension;
Context->RetryCount = 0;
return USBSTOR_IssueBulkOrInterruptRequest(
FDODeviceExtension,
Irp,
FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle,
USBD_TRANSFER_DIRECTION_OUT,
sizeof(CBW),
Context->cbw,
NULL,
USBSTOR_CBWCompletionRoutine,
Context);
}
NTSTATUS
USBSTOR_HandleExecuteSCSI(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG RetryCount)
{
PCDB pCDB;
NTSTATUS Status;
PIO_STACK_LOCATION IoStack;
PSCSI_REQUEST_BLOCK Request;
PPDO_DEVICE_EXTENSION PDODeviceExtension;
PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
IoStack = IoGetCurrentIrpStackLocation(Irp);
Request = IoStack->Parameters.Scsi.Srb;
pCDB = (PCDB)Request->Cdb;
DPRINT("USBSTOR_HandleExecuteSCSI Operation Code %x, Length %lu\n", pCDB->CDB10.OperationCode, Request->DataTransferLength);
// check that we're sending to the right LUN
ASSERT(pCDB->CDB10.LogicalUnitNumber == (PDODeviceExtension->LUN & MAX_LUN));
Status = USBSTOR_SendCBWRequest(PDODeviceExtension->LowerDeviceObject->DeviceExtension, Irp, NULL);
return Status;
}