/*
* PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver
- * LICENSE: GPL - See COPYING in the top level directory
- * FILE: drivers/usb/usbstor/error.c
+ * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: USB block storage device driver.
- * PROGRAMMERS:
- * James Tabor
- * Michael Martin (michael.martin@reactos.org)
- * Johannes Anderwald (johannes.anderwald@reactos.org)
+ * COPYRIGHT: 2005-2006 James Tabor
+ * 2011-2012 Michael Martin (michael.martin@reactos.org)
+ * 2011-2013 Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#include "usbstor.h"
+#define NDEBUG
+#include <debug.h>
+
+
NTSTATUS
USBSTOR_GetEndpointStatus(
IN PDEVICE_OBJECT DeviceObject,
PURB Urb;
NTSTATUS Status;
- //
- // allocate urb
- //
- DPRINT1("Allocating URB\n");
+ DPRINT("Allocating URB\n");
Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
if (!Urb)
{
- //
- // out of memory
- //
DPRINT1("OutofMemory!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
- //
// build status
- //
- UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_ENDPOINT, bEndpointAddress & 0x0F, Value, NULL, NULL);
+ UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_ENDPOINT, bEndpointAddress & 0x0F, Value, NULL, NULL);
- //
// send the request
- //
- DPRINT1("Sending Request DeviceObject %x, Urb %x\n", DeviceObject, Urb);
+ DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb);
Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
- //
- // free urb
- //
FreeItem(Urb);
-
- //
- // done
- //
return Status;
}
-
-
NTSTATUS
USBSTOR_ResetPipeWithHandle(
IN PDEVICE_OBJECT DeviceObject,
PURB Urb;
NTSTATUS Status;
- //
- // allocate urb
- //
- DPRINT1("Allocating URB\n");
+ DPRINT("Allocating URB\n");
Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
if (!Urb)
{
- //
- // out of memory
- //
DPRINT1("OutofMemory!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
- //
- // initialize the urb
- //
Urb->UrbPipeRequest.Hdr.Length = sizeof(struct _URB_PIPE_REQUEST);
Urb->UrbPipeRequest.Hdr.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL;
Urb->UrbPipeRequest.PipeHandle = PipeHandle;
- //
// send the request
- //
- DPRINT1("Sending Request DeviceObject %x, Urb %x\n", DeviceObject, Urb);
+ DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb);
Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
- //
- // free urb
- //
FreeItem(Urb);
-
- //
- // done
- //
return Status;
}
-
NTSTATUS
USBSTOR_HandleTransferError(
PDEVICE_OBJECT DeviceObject,
PIRP_CONTEXT Context)
{
- NTSTATUS Status;
+ PPDO_DEVICE_EXTENSION PDODeviceExtension;
+ NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION Stack;
- USBD_PIPE_HANDLE PipeHandle;
PSCSI_REQUEST_BLOCK Request;
PCDB pCDB;
- DPRINT1("Entered Handle Transfer Error\n");
- //
- // Determine pipehandle
- //
- if (Context->cbw->CommandBlock[0] == SCSIOP_WRITE)
- {
- //
- // write request used bulk out pipe
- //
- PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle;
- }
- else
- {
- //
- // default bulk in pipe
- //
- PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle;
- }
+ ASSERT(Context);
+ ASSERT(Context->Irp);
- switch (Context->Urb.UrbHeader.Status)
+ // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
+ Status = USBSTOR_ResetDevice(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension);
+ if (NT_SUCCESS(Status))
{
- case USBD_STATUS_STALL_PID:
+ // step 2 reset bulk in pipe section 5.3.4
+ Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle);
+ if (NT_SUCCESS(Status))
{
- //
- // First attempt to reset the pipe
- //
- DPRINT1("Resetting Pipe\n");
- Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, PipeHandle);
- if (NT_SUCCESS(Status))
- {
- Status = STATUS_SUCCESS;
- break;
- }
-
- DPRINT1("Failed to reset pipe %x\n", Status);
-
- //
- // FIXME: Reset of pipe failed, attempt to reset port
- //
-
- Status = STATUS_UNSUCCESSFUL;
- break;
- }
- //
- // FIXME: Handle more errors
- //
- default:
- {
- DPRINT1("Error not handled\n");
- Status = STATUS_UNSUCCESSFUL;
+ // finally reset bulk out pipe
+ Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
}
}
Stack = IoGetCurrentIrpStackLocation(Context->Irp);
+ ASSERT(Stack->DeviceObject);
+ PDODeviceExtension = (PPDO_DEVICE_EXTENSION)Stack->DeviceObject->DeviceExtension;
+
Request = (PSCSI_REQUEST_BLOCK)Stack->Parameters.Others.Argument1;
+ ASSERT(Request);
+
+ // obtain request type
pCDB = (PCDB)Request->Cdb;
- if (Status != STATUS_SUCCESS)
+ ASSERT(pCDB);
+
+ if (!NT_SUCCESS(Status))
{
- /* Complete the master IRP */
+ // Complete the master IRP
Context->Irp->IoStatus.Status = Status;
Context->Irp->IoStatus.Information = 0;
- USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Context->Irp);
+ USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Context->Irp);
IoCompleteRequest(Context->Irp, IO_NO_INCREMENT);
- /* Start the next request */
- USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject);
+ // Start the next request
+ USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject);
- /* Signal the context event */
- if (Context->Event)
- KeSetEvent(Context->Event, 0, FALSE);
+ // srb handling finished
+ Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE;
- /* Cleanup the IRP context */
- if (pCDB->AsByte[0] == SCSIOP_READ_CAPACITY)
- FreeItem(Context->TransferData);
- FreeItem(Context->cbw);
- FreeItem(Context);
+ // clear timer srb
+ Context->FDODeviceExtension->LastTimerActiveSrb = NULL;
}
- else
- {
- DPRINT1("Retrying\n");
- Status = USBSTOR_HandleExecuteSCSI(*Context->PDODeviceExtension->PDODeviceObject, Context->Irp);
-
- /* Cleanup the old IRP context */
- if (pCDB->AsByte[0] == SCSIOP_READ_CAPACITY)
- FreeItem(Context->TransferData);
- FreeItem(Context->cbw);
- FreeItem(Context);
- }
+ FreeItem(Context);
DPRINT1("USBSTOR_HandleTransferError returning with Status %x\n", Status);
return Status;
VOID
NTAPI
-ErrorHandlerWorkItemRoutine(
+USBSTOR_ResetHandlerWorkItemRoutine(
PVOID Context)
{
NTSTATUS Status;
PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
-
- Status = USBSTOR_HandleTransferError(WorkItemData->DeviceObject, WorkItemData->Context);
- //
+ // clear stall on BulkIn pipe
+ Status = USBSTOR_ResetPipeWithHandle(WorkItemData->Context->FDODeviceExtension->LowerDeviceObject, WorkItemData->Context->FDODeviceExtension->InterfaceInformation->Pipes[WorkItemData->Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle);
+ DPRINT1("USBSTOR_ResetPipeWithHandle Status %x\n", Status);
+
+ // now resend the csw as the stall got cleared
+ USBSTOR_SendCSWRequest(WorkItemData->Context, WorkItemData->Irp);
+}
+
+VOID
+NTAPI
+ErrorHandlerWorkItemRoutine(
+ PVOID Context)
+{
+ PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
+
+ if (WorkItemData->Context->ErrorIndex == 2)
+ {
+ // reset device
+ USBSTOR_HandleTransferError(WorkItemData->DeviceObject, WorkItemData->Context);
+ }
+ else
+ {
+ // clear stall
+ USBSTOR_ResetHandlerWorkItemRoutine(WorkItemData);
+ }
+
// Free Work Item Data
- //
- ExFreePool(WorkItemData);
+ ExFreePoolWithTag(WorkItemData, USB_STOR_TAG);
+}
+
+VOID
+NTAPI
+USBSTOR_TimerWorkerRoutine(
+ IN PVOID Context)
+{
+ PFDO_DEVICE_EXTENSION FDODeviceExtension;
+ NTSTATUS Status;
+ PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
+
+ FDODeviceExtension = (PFDO_DEVICE_EXTENSION)WorkItemData->DeviceObject->DeviceExtension;
+ ASSERT(FDODeviceExtension->Common.IsFDO);
+
+ // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
+ Status = USBSTOR_ResetDevice(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension);
+ if (NT_SUCCESS(Status))
+ {
+ // step 2 reset bulk in pipe section 5.3.4
+ Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle);
+ if (NT_SUCCESS(Status))
+ {
+ // finally reset bulk out pipe
+ Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
+ }
+ }
+ DPRINT1("Status %x\n", Status);
+
+ // clear timer srb
+ FDODeviceExtension->LastTimerActiveSrb = NULL;
+
+ // re-schedule request
+ //USBSTOR_HandleExecuteSCSI(WorkItemData->Context->PDODeviceExtension->Self, WorkItemData->Context->Irp, Context->RetryCount + 1);
+
+ // do not retry for the same packet again
+ FDODeviceExtension->TimerWorkQueueEnabled = FALSE;
+
+ ExFreePoolWithTag(WorkItemData, USB_STOR_TAG);
+}
+
+VOID
+NTAPI
+USBSTOR_TimerRoutine(
+ PDEVICE_OBJECT DeviceObject,
+ PVOID Context)
+{
+ PFDO_DEVICE_EXTENSION FDODeviceExtension;
+ BOOLEAN ResetDevice = FALSE;
+ PERRORHANDLER_WORKITEM_DATA WorkItemData;
+
+ FDODeviceExtension = (PFDO_DEVICE_EXTENSION)Context;
+ DPRINT1("[USBSTOR] TimerRoutine entered\n");
+ DPRINT1("[USBSTOR] ActiveSrb %p ResetInProgress %x LastTimerActiveSrb %p\n", FDODeviceExtension->ActiveSrb, FDODeviceExtension->ResetInProgress, FDODeviceExtension->LastTimerActiveSrb);
+
+ KeAcquireSpinLockAtDpcLevel(&FDODeviceExtension->IrpListLock);
+
+ // is there an active srb and no global reset is in progress
+ if (FDODeviceExtension->ActiveSrb && FDODeviceExtension->ResetInProgress == FALSE && FDODeviceExtension->TimerWorkQueueEnabled)
+ {
+ if (FDODeviceExtension->LastTimerActiveSrb != NULL && FDODeviceExtension->LastTimerActiveSrb == FDODeviceExtension->ActiveSrb)
+ {
+ // check if empty
+ DPRINT1("[USBSTOR] ActiveSrb %p hang detected\n", FDODeviceExtension->ActiveSrb);
+ ResetDevice = TRUE;
+ }
+ else
+ {
+ // update pointer
+ FDODeviceExtension->LastTimerActiveSrb = FDODeviceExtension->ActiveSrb;
+ }
+ }
+ else
+ {
+ // reset srb
+ FDODeviceExtension->LastTimerActiveSrb = NULL;
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&FDODeviceExtension->IrpListLock);
+
+
+ if (ResetDevice && FDODeviceExtension->TimerWorkQueueEnabled && FDODeviceExtension->SrbErrorHandlingActive == FALSE)
+ {
+ WorkItemData = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(ERRORHANDLER_WORKITEM_DATA),
+ USB_STOR_TAG);
+ if (WorkItemData)
+ {
+ // Initialize and queue the work item to handle the error
+ ExInitializeWorkItem(&WorkItemData->WorkQueueItem,
+ USBSTOR_TimerWorkerRoutine,
+ WorkItemData);
+
+ WorkItemData->DeviceObject = FDODeviceExtension->FunctionalDeviceObject;
+
+ DPRINT1("[USBSTOR] Queing Timer WorkItem\n");
+ ExQueueWorkItem(&WorkItemData->WorkQueueItem, DelayedWorkQueue);
+ }
+ }
}