1 /* 2 * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: USB block storage device driver. 5 * COPYRIGHT: 2005-2006 James Tabor 6 * 2011-2012 Michael Martin (michael.martin@reactos.org) 7 * 2011-2013 Johannes Anderwald (johannes.anderwald@reactos.org) 8 */ 9 10 #include "usbstor.h" 11 12 #define NDEBUG 13 #include <debug.h> 14 15 16 NTSTATUS 17 USBSTOR_GetEndpointStatus( 18 IN PDEVICE_OBJECT DeviceObject, 19 IN UCHAR bEndpointAddress, 20 OUT PUSHORT Value) 21 { 22 PURB Urb; 23 NTSTATUS Status; 24 25 DPRINT("Allocating URB\n"); 26 Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); 27 if (!Urb) 28 { 29 DPRINT1("OutofMemory!\n"); 30 return STATUS_INSUFFICIENT_RESOURCES; 31 } 32 33 // build status 34 UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_ENDPOINT, bEndpointAddress & 0x0F, Value, NULL, NULL); 35 36 // send the request 37 DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb); 38 Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb); 39 40 FreeItem(Urb); 41 return Status; 42 } 43 44 NTSTATUS 45 USBSTOR_ResetPipeWithHandle( 46 IN PDEVICE_OBJECT DeviceObject, 47 IN USBD_PIPE_HANDLE PipeHandle) 48 { 49 PURB Urb; 50 NTSTATUS Status; 51 52 DPRINT("Allocating URB\n"); 53 Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST)); 54 if (!Urb) 55 { 56 DPRINT1("OutofMemory!\n"); 57 return STATUS_INSUFFICIENT_RESOURCES; 58 } 59 60 Urb->UrbPipeRequest.Hdr.Length = sizeof(struct _URB_PIPE_REQUEST); 61 Urb->UrbPipeRequest.Hdr.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL; 62 Urb->UrbPipeRequest.PipeHandle = PipeHandle; 63 64 // send the request 65 DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb); 66 Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb); 67 68 FreeItem(Urb); 69 return Status; 70 } 71 72 NTSTATUS 73 USBSTOR_HandleTransferError( 74 PDEVICE_OBJECT DeviceObject, 75 PIRP_CONTEXT Context) 76 { 77 NTSTATUS Status = STATUS_SUCCESS; 78 PIO_STACK_LOCATION Stack; 79 PSCSI_REQUEST_BLOCK Request; 80 PCDB pCDB; 81 82 ASSERT(Context); 83 ASSERT(Context->PDODeviceExtension); 84 ASSERT(Context->PDODeviceExtension->Self); 85 ASSERT(Context->Irp); 86 87 // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification 88 Status = USBSTOR_ResetDevice(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension); 89 if (NT_SUCCESS(Status)) 90 { 91 // step 2 reset bulk in pipe section 5.3.4 92 Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle); 93 if (NT_SUCCESS(Status)) 94 { 95 // finally reset bulk out pipe 96 Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle); 97 } 98 } 99 100 Stack = IoGetCurrentIrpStackLocation(Context->Irp); 101 102 Request = (PSCSI_REQUEST_BLOCK)Stack->Parameters.Others.Argument1; 103 ASSERT(Request); 104 105 // obtain request type 106 pCDB = (PCDB)Request->Cdb; 107 ASSERT(pCDB); 108 109 if (Status != STATUS_SUCCESS || Context->RetryCount >= 1) 110 { 111 // Complete the master IRP 112 Context->Irp->IoStatus.Status = Status; 113 Context->Irp->IoStatus.Information = 0; 114 USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Context->Irp); 115 IoCompleteRequest(Context->Irp, IO_NO_INCREMENT); 116 117 // Start the next request 118 USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject); 119 120 // srb handling finished 121 Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE; 122 123 // clear timer srb 124 Context->FDODeviceExtension->LastTimerActiveSrb = NULL; 125 } 126 else 127 { 128 DPRINT1("Retrying Count %lu %p\n", Context->RetryCount, Context->PDODeviceExtension->Self); 129 130 // re-schedule request 131 USBSTOR_HandleExecuteSCSI(Context->PDODeviceExtension->Self, Context->Irp, Context->RetryCount + 1); 132 133 // srb error handling finished 134 Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE; 135 136 // srb error handling finished 137 Context->FDODeviceExtension->TimerWorkQueueEnabled = TRUE; 138 139 // clear timer srb 140 Context->FDODeviceExtension->LastTimerActiveSrb = NULL; 141 } 142 143 FreeItem(Context->cbw); 144 FreeItem(Context); 145 146 DPRINT1("USBSTOR_HandleTransferError returning with Status %x\n", Status); 147 return Status; 148 } 149 150 VOID 151 NTAPI 152 USBSTOR_ResetHandlerWorkItemRoutine( 153 PVOID Context) 154 { 155 NTSTATUS Status; 156 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context; 157 158 // clear stall on BulkIn pipe 159 Status = USBSTOR_ResetPipeWithHandle(WorkItemData->Context->FDODeviceExtension->LowerDeviceObject, WorkItemData->Context->FDODeviceExtension->InterfaceInformation->Pipes[WorkItemData->Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle); 160 DPRINT1("USBSTOR_ResetPipeWithHandle Status %x\n", Status); 161 162 // now resend the csw as the stall got cleared 163 USBSTOR_SendCSW(WorkItemData->Context, WorkItemData->Irp); 164 } 165 166 VOID 167 NTAPI 168 ErrorHandlerWorkItemRoutine( 169 PVOID Context) 170 { 171 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context; 172 173 if (WorkItemData->Context->ErrorIndex == 2) 174 { 175 // reset device 176 USBSTOR_HandleTransferError(WorkItemData->DeviceObject, WorkItemData->Context); 177 } 178 else 179 { 180 // clear stall 181 USBSTOR_ResetHandlerWorkItemRoutine(WorkItemData); 182 } 183 184 // Free Work Item Data 185 ExFreePoolWithTag(WorkItemData, USB_STOR_TAG); 186 } 187 188 VOID 189 NTAPI 190 USBSTOR_TimerWorkerRoutine( 191 IN PVOID Context) 192 { 193 PFDO_DEVICE_EXTENSION FDODeviceExtension; 194 NTSTATUS Status; 195 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context; 196 197 FDODeviceExtension = (PFDO_DEVICE_EXTENSION)WorkItemData->DeviceObject->DeviceExtension; 198 ASSERT(FDODeviceExtension->Common.IsFDO); 199 200 // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification 201 Status = USBSTOR_ResetDevice(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension); 202 if (NT_SUCCESS(Status)) 203 { 204 // step 2 reset bulk in pipe section 5.3.4 205 Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle); 206 if (NT_SUCCESS(Status)) 207 { 208 // finally reset bulk out pipe 209 Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle); 210 } 211 } 212 DPRINT1("Status %x\n", Status); 213 214 // clear timer srb 215 FDODeviceExtension->LastTimerActiveSrb = NULL; 216 217 // re-schedule request 218 //USBSTOR_HandleExecuteSCSI(WorkItemData->Context->PDODeviceExtension->Self, WorkItemData->Context->Irp, Context->RetryCount + 1); 219 220 // do not retry for the same packet again 221 FDODeviceExtension->TimerWorkQueueEnabled = FALSE; 222 223 ExFreePoolWithTag(WorkItemData, USB_STOR_TAG); 224 } 225 226 VOID 227 NTAPI 228 USBSTOR_TimerRoutine( 229 PDEVICE_OBJECT DeviceObject, 230 PVOID Context) 231 { 232 PFDO_DEVICE_EXTENSION FDODeviceExtension; 233 BOOLEAN ResetDevice = FALSE; 234 PERRORHANDLER_WORKITEM_DATA WorkItemData; 235 236 FDODeviceExtension = (PFDO_DEVICE_EXTENSION)Context; 237 DPRINT1("[USBSTOR] TimerRoutine entered\n"); 238 DPRINT1("[USBSTOR] ActiveSrb %p ResetInProgress %x LastTimerActiveSrb %p\n", FDODeviceExtension->ActiveSrb, FDODeviceExtension->ResetInProgress, FDODeviceExtension->LastTimerActiveSrb); 239 240 KeAcquireSpinLockAtDpcLevel(&FDODeviceExtension->IrpListLock); 241 242 // is there an active srb and no global reset is in progress 243 if (FDODeviceExtension->ActiveSrb && FDODeviceExtension->ResetInProgress == FALSE && FDODeviceExtension->TimerWorkQueueEnabled) 244 { 245 if (FDODeviceExtension->LastTimerActiveSrb != NULL && FDODeviceExtension->LastTimerActiveSrb == FDODeviceExtension->ActiveSrb) 246 { 247 // check if empty 248 DPRINT1("[USBSTOR] ActiveSrb %p hang detected\n", FDODeviceExtension->ActiveSrb); 249 ResetDevice = TRUE; 250 } 251 else 252 { 253 // update pointer 254 FDODeviceExtension->LastTimerActiveSrb = FDODeviceExtension->ActiveSrb; 255 } 256 } 257 else 258 { 259 // reset srb 260 FDODeviceExtension->LastTimerActiveSrb = NULL; 261 } 262 263 KeReleaseSpinLockFromDpcLevel(&FDODeviceExtension->IrpListLock); 264 265 266 if (ResetDevice && FDODeviceExtension->TimerWorkQueueEnabled && FDODeviceExtension->SrbErrorHandlingActive == FALSE) 267 { 268 WorkItemData = ExAllocatePoolWithTag(NonPagedPool, 269 sizeof(ERRORHANDLER_WORKITEM_DATA), 270 USB_STOR_TAG); 271 if (WorkItemData) 272 { 273 // Initialize and queue the work item to handle the error 274 ExInitializeWorkItem(&WorkItemData->WorkQueueItem, 275 USBSTOR_TimerWorkerRoutine, 276 WorkItemData); 277 278 WorkItemData->DeviceObject = FDODeviceExtension->FunctionalDeviceObject; 279 280 DPRINT1("[USBSTOR] Queing Timer WorkItem\n"); 281 ExQueueWorkItem(&WorkItemData->WorkQueueItem, DelayedWorkQueue); 282 } 283 } 284 } 285