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