xref: /reactos/drivers/usb/usbstor/error.c (revision 94e61c30)
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  *              2019 Victor Perevertkin (victor.perevertkin@reactos.org)
9  */
10 
11 #include "usbstor.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
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     DPRINT("Allocating URB\n");
27     Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
28     if (!Urb)
29     {
30         DPRINT1("OutofMemory!\n");
31         return STATUS_INSUFFICIENT_RESOURCES;
32     }
33 
34     // build status
35     UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_ENDPOINT, bEndpointAddress & 0x0F, Value, NULL, NULL);
36 
37     // send the request
38     DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb);
39     Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
40 
41     FreeItem(Urb);
42     return Status;
43 }
44 
45 NTSTATUS
USBSTOR_ResetPipeWithHandle(IN PDEVICE_OBJECT DeviceObject,IN USBD_PIPE_HANDLE PipeHandle)46 USBSTOR_ResetPipeWithHandle(
47     IN PDEVICE_OBJECT DeviceObject,
48     IN USBD_PIPE_HANDLE PipeHandle)
49 {
50     PURB Urb;
51     NTSTATUS Status;
52 
53     DPRINT("Allocating URB\n");
54     Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
55     if (!Urb)
56     {
57         DPRINT1("OutofMemory!\n");
58         return STATUS_INSUFFICIENT_RESOURCES;
59     }
60 
61     Urb->UrbPipeRequest.Hdr.Length = sizeof(struct _URB_PIPE_REQUEST);
62     Urb->UrbPipeRequest.Hdr.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL;
63     Urb->UrbPipeRequest.PipeHandle = PipeHandle;
64 
65     // send the request
66     DPRINT1("Sending Request DeviceObject %p, Urb %p\n", DeviceObject, Urb);
67     Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
68 
69     FreeItem(Urb);
70     return Status;
71 }
72 
73 VOID
74 NTAPI
USBSTOR_ResetPipeWorkItemRoutine(IN PDEVICE_OBJECT FdoDevice,IN PVOID Ctx)75 USBSTOR_ResetPipeWorkItemRoutine(
76     IN PDEVICE_OBJECT FdoDevice,
77     IN PVOID Ctx)
78 {
79     NTSTATUS Status;
80     PFDO_DEVICE_EXTENSION FDODeviceExtension = (PFDO_DEVICE_EXTENSION)Ctx;
81     PIRP_CONTEXT Context = &FDODeviceExtension->CurrentIrpContext;
82 
83     // clear stall on the corresponding pipe
84     Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, Context->Urb.UrbBulkOrInterruptTransfer.PipeHandle);
85     DPRINT1("USBSTOR_ResetPipeWithHandle Status %x\n", Status);
86 
87     // now resend the csw as the stall got cleared
88     USBSTOR_SendCSWRequest(FDODeviceExtension, Context->Irp);
89 }
90 
91 VOID
92 NTAPI
USBSTOR_ResetDeviceWorkItemRoutine(IN PDEVICE_OBJECT FdoDevice,IN PVOID Context)93 USBSTOR_ResetDeviceWorkItemRoutine(
94     IN PDEVICE_OBJECT FdoDevice,
95     IN PVOID Context)
96 {
97     PFDO_DEVICE_EXTENSION FDODeviceExtension;
98     UINT32 ix;
99     NTSTATUS Status;
100     KIRQL OldIrql;
101 
102     DPRINT("USBSTOR_ResetDeviceWorkItemRoutine\n");
103 
104     FDODeviceExtension = FdoDevice->DeviceExtension;
105 
106     for (ix = 0; ix < 3; ++ix)
107     {
108         // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
109         Status = USBSTOR_ResetDevice(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension);
110         if (NT_SUCCESS(Status))
111         {
112             // step 2 reset bulk in pipe section 5.3.4
113             Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle);
114             if (NT_SUCCESS(Status))
115             {
116                 // finally reset bulk out pipe
117                 Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
118                 if (NT_SUCCESS(Status))
119                 {
120                     break;
121                 }
122             }
123         }
124     }
125 
126     KeAcquireSpinLock(&FDODeviceExtension->CommonLock, &OldIrql);
127     FDODeviceExtension->Flags &= ~USBSTOR_FDO_FLAGS_DEVICE_RESETTING;
128     KeReleaseSpinLock(&FDODeviceExtension->CommonLock, OldIrql);
129 
130     USBSTOR_QueueNextRequest(FdoDevice);
131 }
132 
133 VOID
134 NTAPI
USBSTOR_QueueResetPipe(IN PFDO_DEVICE_EXTENSION FDODeviceExtension)135 USBSTOR_QueueResetPipe(
136     IN PFDO_DEVICE_EXTENSION FDODeviceExtension)
137 {
138     DPRINT("USBSTOR_QueueResetPipe\n");
139 
140     IoQueueWorkItem(FDODeviceExtension->ResetDeviceWorkItem,
141                     USBSTOR_ResetPipeWorkItemRoutine,
142                     CriticalWorkQueue,
143                     FDODeviceExtension);
144 }
145 
146 VOID
147 NTAPI
USBSTOR_QueueResetDevice(IN PFDO_DEVICE_EXTENSION FDODeviceExtension)148 USBSTOR_QueueResetDevice(
149     IN PFDO_DEVICE_EXTENSION FDODeviceExtension)
150 {
151     KIRQL OldIrql;
152 
153     DPRINT("USBSTOR_QueueResetDevice\n");
154 
155     KeAcquireSpinLock(&FDODeviceExtension->CommonLock, &OldIrql);
156     FDODeviceExtension->Flags |= USBSTOR_FDO_FLAGS_DEVICE_RESETTING;
157     KeReleaseSpinLock(&FDODeviceExtension->CommonLock, OldIrql);
158 
159     IoQueueWorkItem(FDODeviceExtension->ResetDeviceWorkItem,
160                     USBSTOR_ResetDeviceWorkItemRoutine,
161                     CriticalWorkQueue,
162                     NULL);
163 }
164 
165 VOID
166 NTAPI
USBSTOR_TimerWorkerRoutine(IN PVOID Context)167 USBSTOR_TimerWorkerRoutine(
168     IN PVOID Context)
169 {
170     PFDO_DEVICE_EXTENSION FDODeviceExtension;
171     NTSTATUS Status;
172     PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
173 
174     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)WorkItemData->DeviceObject->DeviceExtension;
175     ASSERT(FDODeviceExtension->Common.IsFDO);
176 
177     // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
178     Status = USBSTOR_ResetDevice(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension);
179     if (NT_SUCCESS(Status))
180     {
181         // step 2 reset bulk in pipe section 5.3.4
182         Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle);
183         if (NT_SUCCESS(Status))
184         {
185             // finally reset bulk out pipe
186             Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
187         }
188     }
189     DPRINT1("Status %x\n", Status);
190 
191     // clear timer srb
192     FDODeviceExtension->LastTimerActiveSrb = NULL;
193 
194     // re-schedule request
195     //USBSTOR_HandleExecuteSCSI(WorkItemData->Context->PDODeviceExtension->Self, WorkItemData->Context->Irp, Context->RetryCount + 1);
196 
197     // do not retry for the same packet again
198     FDODeviceExtension->TimerWorkQueueEnabled = FALSE;
199 
200     ExFreePoolWithTag(WorkItemData, USB_STOR_TAG);
201 }
202 
203 VOID
204 NTAPI
USBSTOR_TimerRoutine(PDEVICE_OBJECT DeviceObject,PVOID Context)205 USBSTOR_TimerRoutine(
206     PDEVICE_OBJECT DeviceObject,
207      PVOID Context)
208 {
209     PFDO_DEVICE_EXTENSION FDODeviceExtension;
210     BOOLEAN ResetDevice = FALSE;
211     PERRORHANDLER_WORKITEM_DATA WorkItemData;
212 
213     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)Context;
214     DPRINT1("[USBSTOR] TimerRoutine entered\n");
215     // DPRINT1("[USBSTOR] ActiveSrb %p ResetInProgress %x LastTimerActiveSrb %p\n", FDODeviceExtension->ActiveSrb, FDODeviceExtension->ResetInProgress, FDODeviceExtension->LastTimerActiveSrb);
216 
217     KeAcquireSpinLockAtDpcLevel(&FDODeviceExtension->IrpListLock);
218 
219     // is there an active srb and no global reset is in progress
220     if (FDODeviceExtension->ActiveSrb && /* FDODeviceExtension->ResetInProgress == FALSE && */ FDODeviceExtension->TimerWorkQueueEnabled)
221     {
222         if (FDODeviceExtension->LastTimerActiveSrb != NULL && FDODeviceExtension->LastTimerActiveSrb == FDODeviceExtension->ActiveSrb)
223         {
224             // check if empty
225             DPRINT1("[USBSTOR] ActiveSrb %p hang detected\n", FDODeviceExtension->ActiveSrb);
226             ResetDevice = TRUE;
227         }
228         else
229         {
230             // update pointer
231             FDODeviceExtension->LastTimerActiveSrb = FDODeviceExtension->ActiveSrb;
232         }
233     }
234     else
235     {
236         // reset srb
237         FDODeviceExtension->LastTimerActiveSrb = NULL;
238     }
239 
240     KeReleaseSpinLockFromDpcLevel(&FDODeviceExtension->IrpListLock);
241 
242 
243     if (ResetDevice && FDODeviceExtension->TimerWorkQueueEnabled && FDODeviceExtension->SrbErrorHandlingActive == FALSE)
244     {
245         WorkItemData = ExAllocatePoolWithTag(NonPagedPool,
246                                              sizeof(ERRORHANDLER_WORKITEM_DATA),
247                                              USB_STOR_TAG);
248         if (WorkItemData)
249         {
250            // Initialize and queue the work item to handle the error
251            ExInitializeWorkItem(&WorkItemData->WorkQueueItem,
252                                  USBSTOR_TimerWorkerRoutine,
253                                  WorkItemData);
254 
255            WorkItemData->DeviceObject = FDODeviceExtension->FunctionalDeviceObject;
256 
257            DPRINT1("[USBSTOR] Queing Timer WorkItem\n");
258            ExQueueWorkItem(&WorkItemData->WorkQueueItem, DelayedWorkQueue);
259         }
260      }
261 }
262