Home | History | Annotate | Download | only in UhciDxe
      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