1 /** @file 2 3 The EHCI register operation routines. 4 5 Copyright (c) 2007 - 2013, Intel Corporation. All rights reserved.<BR> 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions of the BSD License 8 which accompanies this distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14 **/ 15 16 #include "Uhci.h" 17 18 19 /** 20 Create Frame List Structure. 21 22 @param Uhc UHCI device. 23 24 @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. 25 @retval EFI_UNSUPPORTED Map memory fail. 26 @retval EFI_SUCCESS Success. 27 28 **/ 29 EFI_STATUS 30 UhciInitFrameList ( 31 IN USB_HC_DEV *Uhc 32 ) 33 { 34 EFI_PHYSICAL_ADDRESS MappedAddr; 35 EFI_STATUS Status; 36 VOID *Buffer; 37 VOID *Mapping; 38 UINTN Pages; 39 UINTN Bytes; 40 UINTN Index; 41 EFI_PHYSICAL_ADDRESS PhyAddr; 42 43 // 44 // The Frame List is a common buffer that will be 45 // accessed by both the cpu and the usb bus master 46 // at the same time. The Frame List ocupies 4K bytes, 47 // and must be aligned on 4-Kbyte boundaries. 48 // 49 Bytes = 4096; 50 Pages = EFI_SIZE_TO_PAGES (Bytes); 51 52 Status = Uhc->PciIo->AllocateBuffer ( 53 Uhc->PciIo, 54 AllocateAnyPages, 55 EfiBootServicesData, 56 Pages, 57 &Buffer, 58 0 59 ); 60 61 if (EFI_ERROR (Status)) { 62 return EFI_OUT_OF_RESOURCES; 63 } 64 65 Status = Uhc->PciIo->Map ( 66 Uhc->PciIo, 67 EfiPciIoOperationBusMasterCommonBuffer, 68 Buffer, 69 &Bytes, 70 &MappedAddr, 71 &Mapping 72 ); 73 74 if (EFI_ERROR (Status) || (Bytes != 4096)) { 75 Status = EFI_UNSUPPORTED; 76 goto ON_ERROR; 77 } 78 79 Uhc->FrameBase = (UINT32 *) (UINTN) Buffer; 80 Uhc->FrameMapping = Mapping; 81 82 // 83 // Tell the Host Controller where the Frame List lies, 84 // by set the Frame List Base Address Register. 85 // 86 UhciSetFrameListBaseAddr (Uhc->PciIo, (VOID *) (UINTN) MappedAddr); 87 88 // 89 // Allocate the QH used by sync interrupt/control/bulk transfer. 90 // FS ctrl/bulk queue head is set to loopback so additional BW 91 // can be reclaimed. Notice, LS don't support bulk transfer and 92 // also doesn't support BW reclamation. 93 // 94 Uhc->SyncIntQh = UhciCreateQh (Uhc, 1); 95 Uhc->CtrlQh = UhciCreateQh (Uhc, 1); 96 Uhc->BulkQh = UhciCreateQh (Uhc, 1); 97 98 if ((Uhc->SyncIntQh == NULL) || (Uhc->CtrlQh == NULL) || (Uhc->BulkQh == NULL)) { 99 Uhc->PciIo->Unmap (Uhc->PciIo, Mapping); 100 Status = EFI_OUT_OF_RESOURCES; 101 goto ON_ERROR; 102 } 103 104 // 105 // +-------------+ 106 // | | 107 // Link the three together: SyncIntQh->CtrlQh->BulkQh <---------+ 108 // Each frame entry is linked to this sequence of QH. These QH 109 // will remain on the schedul, never got removed 110 // 111 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_HW)); 112 Uhc->SyncIntQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE); 113 Uhc->SyncIntQh->NextQh = Uhc->CtrlQh; 114 115 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_HW)); 116 Uhc->CtrlQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE); 117 Uhc->CtrlQh->NextQh = Uhc->BulkQh; 118 119 // 120 // Some old platform such as Intel's Tiger 4 has a difficult time 121 // in supporting the full speed bandwidth reclamation in the previous 122 // mentioned form. Most new platforms don't suffer it. 123 // 124 Uhc->BulkQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE); 125 126 Uhc->BulkQh->NextQh = NULL; 127 128 Uhc->FrameBaseHostAddr = AllocateZeroPool (4096); 129 if (Uhc->FrameBaseHostAddr == NULL) { 130 Status = EFI_OUT_OF_RESOURCES; 131 goto ON_ERROR; 132 } 133 134 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_HW)); 135 for (Index = 0; Index < UHCI_FRAME_NUM; Index++) { 136 Uhc->FrameBase[Index] = QH_HLINK (PhyAddr, FALSE); 137 Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Uhc->SyncIntQh; 138 } 139 140 return EFI_SUCCESS; 141 142 ON_ERROR: 143 if (Uhc->SyncIntQh != NULL) { 144 UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW)); 145 } 146 147 if (Uhc->CtrlQh != NULL) { 148 UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW)); 149 } 150 151 if (Uhc->BulkQh != NULL) { 152 UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW)); 153 } 154 155 Uhc->PciIo->FreeBuffer (Uhc->PciIo, Pages, Buffer); 156 return Status; 157 } 158 159 160 /** 161 Destory FrameList buffer. 162 163 @param Uhc The UHCI device. 164 165 **/ 166 VOID 167 UhciDestoryFrameList ( 168 IN USB_HC_DEV *Uhc 169 ) 170 { 171 // 172 // Unmap the common buffer for framelist entry, 173 // and free the common buffer. 174 // Uhci's frame list occupy 4k memory. 175 // 176 Uhc->PciIo->Unmap (Uhc->PciIo, Uhc->FrameMapping); 177 178 Uhc->PciIo->FreeBuffer ( 179 Uhc->PciIo, 180 EFI_SIZE_TO_PAGES (4096), 181 (VOID *) Uhc->FrameBase 182 ); 183 184 if (Uhc->FrameBaseHostAddr != NULL) { 185 FreePool (Uhc->FrameBaseHostAddr); 186 } 187 188 if (Uhc->SyncIntQh != NULL) { 189 UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW)); 190 } 191 192 if (Uhc->CtrlQh != NULL) { 193 UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW)); 194 } 195 196 if (Uhc->BulkQh != NULL) { 197 UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW)); 198 } 199 200 Uhc->FrameBase = NULL; 201 Uhc->FrameBaseHostAddr = NULL; 202 Uhc->SyncIntQh = NULL; 203 Uhc->CtrlQh = NULL; 204 Uhc->BulkQh = NULL; 205 } 206 207 208 /** 209 Convert the poll rate to the maxium 2^n that is smaller 210 than Interval. 211 212 @param Interval The poll rate to convert. 213 214 @return The converted poll rate. 215 216 **/ 217 UINTN 218 UhciConvertPollRate ( 219 IN UINTN Interval 220 ) 221 { 222 UINTN BitCount; 223 224 ASSERT (Interval != 0); 225 226 // 227 // Find the index (1 based) of the highest non-zero bit 228 // 229 BitCount = 0; 230 231 while (Interval != 0) { 232 Interval >>= 1; 233 BitCount++; 234 } 235 236 return (UINTN)1 << (BitCount - 1); 237 } 238 239 240 /** 241 Link a queue head (for asynchronous interrupt transfer) to 242 the frame list. 243 244 @param Uhc The UHCI device. 245 @param Qh The queue head to link into. 246 247 **/ 248 VOID 249 UhciLinkQhToFrameList ( 250 USB_HC_DEV *Uhc, 251 UHCI_QH_SW *Qh 252 ) 253 { 254 UINTN Index; 255 UHCI_QH_SW *Prev; 256 UHCI_QH_SW *Next; 257 EFI_PHYSICAL_ADDRESS PhyAddr; 258 EFI_PHYSICAL_ADDRESS QhPciAddr; 259 260 ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL)); 261 262 QhPciAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_HW)); 263 264 for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) { 265 // 266 // First QH can't be NULL because we always keep static queue 267 // heads on the frame list 268 // 269 ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index])); 270 Next = (UHCI_QH_SW*)(UINTN)Uhc->FrameBaseHostAddr[Index]; 271 Prev = NULL; 272 273 // 274 // Now, insert the queue head (Qh) into this frame: 275 // 1. Find a queue head with the same poll interval, just insert 276 // Qh after this queue head, then we are done. 277 // 278 // 2. Find the position to insert the queue head into: 279 // Previous head's interval is bigger than Qh's 280 // Next head's interval is less than Qh's 281 // Then, insert the Qh between then 282 // 283 // This method is very much the same as that used by EHCI. 284 // Because each QH's interval is round down to 2^n, poll 285 // rate is correct. 286 // 287 while (Next->Interval > Qh->Interval) { 288 Prev = Next; 289 Next = Next->NextQh; 290 ASSERT (Next != NULL); 291 } 292 293 // 294 // The entry may have been linked into the frame by early insertation. 295 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh 296 // with Qh.Interval == 8 on the frame. If so, we are done with this frame. 297 // It isn't necessary to compare all the QH with the same interval to 298 // Qh. This is because if there is other QH with the same interval, Qh 299 // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is 300 // impossible (Next == Qh) 301 // 302 if (Next == Qh) { 303 continue; 304 } 305 306 if (Next->Interval == Qh->Interval) { 307 // 308 // If there is a QH with the same interval, it locates at 309 // FrameBase[0], and we can simply insert it after this QH. We 310 // are all done. 311 // 312 ASSERT ((Index == 0) && (Qh->NextQh == NULL)); 313 314 Prev = Next; 315 Next = Next->NextQh; 316 317 Qh->NextQh = Next; 318 Prev->NextQh = Qh; 319 320 Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink; 321 322 Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE); 323 break; 324 } 325 326 // 327 // OK, find the right position, insert it in. If Qh's next 328 // link has already been set, it is in position. This is 329 // guarranted by 2^n polling interval. 330 // 331 if (Qh->NextQh == NULL) { 332 Qh->NextQh = Next; 333 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Next, sizeof (UHCI_QH_HW)); 334 Qh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE); 335 } 336 337 if (Prev == NULL) { 338 Uhc->FrameBase[Index] = QH_HLINK (QhPciAddr, FALSE); 339 Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Qh; 340 } else { 341 Prev->NextQh = Qh; 342 Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE); 343 } 344 } 345 } 346 347 348 /** 349 Unlink QH from the frame list is easier: find all 350 the precedence node, and pointer there next to QhSw's 351 next. 352 353 @param Uhc The UHCI device. 354 @param Qh The queue head to unlink. 355 356 **/ 357 VOID 358 UhciUnlinkQhFromFrameList ( 359 USB_HC_DEV *Uhc, 360 UHCI_QH_SW *Qh 361 ) 362 { 363 UINTN Index; 364 UHCI_QH_SW *Prev; 365 UHCI_QH_SW *This; 366 367 ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL)); 368 369 for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) { 370 // 371 // Frame link can't be NULL because we always keep static 372 // queue heads on the frame list 373 // 374 ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index])); 375 This = (UHCI_QH_SW*)(UINTN)Uhc->FrameBaseHostAddr[Index]; 376 Prev = NULL; 377 378 // 379 // Walk through the frame's QH list to find the 380 // queue head to remove 381 // 382 while ((This != NULL) && (This != Qh)) { 383 Prev = This; 384 This = This->NextQh; 385 } 386 387 // 388 // Qh may have already been unlinked from this frame 389 // by early action. 390 // 391 if (This == NULL) { 392 continue; 393 } 394 395 if (Prev == NULL) { 396 // 397 // Qh is the first entry in the frame 398 // 399 Uhc->FrameBase[Index] = Qh->QhHw.HorizonLink; 400 Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Qh->NextQh; 401 } else { 402 Prev->NextQh = Qh->NextQh; 403 Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink; 404 } 405 } 406 } 407 408 409 /** 410 Check TDs Results. 411 412 @param Uhc This UHCI device. 413 @param Td UHCI_TD_SW to check. 414 @param IsLow Is Low Speed Device. 415 @param QhResult Return the result of this TD list. 416 417 @return Whether the TD's result is finialized. 418 419 **/ 420 BOOLEAN 421 UhciCheckTdStatus ( 422 IN USB_HC_DEV *Uhc, 423 IN UHCI_TD_SW *Td, 424 IN BOOLEAN IsLow, 425 OUT UHCI_QH_RESULT *QhResult 426 ) 427 { 428 UINTN Len; 429 UINT8 State; 430 UHCI_TD_HW *TdHw; 431 BOOLEAN Finished; 432 433 Finished = TRUE; 434 435 // 436 // Initialize the data toggle to that of the first 437 // TD. The next toggle to use is either: 438 // 1. first TD's toggle if no TD is executed OK 439 // 2. the next toggle of last executed-OK TD 440 // 441 QhResult->Result = EFI_USB_NOERROR; 442 QhResult->NextToggle = (UINT8)Td->TdHw.DataToggle; 443 QhResult->Complete = 0; 444 445 while (Td != NULL) { 446 TdHw = &Td->TdHw; 447 State = (UINT8)TdHw->Status; 448 449 // 450 // UHCI will set STALLED bit when it abort the execution 451 // of TD list. There are several reasons: 452 // 1. BABBLE error happened 453 // 2. Received a STALL response 454 // 3. Error count decreased to zero. 455 // 456 // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error 457 // bits when corresponding conditions happen. But these 458 // conditions are not deadly, that is a TD can successfully 459 // completes even these bits are set. But it is likely that 460 // upper layer won't distinguish these condtions. So, only 461 // set these bits when TD is actually halted. 462 // 463 if ((State & USBTD_STALLED) != 0) { 464 if ((State & USBTD_BABBLE) != 0) { 465 QhResult->Result |= EFI_USB_ERR_BABBLE; 466 467 } else if (TdHw->ErrorCount != 0) { 468 QhResult->Result |= EFI_USB_ERR_STALL; 469 } 470 471 if ((State & USBTD_CRC) != 0) { 472 QhResult->Result |= EFI_USB_ERR_CRC; 473 } 474 475 if ((State & USBTD_BUFFERR) != 0) { 476 QhResult->Result |= EFI_USB_ERR_BUFFER; 477 } 478 479 if ((Td->TdHw.Status & USBTD_BITSTUFF) != 0) { 480 QhResult->Result |= EFI_USB_ERR_BITSTUFF; 481 } 482 483 if (TdHw->ErrorCount == 0) { 484 QhResult->Result |= EFI_USB_ERR_TIMEOUT; 485 } 486 487 Finished = TRUE; 488 goto ON_EXIT; 489 490 } else if ((State & USBTD_ACTIVE) != 0) { 491 // 492 // The TD is still active, no need to check further. 493 // 494 QhResult->Result |= EFI_USB_ERR_NOTEXECUTE; 495 496 Finished = FALSE; 497 goto ON_EXIT; 498 499 } else { 500 // 501 // Update the next data toggle, it is always the 502 // next to the last known-good TD's data toggle if 503 // any TD is executed OK 504 // 505 QhResult->NextToggle = (UINT8) (1 - (UINT8)TdHw->DataToggle); 506 507 // 508 // This TD is finished OK or met short packet read. Update the 509 // transfer length if it isn't a SETUP. 510 // 511 Len = (TdHw->ActualLen + 1) & 0x7FF; 512 513 if (TdHw->PidCode != SETUP_PACKET_ID) { 514 QhResult->Complete += Len; 515 } 516 517 // 518 // Short packet condition for full speed input TD, also 519 // terminate the transfer 520 // 521 if (!IsLow && (TdHw->ShortPacket == 1) && (Len < Td->DataLen)) { 522 DEBUG ((EFI_D_VERBOSE, "UhciCheckTdStatus: short packet read occured\n")); 523 524 Finished = TRUE; 525 goto ON_EXIT; 526 } 527 } 528 529 Td = Td->NextTd; 530 } 531 532 ON_EXIT: 533 // 534 // Check whether HC is halted. Don't move this up. It must be 535 // called after data toggle is successfully updated. 536 // 537 if (!UhciIsHcWorking (Uhc->PciIo)) { 538 QhResult->Result |= EFI_USB_ERR_SYSTEM; 539 Finished = TRUE; 540 } 541 542 if (Finished) { 543 Uhc->PciIo->Flush (Uhc->PciIo); 544 } 545 546 UhciAckAllInterrupt (Uhc); 547 return Finished; 548 } 549 550 551 /** 552 Check the result of the transfer. 553 554 @param Uhc The UHCI device. 555 @param Qh The queue head of the transfer. 556 @param Td The first TDs of the transfer. 557 @param TimeOut TimeOut value in milliseconds. 558 @param IsLow Is Low Speed Device. 559 @param QhResult The variable to return result. 560 561 @retval EFI_SUCCESS The transfer finished with success. 562 @retval EFI_DEVICE_ERROR Transfer failed. 563 564 **/ 565 EFI_STATUS 566 UhciExecuteTransfer ( 567 IN USB_HC_DEV *Uhc, 568 IN UHCI_QH_SW *Qh, 569 IN UHCI_TD_SW *Td, 570 IN UINTN TimeOut, 571 IN BOOLEAN IsLow, 572 OUT UHCI_QH_RESULT *QhResult 573 ) 574 { 575 UINTN Index; 576 UINTN Delay; 577 BOOLEAN Finished; 578 EFI_STATUS Status; 579 BOOLEAN InfiniteLoop; 580 581 Finished = FALSE; 582 Status = EFI_SUCCESS; 583 Delay = TimeOut * UHC_1_MILLISECOND; 584 InfiniteLoop = FALSE; 585 586 // 587 // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller 588 // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR 589 // is returned. 590 // 591 if (TimeOut == 0) { 592 InfiniteLoop = TRUE; 593 } 594 595 for (Index = 0; InfiniteLoop || (Index < Delay); Index++) { 596 Finished = UhciCheckTdStatus (Uhc, Td, IsLow, QhResult); 597 598 // 599 // Transfer is OK or some error occured (TD inactive) 600 // 601 if (Finished) { 602 break; 603 } 604 605 gBS->Stall (UHC_1_MICROSECOND); 606 } 607 608 if (!Finished) { 609 DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution not finished for %dms\n", (UINT32)TimeOut)); 610 UhciDumpQh (Qh); 611 UhciDumpTds (Td); 612 613 Status = EFI_TIMEOUT; 614 615 } else if (QhResult->Result != EFI_USB_NOERROR) { 616 DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result)); 617 UhciDumpQh (Qh); 618 UhciDumpTds (Td); 619 620 Status = EFI_DEVICE_ERROR; 621 } 622 623 return Status; 624 } 625 626 627 /** 628 Update Async Request, QH and TDs. 629 630 @param Uhc The UHCI device. 631 @param AsyncReq The UHCI asynchronous transfer to update. 632 @param Result Transfer reslut. 633 @param NextToggle The toggle of next data. 634 635 **/ 636 VOID 637 UhciUpdateAsyncReq ( 638 IN USB_HC_DEV *Uhc, 639 IN UHCI_ASYNC_REQUEST *AsyncReq, 640 IN UINT32 Result, 641 IN UINT32 NextToggle 642 ) 643 { 644 UHCI_QH_SW *Qh; 645 UHCI_TD_SW *FirstTd; 646 UHCI_TD_SW *Td; 647 648 Qh = AsyncReq->QhSw; 649 FirstTd = AsyncReq->FirstTd; 650 651 if (Result == EFI_USB_NOERROR) { 652 // 653 // The last transfer succeeds. Then we need to update 654 // the Qh and Td for next round of transfer. 655 // 1. Update the TD's data toggle 656 // 2. Activate all the TDs 657 // 3. Link the TD to the queue head again since during 658 // execution, queue head's TD pointer is changed by 659 // hardware. 660 // 661 for (Td = FirstTd; Td != NULL; Td = Td->NextTd) { 662 Td->TdHw.DataToggle = NextToggle; 663 NextToggle ^= 1; 664 Td->TdHw.Status |= USBTD_ACTIVE; 665 } 666 667 UhciLinkTdToQh (Uhc, Qh, FirstTd); 668 return ; 669 } 670 } 671 672 673 /** 674 Create Async Request node, and Link to List. 675 676 @param Uhc The UHCI device. 677 @param Qh The queue head of the transfer. 678 @param FirstTd First TD of the transfer. 679 @param DevAddr Device Address. 680 @param EndPoint EndPoint Address. 681 @param DataLen Data length. 682 @param Interval Polling Interval when inserted to frame list. 683 @param Data Data buffer, unmapped. 684 @param Callback Callback after interrupt transfeer. 685 @param Context Callback Context passed as function parameter. 686 @param IsLow Is Low Speed. 687 688 @retval EFI_SUCCESS An asynchronous transfer is created. 689 @retval EFI_INVALID_PARAMETER Paremeter is error. 690 @retval EFI_OUT_OF_RESOURCES Failed because of resource shortage. 691 692 **/ 693 EFI_STATUS 694 UhciCreateAsyncReq ( 695 IN USB_HC_DEV *Uhc, 696 IN UHCI_QH_SW *Qh, 697 IN UHCI_TD_SW *FirstTd, 698 IN UINT8 DevAddr, 699 IN UINT8 EndPoint, 700 IN UINTN DataLen, 701 IN UINTN Interval, 702 IN UINT8 *Data, 703 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, 704 IN VOID *Context, 705 IN BOOLEAN IsLow 706 ) 707 { 708 UHCI_ASYNC_REQUEST *AsyncReq; 709 710 AsyncReq = AllocatePool (sizeof (UHCI_ASYNC_REQUEST)); 711 712 if (AsyncReq == NULL) { 713 return EFI_OUT_OF_RESOURCES; 714 } 715 716 // 717 // Fill Request field. Data is allocated host memory, not mapped 718 // 719 AsyncReq->Signature = UHCI_ASYNC_INT_SIGNATURE; 720 AsyncReq->DevAddr = DevAddr; 721 AsyncReq->EndPoint = EndPoint; 722 AsyncReq->DataLen = DataLen; 723 AsyncReq->Interval = UhciConvertPollRate(Interval); 724 AsyncReq->Data = Data; 725 AsyncReq->Callback = Callback; 726 AsyncReq->Context = Context; 727 AsyncReq->QhSw = Qh; 728 AsyncReq->FirstTd = FirstTd; 729 AsyncReq->IsLow = IsLow; 730 731 // 732 // Insert the new interrupt transfer to the head of the list. 733 // The interrupt transfer's monitor function scans the whole 734 // list from head to tail. The new interrupt transfer MUST be 735 // added to the head of the list. 736 // 737 InsertHeadList (&(Uhc->AsyncIntList), &(AsyncReq->Link)); 738 739 return EFI_SUCCESS; 740 } 741 742 743 /** 744 Free an asynchronous request's resource such as memory. 745 746 @param Uhc The UHCI device. 747 @param AsyncReq The asynchronous request to free. 748 749 **/ 750 VOID 751 UhciFreeAsyncReq ( 752 IN USB_HC_DEV *Uhc, 753 IN UHCI_ASYNC_REQUEST *AsyncReq 754 ) 755 { 756 ASSERT ((Uhc != NULL) && (AsyncReq != NULL)); 757 758 UhciDestoryTds (Uhc, AsyncReq->FirstTd); 759 UsbHcFreeMem (Uhc->MemPool, AsyncReq->QhSw, sizeof (UHCI_QH_SW)); 760 761 if (AsyncReq->Data != NULL) { 762 UsbHcFreeMem (Uhc->MemPool, AsyncReq->Data, AsyncReq->DataLen); 763 } 764 765 gBS->FreePool (AsyncReq); 766 } 767 768 769 /** 770 Unlink an asynchronous request's from UHC's asynchronus list. 771 also remove the queue head from the frame list. If FreeNow, 772 release its resource also. Otherwise, add the request to the 773 UHC's recycle list to wait for a while before release the memory. 774 Until then, hardware won't hold point to the request. 775 776 @param Uhc The UHCI device. 777 @param AsyncReq The asynchronous request to free. 778 @param FreeNow If TRUE, free the resource immediately, otherwise 779 add the request to recycle wait list. 780 781 **/ 782 VOID 783 UhciUnlinkAsyncReq ( 784 IN USB_HC_DEV *Uhc, 785 IN UHCI_ASYNC_REQUEST *AsyncReq, 786 IN BOOLEAN FreeNow 787 ) 788 { 789 ASSERT ((Uhc != NULL) && (AsyncReq != NULL)); 790 791 RemoveEntryList (&(AsyncReq->Link)); 792 UhciUnlinkQhFromFrameList (Uhc, AsyncReq->QhSw); 793 794 if (FreeNow) { 795 UhciFreeAsyncReq (Uhc, AsyncReq); 796 } else { 797 // 798 // To sychronize with hardware, mark the queue head as inactive 799 // then add AsyncReq to UHC's recycle list 800 // 801 AsyncReq->QhSw->QhHw.VerticalLink = QH_VLINK (NULL, TRUE); 802 AsyncReq->Recycle = Uhc->RecycleWait; 803 Uhc->RecycleWait = AsyncReq; 804 } 805 } 806 807 808 /** 809 Delete Async Interrupt QH and TDs. 810 811 @param Uhc The UHCI device. 812 @param DevAddr Device Address. 813 @param EndPoint EndPoint Address. 814 @param Toggle The next data toggle to use. 815 816 @retval EFI_SUCCESS The request is deleted. 817 @retval EFI_INVALID_PARAMETER Paremeter is error. 818 @retval EFI_NOT_FOUND The asynchronous isn't found. 819 820 **/ 821 EFI_STATUS 822 UhciRemoveAsyncReq ( 823 IN USB_HC_DEV *Uhc, 824 IN UINT8 DevAddr, 825 IN UINT8 EndPoint, 826 OUT UINT8 *Toggle 827 ) 828 { 829 EFI_STATUS Status; 830 UHCI_ASYNC_REQUEST *AsyncReq; 831 UHCI_QH_RESULT QhResult; 832 LIST_ENTRY *Link; 833 BOOLEAN Found; 834 835 Status = EFI_SUCCESS; 836 837 // 838 // If no asynchronous interrupt transaction exists 839 // 840 if (IsListEmpty (&(Uhc->AsyncIntList))) { 841 return EFI_SUCCESS; 842 } 843 844 // 845 // Find the asynchronous transfer to this device/endpoint pair 846 // 847 Found = FALSE; 848 Link = Uhc->AsyncIntList.ForwardLink; 849 850 do { 851 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link); 852 Link = Link->ForwardLink; 853 854 if ((AsyncReq->DevAddr == DevAddr) && (AsyncReq->EndPoint == EndPoint)) { 855 Found = TRUE; 856 break; 857 } 858 859 } while (Link != &(Uhc->AsyncIntList)); 860 861 if (!Found) { 862 return EFI_NOT_FOUND; 863 } 864 865 // 866 // Check the result of the async transfer then update it 867 // to get the next data toggle to use. 868 // 869 UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult); 870 *Toggle = QhResult.NextToggle; 871 872 // 873 // Don't release the request now, keep it to synchronize with hardware. 874 // 875 UhciUnlinkAsyncReq (Uhc, AsyncReq, FALSE); 876 return Status; 877 } 878 879 880 /** 881 Recycle the asynchronouse request. When a queue head 882 is unlinked from frame list, host controller hardware 883 may still hold a cached pointer to it. To synchronize 884 with hardware, the request is released in two steps: 885 first it is linked to the UHC's RecycleWait list. At 886 the next time UhciMonitorAsyncReqList is fired, it is 887 moved to UHC's Recylelist. Then, at another timer 888 activation, all the requests on Recycle list is freed. 889 This guarrantes that each unlink queue head keeps 890 existing for at least 50ms, far enough for the hardware 891 to clear its cache. 892 893 @param Uhc The UHCI device. 894 895 **/ 896 VOID 897 UhciRecycleAsyncReq ( 898 IN USB_HC_DEV *Uhc 899 ) 900 { 901 UHCI_ASYNC_REQUEST *Req; 902 UHCI_ASYNC_REQUEST *Next; 903 904 Req = Uhc->Recycle; 905 906 while (Req != NULL) { 907 Next = Req->Recycle; 908 UhciFreeAsyncReq (Uhc, Req); 909 Req = Next; 910 } 911 912 Uhc->Recycle = Uhc->RecycleWait; 913 Uhc->RecycleWait = NULL; 914 } 915 916 917 918 /** 919 Release all the asynchronous transfers on the lsit. 920 921 @param Uhc The UHCI device. 922 923 **/ 924 VOID 925 UhciFreeAllAsyncReq ( 926 IN USB_HC_DEV *Uhc 927 ) 928 { 929 LIST_ENTRY *Head; 930 UHCI_ASYNC_REQUEST *AsyncReq; 931 932 // 933 // Call UhciRecycleAsyncReq twice. The requests on Recycle 934 // will be released at the first call; The requests on 935 // RecycleWait will be released at the second call. 936 // 937 UhciRecycleAsyncReq (Uhc); 938 UhciRecycleAsyncReq (Uhc); 939 940 Head = &(Uhc->AsyncIntList); 941 942 if (IsListEmpty (Head)) { 943 return; 944 } 945 946 while (!IsListEmpty (Head)) { 947 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Head->ForwardLink); 948 UhciUnlinkAsyncReq (Uhc, AsyncReq, TRUE); 949 } 950 } 951 952 953 /** 954 Interrupt transfer periodic check handler. 955 956 @param Event The event of the time. 957 @param Context Context of the event, pointer to USB_HC_DEV. 958 959 **/ 960 VOID 961 EFIAPI 962 UhciMonitorAsyncReqList ( 963 IN EFI_EVENT Event, 964 IN VOID *Context 965 ) 966 { 967 UHCI_ASYNC_REQUEST *AsyncReq; 968 LIST_ENTRY *Link; 969 USB_HC_DEV *Uhc; 970 VOID *Data; 971 BOOLEAN Finished; 972 UHCI_QH_RESULT QhResult; 973 974 Uhc = (USB_HC_DEV *) Context; 975 976 // 977 // Recycle the asynchronous requests expired, and promote 978 // requests waiting to be recycled the next time when this 979 // timer expires 980 // 981 UhciRecycleAsyncReq (Uhc); 982 983 if (IsListEmpty (&(Uhc->AsyncIntList))) { 984 return ; 985 } 986 987 // 988 // This loop must be delete safe 989 // 990 Link = Uhc->AsyncIntList.ForwardLink; 991 992 do { 993 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link); 994 Link = Link->ForwardLink; 995 996 Finished = UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult); 997 998 if (!Finished) { 999 continue; 1000 } 1001 1002 // 1003 // Copy the data to temporary buffer if there are some 1004 // data transferred. We may have zero-length packet 1005 // 1006 Data = NULL; 1007 1008 if (QhResult.Complete != 0) { 1009 Data = AllocatePool (QhResult.Complete); 1010 1011 if (Data == NULL) { 1012 return ; 1013 } 1014 1015 CopyMem (Data, AsyncReq->FirstTd->Data, QhResult.Complete); 1016 } 1017 1018 UhciUpdateAsyncReq (Uhc, AsyncReq, QhResult.Result, QhResult.NextToggle); 1019 1020 // 1021 // Now, either transfer is SUCCESS or met errors since 1022 // we have skipped to next transfer earlier if current 1023 // transfer is still active. 1024 // 1025 if (QhResult.Result == EFI_USB_NOERROR) { 1026 AsyncReq->Callback (Data, QhResult.Complete, AsyncReq->Context, QhResult.Result); 1027 } else { 1028 // 1029 // Leave error recovery to its related device driver. 1030 // A common case of the error recovery is to re-submit 1031 // the interrupt transfer. When an interrupt transfer 1032 // is re-submitted, its position in the linked list is 1033 // changed. It is inserted to the head of the linked 1034 // list, while this function scans the whole list from 1035 // head to tail. Thus, the re-submitted interrupt transfer's 1036 // callback function will not be called again in this round. 1037 // 1038 AsyncReq->Callback (NULL, 0, AsyncReq->Context, QhResult.Result); 1039 } 1040 1041 if (Data != NULL) { 1042 gBS->FreePool (Data); 1043 } 1044 } while (Link != &(Uhc->AsyncIntList)); 1045 } 1046