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