[FASTFAT] Implement write IOs defering.
[reactos.git] / drivers / usb / usbport / trfsplit.c
1 /*
2 * PROJECT: ReactOS USB Port Driver
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: USBPort split transfer functions
5 * COPYRIGHT: Copyright 2017 Vadim Galyant <vgal@rambler.ru>
6 */
7
8 #include "usbport.h"
9
10 #define NDEBUG
11 #include <debug.h>
12
13 ULONG
14 NTAPI
15 USBPORT_MakeSplitTransfer(IN PDEVICE_OBJECT FdoDevice,
16 IN PUSBPORT_TRANSFER Transfer,
17 IN PUSBPORT_TRANSFER SplitTransfer,
18 IN ULONG MaxTransferSize,
19 IN PULONG SgIdx,
20 IN PULONG SgOffset,
21 IN ULONG TransferRemainLen,
22 IN ULONG TransferOffset)
23 {
24 PUSBPORT_SCATTER_GATHER_LIST SplitSgList;
25 PUSBPORT_SCATTER_GATHER_ELEMENT Element0;
26 PUSBPORT_SCATTER_GATHER_ELEMENT Element1;
27 SIZE_T SgLength;
28 SIZE_T SgRemainLen;
29
30 DPRINT("USBPORT_MakeSplitTransfer: ... \n");
31
32 SplitSgList = &SplitTransfer->SgList;
33 Element0 = &SplitSgList->SgElement[0];
34
35 SgLength = Transfer->SgList.SgElement[*SgIdx].SgTransferLength - *SgOffset;
36
37 if (SgLength > MaxTransferSize)
38 {
39 /* SgLength > MaxTransferSize */
40 SplitTransfer->SgList.SgElementCount = 1;
41
42 Element0->SgOffset = 0;
43 Element0->SgTransferLength = MaxTransferSize;
44 Element0->SgPhysicalAddress.LowPart = Transfer->SgList.SgElement[*SgIdx].SgPhysicalAddress.LowPart + *SgOffset;
45
46 SplitTransfer->TransferParameters.IsTransferSplited = TRUE;
47 SplitTransfer->TransferParameters.TransferBufferLength = MaxTransferSize;
48
49 SplitTransfer->SgList.CurrentVa = Transfer->SgList.CurrentVa + TransferOffset;
50 SplitTransfer->SgList.MappedSystemVa = (PVOID)((ULONG_PTR)Transfer->SgList.MappedSystemVa + TransferOffset);
51
52 SplitTransfer->Flags |= TRANSFER_FLAG_SPLITED;
53
54 *SgOffset += MaxTransferSize;
55 TransferRemainLen -= MaxTransferSize;
56 return TransferRemainLen;
57 }
58
59 /* SgLength <= MaxTransferSize */
60 SplitTransfer->SgList.SgElementCount = 1;
61 TransferRemainLen -= SgLength;
62
63 Element0->SgOffset = 0;
64 Element0->SgTransferLength = SgLength;
65 Element0->SgPhysicalAddress.LowPart = Transfer->SgList.SgElement[*SgIdx].SgPhysicalAddress.LowPart + *SgOffset;
66
67 SplitTransfer->TransferParameters.TransferBufferLength = SgLength;
68 SplitTransfer->TransferParameters.IsTransferSplited = TRUE;
69
70 SplitTransfer->SgList.CurrentVa = Transfer->SgList.CurrentVa + TransferOffset;
71 SplitTransfer->SgList.MappedSystemVa = (PVOID)((ULONG_PTR)Transfer->SgList.MappedSystemVa + TransferOffset);
72
73 SplitTransfer->Flags |= TRANSFER_FLAG_SPLITED;
74
75 *SgOffset += SgLength;
76
77 SgRemainLen = MaxTransferSize - SgLength;
78
79 if (SgRemainLen > TransferRemainLen)
80 {
81 SgRemainLen = TransferRemainLen;
82 }
83
84 if (!SgRemainLen)
85 {
86 /* SgLength == MaxTransferSize */
87 ++*SgIdx;
88 *SgOffset = 0;
89 return TransferRemainLen;
90 }
91
92 /* SgLength < MaxTransferSize */
93
94 DPRINT1("MakeSplitTransfer: SgRemainLen - %x\n", SgRemainLen);
95 DPRINT1("MakeSplitTransfer: SgIdx - %x\n", *SgIdx);
96 ++*SgIdx;
97
98 *SgOffset = 0;
99 SplitTransfer->SgList.SgElementCount++;
100
101 Element1 = &SplitSgList->SgElement[1];
102
103 Element1->SgOffset = SgRemainLen;
104 Element1->SgTransferLength = Element0->SgTransferLength;
105 Element1->SgPhysicalAddress.LowPart = Transfer->SgList.SgElement[*SgIdx].SgPhysicalAddress.LowPart + *SgOffset;
106
107 SplitTransfer->TransferParameters.TransferBufferLength += SgRemainLen;
108
109 *SgOffset += SgRemainLen;
110 TransferRemainLen -= SgRemainLen;
111
112 return TransferRemainLen;
113 }
114
115 VOID
116 NTAPI
117 USBPORT_SplitBulkInterruptTransfer(IN PDEVICE_OBJECT FdoDevice,
118 IN PUSBPORT_ENDPOINT Endpoint,
119 IN PUSBPORT_TRANSFER Transfer,
120 IN PLIST_ENTRY List)
121 {
122 PUSBPORT_TRANSFER SplitTransfer;
123 LIST_ENTRY tmplist;
124 ULONG NeedSplits;
125 SIZE_T TransferBufferLength;
126 SIZE_T MaxTransferSize;
127 SIZE_T TransferOffset = 0;
128 SIZE_T RemainLength;
129 ULONG ix;
130 ULONG SgIdx = 0;
131 ULONG SgOffset = 0;
132
133 DPRINT("USBPORT_SplitBulkInterruptTransfer: ... \n");
134
135 MaxTransferSize = Endpoint->EndpointProperties.TotalMaxPacketSize *
136 (Endpoint->EndpointProperties.MaxTransferSize /
137 Endpoint->EndpointProperties.TotalMaxPacketSize);
138
139 if (Endpoint->EndpointProperties.MaxTransferSize > PAGE_SIZE)
140 {
141 KeBugCheckEx(BUGCODE_USB_DRIVER, 1, 0, 0, 0);
142 }
143
144 TransferBufferLength = Transfer->TransferParameters.TransferBufferLength;
145 Transfer->Flags |= TRANSFER_FLAG_PARENT;
146
147 NeedSplits = TransferBufferLength / MaxTransferSize + 1;
148
149 InitializeListHead(&tmplist);
150
151 DPRINT("USBPORT_SplitBulkInterruptTransfer: TransferBufferLength - %x, NeedSplits - %x\n",
152 TransferBufferLength, NeedSplits);
153
154 if (!NeedSplits)
155 {
156 DPRINT1("USBPORT_SplitBulkInterruptTransfer: DbgBreakPoint \n");
157 DbgBreakPoint();
158 goto Exit;
159 }
160
161 for (ix = 0; ix < NeedSplits; ++ix)
162 {
163 SplitTransfer = ExAllocatePoolWithTag(NonPagedPool,
164 Transfer->FullTransferLength,
165 USB_PORT_TAG);
166
167 if (!SplitTransfer)
168 {
169 DPRINT1("USBPORT_SplitBulkInterruptTransfer: DbgBreakPoint \n");
170 DbgBreakPoint();
171 goto Exit;
172 }
173
174 RtlCopyMemory(SplitTransfer, Transfer, Transfer->FullTransferLength);
175
176 SplitTransfer->MiniportTransfer = (PVOID)((ULONG_PTR)SplitTransfer +
177 SplitTransfer->PortTransferLength);
178
179 InsertTailList(&tmplist, &SplitTransfer->TransferLink);
180 }
181
182 if (Transfer->TransferParameters.TransferBufferLength == 0)
183 {
184 goto Exit;
185 }
186
187 RemainLength = Transfer->TransferParameters.TransferBufferLength;
188
189 do
190 {
191 SplitTransfer = CONTAINING_RECORD(tmplist.Flink,
192 USBPORT_TRANSFER,
193 TransferLink);
194
195 RemoveHeadList(&tmplist);
196
197 RemainLength = USBPORT_MakeSplitTransfer(FdoDevice,
198 Transfer,
199 SplitTransfer,
200 MaxTransferSize,
201 &SgIdx,
202 &SgOffset,
203 RemainLength,
204 TransferOffset);
205
206 TransferOffset += SplitTransfer->TransferParameters.TransferBufferLength;
207
208 InsertTailList(List, &SplitTransfer->TransferLink);
209 InsertTailList(&Transfer->SplitTransfersList,&SplitTransfer->SplitLink);
210 }
211 while (RemainLength != 0);
212
213 Exit:
214
215 while (!IsListEmpty(&tmplist))
216 {
217 DPRINT("USBPORT_SplitBulkInterruptTransfer: ... \n");
218
219 SplitTransfer = CONTAINING_RECORD(tmplist.Flink,
220 USBPORT_TRANSFER,
221 TransferLink);
222 RemoveHeadList(&tmplist);
223
224 ExFreePoolWithTag(SplitTransfer, USB_PORT_TAG);
225 }
226
227 return;
228 }
229
230 VOID
231 NTAPI
232 USBPORT_SplitTransfer(IN PDEVICE_OBJECT FdoDevice,
233 IN PUSBPORT_ENDPOINT Endpoint,
234 IN PUSBPORT_TRANSFER Transfer,
235 IN PLIST_ENTRY List)
236 {
237 ULONG TransferType;
238
239 DPRINT("USBPORT_SplitTransfer ... \n");
240
241 InitializeListHead(List);
242 InitializeListHead(&Transfer->SplitTransfersList);
243
244 Transfer->USBDStatus = USBD_STATUS_SUCCESS;
245
246 if (Transfer->TransferParameters.TransferBufferLength >
247 Endpoint->EndpointProperties.MaxTransferSize)
248 {
249 TransferType = Endpoint->EndpointProperties.TransferType;
250
251 if (TransferType == USBPORT_TRANSFER_TYPE_BULK ||
252 TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT)
253 {
254 USBPORT_SplitBulkInterruptTransfer(FdoDevice,
255 Endpoint,
256 Transfer,
257 List);
258 }
259 else if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS ||
260 TransferType == USBPORT_TRANSFER_TYPE_CONTROL)
261 {
262 KeBugCheckEx(BUGCODE_USB_DRIVER, 1, 0, 0, 0);
263 }
264 else
265 {
266 DPRINT1("USBPORT_SplitTransfer: Unknown TransferType - %x\n",
267 TransferType);
268 }
269 }
270 else
271 {
272 InsertTailList(List, &Transfer->TransferLink);
273 }
274 }
275
276 VOID
277 NTAPI
278 USBPORT_DoneSplitTransfer(IN PUSBPORT_TRANSFER SplitTransfer)
279 {
280 PUSBPORT_TRANSFER ParentTransfer;
281 KIRQL OldIrql;
282
283 DPRINT("USBPORT_DoneSplitTransfer: ... \n");
284
285 ParentTransfer = SplitTransfer->ParentTransfer;
286 ParentTransfer->CompletedTransferLen += SplitTransfer->CompletedTransferLen;
287
288 if (SplitTransfer->USBDStatus != USBD_STATUS_SUCCESS)
289 {
290 DPRINT1("USBPORT_DoneSplitTransfer: SplitTransfer->USBDStatus - %X\n",
291 SplitTransfer->USBDStatus);
292
293 ParentTransfer->USBDStatus = SplitTransfer->USBDStatus;
294 }
295
296 KeAcquireSpinLock(&ParentTransfer->TransferSpinLock, &OldIrql);
297
298 RemoveEntryList(&SplitTransfer->SplitLink);
299 ExFreePoolWithTag(SplitTransfer, USB_PORT_TAG);
300
301 if (IsListEmpty(&ParentTransfer->SplitTransfersList))
302 {
303 KeReleaseSpinLock(&ParentTransfer->TransferSpinLock, OldIrql);
304 USBPORT_DoneTransfer(ParentTransfer);
305 }
306 else
307 {
308 KeReleaseSpinLock(&ParentTransfer->TransferSpinLock, OldIrql);
309 }
310 }
311
312 VOID
313 NTAPI
314 USBPORT_CancelSplitTransfer(IN PUSBPORT_TRANSFER SplitTransfer)
315 {
316 PUSBPORT_TRANSFER ParentTransfer;
317 PUSBPORT_ENDPOINT Endpoint;
318 KIRQL OldIrql;
319
320 DPRINT("USBPORT_CancelSplitTransfer \n");
321
322 Endpoint = SplitTransfer->Endpoint;
323 ParentTransfer = SplitTransfer->ParentTransfer;
324 ParentTransfer->CompletedTransferLen += SplitTransfer->CompletedTransferLen;
325
326 KeAcquireSpinLock(&ParentTransfer->TransferSpinLock, &OldIrql);
327 RemoveEntryList(&SplitTransfer->SplitLink);
328 KeReleaseSpinLock(&ParentTransfer->TransferSpinLock, OldIrql);
329
330 ExFreePool(SplitTransfer);
331
332 if (IsListEmpty(&ParentTransfer->SplitTransfersList))
333 {
334 InsertTailList(&Endpoint->CancelList, &ParentTransfer->TransferLink);
335 }
336 }