Revert "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
373b1d0e 4 Copyright (c) 2014 - 2015, 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
373b1d0e 148 TRB_TEMPLATE *EvtTrb;\r
2cb6eabe
EL
149\r
150 ASSERT (EvtRing != NULL);\r
151\r
373b1d0e 152 EvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;\r
2cb6eabe
EL
153 *NewEvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;\r
154\r
155 if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) {\r
156 return EFI_NOT_READY;\r
157 }\r
158\r
159 Status = EFI_SUCCESS;\r
160\r
161 EvtRing->EventRingDequeue += sizeof (TRB_TEMPLATE);\r
162 //\r
163 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.\r
164 //\r
165 if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {\r
166 EvtRing->EventRingDequeue = EvtRing->EventRingSeg0;\r
167 }\r
168\r
169 return Status;\r
170}\r
171\r
172/**\r
173 Check if the Trb is a transaction of the URB.\r
174\r
175 @param Ring The transfer ring to be checked.\r
176 @param Trb The TRB to be checked.\r
177\r
178 @retval TRUE It is a transaction of the URB.\r
179 @retval FALSE It is not any transaction of the URB.\r
180\r
181**/\r
182BOOLEAN\r
183IsTrbInTrsRing (\r
184 IN TRANSFER_RING *Ring,\r
185 IN TRB_TEMPLATE *Trb\r
186 )\r
187{\r
188 TRB_TEMPLATE *CheckedTrb;\r
189 UINTN Index;\r
190 \r
191 CheckedTrb = (TRB_TEMPLATE *)(UINTN) Ring->RingSeg0;\r
192 \r
193 ASSERT (Ring->TrbNumber == TR_RING_TRB_NUMBER);\r
194\r
195 for (Index = 0; Index < Ring->TrbNumber; Index++) {\r
196 if (Trb == CheckedTrb) {\r
197 return TRUE;\r
198 }\r
199 CheckedTrb++;\r
200 }\r
201\r
202 return FALSE;\r
203}\r
204\r
205/**\r
206 Check the URB's execution result and update the URB's\r
207 result accordingly.\r
208\r
209 @param Handle Debug port handle.\r
210 @param Urb The URB to check result.\r
211\r
212**/\r
213VOID\r
214XhcCheckUrbResult (\r
215 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
216 IN URB *Urb\r
217 )\r
218{\r
219 EVT_TRB_TRANSFER *EvtTrb;\r
220 TRB_TEMPLATE *TRBPtr;\r
221 UINTN Index;\r
222 EFI_STATUS Status;\r
223 URB *CheckedUrb;\r
224 UINT64 XhcDequeue;\r
225 UINT32 High;\r
226 UINT32 Low;\r
227 \r
228 ASSERT ((Handle != NULL) && (Urb != NULL));\r
229\r
230 if (Urb->Finished) {\r
231 goto EXIT;\r
232 }\r
233\r
234 EvtTrb = NULL;\r
235 \r
236 //\r
237 // Traverse the event ring to find out all new events from the previous check.\r
238 //\r
239 XhcSyncEventRing (Handle, &Handle->EventRing);\r
240 \r
241 for (Index = 0; Index < Handle->EventRing.TrbNumber; Index++) {\r
242\r
243 Status = XhcCheckNewEvent (Handle, &Handle->EventRing, ((TRB_TEMPLATE **)&EvtTrb));\r
244 if (Status == EFI_NOT_READY) {\r
245 //\r
246 // All new events are handled, return directly.\r
247 //\r
248 goto EXIT;\r
249 }\r
250 \r
251 if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) {\r
252 continue;\r
253 }\r
254 \r
255 TRBPtr = (TRB_TEMPLATE *)(UINTN)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32));\r
256 \r
257 if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Urb->Ring), TRBPtr)) {\r
258 CheckedUrb = Urb;\r
259 } else if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Handle->UrbIn.Ring), TRBPtr)) {\r
260 //\r
261 // 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
262 // Internal buffer is used by next read.\r
263 //\r
264 Handle->DataCount = (UINT8) (Handle->UrbIn.DataLen - EvtTrb->Length);\r
5d6507a1 265 CopyMem ((VOID *)(UINTN)Handle->Data, (VOID *)(UINTN)Handle->UrbIn.Data, Handle->DataCount);\r
2cb6eabe
EL
266 //\r
267 // Fill this TRB complete with CycleBit, otherwise next read will fail with old TRB.\r
268 //\r
269 TRBPtr->CycleBit = (TRBPtr->CycleBit & BIT0) ? 0 : 1;\r
270 continue;\r
271 } else {\r
272 continue;\r
273 }\r
274 \r
275 if ((EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) ||\r
276 (EvtTrb->Completecode == TRB_COMPLETION_SUCCESS)) {\r
277 //\r
278 // The length of data which were transferred.\r
279 //\r
b753b3cc 280 CheckedUrb->Completed += (((TRANSFER_TRB_NORMAL*)TRBPtr)->Length - EvtTrb->Length);\r
2cb6eabe
EL
281 } else {\r
282 CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;\r
283 }\r
284 //\r
285 // This Urb has been processed\r
286 //\r
287 CheckedUrb->Finished = TRUE;\r
288 }\r
289\r
290EXIT:\r
291 //\r
292 // Advance event ring to last available entry\r
293 //\r
294 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,\r
295 // So divide it to two 32-bytes width register access.\r
296 //\r
297 Low = XhcReadDebugReg (Handle, XHC_DC_DCERDP);\r
298 High = XhcReadDebugReg (Handle, XHC_DC_DCERDP + 4);\r
299 XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low);\r
300\r
301 if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)Handle->EventRing.EventRingDequeue & (~0x0F))) {\r
302 //\r
303 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,\r
304 // So divide it to two 32-bytes width register access.\r
305 //\r
306 XhcWriteDebugReg (Handle, XHC_DC_DCERDP, XHC_LOW_32BIT (Handle->EventRing.EventRingDequeue));\r
307 XhcWriteDebugReg (Handle, XHC_DC_DCERDP + 4, XHC_HIGH_32BIT (Handle->EventRing.EventRingDequeue));\r
308 }\r
309}\r
310\r
311/**\r
312 Ring the door bell to notify XHCI there is a transaction to be executed.\r
313\r
314 @param Handle Debug port handle.\r
315 @param Urb The pointer to URB.\r
316\r
317 @retval EFI_SUCCESS Successfully ring the door bell.\r
318\r
319**/\r
320EFI_STATUS\r
321EFIAPI\r
322XhcRingDoorBell (\r
323 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
324 IN URB *Urb\r
325 )\r
326{\r
327 UINT32 Dcdb;\r
328\r
329 //\r
330 // 7.6.8.2 DCDB Register\r
331 // \r
332 Dcdb = (Urb->Direction == EfiUsbDataIn) ? 0x100 : 0x0;\r
333 \r
334 XhcWriteDebugReg (\r
335 Handle,\r
336 XHC_DC_DCDB,\r
337 Dcdb\r
338 );\r
339\r
340 return EFI_SUCCESS;\r
341}\r
342\r
343/**\r
344 Execute the transfer by polling the URB. This is a synchronous operation.\r
345\r
346 @param Handle Debug port handle.\r
347 @param Urb The URB to execute.\r
348 @param Timeout The time to wait before abort, in microsecond.\r
349\r
350**/\r
351VOID\r
352XhcExecTransfer (\r
353 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
354 IN URB *Urb,\r
355 IN UINTN Timeout\r
356 )\r
357{\r
358 TRANSFER_RING *Ring;\r
2cb6eabe 359 TRB_TEMPLATE *Trb;\r
08021523
JF
360 UINTN Loop;\r
361 UINTN Index;\r
2cb6eabe 362\r
08021523
JF
363 Loop = Timeout / XHC_DEBUG_PORT_1_MILLISECOND;\r
364 if (Timeout == 0) {\r
365 Loop = 0xFFFFFFFF;\r
2cb6eabe 366 }\r
08021523 367 XhcRingDoorBell (Handle, Urb);\r
2cb6eabe
EL
368 //\r
369 // Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay.\r
370 //\r
08021523 371 for (Index = 0; Index < Loop; Index++) {\r
2cb6eabe
EL
372 XhcCheckUrbResult (Handle, Urb);\r
373 if (Urb->Finished) {\r
374 break;\r
375 }\r
08021523 376 MicroSecondDelay (XHC_DEBUG_PORT_1_MILLISECOND);\r
2cb6eabe 377 }\r
08021523
JF
378 if (Index == Loop) {\r
379 //\r
380 // If time out occurs.\r
381 //\r
382 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
383 } \r
2cb6eabe
EL
384 //\r
385 // If URB transfer is error, restore transfer ring to original value before URB transfer\r
386 // This will make the current transfer TRB is always at the latest unused one in transfer ring.\r
387 //\r
388 Ring = (TRANSFER_RING *)(UINTN) Urb->Ring;\r
389 if ((Urb->Result != EFI_USB_NOERROR) && (Urb->Direction == EfiUsbDataIn)) {\r
390 //\r
391 // Adjust Enqueue pointer\r
392 //\r
393 Ring->RingEnqueue = Urb->Trb;\r
394 //\r
395 // Clear CCS flag for next use\r
396 //\r
397 Trb = (TRB_TEMPLATE *)(UINTN) Urb->Trb;\r
398 Trb->CycleBit = ((~Ring->RingPCS) & BIT0);\r
399 } else {\r
400 //\r
401 // Update transfer ring for next transfer.\r
402 //\r
403 XhcSyncTrsRing (Handle, Ring);\r
404 }\r
405}\r
406\r
407/**\r
408 Create a transfer TRB.\r
409\r
410 @param Handle Debug port handle.\r
411 @param Urb The urb used to construct the transfer TRB.\r
412\r
413 @return Created TRB or NULL\r
414\r
415**/\r
416EFI_STATUS\r
417XhcCreateTransferTrb (\r
418 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
419 IN URB *Urb\r
420 )\r
421{\r
422 TRANSFER_RING *EPRing;\r
423 TRB *Trb;\r
424\r
425 if (Urb->Direction == EfiUsbDataIn) {\r
426 EPRing = &Handle->TransferRingIn;\r
427 } else {\r
428 EPRing = &Handle->TransferRingOut;\r
429 }\r
430 \r
431 Urb->Ring = (EFI_PHYSICAL_ADDRESS)(UINTN) EPRing;\r
432 XhcSyncTrsRing (Handle, EPRing);\r
433\r
434 Urb->Trb = EPRing->RingEnqueue;\r
435 Trb = (TRB *)(UINTN)EPRing->RingEnqueue;\r
2cb6eabe
EL
436 Trb->TrbNormal.TRBPtrLo = XHC_LOW_32BIT (Urb->Data);\r
437 Trb->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT (Urb->Data);\r
438 Trb->TrbNormal.Length = Urb->DataLen;\r
439 Trb->TrbNormal.TDSize = 0;\r
440 Trb->TrbNormal.IntTarget = 0;\r
441 Trb->TrbNormal.ISP = 1;\r
442 Trb->TrbNormal.IOC = 1;\r
443 Trb->TrbNormal.Type = TRB_TYPE_NORMAL;\r
444 \r
445 //\r
446 // Update the cycle bit to indicate this TRB has been consumed.\r
447 //\r
448 Trb->TrbNormal.CycleBit = EPRing->RingPCS & BIT0;\r
449 \r
450 return EFI_SUCCESS;\r
451}\r
452\r
453/**\r
454 Create a new URB for a new transaction.\r
455\r
456 @param Handle Debug port handle.\r
457 @param Direction The direction of data flow.\r
458 @param Data The user data to transfer\r
459 @param DataLen The length of data buffer\r
460\r
461 @return Created URB or NULL\r
462\r
463**/\r
464URB*\r
465XhcCreateUrb (\r
466 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
467 IN EFI_USB_DATA_DIRECTION Direction,\r
468 IN VOID *Data,\r
469 IN UINTN DataLen\r
470 )\r
471{\r
472 EFI_STATUS Status;\r
473 URB *Urb;\r
474 EFI_PHYSICAL_ADDRESS UrbData;\r
475 \r
476 if (Direction == EfiUsbDataIn) {\r
477 Urb = &Handle->UrbIn;\r
478 } else {\r
479 Urb = &Handle->UrbOut;\r
480 }\r
481\r
482 UrbData = Urb->Data;\r
483 \r
484 ZeroMem (Urb, sizeof (URB));\r
485 Urb->Direction = Direction;\r
486 \r
487 //\r
488 // Allocate memory to move data from CAR or SMRAM to normal memory\r
489 // to make XHCI DMA successfully\r
490 // re-use the pre-allocate buffer in PEI to avoid DXE memory service or gBS are not ready\r
491 //\r
492 Urb->Data = UrbData;\r
493 \r
494 if (Direction == EfiUsbDataIn) {\r
495 //\r
496 // Do not break URB data in buffer as it may contain the data which were just put in via DMA by XHC\r
497 //\r
498 Urb->DataLen = (UINT32) DataLen;\r
499 } else {\r
500 //\r
501 // Put data into URB data out buffer which will create TRBs\r
502 //\r
503 ZeroMem ((VOID*)(UINTN) Urb->Data, DataLen);\r
504 CopyMem ((VOID*)(UINTN) Urb->Data, Data, DataLen);\r
505 Urb->DataLen = (UINT32) DataLen;\r
506 }\r
507 \r
508 Status = XhcCreateTransferTrb (Handle, Urb);\r
509 ASSERT_EFI_ERROR (Status);\r
510\r
511 return Urb;\r
512}\r
513\r
514/**\r
515 Submits bulk transfer to a bulk endpoint of a USB device.\r
516\r
517 @param Handle Debug port handle.\r
518 @param Direction The direction of data transfer.\r
519 @param Data Array of pointers to the buffers of data to transmit\r
520 from or receive into.\r
521 @param DataLength The lenght of the data buffer.\r
522 @param Timeout Indicates the maximum time, in microsecond, which\r
523 the transfer is allowed to complete.\r
524\r
525 @retval EFI_SUCCESS The transfer was completed successfully.\r
526 @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.\r
527 @retval EFI_INVALID_PARAMETER Some parameters are invalid.\r
528 @retval EFI_TIMEOUT The transfer failed due to timeout.\r
529 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.\r
530\r
531**/\r
532EFI_STATUS\r
533EFIAPI\r
534XhcDataTransfer (\r
535 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
536 IN EFI_USB_DATA_DIRECTION Direction,\r
537 IN OUT VOID *Data,\r
538 IN OUT UINTN *DataLength,\r
539 IN UINTN Timeout\r
540 )\r
541{\r
542 URB *Urb;\r
543 EFI_STATUS Status;\r
544 \r
545 //\r
546 // Validate the parameters\r
547 //\r
548 if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL)) {\r
549 return EFI_INVALID_PARAMETER;\r
550 }\r
551\r
552 //\r
553 // Create a new URB, insert it into the asynchronous\r
554 // schedule list, then poll the execution status.\r
555 //\r
556 Urb = XhcCreateUrb (Handle, Direction, Data, *DataLength);\r
557 ASSERT (Urb != NULL);\r
558\r
559 XhcExecTransfer (Handle, Urb, Timeout);\r
560\r
561 *DataLength = Urb->Completed;\r
562\r
563 Status = EFI_TIMEOUT;\r
564 if (Urb->Result == EFI_USB_NOERROR) {\r
565 Status = EFI_SUCCESS;\r
566 }\r
567 \r
568 if (Direction == EfiUsbDataIn) {\r
569 //\r
570 // Move data from internal buffer to outside buffer (outside buffer may be in SMRAM...)\r
571 // SMRAM does not allow to do DMA, so we create an internal buffer.\r
572 //\r
573 CopyMem (Data, (VOID *)(UINTN)Urb->Data, *DataLength);\r
574 }\r
575\r
576 return Status;\r
577}\r
578\r