571c89dab42ce2fe777af5ba35fac39a6e651bab
[reactos.git] / drivers / usb / usbstor / error.c
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 PPDO_DEVICE_EXTENSION PDODeviceExtension;
78 NTSTATUS Status = STATUS_SUCCESS;
79 PIO_STACK_LOCATION Stack;
80 PSCSI_REQUEST_BLOCK Request;
81 PCDB pCDB;
82
83 ASSERT(Context);
84 ASSERT(Context->Irp);
85
86 // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
87 Status = USBSTOR_ResetDevice(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension);
88 if (NT_SUCCESS(Status))
89 {
90 // step 2 reset bulk in pipe section 5.3.4
91 Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle);
92 if (NT_SUCCESS(Status))
93 {
94 // finally reset bulk out pipe
95 Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
96 }
97 }
98
99 Stack = IoGetCurrentIrpStackLocation(Context->Irp);
100 ASSERT(Stack->DeviceObject);
101 PDODeviceExtension = (PPDO_DEVICE_EXTENSION)Stack->DeviceObject->DeviceExtension;
102
103 Request = (PSCSI_REQUEST_BLOCK)Stack->Parameters.Others.Argument1;
104 ASSERT(Request);
105
106 // obtain request type
107 pCDB = (PCDB)Request->Cdb;
108 ASSERT(pCDB);
109
110 if (!NT_SUCCESS(Status))
111 {
112 // Complete the master IRP
113 Context->Irp->IoStatus.Status = Status;
114 Context->Irp->IoStatus.Information = 0;
115 USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Context->Irp);
116 IoCompleteRequest(Context->Irp, IO_NO_INCREMENT);
117
118 // Start the next request
119 USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject);
120
121 // srb handling finished
122 Context->FDODeviceExtension->SrbErrorHandlingActive = FALSE;
123
124 // clear timer srb
125 Context->FDODeviceExtension->LastTimerActiveSrb = NULL;
126 }
127
128 FreeItem(Context);
129
130 DPRINT1("USBSTOR_HandleTransferError returning with Status %x\n", Status);
131 return Status;
132 }
133
134 VOID
135 NTAPI
136 USBSTOR_ResetHandlerWorkItemRoutine(
137 PVOID Context)
138 {
139 NTSTATUS Status;
140 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
141
142 // clear stall on BulkIn pipe
143 Status = USBSTOR_ResetPipeWithHandle(WorkItemData->Context->FDODeviceExtension->LowerDeviceObject, WorkItemData->Context->FDODeviceExtension->InterfaceInformation->Pipes[WorkItemData->Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle);
144 DPRINT1("USBSTOR_ResetPipeWithHandle Status %x\n", Status);
145
146 // now resend the csw as the stall got cleared
147 USBSTOR_SendCSWRequest(WorkItemData->Context, WorkItemData->Irp);
148 }
149
150 VOID
151 NTAPI
152 ErrorHandlerWorkItemRoutine(
153 PVOID Context)
154 {
155 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
156
157 if (WorkItemData->Context->ErrorIndex == 2)
158 {
159 // reset device
160 USBSTOR_HandleTransferError(WorkItemData->DeviceObject, WorkItemData->Context);
161 }
162 else
163 {
164 // clear stall
165 USBSTOR_ResetHandlerWorkItemRoutine(WorkItemData);
166 }
167
168 // Free Work Item Data
169 ExFreePoolWithTag(WorkItemData, USB_STOR_TAG);
170 }
171
172 VOID
173 NTAPI
174 USBSTOR_TimerWorkerRoutine(
175 IN PVOID Context)
176 {
177 PFDO_DEVICE_EXTENSION FDODeviceExtension;
178 NTSTATUS Status;
179 PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context;
180
181 FDODeviceExtension = (PFDO_DEVICE_EXTENSION)WorkItemData->DeviceObject->DeviceExtension;
182 ASSERT(FDODeviceExtension->Common.IsFDO);
183
184 // first perform a mass storage reset step 1 in 5.3.4 USB Mass Storage Bulk Only Specification
185 Status = USBSTOR_ResetDevice(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension);
186 if (NT_SUCCESS(Status))
187 {
188 // step 2 reset bulk in pipe section 5.3.4
189 Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle);
190 if (NT_SUCCESS(Status))
191 {
192 // finally reset bulk out pipe
193 Status = USBSTOR_ResetPipeWithHandle(FDODeviceExtension->LowerDeviceObject, FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle);
194 }
195 }
196 DPRINT1("Status %x\n", Status);
197
198 // clear timer srb
199 FDODeviceExtension->LastTimerActiveSrb = NULL;
200
201 // re-schedule request
202 //USBSTOR_HandleExecuteSCSI(WorkItemData->Context->PDODeviceExtension->Self, WorkItemData->Context->Irp, Context->RetryCount + 1);
203
204 // do not retry for the same packet again
205 FDODeviceExtension->TimerWorkQueueEnabled = FALSE;
206
207 ExFreePoolWithTag(WorkItemData, USB_STOR_TAG);
208 }
209
210 VOID
211 NTAPI
212 USBSTOR_TimerRoutine(
213 PDEVICE_OBJECT DeviceObject,
214 PVOID Context)
215 {
216 PFDO_DEVICE_EXTENSION FDODeviceExtension;
217 BOOLEAN ResetDevice = FALSE;
218 PERRORHANDLER_WORKITEM_DATA WorkItemData;
219
220 FDODeviceExtension = (PFDO_DEVICE_EXTENSION)Context;
221 DPRINT1("[USBSTOR] TimerRoutine entered\n");
222 DPRINT1("[USBSTOR] ActiveSrb %p ResetInProgress %x LastTimerActiveSrb %p\n", FDODeviceExtension->ActiveSrb, FDODeviceExtension->ResetInProgress, FDODeviceExtension->LastTimerActiveSrb);
223
224 KeAcquireSpinLockAtDpcLevel(&FDODeviceExtension->IrpListLock);
225
226 // is there an active srb and no global reset is in progress
227 if (FDODeviceExtension->ActiveSrb && FDODeviceExtension->ResetInProgress == FALSE && FDODeviceExtension->TimerWorkQueueEnabled)
228 {
229 if (FDODeviceExtension->LastTimerActiveSrb != NULL && FDODeviceExtension->LastTimerActiveSrb == FDODeviceExtension->ActiveSrb)
230 {
231 // check if empty
232 DPRINT1("[USBSTOR] ActiveSrb %p hang detected\n", FDODeviceExtension->ActiveSrb);
233 ResetDevice = TRUE;
234 }
235 else
236 {
237 // update pointer
238 FDODeviceExtension->LastTimerActiveSrb = FDODeviceExtension->ActiveSrb;
239 }
240 }
241 else
242 {
243 // reset srb
244 FDODeviceExtension->LastTimerActiveSrb = NULL;
245 }
246
247 KeReleaseSpinLockFromDpcLevel(&FDODeviceExtension->IrpListLock);
248
249
250 if (ResetDevice && FDODeviceExtension->TimerWorkQueueEnabled && FDODeviceExtension->SrbErrorHandlingActive == FALSE)
251 {
252 WorkItemData = ExAllocatePoolWithTag(NonPagedPool,
253 sizeof(ERRORHANDLER_WORKITEM_DATA),
254 USB_STOR_TAG);
255 if (WorkItemData)
256 {
257 // Initialize and queue the work item to handle the error
258 ExInitializeWorkItem(&WorkItemData->WorkQueueItem,
259 USBSTOR_TimerWorkerRoutine,
260 WorkItemData);
261
262 WorkItemData->DeviceObject = FDODeviceExtension->FunctionalDeviceObject;
263
264 DPRINT1("[USBSTOR] Queing Timer WorkItem\n");
265 ExQueueWorkItem(&WorkItemData->WorkQueueItem, DelayedWorkQueue);
266 }
267 }
268 }