[USBSTOR] Do not try to retry a failed request
[reactos.git] / drivers / usb / usbstor / error.c
index be91d44..571c89d 100644 (file)
@@ -1,16 +1,18 @@
 /*
  * 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,
@@ -20,44 +22,25 @@ USBSTOR_GetEndpointStatus(
     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,
@@ -66,145 +49,83 @@ USBSTOR_ResetPipeWithHandle(
     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;
@@ -212,16 +133,136 @@ USBSTOR_HandleTransferError(
 
 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);
+        }
+     }
 }