[USBSTOR] Refactor request issue code.
[reactos.git] / drivers / usb / usbstor / scsi.c
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 * 2017 Vadim Galyant
9 * 2019 Victor Perevertkin (victor.perevertkin@reactos.org)
10 */
11
12 #include "usbstor.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17
18 static
19 NTSTATUS
20 USBSTOR_SrbStatusToNtStatus(
21 IN PSCSI_REQUEST_BLOCK Srb)
22 {
23 UCHAR SrbStatus;
24
25 SrbStatus = SRB_STATUS(Srb->SrbStatus);
26
27 switch (SrbStatus)
28 {
29 case SRB_STATUS_SUCCESS:
30 return STATUS_SUCCESS;
31
32 case SRB_STATUS_DATA_OVERRUN:
33 return STATUS_BUFFER_OVERFLOW;
34
35 case SRB_STATUS_BAD_FUNCTION:
36 case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
37 return STATUS_INVALID_DEVICE_REQUEST;
38
39 case SRB_STATUS_INVALID_LUN:
40 case SRB_STATUS_INVALID_TARGET_ID:
41 case SRB_STATUS_NO_HBA:
42 case SRB_STATUS_NO_DEVICE:
43 return STATUS_DEVICE_DOES_NOT_EXIST;
44
45 case SRB_STATUS_TIMEOUT:
46 return STATUS_IO_TIMEOUT;
47
48 case SRB_STATUS_BUS_RESET:
49 case SRB_STATUS_COMMAND_TIMEOUT:
50 case SRB_STATUS_SELECTION_TIMEOUT:
51 return STATUS_DEVICE_NOT_CONNECTED;
52
53 default:
54 return STATUS_IO_DEVICE_ERROR;
55 }
56 }
57
58 static
59 NTSTATUS
60 USBSTOR_IssueBulkOrInterruptRequest(
61 IN PFDO_DEVICE_EXTENSION FDODeviceExtension,
62 IN PIRP Irp,
63 IN USBD_PIPE_HANDLE PipeHandle,
64 IN ULONG TransferFlags,
65 IN ULONG TransferBufferLength,
66 IN PVOID TransferBuffer,
67 IN PMDL TransferBufferMDL,
68 IN PIO_COMPLETION_ROUTINE CompletionRoutine,
69 IN PIRP_CONTEXT Context)
70 {
71 PIO_STACK_LOCATION NextStack;
72
73 RtlZeroMemory(&Context->Urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
74
75 Context->Urb.UrbHeader.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
76 Context->Urb.UrbHeader.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
77
78 Context->Urb.UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;
79 Context->Urb.UrbBulkOrInterruptTransfer.TransferFlags = TransferFlags;
80 Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength = TransferBufferLength;
81 Context->Urb.UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;
82 Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferMDL = TransferBufferMDL;
83
84 NextStack = IoGetNextIrpStackLocation(Irp);
85 NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
86 NextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
87 NextStack->Parameters.Others.Argument1 = &Context->Urb;
88
89 IoSetCompletionRoutine(Irp,
90 CompletionRoutine,
91 Context,
92 TRUE,
93 TRUE,
94 TRUE);
95
96 return IoCallDriver(FDODeviceExtension->LowerDeviceObject, Irp);
97 }
98
99 PIRP_CONTEXT
100 USBSTOR_AllocateIrpContext()
101 {
102 PIRP_CONTEXT Context;
103
104 Context = (PIRP_CONTEXT)AllocateItem(NonPagedPool, sizeof(IRP_CONTEXT));
105 if (!Context)
106 {
107 return NULL;
108 }
109
110 Context->cbw = (PCBW)AllocateItem(NonPagedPool, 512);
111 if (!Context->cbw)
112 {
113 FreeItem(Context);
114 return NULL;
115 }
116
117 return Context;
118 }
119
120 static
121 BOOLEAN
122 USBSTOR_IsCSWValid(
123 PIRP_CONTEXT Context)
124 {
125 if (Context->csw->Signature != CSW_SIGNATURE)
126 {
127 DPRINT1("[USBSTOR] Expected Signature %x but got %x\n", CSW_SIGNATURE, Context->csw->Signature);
128 return FALSE;
129 }
130
131 if (Context->csw->Tag != (ULONG_PTR)Context->csw)
132 {
133 DPRINT1("[USBSTOR] Expected Tag %Ix but got %x\n", (ULONG_PTR)Context->csw, Context->csw->Tag);
134 return FALSE;
135 }
136
137 return TRUE;
138 }
139
140 NTSTATUS
141 USBSTOR_QueueWorkItem(
142 PIRP_CONTEXT Context,
143 PIRP Irp)
144 {
145 PERRORHANDLER_WORKITEM_DATA ErrorHandlerWorkItemData;
146
147 ErrorHandlerWorkItemData = ExAllocatePoolWithTag(NonPagedPool, sizeof(ERRORHANDLER_WORKITEM_DATA), USB_STOR_TAG);
148 if (!ErrorHandlerWorkItemData)
149 {
150 return STATUS_INSUFFICIENT_RESOURCES;
151 }
152
153 // error handling started
154 Context->FDODeviceExtension->SrbErrorHandlingActive = TRUE;
155
156 // srb error handling finished
157 Context->FDODeviceExtension->TimerWorkQueueEnabled = FALSE;
158
159 // Initialize and queue the work item to handle the error
160 ExInitializeWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem,
161 ErrorHandlerWorkItemRoutine,
162 ErrorHandlerWorkItemData);
163
164 ErrorHandlerWorkItemData->DeviceObject = Context->FDODeviceExtension->FunctionalDeviceObject;
165 ErrorHandlerWorkItemData->Context = Context;
166 ErrorHandlerWorkItemData->Irp = Irp;
167 ErrorHandlerWorkItemData->DeviceObject = Context->FDODeviceExtension->FunctionalDeviceObject;
168
169 DPRINT1("Queuing WorkItemROutine\n");
170 ExQueueWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem, DelayedWorkQueue);
171 return STATUS_MORE_PROCESSING_REQUIRED;
172 }
173
174 IO_COMPLETION_ROUTINE USBSTOR_CSWCompletionRoutine;
175
176 NTSTATUS
177 NTAPI
178 USBSTOR_CSWCompletionRoutine(
179 PDEVICE_OBJECT DeviceObject,
180 PIRP Irp,
181 PVOID Ctx)
182 {
183 PIRP_CONTEXT Context;
184 PIO_STACK_LOCATION IoStack;
185 PSCSI_REQUEST_BLOCK Request;
186 PUFI_CAPACITY_RESPONSE Response;
187 NTSTATUS Status;
188
189 Context = (PIRP_CONTEXT)Ctx;
190
191 DPRINT("USBSTOR_CSWCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
192
193 // first check for Irp errors
194 if (!NT_SUCCESS(Irp->IoStatus.Status))
195 {
196 if (USBD_STATUS(Context->Urb.UrbHeader.Status) == USBD_STATUS(USBD_STATUS_STALL_PID))
197 {
198 if (Context->RetryCount < 2)
199 {
200 ++Context->RetryCount;
201
202 // clear stall and resend cbw
203 Context->ErrorIndex = 1;
204 Status = USBSTOR_QueueWorkItem(Context, Irp);
205 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
206
207 return STATUS_MORE_PROCESSING_REQUIRED;
208 }
209 }
210 else
211 {
212 DPRINT1("USBSTOR_CSWCompletionRoutine: Urb.Hdr.Status - %x\n", Context->Urb.UrbHeader.Status);
213 }
214
215 // perform reset recovery
216 Context->ErrorIndex = 2;
217 Status = USBSTOR_QueueWorkItem(Context, NULL);
218 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
219 return STATUS_MORE_PROCESSING_REQUIRED;
220 }
221
222 // now check the CSW packet validity
223 if (!USBSTOR_IsCSWValid(Context) || Context->csw->Status == CSW_STATUS_PHASE_ERROR)
224 {
225 // perform reset recovery
226 Context->ErrorIndex = 2;
227 Status = USBSTOR_QueueWorkItem(Context, NULL);
228 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
229 return STATUS_MORE_PROCESSING_REQUIRED;
230 }
231
232 IoStack = IoGetCurrentIrpStackLocation(Irp);
233 Request = IoStack->Parameters.Scsi.Srb;
234 ASSERT(Request);
235
236 // finally check for CSW errors
237 if (Context->csw->Status == CSW_STATUS_COMMAND_PASSED)
238 {
239 // read capacity needs special work
240 if (Request->Cdb[0] == SCSIOP_READ_CAPACITY)
241 {
242 // get output buffer
243 Response = (PUFI_CAPACITY_RESPONSE)Context->TransferData;
244
245 // store in pdo
246 Context->PDODeviceExtension->BlockLength = NTOHL(Response->BlockLength);
247 Context->PDODeviceExtension->LastLogicBlockAddress = NTOHL(Response->LastLogicalBlockAddress);
248 }
249
250 Status = USBSTOR_SrbStatusToNtStatus(Request);
251 }
252 else if (Context->csw->Status == CSW_STATUS_COMMAND_FAILED)
253 {
254 // the command is correct but with failed status - issue request sense
255 DPRINT("USBSTOR_CSWCompletionRoutine: CSW_STATUS_COMMAND_FAILED\n");
256
257 ASSERT(Context->FDODeviceExtension->ActiveSrb == Request);
258
259 // setting a generic error status, additional information
260 // should be read by higher-level driver from SenseInfoBuffer
261 Request->SrbStatus = SRB_STATUS_ERROR;
262 Request->ScsiStatus = 2;
263 Request->DataTransferLength = 0;
264
265 DPRINT("Flags: %x SBL: %x, buf: %p\n", Request->SrbFlags, Request->SenseInfoBufferLength, Request->SenseInfoBuffer);
266
267 if (!(Request->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
268 Request->SenseInfoBufferLength &&
269 Request->SenseInfoBuffer)
270 {
271 // TODO: issue request sense
272 }
273
274 Status = STATUS_IO_DEVICE_ERROR;
275 }
276
277 Irp->IoStatus.Status = Status;
278 Irp->IoStatus.Information = Request->DataTransferLength;
279
280 FreeItem(Context->cbw);
281
282 // terminate current request
283 USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Irp);
284 USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject);
285
286 FreeItem(Context);
287 return Status;
288 }
289
290 NTSTATUS
291 USBSTOR_SendCSWRequest(
292 PIRP_CONTEXT Context,
293 PIRP Irp)
294 {
295 return USBSTOR_IssueBulkOrInterruptRequest(Context->FDODeviceExtension,
296 Irp,
297 Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle,
298 USBD_TRANSFER_DIRECTION_IN,
299 sizeof(CSW),
300 Context->csw,
301 NULL,
302 USBSTOR_CSWCompletionRoutine,
303 Context);
304 }
305
306 IO_COMPLETION_ROUTINE USBSTOR_DataCompletionRoutine;
307
308 NTSTATUS
309 NTAPI
310 USBSTOR_DataCompletionRoutine(
311 PDEVICE_OBJECT DeviceObject,
312 PIRP Irp,
313 PVOID Ctx)
314 {
315 PIRP_CONTEXT Context;
316 NTSTATUS Status;
317 PIO_STACK_LOCATION IoStack;
318 PSCSI_REQUEST_BLOCK Request;
319
320 DPRINT("USBSTOR_DataCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
321
322 Context = (PIRP_CONTEXT)Ctx;
323 IoStack = IoGetCurrentIrpStackLocation(Irp);
324 Request = IoStack->Parameters.Scsi.Srb;
325
326 if (Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferMDL != Irp->MdlAddress)
327 {
328 IoFreeMdl(Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferMDL);
329 }
330
331 if (NT_SUCCESS(Irp->IoStatus.Status))
332 {
333 if (Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength < Request->DataTransferLength)
334 {
335 Request->SrbStatus = SRB_STATUS_DATA_OVERRUN;
336 }
337 else
338 {
339 Request->SrbStatus = SRB_STATUS_SUCCESS;
340 }
341
342 Request->DataTransferLength = Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength;
343 USBSTOR_SendCSWRequest(Context, Irp);
344 }
345 else if (USBD_STATUS(Context->Urb.UrbHeader.Status) == USBD_STATUS(USBD_STATUS_STALL_PID))
346 {
347 ++Context->RetryCount;
348
349 Request->SrbStatus = SRB_STATUS_DATA_OVERRUN;
350 Request->DataTransferLength = Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength;
351
352 // clear stall and resend cbw
353 Context->ErrorIndex = 1;
354 Status = USBSTOR_QueueWorkItem(Context, Irp);
355 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
356 }
357 else
358 {
359 // perform reset recovery
360 Context->ErrorIndex = 2;
361 Status = USBSTOR_QueueWorkItem(Context, NULL);
362 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
363 }
364
365 return STATUS_MORE_PROCESSING_REQUIRED;
366 }
367
368 IO_COMPLETION_ROUTINE USBSTOR_CBWCompletionRoutine;
369
370 NTSTATUS
371 NTAPI
372 USBSTOR_CBWCompletionRoutine(
373 PDEVICE_OBJECT DeviceObject,
374 PIRP Irp,
375 PVOID Ctx)
376 {
377 PIRP_CONTEXT Context;
378 PIO_STACK_LOCATION IoStack;
379 PSCSI_REQUEST_BLOCK Request;
380 USBD_PIPE_HANDLE PipeHandle;
381 ULONG TransferFlags;
382 PMDL Mdl = NULL;
383 PVOID TransferBuffer = NULL;
384 NTSTATUS Status;
385
386 DPRINT("USBSTOR_CBWCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
387
388 Context = (PIRP_CONTEXT)Ctx;
389 IoStack = IoGetCurrentIrpStackLocation(Irp);
390 Request = IoStack->Parameters.Scsi.Srb;
391
392 if (!NT_SUCCESS(Irp->IoStatus.Status))
393 {
394 goto ResetRecovery;
395 }
396
397 // a request without the buffer
398 if (!Irp->MdlAddress)
399 {
400 Request->SrbStatus = SRB_STATUS_SUCCESS;
401 USBSTOR_SendCSWRequest(Context, Irp);
402 return STATUS_MORE_PROCESSING_REQUIRED;
403 }
404
405 // a request with the data buffer
406
407 if ((Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_IN)
408 {
409 PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle;
410 TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
411 }
412 else if ((Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_OUT)
413 {
414 PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle;
415 TransferFlags = USBD_TRANSFER_DIRECTION_OUT;
416 }
417 else
418 {
419 // we check the validity of a request in disk.c so we should never be here
420 DPRINT1("Warning: shouldn't be here\n");
421 goto ResetRecovery;
422 }
423
424 if (MmGetMdlVirtualAddress(Irp->MdlAddress) == Request->DataBuffer)
425 {
426 Mdl = Irp->MdlAddress;
427 }
428 else
429 {
430 Mdl = IoAllocateMdl(Request->DataBuffer,
431 Request->DataTransferLength,
432 FALSE,
433 FALSE,
434 NULL);
435
436 if (Mdl)
437 {
438 IoBuildPartialMdl(Irp->MdlAddress,
439 Mdl,
440 Request->DataBuffer,
441 Request->DataTransferLength);
442 }
443 }
444
445 if (!Mdl)
446 {
447 DPRINT1("USBSTOR_DataTransfer: Mdl - %p\n", Mdl);
448 goto ResetRecovery;
449 }
450
451 USBSTOR_IssueBulkOrInterruptRequest(Context->FDODeviceExtension,
452 Irp,
453 PipeHandle,
454 TransferFlags,
455 Request->DataTransferLength,
456 TransferBuffer,
457 Mdl,
458 USBSTOR_DataCompletionRoutine,
459 Context);
460
461 return STATUS_MORE_PROCESSING_REQUIRED;
462
463 ResetRecovery:
464 Context->ErrorIndex = 2;
465 Status = USBSTOR_QueueWorkItem(Context, NULL);
466 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
467 return STATUS_MORE_PROCESSING_REQUIRED;
468 }
469
470 VOID
471 DumpCBW(
472 PUCHAR Block)
473 {
474 DPRINT("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
475 Block[0] & 0xFF, Block[1] & 0xFF, Block[2] & 0xFF, Block[3] & 0xFF, Block[4] & 0xFF, Block[5] & 0xFF, Block[6] & 0xFF, Block[7] & 0xFF, Block[8] & 0xFF, Block[9] & 0xFF,
476 Block[10] & 0xFF, Block[11] & 0xFF, Block[12] & 0xFF, Block[13] & 0xFF, Block[14] & 0xFF, Block[15] & 0xFF, Block[16] & 0xFF, Block[17] & 0xFF, Block[18] & 0xFF, Block[19] & 0xFF,
477 Block[20] & 0xFF, Block[21] & 0xFF, Block[22] & 0xFF, Block[23] & 0xFF, Block[24] & 0xFF, Block[25] & 0xFF, Block[26] & 0xFF, Block[27] & 0xFF, Block[28] & 0xFF, Block[29] & 0xFF,
478 Block[30] & 0xFF);
479 }
480
481 static
482 NTSTATUS
483 USBSTOR_SendCBWRequest(
484 IN PFDO_DEVICE_EXTENSION FDODeviceExtension,
485 IN PIRP Irp,
486 IN PIRP_CONTEXT Context)
487 {
488 PPDO_DEVICE_EXTENSION PDODeviceExtension;
489 PIO_STACK_LOCATION IoStack;
490 PSCSI_REQUEST_BLOCK Request;
491
492 if (!Context)
493 {
494 Context = USBSTOR_AllocateIrpContext();
495 if (!Context)
496 {
497 return STATUS_INSUFFICIENT_RESOURCES;
498 }
499 }
500 else
501 {
502 RtlZeroMemory(Context->cbw, sizeof(CBW));
503 RtlZeroMemory(&Context->Urb, sizeof(URB));
504 }
505
506 IoStack = IoGetCurrentIrpStackLocation(Irp);
507 PDODeviceExtension = IoStack->DeviceObject->DeviceExtension;
508 Request = IoStack->Parameters.Scsi.Srb;
509
510 Context->cbw->Signature = CBW_SIGNATURE;
511 Context->cbw->Tag = PtrToUlong(Context->cbw);
512 Context->cbw->DataTransferLength = Request->DataTransferLength;
513 Context->cbw->Flags = ((UCHAR)Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) << 1;
514 Context->cbw->LUN = PDODeviceExtension->LUN;
515 Context->cbw->CommandBlockLength = Request->CdbLength;
516
517 RtlCopyMemory(Context->cbw->CommandBlock, Request->Cdb, Request->CdbLength);
518
519 DPRINT("CBW %p\n", Context->cbw);
520 DumpCBW((PUCHAR)Context->cbw);
521
522 // initialize rest of context
523 Context->Irp = Irp;
524 Context->TransferData = Request->DataBuffer;
525 Context->TransferDataLength = Request->DataTransferLength;
526 Context->FDODeviceExtension = FDODeviceExtension;
527 Context->PDODeviceExtension = PDODeviceExtension;
528 Context->RetryCount = 0;
529
530 return USBSTOR_IssueBulkOrInterruptRequest(
531 FDODeviceExtension,
532 Irp,
533 FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle,
534 USBD_TRANSFER_DIRECTION_OUT,
535 sizeof(CBW),
536 Context->cbw,
537 NULL,
538 USBSTOR_CBWCompletionRoutine,
539 Context);
540 }
541
542 NTSTATUS
543 USBSTOR_HandleExecuteSCSI(
544 IN PDEVICE_OBJECT DeviceObject,
545 IN PIRP Irp,
546 IN ULONG RetryCount)
547 {
548 PCDB pCDB;
549 NTSTATUS Status;
550 PIO_STACK_LOCATION IoStack;
551 PSCSI_REQUEST_BLOCK Request;
552 PPDO_DEVICE_EXTENSION PDODeviceExtension;
553
554 PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
555 ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
556
557 IoStack = IoGetCurrentIrpStackLocation(Irp);
558 Request = IoStack->Parameters.Scsi.Srb;
559 pCDB = (PCDB)Request->Cdb;
560
561 DPRINT("USBSTOR_HandleExecuteSCSI Operation Code %x, Length %lu\n", pCDB->CDB10.OperationCode, Request->DataTransferLength);
562
563 // check that we're sending to the right LUN
564 ASSERT(pCDB->CDB10.LogicalUnitNumber == (PDODeviceExtension->LUN & MAX_LUN));
565 Status = USBSTOR_SendCBWRequest(PDODeviceExtension->LowerDeviceObject->DeviceExtension, Irp, NULL);
566
567 return Status;
568 }