xref: /reactos/drivers/usb/usbstor_new/error.c (revision 94e09426)
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 #define NDEBUG
15 #include <debug.h>
16 
17 NTSTATUS
USBSTOR_GetEndpointStatus(IN PDEVICE_OBJECT DeviceObject,IN UCHAR bEndpointAddress,OUT PUSHORT Value)18 USBSTOR_GetEndpointStatus(
19     IN PDEVICE_OBJECT DeviceObject,
20     IN UCHAR bEndpointAddress,
21     OUT PUSHORT Value)
22 {
23     PURB Urb;
24     NTSTATUS Status;
25 
26     //
27     // allocate urb
28     //
29     DPRINT("Allocating URB\n");
30     Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
31     if (!Urb)
32     {
33         //
34         // out of memory
35         //
36         DPRINT1("OutofMemory!\n");
37         return STATUS_INSUFFICIENT_RESOURCES;
38     }
39 
40     //
41     // build status
42     //
43    UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_ENDPOINT, bEndpointAddress & 0x0F, Value, NULL, NULL);
44 
45     //
46     // send the request
47     //
48     DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb);
49     Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
50 
51     //
52     // free urb
53     //
54     FreeItem(Urb);
55 
56     //
57     // done
58     //
59     return Status;
60 }
61 
62 
63 
64 NTSTATUS
USBSTOR_ResetPipeWithHandle(IN PDEVICE_OBJECT DeviceObject,IN USBD_PIPE_HANDLE PipeHandle)65 USBSTOR_ResetPipeWithHandle(
66     IN PDEVICE_OBJECT DeviceObject,
67     IN USBD_PIPE_HANDLE PipeHandle)
68 {
69     PURB Urb;
70     NTSTATUS Status;
71 
72     //
73     // allocate urb
74     //
75     DPRINT("Allocating URB\n");
76     Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
77     if (!Urb)
78     {
79         //
80         // out of memory
81         //
82         DPRINT1("OutofMemory!\n");
83         return STATUS_INSUFFICIENT_RESOURCES;
84     }
85 
86     //
87     // initialize the urb
88     //
89     Urb->UrbPipeRequest.Hdr.Length = sizeof(struct _URB_PIPE_REQUEST);
90     Urb->UrbPipeRequest.Hdr.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL;
91     Urb->UrbPipeRequest.PipeHandle = PipeHandle;
92 
93     //
94     // send the request
95     //
96     DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb);
97     Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
98 
99     //
100     // free urb
101     //
102     FreeItem(Urb);
103 
104     //
105     // done
106     //
107     return Status;
108 }
109 
110 
111 NTSTATUS
USBSTOR_HandleTransferError(PDEVICE_OBJECT DeviceObject,PIRP_CONTEXT Context)112 USBSTOR_HandleTransferError(
113     PDEVICE_OBJECT DeviceObject,
114     PIRP_CONTEXT Context)
115 {
116     NTSTATUS Status = STATUS_SUCCESS;
117     PIO_STACK_LOCATION Stack;
118     PSCSI_REQUEST_BLOCK Request;
119     PCDB pCDB;
120 
121     //
122     // sanity checks
123     //
124     ASSERT(Context);
125     ASSERT(Context->PDODeviceExtension);
126     ASSERT(Context->PDODeviceExtension->Self);
127     ASSERT(Context->Irp);
128 
129     //
130     // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
131     //
132     Status = USBSTOR_ResetDevice(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension);
133     if (NT_SUCCESS(Status))
134     {
135         //
136         // step 2 reset bulk in pipe section 5.3.4
137         //
138         Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle);
139         if (NT_SUCCESS(Status))
140         {
141             //
142             // finally reset bulk out pipe
143             //
144             Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
145         }
146     }
147 
148     //
149     // get next stack location
150     //
151     Stack = IoGetCurrentIrpStackLocation(Context->Irp);
152 
153     //
154     // get request block
155     //
156     Request = (PSCSI_REQUEST_BLOCK)Stack->Parameters.Others.Argument1;
157     ASSERT(Request);
158 
159     //
160     // obtain request type
161     //
162     pCDB = (PCDB)Request->Cdb;
163     ASSERT(pCDB);
164 
165     if (Status != STATUS_SUCCESS || Context->RetryCount >= 1)
166     {
167         //
168         // Complete the master IRP
169         //
170         Context->Irp->IoStatus.Status = Status;
171         Context->Irp->IoStatus.Information = 0;
172         USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Context->Irp);
173         IoCompleteRequest(Context->Irp, IO_NO_INCREMENT);
174 
175         //
176         // Start the next request
177         //
178         USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject);
179 
180         //
181         // srb handling finished
182         //
183         Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE;
184 
185         //
186         // clear timer srb
187         //
188         Context->FDODeviceExtension->LastTimerActiveSrb = NULL;
189     }
190     else
191     {
192         DPRINT1("Retrying Count %lu %p\n", Context->RetryCount, Context->PDODeviceExtension->Self);
193 
194         //
195         // re-schedule request
196         //
197         USBSTOR_HandleExecuteSCSI(Context->PDODeviceExtension->Self, Context->Irp, Context->RetryCount + 1);
198 
199         //
200         // srb error handling finished
201         //
202         Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE;
203 
204         //
205         // srb error handling finished
206         //
207         Context->FDODeviceExtension->TimerWorkQueueEnabled = TRUE;
208 
209         //
210         // clear timer srb
211         //
212         Context->FDODeviceExtension->LastTimerActiveSrb = NULL;
213     }
214 
215     //
216     // cleanup irp context
217     //
218     FreeItem(Context->cbw);
219     FreeItem(Context);
220 
221 
222     DPRINT1("USBSTOR_HandleTransferError returning with Status %x\n", Status);
223     return Status;
224 }
225 
226 VOID
227 NTAPI
USBSTOR_ResetHandlerWorkItemRoutine(PVOID Context)228 USBSTOR_ResetHandlerWorkItemRoutine(
229     PVOID Context)
230 {
231     NTSTATUS Status;
232     PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
233 
234     //
235     // clear stall on BulkIn pipe
236     //
237     Status = USBSTOR_ResetPipeWithHandle(WorkItemData->Context->FDODeviceExtension->LowerDeviceObject, WorkItemData->Context->FDODeviceExtension->InterfaceInformation->Pipes[WorkItemData->Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle);
238     DPRINT1("USBSTOR_ResetPipeWithHandle Status %x\n", Status);
239 
240     //
241     // now resend the csw as the stall got cleared
242     //
243     USBSTOR_SendCSW(WorkItemData->Context, WorkItemData->Irp);
244 }
245 
246 VOID
247 NTAPI
ErrorHandlerWorkItemRoutine(PVOID Context)248 ErrorHandlerWorkItemRoutine(
249     PVOID Context)
250 {
251     PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
252 
253     if (WorkItemData->Context->ErrorIndex == 2)
254     {
255         //
256         // reset device
257         //
258         USBSTOR_HandleTransferError(WorkItemData->DeviceObject, WorkItemData->Context);
259     }
260     else
261     {
262         //
263         // clear stall
264         //
265         USBSTOR_ResetHandlerWorkItemRoutine(WorkItemData);
266     }
267 
268     //
269     // Free Work Item Data
270     //
271     ExFreePoolWithTag(WorkItemData, USB_STOR_TAG);
272 }
273 
274 VOID
275 NTAPI
USBSTOR_TimerWorkerRoutine(IN PVOID Context)276 USBSTOR_TimerWorkerRoutine(
277     IN PVOID Context)
278 {
279     PFDO_DEVICE_EXTENSION FDODeviceExtension;
280     NTSTATUS Status;
281     PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
282 
283     //
284     // get device extension
285     //
286     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)WorkItemData->DeviceObject->DeviceExtension;
287     ASSERT(FDODeviceExtension->Common.IsFDO);
288 
289     //
290     // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
291     //
292     Status = USBSTOR_ResetDevice(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension);
293     if (NT_SUCCESS(Status))
294     {
295         //
296         // step 2 reset bulk in pipe section 5.3.4
297         //
298         Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle);
299         if (NT_SUCCESS(Status))
300         {
301             //
302             // finally reset bulk out pipe
303             //
304             Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
305         }
306     }
307     DPRINT1("Status %x\n", Status);
308 
309     //
310     // clear timer srb
311     //
312     FDODeviceExtension->LastTimerActiveSrb = NULL;
313 
314     //
315     // re-schedule request
316     //
317     //USBSTOR_HandleExecuteSCSI(WorkItemData->Context->PDODeviceExtension->Self, WorkItemData->Context->Irp, Context->RetryCount + 1);
318 
319 
320 
321     //
322     // do not retry for the same packet again
323     //
324     FDODeviceExtension->TimerWorkQueueEnabled = FALSE;
325 
326     //
327     // Free Work Item Data
328     //
329     ExFreePoolWithTag(WorkItemData, USB_STOR_TAG);
330 }
331 
332 
333 VOID
334 NTAPI
USBSTOR_TimerRoutine(PDEVICE_OBJECT DeviceObject,PVOID Context)335 USBSTOR_TimerRoutine(
336     PDEVICE_OBJECT DeviceObject,
337      PVOID Context)
338 {
339     PFDO_DEVICE_EXTENSION FDODeviceExtension;
340     BOOLEAN ResetDevice = FALSE;
341     PERRORHANDLER_WORKITEM_DATA WorkItemData;
342 
343     //
344     // get device extension
345     //
346     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)Context;
347     DPRINT1("[USBSTOR] TimerRoutine entered\n");
348     DPRINT1("[USBSTOR] ActiveSrb %p ResetInProgress %x LastTimerActiveSrb %p\n", FDODeviceExtension->ActiveSrb, FDODeviceExtension->ResetInProgress, FDODeviceExtension->LastTimerActiveSrb);
349 
350     //
351     // acquire spinlock
352     //
353     KeAcquireSpinLockAtDpcLevel(&FDODeviceExtension->IrpListLock);
354 
355     //
356     // is there an active srb and no global reset is in progress
357     //
358     if (FDODeviceExtension->ActiveSrb && FDODeviceExtension->ResetInProgress == FALSE && FDODeviceExtension->TimerWorkQueueEnabled)
359     {
360         if (FDODeviceExtension->LastTimerActiveSrb != NULL && FDODeviceExtension->LastTimerActiveSrb == FDODeviceExtension->ActiveSrb)
361         {
362             //
363             // check if empty
364             //
365             DPRINT1("[USBSTOR] ActiveSrb %p hang detected\n", FDODeviceExtension->ActiveSrb);
366             ResetDevice = TRUE;
367         }
368         else
369         {
370             //
371             // update pointer
372             //
373             FDODeviceExtension->LastTimerActiveSrb = FDODeviceExtension->ActiveSrb;
374         }
375     }
376     else
377     {
378         //
379         // reset srb
380         //
381         FDODeviceExtension->LastTimerActiveSrb = NULL;
382     }
383 
384     //
385     // release lock
386     //
387     KeReleaseSpinLockFromDpcLevel(&FDODeviceExtension->IrpListLock);
388 
389 
390     if (ResetDevice && FDODeviceExtension->TimerWorkQueueEnabled && FDODeviceExtension->SrbErrorHandlingActive == FALSE)
391     {
392         WorkItemData = ExAllocatePoolWithTag(NonPagedPool,
393                                              sizeof(ERRORHANDLER_WORKITEM_DATA),
394                                              USB_STOR_TAG);
395         if (WorkItemData)
396         {
397            //
398            // Initialize and queue the work item to handle the error
399            //
400            ExInitializeWorkItem(&WorkItemData->WorkQueueItem,
401                                  USBSTOR_TimerWorkerRoutine,
402                                  WorkItemData);
403 
404            WorkItemData->DeviceObject = FDODeviceExtension->FunctionalDeviceObject;
405 
406            DPRINT1("[USBSTOR] Queing Timer WorkItem\n");
407            ExQueueWorkItem(&WorkItemData->WorkQueueItem, DelayedWorkQueue);
408         }
409      }
410 }
411