xref: /reactos/drivers/usb/usbport/trfsplit.c (revision d5399189)
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 }
337