Create a branch for Aleksandar Andrejevic for his work on NTVDM. See http://jira...
[reactos.git] / drivers / usb / usbstor / error.c
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 }