| 1 | /** @file\r |
| 2 | Debug Port Library implementation based on usb3 debug port.\r |
| 3 | \r |
| 4 | Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>\r |
| 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 |
| 25 | EFI_STATUS\r |
| 26 | EFIAPI\r |
| 27 | XhcSyncTrsRing (\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 |
| 88 | EFI_STATUS\r |
| 89 | EFIAPI\r |
| 90 | XhcSyncEventRing (\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 |
| 139 | EFI_STATUS\r |
| 140 | EFIAPI\r |
| 141 | XhcCheckNewEvent (\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 |
| 148 | TRB_TEMPLATE *EvtTrb;\r |
| 149 | \r |
| 150 | ASSERT (EvtRing != NULL);\r |
| 151 | \r |
| 152 | EvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;\r |
| 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 |
| 182 | BOOLEAN\r |
| 183 | IsTrbInTrsRing (\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 |
| 213 | VOID\r |
| 214 | XhcCheckUrbResult (\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 |
| 265 | CopyMem ((VOID *)(UINTN)Handle->Data, (VOID *)(UINTN)Handle->UrbIn.Data, Handle->DataCount);\r |
| 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 |
| 280 | CheckedUrb->Completed += (((TRANSFER_TRB_NORMAL*)TRBPtr)->Length - EvtTrb->Length);\r |
| 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 |
| 290 | EXIT:\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 |
| 320 | EFI_STATUS\r |
| 321 | EFIAPI\r |
| 322 | XhcRingDoorBell (\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 |
| 351 | VOID\r |
| 352 | XhcExecTransfer (\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 |
| 359 | TRB_TEMPLATE *Trb;\r |
| 360 | UINTN Loop;\r |
| 361 | UINTN Index;\r |
| 362 | \r |
| 363 | Loop = Timeout / XHC_DEBUG_PORT_1_MILLISECOND;\r |
| 364 | if (Timeout == 0) {\r |
| 365 | Loop = 0xFFFFFFFF;\r |
| 366 | }\r |
| 367 | XhcRingDoorBell (Handle, Urb);\r |
| 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 |
| 371 | for (Index = 0; Index < Loop; Index++) {\r |
| 372 | XhcCheckUrbResult (Handle, Urb);\r |
| 373 | if (Urb->Finished) {\r |
| 374 | break;\r |
| 375 | }\r |
| 376 | MicroSecondDelay (XHC_DEBUG_PORT_1_MILLISECOND);\r |
| 377 | }\r |
| 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 |
| 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 |
| 416 | EFI_STATUS\r |
| 417 | XhcCreateTransferTrb (\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 |
| 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 |
| 464 | URB*\r |
| 465 | XhcCreateUrb (\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 |
| 532 | EFI_STATUS\r |
| 533 | EFIAPI\r |
| 534 | XhcDataTransfer (\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 |