reactos/drivers/usb/usbstor/error.c
Victor Perevertkin 7bb93db372 [USBSTOR] Refactor device reset and pipe reset code.
Return correct status codes from completion routines.
Reject requests while the device is being reset
2019-06-11 04:39:43 +03:00

263 lines
8.6 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)
* 2019 Victor Perevertkin (victor.perevertkin@reactos.org)
*/
#include "usbstor.h"
#define NDEBUG
#include <debug.h>
NTSTATUS
USBSTOR_GetEndpointStatus(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR bEndpointAddress,
OUT PUSHORT Value)
{
PURB Urb;
NTSTATUS Status;
DPRINT("Allocating URB\n");
Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
if (!Urb)
{
DPRINT1("OutofMemory!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
// build status
UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_ENDPOINT, bEndpointAddress & 0x0F, Value, NULL, NULL);
// send the request
DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb);
Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
FreeItem(Urb);
return Status;
}
NTSTATUS
USBSTOR_ResetPipeWithHandle(
IN PDEVICE_OBJECT DeviceObject,
IN USBD_PIPE_HANDLE PipeHandle)
{
PURB Urb;
NTSTATUS Status;
DPRINT("Allocating URB\n");
Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
if (!Urb)
{
DPRINT1("OutofMemory!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
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 %p, Urb %p\n", DeviceObject, Urb);
Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
FreeItem(Urb);
return Status;
}
VOID
NTAPI
USBSTOR_ResetPipeWorkItemRoutine(
IN PDEVICE_OBJECT FdoDevice,
IN PVOID Ctx)
{
NTSTATUS Status;
PIRP_CONTEXT Context = (PIRP_CONTEXT)Ctx;
PFDO_DEVICE_EXTENSION FDODeviceExtension = (PFDO_DEVICE_EXTENSION)FdoDevice->DeviceExtension;
// clear stall on the corresponding pipe
Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, Context->Urb.UrbBulkOrInterruptTransfer.PipeHandle);
DPRINT1("USBSTOR_ResetPipeWithHandle Status %x\n", Status);
// now resend the csw as the stall got cleared
USBSTOR_SendCSWRequest(Context, Context->Irp);
}
VOID
NTAPI
USBSTOR_ResetDeviceWorkItemRoutine(
IN PDEVICE_OBJECT FdoDevice,
IN PVOID Context)
{
PFDO_DEVICE_EXTENSION FDODeviceExtension;
UINT32 ix;
NTSTATUS Status;
KIRQL OldIrql;
DPRINT("USBSTOR_ResetDeviceWorkItemRoutine\n");
FDODeviceExtension = FdoDevice->DeviceExtension;
for (ix = 0; ix < 3; ++ix)
{
// 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);
if (NT_SUCCESS(Status))
{
break;
}
}
}
}
KeAcquireSpinLock(&FDODeviceExtension->CommonLock, &OldIrql);
FDODeviceExtension->Flags &= ~USBSTOR_FDO_FLAGS_DEVICE_RESETTING;
KeReleaseSpinLock(&FDODeviceExtension->CommonLock, OldIrql);
USBSTOR_QueueNextRequest(FdoDevice);
}
VOID
NTAPI
USBSTOR_QueueResetPipe(
IN PFDO_DEVICE_EXTENSION FDODeviceExtension,
IN PIRP_CONTEXT Context)
{
DPRINT("USBSTOR_QueueResetPipe\n");
IoQueueWorkItem(FDODeviceExtension->ResetDeviceWorkItem,
USBSTOR_ResetPipeWorkItemRoutine,
CriticalWorkQueue,
Context);
}
VOID
NTAPI
USBSTOR_QueueResetDevice(
IN PFDO_DEVICE_EXTENSION FDODeviceExtension)
{
KIRQL OldIrql;
DPRINT("USBSTOR_QueueResetDevice\n");
KeAcquireSpinLock(&FDODeviceExtension->CommonLock, &OldIrql);
FDODeviceExtension->Flags |= USBSTOR_FDO_FLAGS_DEVICE_RESETTING;
KeReleaseSpinLock(&FDODeviceExtension->CommonLock, OldIrql);
IoQueueWorkItem(FDODeviceExtension->ResetDeviceWorkItem,
USBSTOR_ResetDeviceWorkItemRoutine,
CriticalWorkQueue,
NULL);
}
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);
}
}
}