mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 21:13:52 +00:00
410 lines
11 KiB
C
410 lines
11 KiB
C
/*
|
|
* PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: drivers/usb/usbstor/error.c
|
|
* PURPOSE: USB block storage device driver.
|
|
* PROGRAMMERS:
|
|
* James Tabor
|
|
* Michael Martin (michael.martin@reactos.org)
|
|
* Johannes Anderwald (johannes.anderwald@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;
|
|
|
|
//
|
|
// allocate urb
|
|
//
|
|
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);
|
|
|
|
//
|
|
// send the request
|
|
//
|
|
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,
|
|
IN USBD_PIPE_HANDLE PipeHandle)
|
|
{
|
|
PURB Urb;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// allocate urb
|
|
//
|
|
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 %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 = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION Stack;
|
|
PSCSI_REQUEST_BLOCK Request;
|
|
PCDB pCDB;
|
|
|
|
//
|
|
// sanity checks
|
|
//
|
|
ASSERT(Context);
|
|
ASSERT(Context->PDODeviceExtension);
|
|
ASSERT(Context->PDODeviceExtension->Self);
|
|
ASSERT(Context->Irp);
|
|
|
|
//
|
|
// 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))
|
|
{
|
|
//
|
|
// 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))
|
|
{
|
|
//
|
|
// finally reset bulk out pipe
|
|
//
|
|
Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
|
|
}
|
|
}
|
|
|
|
//
|
|
// get next stack location
|
|
//
|
|
Stack = IoGetCurrentIrpStackLocation(Context->Irp);
|
|
|
|
//
|
|
// get request block
|
|
//
|
|
Request = (PSCSI_REQUEST_BLOCK)Stack->Parameters.Others.Argument1;
|
|
ASSERT(Request);
|
|
|
|
//
|
|
// obtain request type
|
|
//
|
|
pCDB = (PCDB)Request->Cdb;
|
|
ASSERT(pCDB);
|
|
|
|
if (Status != STATUS_SUCCESS || Context->RetryCount >= 1)
|
|
{
|
|
//
|
|
// Complete the master IRP
|
|
//
|
|
Context->Irp->IoStatus.Status = Status;
|
|
Context->Irp->IoStatus.Information = 0;
|
|
USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Context->Irp);
|
|
IoCompleteRequest(Context->Irp, IO_NO_INCREMENT);
|
|
|
|
//
|
|
// Start the next request
|
|
//
|
|
USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject);
|
|
|
|
//
|
|
// srb handling finished
|
|
//
|
|
Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE;
|
|
|
|
//
|
|
// clear timer srb
|
|
//
|
|
Context->FDODeviceExtension->LastTimerActiveSrb = NULL;
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Retrying Count %lu %p\n", Context->RetryCount, Context->PDODeviceExtension->Self);
|
|
|
|
//
|
|
// re-schedule request
|
|
//
|
|
USBSTOR_HandleExecuteSCSI(Context->PDODeviceExtension->Self, Context->Irp, Context->RetryCount + 1);
|
|
|
|
//
|
|
// srb error handling finished
|
|
//
|
|
Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE;
|
|
|
|
//
|
|
// srb error handling finished
|
|
//
|
|
Context->FDODeviceExtension->TimerWorkQueueEnabled = TRUE;
|
|
|
|
//
|
|
// clear timer srb
|
|
//
|
|
Context->FDODeviceExtension->LastTimerActiveSrb = NULL;
|
|
}
|
|
|
|
//
|
|
// cleanup irp context
|
|
//
|
|
FreeItem(Context->cbw);
|
|
FreeItem(Context);
|
|
|
|
|
|
DPRINT1("USBSTOR_HandleTransferError returning with Status %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
USBSTOR_ResetHandlerWorkItemRoutine(
|
|
PVOID Context)
|
|
{
|
|
NTSTATUS Status;
|
|
PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)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_SendCSW(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
|
|
//
|
|
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;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
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;
|
|
|
|
//
|
|
// Free Work Item Data
|
|
//
|
|
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;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
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);
|
|
|
|
//
|
|
// acquire spinlock
|
|
//
|
|
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;
|
|
}
|
|
|
|
//
|
|
// release lock
|
|
//
|
|
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);
|
|
}
|
|
}
|
|
}
|