xref: /reactos/drivers/usb/usbstor/error.c (revision 4f0b8d3d)
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