]> git.proxmox.com Git - mirror_edk2.git/blame - SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c
SourceLevelDebugPkg DebugUsb3: Fix GCC build failures
[mirror_edk2.git] / SourceLevelDebugPkg / Library / DebugCommunicationLibUsb3 / DebugCommunicationLibUsb3Transfer.c
CommitLineData
2cb6eabe
EL
1/** @file\r
2 Debug Port Library implementation based on usb3 debug port.\r
3\r
ad6040ec 4 Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>\r
2cb6eabe
EL
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php.\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14#include "DebugCommunicationLibUsb3Internal.h"\r
15\r
16/**\r
17 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.\r
18\r
19 @param Handle Debug port handle.\r
20 @param TrsRing The transfer ring to sync.\r
21\r
22 @retval EFI_SUCCESS The transfer ring is synchronized successfully.\r
23\r
24**/\r
25EFI_STATUS\r
26EFIAPI\r
27XhcSyncTrsRing (\r
28 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
29 IN TRANSFER_RING *TrsRing\r
30 )\r
31{\r
32 UINTN Index;\r
33 TRB_TEMPLATE *TrsTrb;\r
34 UINT32 CycleBit;\r
35\r
36 ASSERT (TrsRing != NULL);\r
37\r
38 //\r
39 // Calculate the latest RingEnqueue and RingPCS\r
40 //\r
41 TrsTrb = (TRB_TEMPLATE *)(UINTN) TrsRing->RingEnqueue;\r
42\r
43 ASSERT (TrsTrb != NULL);\r
44 \r
45 for (Index = 0; Index < TrsRing->TrbNumber; Index++) {\r
46 if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) {\r
47 break;\r
48 }\r
49 TrsTrb++;\r
50 if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) {\r
51 ASSERT (((LINK_TRB*)TrsTrb)->TC != 0);\r
52 //\r
53 // set cycle bit in Link TRB as normal\r
54 //\r
55 ((LINK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0;\r
56 //\r
57 // Toggle PCS maintained by software\r
58 //\r
59 TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1;\r
60 TrsTrb = (TRB_TEMPLATE *)(UINTN)((TrsTrb->Parameter1 | LShiftU64 ((UINT64)TrsTrb->Parameter2, 32)) & ~0x0F);\r
61 }\r
62 }\r
63 ASSERT (Index != TrsRing->TrbNumber);\r
64\r
65 if ((EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb != TrsRing->RingEnqueue) {\r
66 TrsRing->RingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb;\r
67 }\r
68\r
69 //\r
70 // Clear the Trb context for enqueue, but reserve the PCS bit which indicates free Trb.\r
71 //\r
72 CycleBit = TrsTrb->CycleBit;\r
73 ZeroMem (TrsTrb, sizeof (TRB_TEMPLATE));\r
74 TrsTrb->CycleBit = CycleBit;\r
75\r
76 return EFI_SUCCESS;\r
77}\r
78\r
79/**\r
80 Synchronize the specified event ring to update the enqueue and dequeue pointer.\r
81\r
82 @param Handle Debug port handle.\r
83 @param EvtRing The event ring to sync.\r
84\r
85 @retval EFI_SUCCESS The event ring is synchronized successfully.\r
86\r
87**/\r
88EFI_STATUS\r
89EFIAPI\r
90XhcSyncEventRing (\r
91 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
92 IN EVENT_RING *EvtRing\r
93 )\r
94{\r
95 UINTN Index;\r
96 TRB_TEMPLATE *EvtTrb1;\r
97\r
98 ASSERT (EvtRing != NULL);\r
99\r
100 //\r
101 // Calculate the EventRingEnqueue and EventRingCCS.\r
102 // Note: only support single Segment\r
103 //\r
104 EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;\r
105\r
106 for (Index = 0; Index < EvtRing->TrbNumber; Index++) {\r
107 if (EvtTrb1->CycleBit != EvtRing->EventRingCCS) {\r
108 break;\r
109 }\r
110\r
111 EvtTrb1++;\r
112\r
113 if ((UINTN)EvtTrb1 >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {\r
114 EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingSeg0;\r
115 EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1;\r
116 }\r
117 }\r
118\r
119 if (Index < EvtRing->TrbNumber) {\r
120 EvtRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN)EvtTrb1;\r
121 } else {\r
122 ASSERT (FALSE);\r
123 }\r
124\r
125 return EFI_SUCCESS;\r
126}\r
127\r
128/**\r
129 Check if there is a new generated event.\r
130\r
131 @param Handle Debug port handle.\r
132 @param EvtRing The event ring to check.\r
133 @param NewEvtTrb The new event TRB found.\r
134\r
135 @retval EFI_SUCCESS Found a new event TRB at the event ring.\r
136 @retval EFI_NOT_READY The event ring has no new event.\r
137\r
138**/\r
139EFI_STATUS\r
140EFIAPI\r
141XhcCheckNewEvent (\r
142 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
143 IN EVENT_RING *EvtRing,\r
144 OUT TRB_TEMPLATE **NewEvtTrb\r
145 )\r
146{\r
147 EFI_STATUS Status;\r
2cb6eabe
EL
148\r
149 ASSERT (EvtRing != NULL);\r
150\r
2cb6eabe
EL
151 *NewEvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;\r
152\r
153 if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) {\r
154 return EFI_NOT_READY;\r
155 }\r
156\r
157 Status = EFI_SUCCESS;\r
158\r
159 EvtRing->EventRingDequeue += sizeof (TRB_TEMPLATE);\r
160 //\r
161 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.\r
162 //\r
163 if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {\r
164 EvtRing->EventRingDequeue = EvtRing->EventRingSeg0;\r
165 }\r
166\r
167 return Status;\r
168}\r
169\r
170/**\r
171 Check if the Trb is a transaction of the URB.\r
172\r
173 @param Ring The transfer ring to be checked.\r
174 @param Trb The TRB to be checked.\r
175\r
176 @retval TRUE It is a transaction of the URB.\r
177 @retval FALSE It is not any transaction of the URB.\r
178\r
179**/\r
180BOOLEAN\r
181IsTrbInTrsRing (\r
182 IN TRANSFER_RING *Ring,\r
183 IN TRB_TEMPLATE *Trb\r
184 )\r
185{\r
186 TRB_TEMPLATE *CheckedTrb;\r
187 UINTN Index;\r
188 \r
189 CheckedTrb = (TRB_TEMPLATE *)(UINTN) Ring->RingSeg0;\r
190 \r
191 ASSERT (Ring->TrbNumber == TR_RING_TRB_NUMBER);\r
192\r
193 for (Index = 0; Index < Ring->TrbNumber; Index++) {\r
194 if (Trb == CheckedTrb) {\r
195 return TRUE;\r
196 }\r
197 CheckedTrb++;\r
198 }\r
199\r
200 return FALSE;\r
201}\r
202\r
203/**\r
204 Check the URB's execution result and update the URB's\r
205 result accordingly.\r
206\r
207 @param Handle Debug port handle.\r
208 @param Urb The URB to check result.\r
209\r
210**/\r
211VOID\r
212XhcCheckUrbResult (\r
213 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
214 IN URB *Urb\r
215 )\r
216{\r
217 EVT_TRB_TRANSFER *EvtTrb;\r
218 TRB_TEMPLATE *TRBPtr;\r
219 UINTN Index;\r
220 EFI_STATUS Status;\r
221 URB *CheckedUrb;\r
222 UINT64 XhcDequeue;\r
223 UINT32 High;\r
224 UINT32 Low;\r
225 \r
226 ASSERT ((Handle != NULL) && (Urb != NULL));\r
227\r
228 if (Urb->Finished) {\r
229 goto EXIT;\r
230 }\r
231\r
232 EvtTrb = NULL;\r
233 \r
234 //\r
235 // Traverse the event ring to find out all new events from the previous check.\r
236 //\r
237 XhcSyncEventRing (Handle, &Handle->EventRing);\r
238 \r
239 for (Index = 0; Index < Handle->EventRing.TrbNumber; Index++) {\r
240\r
241 Status = XhcCheckNewEvent (Handle, &Handle->EventRing, ((TRB_TEMPLATE **)&EvtTrb));\r
242 if (Status == EFI_NOT_READY) {\r
243 //\r
244 // All new events are handled, return directly.\r
245 //\r
246 goto EXIT;\r
247 }\r
248 \r
249 if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) {\r
250 continue;\r
251 }\r
252 \r
253 TRBPtr = (TRB_TEMPLATE *)(UINTN)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32));\r
254 \r
255 if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Urb->Ring), TRBPtr)) {\r
256 CheckedUrb = Urb;\r
257 } else if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Handle->UrbIn.Ring), TRBPtr)) {\r
258 //\r
259 // If it is read event and it should be generated by poll, and current operation is write, we need save data into internal buffer.\r
260 // Internal buffer is used by next read.\r
261 //\r
262 Handle->DataCount = (UINT8) (Handle->UrbIn.DataLen - EvtTrb->Length);\r
5d6507a1 263 CopyMem ((VOID *)(UINTN)Handle->Data, (VOID *)(UINTN)Handle->UrbIn.Data, Handle->DataCount);\r
2cb6eabe
EL
264 //\r
265 // Fill this TRB complete with CycleBit, otherwise next read will fail with old TRB.\r
266 //\r
267 TRBPtr->CycleBit = (TRBPtr->CycleBit & BIT0) ? 0 : 1;\r
268 continue;\r
269 } else {\r
270 continue;\r
271 }\r
272 \r
273 if ((EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) ||\r
274 (EvtTrb->Completecode == TRB_COMPLETION_SUCCESS)) {\r
275 //\r
276 // The length of data which were transferred.\r
277 //\r
b753b3cc 278 CheckedUrb->Completed += (((TRANSFER_TRB_NORMAL*)TRBPtr)->Length - EvtTrb->Length);\r
2cb6eabe
EL
279 } else {\r
280 CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;\r
281 }\r
282 //\r
283 // This Urb has been processed\r
284 //\r
285 CheckedUrb->Finished = TRUE;\r
286 }\r
287\r
288EXIT:\r
289 //\r
290 // Advance event ring to last available entry\r
291 //\r
292 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,\r
293 // So divide it to two 32-bytes width register access.\r
294 //\r
295 Low = XhcReadDebugReg (Handle, XHC_DC_DCERDP);\r
296 High = XhcReadDebugReg (Handle, XHC_DC_DCERDP + 4);\r
297 XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low);\r
298\r
299 if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)Handle->EventRing.EventRingDequeue & (~0x0F))) {\r
300 //\r
301 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,\r
302 // So divide it to two 32-bytes width register access.\r
303 //\r
304 XhcWriteDebugReg (Handle, XHC_DC_DCERDP, XHC_LOW_32BIT (Handle->EventRing.EventRingDequeue));\r
305 XhcWriteDebugReg (Handle, XHC_DC_DCERDP + 4, XHC_HIGH_32BIT (Handle->EventRing.EventRingDequeue));\r
306 }\r
307}\r
308\r
309/**\r
310 Ring the door bell to notify XHCI there is a transaction to be executed.\r
311\r
312 @param Handle Debug port handle.\r
313 @param Urb The pointer to URB.\r
314\r
315 @retval EFI_SUCCESS Successfully ring the door bell.\r
316\r
317**/\r
318EFI_STATUS\r
319EFIAPI\r
320XhcRingDoorBell (\r
321 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
322 IN URB *Urb\r
323 )\r
324{\r
325 UINT32 Dcdb;\r
326\r
327 //\r
328 // 7.6.8.2 DCDB Register\r
329 // \r
330 Dcdb = (Urb->Direction == EfiUsbDataIn) ? 0x100 : 0x0;\r
331 \r
332 XhcWriteDebugReg (\r
333 Handle,\r
334 XHC_DC_DCDB,\r
335 Dcdb\r
336 );\r
337\r
338 return EFI_SUCCESS;\r
339}\r
340\r
341/**\r
342 Execute the transfer by polling the URB. This is a synchronous operation.\r
343\r
344 @param Handle Debug port handle.\r
345 @param Urb The URB to execute.\r
346 @param Timeout The time to wait before abort, in microsecond.\r
347\r
348**/\r
349VOID\r
350XhcExecTransfer (\r
351 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
352 IN URB *Urb,\r
353 IN UINTN Timeout\r
354 )\r
355{\r
356 TRANSFER_RING *Ring;\r
2cb6eabe 357 TRB_TEMPLATE *Trb;\r
08021523
JF
358 UINTN Loop;\r
359 UINTN Index;\r
2cb6eabe 360\r
08021523
JF
361 Loop = Timeout / XHC_DEBUG_PORT_1_MILLISECOND;\r
362 if (Timeout == 0) {\r
363 Loop = 0xFFFFFFFF;\r
2cb6eabe 364 }\r
08021523 365 XhcRingDoorBell (Handle, Urb);\r
2cb6eabe
EL
366 //\r
367 // Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay.\r
368 //\r
08021523 369 for (Index = 0; Index < Loop; Index++) {\r
2cb6eabe
EL
370 XhcCheckUrbResult (Handle, Urb);\r
371 if (Urb->Finished) {\r
372 break;\r
373 }\r
08021523 374 MicroSecondDelay (XHC_DEBUG_PORT_1_MILLISECOND);\r
2cb6eabe 375 }\r
08021523
JF
376 if (Index == Loop) {\r
377 //\r
378 // If time out occurs.\r
379 //\r
380 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
381 } \r
2cb6eabe
EL
382 //\r
383 // If URB transfer is error, restore transfer ring to original value before URB transfer\r
384 // This will make the current transfer TRB is always at the latest unused one in transfer ring.\r
385 //\r
386 Ring = (TRANSFER_RING *)(UINTN) Urb->Ring;\r
387 if ((Urb->Result != EFI_USB_NOERROR) && (Urb->Direction == EfiUsbDataIn)) {\r
388 //\r
389 // Adjust Enqueue pointer\r
390 //\r
391 Ring->RingEnqueue = Urb->Trb;\r
392 //\r
393 // Clear CCS flag for next use\r
394 //\r
395 Trb = (TRB_TEMPLATE *)(UINTN) Urb->Trb;\r
396 Trb->CycleBit = ((~Ring->RingPCS) & BIT0);\r
397 } else {\r
398 //\r
399 // Update transfer ring for next transfer.\r
400 //\r
401 XhcSyncTrsRing (Handle, Ring);\r
402 }\r
403}\r
404\r
405/**\r
406 Create a transfer TRB.\r
407\r
408 @param Handle Debug port handle.\r
409 @param Urb The urb used to construct the transfer TRB.\r
410\r
411 @return Created TRB or NULL\r
412\r
413**/\r
414EFI_STATUS\r
415XhcCreateTransferTrb (\r
416 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
417 IN URB *Urb\r
418 )\r
419{\r
420 TRANSFER_RING *EPRing;\r
421 TRB *Trb;\r
422\r
423 if (Urb->Direction == EfiUsbDataIn) {\r
424 EPRing = &Handle->TransferRingIn;\r
425 } else {\r
426 EPRing = &Handle->TransferRingOut;\r
427 }\r
428 \r
429 Urb->Ring = (EFI_PHYSICAL_ADDRESS)(UINTN) EPRing;\r
430 XhcSyncTrsRing (Handle, EPRing);\r
431\r
432 Urb->Trb = EPRing->RingEnqueue;\r
433 Trb = (TRB *)(UINTN)EPRing->RingEnqueue;\r
2cb6eabe
EL
434 Trb->TrbNormal.TRBPtrLo = XHC_LOW_32BIT (Urb->Data);\r
435 Trb->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT (Urb->Data);\r
436 Trb->TrbNormal.Length = Urb->DataLen;\r
437 Trb->TrbNormal.TDSize = 0;\r
438 Trb->TrbNormal.IntTarget = 0;\r
439 Trb->TrbNormal.ISP = 1;\r
440 Trb->TrbNormal.IOC = 1;\r
441 Trb->TrbNormal.Type = TRB_TYPE_NORMAL;\r
442 \r
443 //\r
444 // Update the cycle bit to indicate this TRB has been consumed.\r
445 //\r
446 Trb->TrbNormal.CycleBit = EPRing->RingPCS & BIT0;\r
447 \r
448 return EFI_SUCCESS;\r
449}\r
450\r
451/**\r
452 Create a new URB for a new transaction.\r
453\r
454 @param Handle Debug port handle.\r
455 @param Direction The direction of data flow.\r
456 @param Data The user data to transfer\r
457 @param DataLen The length of data buffer\r
458\r
459 @return Created URB or NULL\r
460\r
461**/\r
462URB*\r
463XhcCreateUrb (\r
464 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
465 IN EFI_USB_DATA_DIRECTION Direction,\r
466 IN VOID *Data,\r
467 IN UINTN DataLen\r
468 )\r
469{\r
470 EFI_STATUS Status;\r
471 URB *Urb;\r
472 EFI_PHYSICAL_ADDRESS UrbData;\r
473 \r
474 if (Direction == EfiUsbDataIn) {\r
475 Urb = &Handle->UrbIn;\r
476 } else {\r
477 Urb = &Handle->UrbOut;\r
478 }\r
479\r
480 UrbData = Urb->Data;\r
481 \r
482 ZeroMem (Urb, sizeof (URB));\r
483 Urb->Direction = Direction;\r
484 \r
485 //\r
486 // Allocate memory to move data from CAR or SMRAM to normal memory\r
487 // to make XHCI DMA successfully\r
488 // re-use the pre-allocate buffer in PEI to avoid DXE memory service or gBS are not ready\r
489 //\r
490 Urb->Data = UrbData;\r
491 \r
492 if (Direction == EfiUsbDataIn) {\r
493 //\r
494 // Do not break URB data in buffer as it may contain the data which were just put in via DMA by XHC\r
495 //\r
496 Urb->DataLen = (UINT32) DataLen;\r
497 } else {\r
498 //\r
499 // Put data into URB data out buffer which will create TRBs\r
500 //\r
501 ZeroMem ((VOID*)(UINTN) Urb->Data, DataLen);\r
502 CopyMem ((VOID*)(UINTN) Urb->Data, Data, DataLen);\r
503 Urb->DataLen = (UINT32) DataLen;\r
504 }\r
505 \r
506 Status = XhcCreateTransferTrb (Handle, Urb);\r
507 ASSERT_EFI_ERROR (Status);\r
508\r
509 return Urb;\r
510}\r
511\r
512/**\r
513 Submits bulk transfer to a bulk endpoint of a USB device.\r
514\r
515 @param Handle Debug port handle.\r
516 @param Direction The direction of data transfer.\r
517 @param Data Array of pointers to the buffers of data to transmit\r
518 from or receive into.\r
519 @param DataLength The lenght of the data buffer.\r
520 @param Timeout Indicates the maximum time, in microsecond, which\r
521 the transfer is allowed to complete.\r
522\r
523 @retval EFI_SUCCESS The transfer was completed successfully.\r
524 @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.\r
525 @retval EFI_INVALID_PARAMETER Some parameters are invalid.\r
526 @retval EFI_TIMEOUT The transfer failed due to timeout.\r
527 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.\r
528\r
529**/\r
530EFI_STATUS\r
531EFIAPI\r
532XhcDataTransfer (\r
533 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
534 IN EFI_USB_DATA_DIRECTION Direction,\r
535 IN OUT VOID *Data,\r
536 IN OUT UINTN *DataLength,\r
537 IN UINTN Timeout\r
538 )\r
539{\r
540 URB *Urb;\r
541 EFI_STATUS Status;\r
542 \r
543 //\r
544 // Validate the parameters\r
545 //\r
546 if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL)) {\r
547 return EFI_INVALID_PARAMETER;\r
548 }\r
549\r
550 //\r
551 // Create a new URB, insert it into the asynchronous\r
552 // schedule list, then poll the execution status.\r
553 //\r
554 Urb = XhcCreateUrb (Handle, Direction, Data, *DataLength);\r
555 ASSERT (Urb != NULL);\r
556\r
557 XhcExecTransfer (Handle, Urb, Timeout);\r
558\r
559 *DataLength = Urb->Completed;\r
560\r
561 Status = EFI_TIMEOUT;\r
562 if (Urb->Result == EFI_USB_NOERROR) {\r
563 Status = EFI_SUCCESS;\r
564 }\r
565 \r
566 if (Direction == EfiUsbDataIn) {\r
567 //\r
568 // Move data from internal buffer to outside buffer (outside buffer may be in SMRAM...)\r
569 // SMRAM does not allow to do DMA, so we create an internal buffer.\r
570 //\r
571 CopyMem (Data, (VOID *)(UINTN)Urb->Data, *DataLength);\r
572 }\r
573\r
574 return Status;\r
575}\r
576\r