1 /* 2 * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: drivers/usb/usbstor/error.c 5 * PURPOSE: USB block storage device driver. 6 * PROGRAMMERS: 7 * James Tabor 8 * Michael Martin (michael.martin@reactos.org) 9 * Johannes Anderwald (johannes.anderwald@reactos.org) 10 */ 11 12 #include "usbstor.h" 13 14 NTSTATUS 15 USBSTOR_GetEndpointStatus( 16 IN PDEVICE_OBJECT DeviceObject, 17 IN UCHAR bEndpointAddress, 18 OUT PUSHORT Value) 19 { 20 PURB Urb; 21 NTSTATUS Status; 22 23 // 24 // allocate urb 25 // 26 DPRINT("Allocating URB\n"); 27 Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); 28 if (!Urb) 29 { 30 // 31 // out of memory 32 // 33 DPRINT1("OutofMemory!\n"); 34 return STATUS_INSUFFICIENT_RESOURCES; 35 } 36 37 // 38 // build status 39 // 40 UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_ENDPOINT, bEndpointAddress & 0x0F, Value, NULL, NULL); 41 42 // 43 // send the request 44 // 45 DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb); 46 Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb); 47 48 // 49 // free urb 50 // 51 FreeItem(Urb); 52 53 // 54 // done 55 // 56 return Status; 57 } 58 59 60 61 NTSTATUS 62 USBSTOR_ResetPipeWithHandle( 63 IN PDEVICE_OBJECT DeviceObject, 64 IN USBD_PIPE_HANDLE PipeHandle) 65 { 66 PURB Urb; 67 NTSTATUS Status; 68 69 // 70 // allocate urb 71 // 72 DPRINT("Allocating URB\n"); 73 Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST)); 74 if (!Urb) 75 { 76 // 77 // out of memory 78 // 79 DPRINT1("OutofMemory!\n"); 80 return STATUS_INSUFFICIENT_RESOURCES; 81 } 82 83 // 84 // initialize the urb 85 // 86 Urb->UrbPipeRequest.Hdr.Length = sizeof(struct _URB_PIPE_REQUEST); 87 Urb->UrbPipeRequest.Hdr.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL; 88 Urb->UrbPipeRequest.PipeHandle = PipeHandle; 89 90 // 91 // send the request 92 // 93 DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb); 94 Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb); 95 96 // 97 // free urb 98 // 99 FreeItem(Urb); 100 101 // 102 // done 103 // 104 return Status; 105 } 106 107 108 NTSTATUS 109 USBSTOR_HandleTransferError( 110 PDEVICE_OBJECT DeviceObject, 111 PIRP_CONTEXT Context) 112 { 113 NTSTATUS Status = STATUS_SUCCESS; 114 PIO_STACK_LOCATION Stack; 115 PSCSI_REQUEST_BLOCK Request; 116 PCDB pCDB; 117 118 // 119 // sanity checks 120 // 121 ASSERT(Context); 122 ASSERT(Context->PDODeviceExtension); 123 ASSERT(Context->PDODeviceExtension->Self); 124 ASSERT(Context->Irp); 125 126 // 127 // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification 128 // 129 Status = USBSTOR_ResetDevice(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension); 130 if (NT_SUCCESS(Status)) 131 { 132 // 133 // step 2 reset bulk in pipe section 5.3.4 134 // 135 Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle); 136 if (NT_SUCCESS(Status)) 137 { 138 // 139 // finally reset bulk out pipe 140 // 141 Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle); 142 } 143 } 144 145 // 146 // get next stack location 147 // 148 Stack = IoGetCurrentIrpStackLocation(Context->Irp); 149 150 // 151 // get request block 152 // 153 Request = (PSCSI_REQUEST_BLOCK)Stack->Parameters.Others.Argument1; 154 ASSERT(Request); 155 156 // 157 // obtain request type 158 // 159 pCDB = (PCDB)Request->Cdb; 160 ASSERT(pCDB); 161 162 if (Status != STATUS_SUCCESS || Context->RetryCount >= 1) 163 { 164 // 165 // Complete the master IRP 166 // 167 Context->Irp->IoStatus.Status = Status; 168 Context->Irp->IoStatus.Information = 0; 169 USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Context->Irp); 170 IoCompleteRequest(Context->Irp, IO_NO_INCREMENT); 171 172 // 173 // Start the next request 174 // 175 USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject); 176 177 // 178 // srb handling finished 179 // 180 Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE; 181 182 // 183 // clear timer srb 184 // 185 Context->FDODeviceExtension->LastTimerActiveSrb = NULL; 186 } 187 else 188 { 189 DPRINT1("Retrying Count %lu %p\n", Context->RetryCount, Context->PDODeviceExtension->Self); 190 191 // 192 // re-schedule request 193 // 194 USBSTOR_HandleExecuteSCSI(Context->PDODeviceExtension->Self, Context->Irp, Context->RetryCount + 1); 195 196 // 197 // srb error handling finished 198 // 199 Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE; 200 201 // 202 // srb error handling finished 203 // 204 Context->FDODeviceExtension->TimerWorkQueueEnabled = TRUE; 205 206 // 207 // clear timer srb 208 // 209 Context->FDODeviceExtension->LastTimerActiveSrb = NULL; 210 } 211 212 // 213 // cleanup irp context 214 // 215 FreeItem(Context->cbw); 216 FreeItem(Context); 217 218 219 DPRINT1("USBSTOR_HandleTransferError returning with Status %x\n", Status); 220 return Status; 221 } 222 223 VOID 224 NTAPI 225 USBSTOR_ResetHandlerWorkItemRoutine( 226 PVOID Context) 227 { 228 NTSTATUS Status; 229 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context; 230 231 // 232 // clear stall on BulkIn pipe 233 // 234 Status = USBSTOR_ResetPipeWithHandle(WorkItemData->Context->FDODeviceExtension->LowerDeviceObject, WorkItemData->Context->FDODeviceExtension->InterfaceInformation->Pipes[WorkItemData->Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle); 235 DPRINT1("USBSTOR_ResetPipeWithHandle Status %x\n", Status); 236 237 // 238 // now resend the csw as the stall got cleared 239 // 240 USBSTOR_SendCSW(WorkItemData->Context, WorkItemData->Irp); 241 } 242 243 VOID 244 NTAPI 245 ErrorHandlerWorkItemRoutine( 246 PVOID Context) 247 { 248 NTSTATUS Status; 249 PFDO_DEVICE_EXTENSION FDODeviceExtension; 250 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context; 251 252 // 253 // get fdo 254 // 255 FDODeviceExtension = WorkItemData->Context->FDODeviceExtension; 256 257 if (WorkItemData->Context->ErrorIndex == 2) 258 { 259 // 260 // reset device 261 // 262 Status = USBSTOR_HandleTransferError(WorkItemData->DeviceObject, WorkItemData->Context); 263 } 264 else 265 { 266 // 267 // clear stall 268 // 269 USBSTOR_ResetHandlerWorkItemRoutine(WorkItemData); 270 } 271 272 // 273 // Free Work Item Data 274 // 275 ExFreePool(WorkItemData); 276 } 277 278 VOID 279 NTAPI 280 USBSTOR_TimerWorkerRoutine( 281 IN PVOID Context) 282 { 283 PFDO_DEVICE_EXTENSION FDODeviceExtension; 284 NTSTATUS Status; 285 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context; 286 287 // 288 // get device extension 289 // 290 FDODeviceExtension = (PFDO_DEVICE_EXTENSION)WorkItemData->DeviceObject->DeviceExtension; 291 ASSERT(FDODeviceExtension->Common.IsFDO); 292 293 // 294 // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification 295 // 296 Status = USBSTOR_ResetDevice(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension); 297 if (NT_SUCCESS(Status)) 298 { 299 // 300 // step 2 reset bulk in pipe section 5.3.4 301 // 302 Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle); 303 if (NT_SUCCESS(Status)) 304 { 305 // 306 // finally reset bulk out pipe 307 // 308 Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle); 309 } 310 } 311 DPRINT1("Status %x\n", Status); 312 313 // 314 // clear timer srb 315 // 316 FDODeviceExtension->LastTimerActiveSrb = NULL; 317 318 // 319 // re-schedule request 320 // 321 //USBSTOR_HandleExecuteSCSI(WorkItemData->Context->PDODeviceExtension->Self, WorkItemData->Context->Irp, Context->RetryCount + 1); 322 323 324 325 // 326 // do not retry for the same packet again 327 // 328 FDODeviceExtension->TimerWorkQueueEnabled = FALSE; 329 330 // 331 // Free Work Item Data 332 // 333 ExFreePool(WorkItemData); 334 } 335 336 337 VOID 338 NTAPI 339 USBSTOR_TimerRoutine( 340 PDEVICE_OBJECT DeviceObject, 341 PVOID Context) 342 { 343 PFDO_DEVICE_EXTENSION FDODeviceExtension; 344 BOOLEAN ResetDevice = FALSE; 345 PERRORHANDLER_WORKITEM_DATA WorkItemData; 346 347 // 348 // get device extension 349 // 350 FDODeviceExtension = (PFDO_DEVICE_EXTENSION)Context; 351 DPRINT1("[USBSTOR] TimerRoutine entered\n"); 352 DPRINT1("[USBSTOR] ActiveSrb %p ResetInProgress %x LastTimerActiveSrb %p\n", FDODeviceExtension->ActiveSrb, FDODeviceExtension->ResetInProgress, FDODeviceExtension->LastTimerActiveSrb); 353 354 // 355 // acquire spinlock 356 // 357 KeAcquireSpinLockAtDpcLevel(&FDODeviceExtension->IrpListLock); 358 359 // 360 // is there an active srb and no global reset is in progress 361 // 362 if (FDODeviceExtension->ActiveSrb && FDODeviceExtension->ResetInProgress == FALSE && FDODeviceExtension->TimerWorkQueueEnabled) 363 { 364 if (FDODeviceExtension->LastTimerActiveSrb != NULL && FDODeviceExtension->LastTimerActiveSrb == FDODeviceExtension->ActiveSrb) 365 { 366 // 367 // check if empty 368 // 369 DPRINT1("[USBSTOR] ActiveSrb %p hang detected\n", FDODeviceExtension->ActiveSrb); 370 ResetDevice = TRUE; 371 } 372 else 373 { 374 // 375 // update pointer 376 // 377 FDODeviceExtension->LastTimerActiveSrb = FDODeviceExtension->ActiveSrb; 378 } 379 } 380 else 381 { 382 // 383 // reset srb 384 // 385 FDODeviceExtension->LastTimerActiveSrb = NULL; 386 } 387 388 // 389 // release lock 390 // 391 KeReleaseSpinLockFromDpcLevel(&FDODeviceExtension->IrpListLock); 392 393 394 if (ResetDevice && FDODeviceExtension->TimerWorkQueueEnabled && FDODeviceExtension->SrbErrorHandlingActive == FALSE) 395 { 396 WorkItemData = (PERRORHANDLER_WORKITEM_DATA)ExAllocatePool(NonPagedPool, sizeof(ERRORHANDLER_WORKITEM_DATA)); 397 if (WorkItemData) 398 { 399 // 400 // Initialize and queue the work item to handle the error 401 // 402 ExInitializeWorkItem(&WorkItemData->WorkQueueItem, 403 USBSTOR_TimerWorkerRoutine, 404 WorkItemData); 405 406 WorkItemData->DeviceObject = FDODeviceExtension->FunctionalDeviceObject; 407 408 DPRINT1("[USBSTOR] Queing Timer WorkItem\n"); 409 ExQueueWorkItem(&WorkItemData->WorkQueueItem, DelayedWorkQueue); 410 } 411 } 412 } 413