[USBSTOR] Keep CBW and CSW inside an IRP context.
[reactos.git] / drivers / usb / usbstor / error.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 */
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 PPDO_DEVICE_EXTENSION PDODeviceExtension;
78 NTSTATUS Status = STATUS_SUCCESS;
79 PIO_STACK_LOCATION Stack;
80 PSCSI_REQUEST_BLOCK Request;
81 PCDB pCDB;
82
83 ASSERT(Context);
84 ASSERT(Context->Irp);
85
86 // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
87 Status = USBSTOR_ResetDevice(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension);
88 if (NT_SUCCESS(Status))
89 {
90 // step 2 reset bulk in pipe section 5.3.4
91 Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle);
92 if (NT_SUCCESS(Status))
93 {
94 // finally reset bulk out pipe
95 Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
96 }
97 }
98
99 Stack = IoGetCurrentIrpStackLocation(Context->Irp);
100 ASSERT(Stack->DeviceObject);
101 PDODeviceExtension = (PPDO_DEVICE_EXTENSION)Stack->DeviceObject->DeviceExtension;
102
103 Request = (PSCSI_REQUEST_BLOCK)Stack->Parameters.Others.Argument1;
104 ASSERT(Request);
105
106 // obtain request type
107 pCDB = (PCDB)Request->Cdb;
108 ASSERT(pCDB);
109
110 if (Status != STATUS_SUCCESS || Context->RetryCount >= 1)
111 {
112 // Complete the master IRP
113 Context->Irp->IoStatus.Status = Status;
114 Context->Irp->IoStatus.Information = 0;
115 USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Context->Irp);
116 IoCompleteRequest(Context->Irp, IO_NO_INCREMENT);
117
118 // Start the next request
119 USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject);
120
121 // srb handling finished
122 Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE;
123
124 // clear timer srb
125 Context->FDODeviceExtension->LastTimerActiveSrb = NULL;
126 }
127 else
128 {
129 DPRINT1("Retrying Count %lu %p\n", Context->RetryCount, Stack->DeviceObject);
130
131 // re-schedule request
132 USBSTOR_HandleExecuteSCSI(Stack->DeviceObject, Context->Irp, Context->RetryCount + 1);
133
134 // srb error handling finished
135 Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE;
136
137 // srb error handling finished
138 Context->FDODeviceExtension->TimerWorkQueueEnabled = TRUE;
139
140 // clear timer srb
141 Context->FDODeviceExtension->LastTimerActiveSrb = NULL;
142 }
143
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_SendCSWRequest(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 }