/* * 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 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); } } }