/* * 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 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); } 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 != PtrToUlong(&Context->csw)) { DPRINT1("[USBSTOR] Expected Tag %Ix but got %x\n", PtrToUlong(&Context->csw), Context->csw.Tag); return FALSE; } return TRUE; } static NTSTATUS USBSTOR_IssueRequestSense( IN PFDO_DEVICE_EXTENSION FDODeviceExtension, IN PIRP Irp, IN PIRP_CONTEXT Context); IO_COMPLETION_ROUTINE USBSTOR_CSWCompletionRoutine; NTSTATUS NTAPI USBSTOR_CSWCompletionRoutine( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Ctx) { PIRP_CONTEXT Context; PIO_STACK_LOCATION IoStack; PPDO_DEVICE_EXTENSION PDODeviceExtension; PFDO_DEVICE_EXTENSION FDODeviceExtension; PSCSI_REQUEST_BLOCK Request; PUFI_CAPACITY_RESPONSE Response; Context = (PIRP_CONTEXT)Ctx; DPRINT("USBSTOR_CSWCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status); IoStack = IoGetCurrentIrpStackLocation(Irp); PDODeviceExtension = (PPDO_DEVICE_EXTENSION)IoStack->DeviceObject->DeviceExtension; FDODeviceExtension = Context->FDODeviceExtension; Request = IoStack->Parameters.Scsi.Srb; ASSERT(Request); // 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->StallRetryCount < 2) { ++Context->StallRetryCount; // clear stall and resend cbw USBSTOR_QueueResetPipe(FDODeviceExtension, Context); return STATUS_MORE_PROCESSING_REQUIRED; } } else { DPRINT1("USBSTOR_CSWCompletionRoutine: Urb.Hdr.Status - %x\n", Context->Urb.UrbHeader.Status); } goto ResetRecovery; } // now check the CSW packet validity if (!USBSTOR_IsCSWValid(Context) || Context->csw.Status == CSW_STATUS_PHASE_ERROR) { goto ResetRecovery; } // finally check for CSW errors if (Context->csw.Status == CSW_STATUS_COMMAND_PASSED) { // should happen only when a sense request was sent if (Request != FDODeviceExtension->ActiveSrb) { ASSERT(IoStack->Parameters.Scsi.Srb == &Context->SenseSrb); FDODeviceExtension->ActiveSrb->SenseInfoBufferLength = Request->DataTransferLength; Request = FDODeviceExtension->ActiveSrb; IoStack->Parameters.Scsi.Srb = Request; Request->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; } // read capacity needs special work if (Request->Cdb[0] == SCSIOP_READ_CAPACITY) { // get output buffer Response = (PUFI_CAPACITY_RESPONSE)Request->DataBuffer; // store in pdo PDODeviceExtension->BlockLength = NTOHL(Response->BlockLength); PDODeviceExtension->LastLogicBlockAddress = NTOHL(Response->LastLogicalBlockAddress); } Irp->IoStatus.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(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) { USBSTOR_IssueRequestSense(FDODeviceExtension, Irp, Context); return STATUS_MORE_PROCESSING_REQUIRED; } Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; } Irp->IoStatus.Information = Request->DataTransferLength; // terminate current request USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp); USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject); ExFreePoolWithTag(Context, USB_STOR_TAG); return STATUS_CONTINUE_COMPLETION; ResetRecovery: Request = FDODeviceExtension->ActiveSrb; IoStack->Parameters.Scsi.Srb = Request; Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; Request->SrbStatus = SRB_STATUS_BUS_RESET; USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp); USBSTOR_QueueResetDevice(FDODeviceExtension); ExFreePoolWithTag(Context, USB_STOR_TAG); return STATUS_CONTINUE_COMPLETION; } 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; PIO_STACK_LOCATION IoStack; PSCSI_REQUEST_BLOCK Request; PPDO_DEVICE_EXTENSION PDODeviceExtension; 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; PDODeviceExtension = (PPDO_DEVICE_EXTENSION)IoStack->DeviceObject->DeviceExtension; // for Sense Request a partial MDL was already freed (if existed) if (Request == Context->FDODeviceExtension->ActiveSrb && 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->StallRetryCount; Request->SrbStatus = SRB_STATUS_DATA_OVERRUN; Request->DataTransferLength = Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength; // clear stall and resend cbw USBSTOR_QueueResetPipe(Context->FDODeviceExtension, Context); } else { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; Request->SrbStatus = SRB_STATUS_BUS_RESET; USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp); USBSTOR_QueueResetDevice(Context->FDODeviceExtension); ExFreePoolWithTag(Context, USB_STOR_TAG); return STATUS_CONTINUE_COMPLETION; } 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; PPDO_DEVICE_EXTENSION PDODeviceExtension; USBD_PIPE_HANDLE PipeHandle; ULONG TransferFlags; PMDL Mdl = NULL; PVOID TransferBuffer = NULL; 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; PDODeviceExtension = (PPDO_DEVICE_EXTENSION)IoStack->DeviceObject->DeviceExtension; if (!NT_SUCCESS(Irp->IoStatus.Status)) { goto ResetRecovery; } // a request without the buffer AND not a sense request // for a sense request we provide just a TransferBuffer, an Mdl will be allocated by usbport (see below) if (!Irp->MdlAddress && Request == Context->FDODeviceExtension->ActiveSrb) { 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 it is not a Sense Request if (Request == Context->FDODeviceExtension->ActiveSrb) { 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_CBWCompletionRoutine: Mdl - %p\n", Mdl); goto ResetRecovery; } } else { ASSERT(Request->DataBuffer); TransferBuffer = Request->DataBuffer; } USBSTOR_IssueBulkOrInterruptRequest(Context->FDODeviceExtension, Irp, PipeHandle, TransferFlags, Request->DataTransferLength, TransferBuffer, Mdl, USBSTOR_DataCompletionRoutine, Context); return STATUS_MORE_PROCESSING_REQUIRED; ResetRecovery: Request = Context->FDODeviceExtension->ActiveSrb; IoStack->Parameters.Scsi.Srb = Request; Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; Request->SrbStatus = SRB_STATUS_BUS_RESET; USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp); USBSTOR_QueueResetDevice(Context->FDODeviceExtension); ExFreePoolWithTag(Context, USB_STOR_TAG); return STATUS_CONTINUE_COMPLETION; } 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; 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 for IRP %p\n", Irp); DumpCBW((PUCHAR)&Context->cbw); // initialize rest of context Context->Irp = Irp; Context->FDODeviceExtension = FDODeviceExtension; Context->StallRetryCount = 0; return USBSTOR_IssueBulkOrInterruptRequest( FDODeviceExtension, Irp, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle, USBD_TRANSFER_DIRECTION_OUT, sizeof(CBW), &Context->cbw, NULL, USBSTOR_CBWCompletionRoutine, Context); } static NTSTATUS USBSTOR_IssueRequestSense( IN PFDO_DEVICE_EXTENSION FDODeviceExtension, IN PIRP Irp, IN PIRP_CONTEXT Context) { PIO_STACK_LOCATION IoStack; PSCSI_REQUEST_BLOCK CurrentSrb; PSCSI_REQUEST_BLOCK SenseSrb; PCDB pCDB; DPRINT("USBSTOR_IssueRequestSense: \n"); CurrentSrb = FDODeviceExtension->ActiveSrb; SenseSrb = &Context->SenseSrb; IoStack = IoGetCurrentIrpStackLocation(Irp); IoStack->Parameters.Scsi.Srb = SenseSrb; RtlZeroMemory(SenseSrb, sizeof(*SenseSrb)); SenseSrb->Function = SRB_FUNCTION_EXECUTE_SCSI; SenseSrb->Length = sizeof(*SenseSrb); SenseSrb->CdbLength = CDB6GENERIC_LENGTH; SenseSrb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_DISABLE_AUTOSENSE; ASSERT(CurrentSrb->SenseInfoBufferLength); ASSERT(CurrentSrb->SenseInfoBuffer); DPRINT("SenseInfoBuffer %x, SenseInfoBufferLength %x\n", CurrentSrb->SenseInfoBuffer, CurrentSrb->SenseInfoBufferLength); SenseSrb->DataTransferLength = CurrentSrb->SenseInfoBufferLength; SenseSrb->DataBuffer = CurrentSrb->SenseInfoBuffer; pCDB = (PCDB)SenseSrb->Cdb; pCDB->CDB6GENERIC.OperationCode = SCSIOP_REQUEST_SENSE; pCDB->AsByte[4] = CurrentSrb->SenseInfoBufferLength; return USBSTOR_SendCBWRequest(FDODeviceExtension, Irp, Context); } NTSTATUS USBSTOR_HandleExecuteSCSI( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PCDB pCDB; NTSTATUS Status; PIO_STACK_LOCATION IoStack; PSCSI_REQUEST_BLOCK Request; PPDO_DEVICE_EXTENSION PDODeviceExtension; PIRP_CONTEXT Context; 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)); Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(IRP_CONTEXT), USB_STOR_TAG); if (!Context) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { Status = USBSTOR_SendCBWRequest(PDODeviceExtension->LowerDeviceObject->DeviceExtension, Irp, Context); } return Status; }