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