71dafea35cfcdcbfae8c860ae4de01716530aef8
[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 static
100 BOOLEAN
101 USBSTOR_IsCSWValid(
102 PIRP_CONTEXT Context)
103 {
104 if (Context->csw.Signature != CSW_SIGNATURE)
105 {
106 DPRINT1("[USBSTOR] Expected Signature %x but got %x\n", CSW_SIGNATURE, Context->csw.Signature);
107 return FALSE;
108 }
109
110 if (Context->csw.Tag != PtrToUlong(&Context->csw))
111 {
112 DPRINT1("[USBSTOR] Expected Tag %Ix but got %x\n", PtrToUlong(&Context->csw), Context->csw.Tag);
113 return FALSE;
114 }
115
116 return TRUE;
117 }
118
119 NTSTATUS
120 USBSTOR_QueueWorkItem(
121 PIRP_CONTEXT Context,
122 PIRP Irp)
123 {
124 PERRORHANDLER_WORKITEM_DATA ErrorHandlerWorkItemData;
125
126 ErrorHandlerWorkItemData = ExAllocatePoolWithTag(NonPagedPool, sizeof(ERRORHANDLER_WORKITEM_DATA), USB_STOR_TAG);
127 if (!ErrorHandlerWorkItemData)
128 {
129 return STATUS_INSUFFICIENT_RESOURCES;
130 }
131
132 // error handling started
133 Context->FDODeviceExtension->SrbErrorHandlingActive = TRUE;
134
135 // srb error handling finished
136 Context->FDODeviceExtension->TimerWorkQueueEnabled = FALSE;
137
138 // Initialize and queue the work item to handle the error
139 ExInitializeWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem,
140 ErrorHandlerWorkItemRoutine,
141 ErrorHandlerWorkItemData);
142
143 ErrorHandlerWorkItemData->DeviceObject = Context->FDODeviceExtension->FunctionalDeviceObject;
144 ErrorHandlerWorkItemData->Context = Context;
145 ErrorHandlerWorkItemData->Irp = Irp;
146 ErrorHandlerWorkItemData->DeviceObject = Context->FDODeviceExtension->FunctionalDeviceObject;
147
148 DPRINT1("Queuing WorkItemROutine\n");
149 ExQueueWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem, DelayedWorkQueue);
150 return STATUS_MORE_PROCESSING_REQUIRED;
151 }
152
153 IO_COMPLETION_ROUTINE USBSTOR_CSWCompletionRoutine;
154
155 NTSTATUS
156 NTAPI
157 USBSTOR_CSWCompletionRoutine(
158 PDEVICE_OBJECT DeviceObject,
159 PIRP Irp,
160 PVOID Ctx)
161 {
162 PIRP_CONTEXT Context;
163 PIO_STACK_LOCATION IoStack;
164 PPDO_DEVICE_EXTENSION PDODeviceExtension;
165 PSCSI_REQUEST_BLOCK Request;
166 PUFI_CAPACITY_RESPONSE Response;
167 NTSTATUS Status;
168
169 Context = (PIRP_CONTEXT)Ctx;
170
171 DPRINT("USBSTOR_CSWCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
172
173 // first check for Irp errors
174 if (!NT_SUCCESS(Irp->IoStatus.Status))
175 {
176 if (USBD_STATUS(Context->Urb.UrbHeader.Status) == USBD_STATUS(USBD_STATUS_STALL_PID))
177 {
178 if (Context->RetryCount < 2)
179 {
180 ++Context->RetryCount;
181
182 // clear stall and resend cbw
183 Context->ErrorIndex = 1;
184 Status = USBSTOR_QueueWorkItem(Context, Irp);
185 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
186
187 return STATUS_MORE_PROCESSING_REQUIRED;
188 }
189 }
190 else
191 {
192 DPRINT1("USBSTOR_CSWCompletionRoutine: Urb.Hdr.Status - %x\n", Context->Urb.UrbHeader.Status);
193 }
194
195 // perform reset recovery
196 Context->ErrorIndex = 2;
197 Status = USBSTOR_QueueWorkItem(Context, NULL);
198 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
199 return STATUS_MORE_PROCESSING_REQUIRED;
200 }
201
202 // now check the CSW packet validity
203 if (!USBSTOR_IsCSWValid(Context) || Context->csw.Status == CSW_STATUS_PHASE_ERROR)
204 {
205 // perform reset recovery
206 Context->ErrorIndex = 2;
207 Status = USBSTOR_QueueWorkItem(Context, NULL);
208 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
209 return STATUS_MORE_PROCESSING_REQUIRED;
210 }
211
212 IoStack = IoGetCurrentIrpStackLocation(Irp);
213 PDODeviceExtension = (PPDO_DEVICE_EXTENSION)IoStack->DeviceObject->DeviceExtension;
214 Request = IoStack->Parameters.Scsi.Srb;
215 ASSERT(Request);
216
217 // finally check for CSW errors
218 if (Context->csw.Status == CSW_STATUS_COMMAND_PASSED)
219 {
220 // read capacity needs special work
221 if (Request->Cdb[0] == SCSIOP_READ_CAPACITY)
222 {
223 // get output buffer
224 Response = (PUFI_CAPACITY_RESPONSE)Request->DataBuffer;
225
226 // store in pdo
227 PDODeviceExtension->BlockLength = NTOHL(Response->BlockLength);
228 PDODeviceExtension->LastLogicBlockAddress = NTOHL(Response->LastLogicalBlockAddress);
229 }
230
231 Status = USBSTOR_SrbStatusToNtStatus(Request);
232 }
233 else if (Context->csw.Status == CSW_STATUS_COMMAND_FAILED)
234 {
235 // the command is correct but with failed status - issue request sense
236 DPRINT("USBSTOR_CSWCompletionRoutine: CSW_STATUS_COMMAND_FAILED\n");
237
238 ASSERT(Context->FDODeviceExtension->ActiveSrb == Request);
239
240 // setting a generic error status, additional information
241 // should be read by higher-level driver from SenseInfoBuffer
242 Request->SrbStatus = SRB_STATUS_ERROR;
243 Request->ScsiStatus = 2;
244 Request->DataTransferLength = 0;
245
246 DPRINT("Flags: %x SBL: %x, buf: %p\n", Request->SrbFlags, Request->SenseInfoBufferLength, Request->SenseInfoBuffer);
247
248 if (!(Request->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
249 Request->SenseInfoBufferLength &&
250 Request->SenseInfoBuffer)
251 {
252 // TODO: issue request sense
253 }
254
255 Status = STATUS_IO_DEVICE_ERROR;
256 }
257
258 Irp->IoStatus.Status = Status;
259 Irp->IoStatus.Information = Request->DataTransferLength;
260
261 // terminate current request
262 USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp);
263 USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject);
264
265 ExFreePoolWithTag(Context, USB_STOR_TAG);
266 return Status;
267 }
268
269 NTSTATUS
270 USBSTOR_SendCSWRequest(
271 PIRP_CONTEXT Context,
272 PIRP Irp)
273 {
274 return USBSTOR_IssueBulkOrInterruptRequest(Context->FDODeviceExtension,
275 Irp,
276 Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle,
277 USBD_TRANSFER_DIRECTION_IN,
278 sizeof(CSW),
279 &Context->csw,
280 NULL,
281 USBSTOR_CSWCompletionRoutine,
282 Context);
283 }
284
285 IO_COMPLETION_ROUTINE USBSTOR_DataCompletionRoutine;
286
287 NTSTATUS
288 NTAPI
289 USBSTOR_DataCompletionRoutine(
290 PDEVICE_OBJECT DeviceObject,
291 PIRP Irp,
292 PVOID Ctx)
293 {
294 PIRP_CONTEXT Context;
295 NTSTATUS Status;
296 PIO_STACK_LOCATION IoStack;
297 PSCSI_REQUEST_BLOCK Request;
298
299 DPRINT("USBSTOR_DataCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
300
301 Context = (PIRP_CONTEXT)Ctx;
302 IoStack = IoGetCurrentIrpStackLocation(Irp);
303 Request = IoStack->Parameters.Scsi.Srb;
304
305 if (Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferMDL != Irp->MdlAddress)
306 {
307 IoFreeMdl(Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferMDL);
308 }
309
310 if (NT_SUCCESS(Irp->IoStatus.Status))
311 {
312 if (Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength < Request->DataTransferLength)
313 {
314 Request->SrbStatus = SRB_STATUS_DATA_OVERRUN;
315 }
316 else
317 {
318 Request->SrbStatus = SRB_STATUS_SUCCESS;
319 }
320
321 Request->DataTransferLength = Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength;
322 USBSTOR_SendCSWRequest(Context, Irp);
323 }
324 else if (USBD_STATUS(Context->Urb.UrbHeader.Status) == USBD_STATUS(USBD_STATUS_STALL_PID))
325 {
326 ++Context->RetryCount;
327
328 Request->SrbStatus = SRB_STATUS_DATA_OVERRUN;
329 Request->DataTransferLength = Context->Urb.UrbBulkOrInterruptTransfer.TransferBufferLength;
330
331 // clear stall and resend cbw
332 Context->ErrorIndex = 1;
333 Status = USBSTOR_QueueWorkItem(Context, Irp);
334 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
335 }
336 else
337 {
338 // perform reset recovery
339 Context->ErrorIndex = 2;
340 Status = USBSTOR_QueueWorkItem(Context, NULL);
341 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
342 }
343
344 return STATUS_MORE_PROCESSING_REQUIRED;
345 }
346
347 IO_COMPLETION_ROUTINE USBSTOR_CBWCompletionRoutine;
348
349 NTSTATUS
350 NTAPI
351 USBSTOR_CBWCompletionRoutine(
352 PDEVICE_OBJECT DeviceObject,
353 PIRP Irp,
354 PVOID Ctx)
355 {
356 PIRP_CONTEXT Context;
357 PIO_STACK_LOCATION IoStack;
358 PSCSI_REQUEST_BLOCK Request;
359 USBD_PIPE_HANDLE PipeHandle;
360 ULONG TransferFlags;
361 PMDL Mdl = NULL;
362 PVOID TransferBuffer = NULL;
363 NTSTATUS Status;
364
365 DPRINT("USBSTOR_CBWCompletionRoutine Irp %p Ctx %p Status %x\n", Irp, Ctx, Irp->IoStatus.Status);
366
367 Context = (PIRP_CONTEXT)Ctx;
368 IoStack = IoGetCurrentIrpStackLocation(Irp);
369 Request = IoStack->Parameters.Scsi.Srb;
370
371 if (!NT_SUCCESS(Irp->IoStatus.Status))
372 {
373 goto ResetRecovery;
374 }
375
376 // a request without the buffer
377 if (!Irp->MdlAddress)
378 {
379 Request->SrbStatus = SRB_STATUS_SUCCESS;
380 USBSTOR_SendCSWRequest(Context, Irp);
381 return STATUS_MORE_PROCESSING_REQUIRED;
382 }
383
384 // a request with the data buffer
385
386 if ((Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_IN)
387 {
388 PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle;
389 TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
390 }
391 else if ((Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_OUT)
392 {
393 PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle;
394 TransferFlags = USBD_TRANSFER_DIRECTION_OUT;
395 }
396 else
397 {
398 // we check the validity of a request in disk.c so we should never be here
399 DPRINT1("Warning: shouldn't be here\n");
400 goto ResetRecovery;
401 }
402
403 if (MmGetMdlVirtualAddress(Irp->MdlAddress) == Request->DataBuffer)
404 {
405 Mdl = Irp->MdlAddress;
406 }
407 else
408 {
409 Mdl = IoAllocateMdl(Request->DataBuffer,
410 Request->DataTransferLength,
411 FALSE,
412 FALSE,
413 NULL);
414
415 if (Mdl)
416 {
417 IoBuildPartialMdl(Irp->MdlAddress,
418 Mdl,
419 Request->DataBuffer,
420 Request->DataTransferLength);
421 }
422 }
423
424 if (!Mdl)
425 {
426 DPRINT1("USBSTOR_DataTransfer: Mdl - %p\n", Mdl);
427 goto ResetRecovery;
428 }
429
430 USBSTOR_IssueBulkOrInterruptRequest(Context->FDODeviceExtension,
431 Irp,
432 PipeHandle,
433 TransferFlags,
434 Request->DataTransferLength,
435 TransferBuffer,
436 Mdl,
437 USBSTOR_DataCompletionRoutine,
438 Context);
439
440 return STATUS_MORE_PROCESSING_REQUIRED;
441
442 ResetRecovery:
443 Context->ErrorIndex = 2;
444 Status = USBSTOR_QueueWorkItem(Context, NULL);
445 ASSERT(Status == STATUS_MORE_PROCESSING_REQUIRED);
446 return STATUS_MORE_PROCESSING_REQUIRED;
447 }
448
449 VOID
450 DumpCBW(
451 PUCHAR Block)
452 {
453 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",
454 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,
455 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,
456 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,
457 Block[30] & 0xFF);
458 }
459
460 static
461 NTSTATUS
462 USBSTOR_SendCBWRequest(
463 IN PFDO_DEVICE_EXTENSION FDODeviceExtension,
464 IN PIRP Irp,
465 IN PIRP_CONTEXT Context)
466 {
467 PPDO_DEVICE_EXTENSION PDODeviceExtension;
468 PIO_STACK_LOCATION IoStack;
469 PSCSI_REQUEST_BLOCK Request;
470
471 RtlZeroMemory(&Context->cbw, sizeof(CBW));
472 RtlZeroMemory(&Context->Urb, sizeof(URB));
473
474 IoStack = IoGetCurrentIrpStackLocation(Irp);
475 PDODeviceExtension = IoStack->DeviceObject->DeviceExtension;
476 Request = IoStack->Parameters.Scsi.Srb;
477
478 Context->cbw.Signature = CBW_SIGNATURE;
479 Context->cbw.Tag = PtrToUlong(&Context->cbw);
480 Context->cbw.DataTransferLength = Request->DataTransferLength;
481 Context->cbw.Flags = ((UCHAR)Request->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) << 1;
482 Context->cbw.LUN = PDODeviceExtension->LUN;
483 Context->cbw.CommandBlockLength = Request->CdbLength;
484
485 RtlCopyMemory(&Context->cbw.CommandBlock, Request->Cdb, Request->CdbLength);
486
487 DPRINT("CBW for IRP %p\n", Irp);
488 DumpCBW((PUCHAR)&Context->cbw);
489
490 // initialize rest of context
491 Context->Irp = Irp;
492 Context->FDODeviceExtension = FDODeviceExtension;
493 Context->RetryCount = 0;
494
495 return USBSTOR_IssueBulkOrInterruptRequest(
496 FDODeviceExtension,
497 Irp,
498 FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle,
499 USBD_TRANSFER_DIRECTION_OUT,
500 sizeof(CBW),
501 &Context->cbw,
502 NULL,
503 USBSTOR_CBWCompletionRoutine,
504 Context);
505 }
506
507 NTSTATUS
508 USBSTOR_HandleExecuteSCSI(
509 IN PDEVICE_OBJECT DeviceObject,
510 IN PIRP Irp,
511 IN ULONG RetryCount)
512 {
513 PCDB pCDB;
514 NTSTATUS Status;
515 PIO_STACK_LOCATION IoStack;
516 PSCSI_REQUEST_BLOCK Request;
517 PPDO_DEVICE_EXTENSION PDODeviceExtension;
518 PIRP_CONTEXT Context;
519
520 PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
521 ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
522
523 IoStack = IoGetCurrentIrpStackLocation(Irp);
524 Request = IoStack->Parameters.Scsi.Srb;
525 pCDB = (PCDB)Request->Cdb;
526
527 DPRINT("USBSTOR_HandleExecuteSCSI Operation Code %x, Length %lu\n", pCDB->CDB10.OperationCode, Request->DataTransferLength);
528
529 // check that we're sending to the right LUN
530 ASSERT(pCDB->CDB10.LogicalUnitNumber == (PDODeviceExtension->LUN & MAX_LUN));
531 Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(IRP_CONTEXT), USB_STOR_TAG);
532
533 if (!Context)
534 {
535 Status = STATUS_INSUFFICIENT_RESOURCES;
536 }
537 else
538 {
539 Status = USBSTOR_SendCBWRequest(PDODeviceExtension->LowerDeviceObject->DeviceExtension, Irp, Context);
540 }
541
542 return Status;
543 }