Fixed some spelling typos in some comments. Added a couple of useful DEBUG messages Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Samer El-Haj-Mahmoud <samer.el-haj-mahmoud@hp.com> Reviewed-by: Feng Tian <feng.tian@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16450 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1046 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1046 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
 | 
						|
  The EHCI register operation routines.
 | 
						|
 | 
						|
Copyright (c) 2007 - 2013, Intel Corporation. All rights reserved.<BR>
 | 
						|
This program and the accompanying materials
 | 
						|
are licensed and made available under the terms and conditions of the BSD License
 | 
						|
which accompanies this distribution.  The full text of the license may be found at
 | 
						|
http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "Uhci.h"
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Create Frame List Structure.
 | 
						|
 | 
						|
  @param  Uhc                    UHCI device.
 | 
						|
 | 
						|
  @retval EFI_OUT_OF_RESOURCES   Can't allocate memory resources.
 | 
						|
  @retval EFI_UNSUPPORTED        Map memory fail.
 | 
						|
  @retval EFI_SUCCESS            Success.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
UhciInitFrameList (
 | 
						|
  IN USB_HC_DEV         *Uhc
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_PHYSICAL_ADDRESS  MappedAddr;
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  VOID                  *Buffer;
 | 
						|
  VOID                  *Mapping;
 | 
						|
  UINTN                 Pages;
 | 
						|
  UINTN                 Bytes;
 | 
						|
  UINTN                 Index;
 | 
						|
  EFI_PHYSICAL_ADDRESS  PhyAddr;
 | 
						|
 | 
						|
  //
 | 
						|
  // The Frame List is a common buffer that will be
 | 
						|
  // accessed by both the cpu and the usb bus master
 | 
						|
  // at the same time. The Frame List ocupies 4K bytes,
 | 
						|
  // and must be aligned on 4-Kbyte boundaries.
 | 
						|
  //
 | 
						|
  Bytes = 4096;
 | 
						|
  Pages = EFI_SIZE_TO_PAGES (Bytes);
 | 
						|
 | 
						|
  Status = Uhc->PciIo->AllocateBuffer (
 | 
						|
                         Uhc->PciIo,
 | 
						|
                         AllocateAnyPages,
 | 
						|
                         EfiBootServicesData,
 | 
						|
                         Pages,
 | 
						|
                         &Buffer,
 | 
						|
                         0
 | 
						|
                         );
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = Uhc->PciIo->Map (
 | 
						|
                         Uhc->PciIo,
 | 
						|
                         EfiPciIoOperationBusMasterCommonBuffer,
 | 
						|
                         Buffer,
 | 
						|
                         &Bytes,
 | 
						|
                         &MappedAddr,
 | 
						|
                         &Mapping
 | 
						|
                         );
 | 
						|
 | 
						|
  if (EFI_ERROR (Status) || (Bytes != 4096)) {
 | 
						|
    Status = EFI_UNSUPPORTED;
 | 
						|
    goto ON_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  Uhc->FrameBase           = (UINT32 *) (UINTN) Buffer;
 | 
						|
  Uhc->FrameMapping        = Mapping;
 | 
						|
 | 
						|
  //
 | 
						|
  // Tell the Host Controller where the Frame List lies,
 | 
						|
  // by set the Frame List Base Address Register.
 | 
						|
  //
 | 
						|
  UhciSetFrameListBaseAddr (Uhc->PciIo, (VOID *) (UINTN) MappedAddr);
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate the QH used by sync interrupt/control/bulk transfer.
 | 
						|
  // FS ctrl/bulk queue head is set to loopback so additional BW
 | 
						|
  // can be reclaimed. Notice, LS don't support bulk transfer and
 | 
						|
  // also doesn't support BW reclamation.
 | 
						|
  //
 | 
						|
  Uhc->SyncIntQh  = UhciCreateQh (Uhc, 1);
 | 
						|
  Uhc->CtrlQh     = UhciCreateQh (Uhc, 1);
 | 
						|
  Uhc->BulkQh     = UhciCreateQh (Uhc, 1);
 | 
						|
 | 
						|
  if ((Uhc->SyncIntQh == NULL) || (Uhc->CtrlQh == NULL) || (Uhc->BulkQh == NULL)) {
 | 
						|
    Uhc->PciIo->Unmap (Uhc->PciIo, Mapping);
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //                                                +-------------+
 | 
						|
  //                                                |             |
 | 
						|
  // Link the three together: SyncIntQh->CtrlQh->BulkQh <---------+
 | 
						|
  // Each frame entry is linked to this sequence of QH. These QH
 | 
						|
  // will remain on the schedul, never got removed
 | 
						|
  //
 | 
						|
  PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_HW));
 | 
						|
  Uhc->SyncIntQh->QhHw.HorizonLink  = QH_HLINK (PhyAddr, FALSE);
 | 
						|
  Uhc->SyncIntQh->NextQh            = Uhc->CtrlQh;
 | 
						|
 | 
						|
  PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_HW));
 | 
						|
  Uhc->CtrlQh->QhHw.HorizonLink     = QH_HLINK (PhyAddr, FALSE);
 | 
						|
  Uhc->CtrlQh->NextQh               = Uhc->BulkQh;
 | 
						|
 | 
						|
  //
 | 
						|
  // Some old platform such as Intel's Tiger 4 has a difficult time
 | 
						|
  // in supporting the full speed bandwidth reclamation in the previous
 | 
						|
  // mentioned form. Most new platforms don't suffer it.
 | 
						|
  //
 | 
						|
  Uhc->BulkQh->QhHw.HorizonLink     = QH_HLINK (PhyAddr, FALSE);
 | 
						|
 | 
						|
  Uhc->BulkQh->NextQh               = NULL;
 | 
						|
 | 
						|
  Uhc->FrameBaseHostAddr = AllocateZeroPool (4096);
 | 
						|
  if (Uhc->FrameBaseHostAddr == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_HW));
 | 
						|
  for (Index = 0; Index < UHCI_FRAME_NUM; Index++) {
 | 
						|
    Uhc->FrameBase[Index] = QH_HLINK (PhyAddr, FALSE);
 | 
						|
    Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Uhc->SyncIntQh;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
 | 
						|
ON_ERROR:
 | 
						|
  if (Uhc->SyncIntQh != NULL) {
 | 
						|
    UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
 | 
						|
  }
 | 
						|
 | 
						|
  if (Uhc->CtrlQh != NULL) {
 | 
						|
    UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
 | 
						|
  }
 | 
						|
 | 
						|
  if (Uhc->BulkQh != NULL) {
 | 
						|
    UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
 | 
						|
  }
 | 
						|
 | 
						|
  Uhc->PciIo->FreeBuffer (Uhc->PciIo, Pages, Buffer);
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Destory FrameList buffer.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UhciDestoryFrameList (
 | 
						|
  IN USB_HC_DEV           *Uhc
 | 
						|
  )
 | 
						|
{
 | 
						|
  //
 | 
						|
  // Unmap the common buffer for framelist entry,
 | 
						|
  // and free the common buffer.
 | 
						|
  // Uhci's frame list occupy 4k memory.
 | 
						|
  //
 | 
						|
  Uhc->PciIo->Unmap (Uhc->PciIo, Uhc->FrameMapping);
 | 
						|
 | 
						|
  Uhc->PciIo->FreeBuffer (
 | 
						|
                Uhc->PciIo,
 | 
						|
                EFI_SIZE_TO_PAGES (4096),
 | 
						|
                (VOID *) Uhc->FrameBase
 | 
						|
                );
 | 
						|
 | 
						|
  if (Uhc->FrameBaseHostAddr != NULL) {
 | 
						|
    FreePool (Uhc->FrameBaseHostAddr);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Uhc->SyncIntQh != NULL) {
 | 
						|
    UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
 | 
						|
  }
 | 
						|
 | 
						|
  if (Uhc->CtrlQh != NULL) {
 | 
						|
    UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
 | 
						|
  }
 | 
						|
 | 
						|
  if (Uhc->BulkQh != NULL) {
 | 
						|
    UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
 | 
						|
  }
 | 
						|
 | 
						|
  Uhc->FrameBase           = NULL;
 | 
						|
  Uhc->FrameBaseHostAddr   = NULL;
 | 
						|
  Uhc->SyncIntQh           = NULL;
 | 
						|
  Uhc->CtrlQh              = NULL;
 | 
						|
  Uhc->BulkQh              = NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert the poll rate to the maxium 2^n that is smaller
 | 
						|
  than Interval.
 | 
						|
 | 
						|
  @param  Interval               The poll rate to convert.
 | 
						|
 | 
						|
  @return The converted poll rate.
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
UhciConvertPollRate (
 | 
						|
  IN  UINTN               Interval
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                   BitCount;
 | 
						|
 | 
						|
  ASSERT (Interval != 0);
 | 
						|
 | 
						|
  //
 | 
						|
  // Find the index (1 based) of the highest non-zero bit
 | 
						|
  //
 | 
						|
  BitCount = 0;
 | 
						|
 | 
						|
  while (Interval != 0) {
 | 
						|
    Interval >>= 1;
 | 
						|
    BitCount++;
 | 
						|
  }
 | 
						|
 | 
						|
  return (UINTN)1 << (BitCount - 1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Link a queue head (for asynchronous interrupt transfer) to
 | 
						|
  the frame list.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
  @param  Qh                     The queue head to link into.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UhciLinkQhToFrameList (
 | 
						|
  USB_HC_DEV              *Uhc,
 | 
						|
  UHCI_QH_SW              *Qh
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                   Index;
 | 
						|
  UHCI_QH_SW              *Prev;
 | 
						|
  UHCI_QH_SW              *Next;
 | 
						|
  EFI_PHYSICAL_ADDRESS    PhyAddr;
 | 
						|
  EFI_PHYSICAL_ADDRESS    QhPciAddr;
 | 
						|
 | 
						|
  ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
 | 
						|
 | 
						|
  QhPciAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_HW));
 | 
						|
 | 
						|
  for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
 | 
						|
    //
 | 
						|
    // First QH can't be NULL because we always keep static queue
 | 
						|
    // heads on the frame list
 | 
						|
    //
 | 
						|
    ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
 | 
						|
    Next  = (UHCI_QH_SW*)(UINTN)Uhc->FrameBaseHostAddr[Index];
 | 
						|
    Prev  = NULL;
 | 
						|
 | 
						|
    //
 | 
						|
    // Now, insert the queue head (Qh) into this frame:
 | 
						|
    // 1. Find a queue head with the same poll interval, just insert
 | 
						|
    //    Qh after this queue head, then we are done.
 | 
						|
    //
 | 
						|
    // 2. Find the position to insert the queue head into:
 | 
						|
    //      Previous head's interval is bigger than Qh's
 | 
						|
    //      Next head's interval is less than Qh's
 | 
						|
    //    Then, insert the Qh between then
 | 
						|
    //
 | 
						|
    // This method is very much the same as that used by EHCI.
 | 
						|
    // Because each QH's interval is round down to 2^n, poll
 | 
						|
    // rate is correct.
 | 
						|
    //
 | 
						|
    while (Next->Interval > Qh->Interval) {
 | 
						|
      Prev  = Next;
 | 
						|
      Next  = Next->NextQh;
 | 
						|
      ASSERT (Next != NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // The entry may have been linked into the frame by early insertation.
 | 
						|
    // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
 | 
						|
    // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
 | 
						|
    // It isn't necessary to compare all the QH with the same interval to
 | 
						|
    // Qh. This is because if there is other QH with the same interval, Qh
 | 
						|
    // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is
 | 
						|
    // impossible (Next == Qh)
 | 
						|
    //
 | 
						|
    if (Next == Qh) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Next->Interval == Qh->Interval) {
 | 
						|
      //
 | 
						|
      // If there is a QH with the same interval, it locates at
 | 
						|
      // FrameBase[0], and we can simply insert it after this QH. We
 | 
						|
      // are all done.
 | 
						|
      //
 | 
						|
      ASSERT ((Index == 0) && (Qh->NextQh == NULL));
 | 
						|
 | 
						|
      Prev                    = Next;
 | 
						|
      Next                    = Next->NextQh;
 | 
						|
 | 
						|
      Qh->NextQh              = Next;
 | 
						|
      Prev->NextQh            = Qh;
 | 
						|
 | 
						|
      Qh->QhHw.HorizonLink    = Prev->QhHw.HorizonLink;
 | 
						|
 | 
						|
      Prev->QhHw.HorizonLink  = QH_HLINK (QhPciAddr, FALSE);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // OK, find the right position, insert it in. If Qh's next
 | 
						|
    // link has already been set, it is in position. This is
 | 
						|
    // guarranted by 2^n polling interval.
 | 
						|
    //
 | 
						|
    if (Qh->NextQh == NULL) {
 | 
						|
      Qh->NextQh            = Next;
 | 
						|
      PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Next, sizeof (UHCI_QH_HW));
 | 
						|
      Qh->QhHw.HorizonLink  = QH_HLINK (PhyAddr, FALSE);
 | 
						|
    }
 | 
						|
 | 
						|
    if (Prev == NULL) {
 | 
						|
      Uhc->FrameBase[Index]           = QH_HLINK (QhPciAddr, FALSE);
 | 
						|
      Uhc->FrameBaseHostAddr[Index]   = (UINT32)(UINTN)Qh;
 | 
						|
    } else {
 | 
						|
      Prev->NextQh            = Qh;
 | 
						|
      Prev->QhHw.HorizonLink  = QH_HLINK (QhPciAddr, FALSE);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Unlink QH from the frame list is easier: find all
 | 
						|
  the precedence node, and pointer there next to QhSw's
 | 
						|
  next.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
  @param  Qh                     The queue head to unlink.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UhciUnlinkQhFromFrameList (
 | 
						|
  USB_HC_DEV              *Uhc,
 | 
						|
  UHCI_QH_SW              *Qh
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                   Index;
 | 
						|
  UHCI_QH_SW              *Prev;
 | 
						|
  UHCI_QH_SW              *This;
 | 
						|
 | 
						|
  ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
 | 
						|
 | 
						|
  for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
 | 
						|
    //
 | 
						|
    // Frame link can't be NULL because we always keep static
 | 
						|
    // queue heads on the frame list
 | 
						|
    //
 | 
						|
    ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
 | 
						|
    This  = (UHCI_QH_SW*)(UINTN)Uhc->FrameBaseHostAddr[Index];
 | 
						|
    Prev  = NULL;
 | 
						|
 | 
						|
    //
 | 
						|
    // Walk through the frame's QH list to find the
 | 
						|
    // queue head to remove
 | 
						|
    //
 | 
						|
    while ((This != NULL) && (This != Qh)) {
 | 
						|
      Prev  = This;
 | 
						|
      This  = This->NextQh;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Qh may have already been unlinked from this frame
 | 
						|
    // by early action.
 | 
						|
    //
 | 
						|
    if (This == NULL) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Prev == NULL) {
 | 
						|
      //
 | 
						|
      // Qh is the first entry in the frame
 | 
						|
      //
 | 
						|
      Uhc->FrameBase[Index]           = Qh->QhHw.HorizonLink;
 | 
						|
      Uhc->FrameBaseHostAddr[Index]   = (UINT32)(UINTN)Qh->NextQh;
 | 
						|
    } else {
 | 
						|
      Prev->NextQh            = Qh->NextQh;
 | 
						|
      Prev->QhHw.HorizonLink  = Qh->QhHw.HorizonLink;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check TDs Results.
 | 
						|
 | 
						|
  @param  Uhc                    This UHCI device.
 | 
						|
  @param  Td                     UHCI_TD_SW to check.
 | 
						|
  @param  IsLow                  Is Low Speed Device.
 | 
						|
  @param  QhResult               Return the result of this TD list.
 | 
						|
 | 
						|
  @return Whether the TD's result is finialized.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
UhciCheckTdStatus (
 | 
						|
  IN  USB_HC_DEV          *Uhc,
 | 
						|
  IN  UHCI_TD_SW          *Td,
 | 
						|
  IN  BOOLEAN             IsLow,
 | 
						|
  OUT UHCI_QH_RESULT      *QhResult
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                   Len;
 | 
						|
  UINT8                   State;
 | 
						|
  UHCI_TD_HW              *TdHw;
 | 
						|
  BOOLEAN                 Finished;
 | 
						|
 | 
						|
  Finished             = TRUE;
 | 
						|
 | 
						|
  //
 | 
						|
  // Initialize the data toggle to that of the first
 | 
						|
  // TD. The next toggle to use is either:
 | 
						|
  // 1. first TD's toggle if no TD is executed OK
 | 
						|
  // 2. the next toggle of last executed-OK TD
 | 
						|
  //
 | 
						|
  QhResult->Result     = EFI_USB_NOERROR;
 | 
						|
  QhResult->NextToggle = (UINT8)Td->TdHw.DataToggle;
 | 
						|
  QhResult->Complete   = 0;
 | 
						|
 | 
						|
  while (Td != NULL) {
 | 
						|
    TdHw  = &Td->TdHw;
 | 
						|
    State = (UINT8)TdHw->Status;
 | 
						|
 | 
						|
    //
 | 
						|
    // UHCI will set STALLED bit when it abort the execution
 | 
						|
    // of TD list. There are several reasons:
 | 
						|
    //   1. BABBLE error happened
 | 
						|
    //   2. Received a STALL response
 | 
						|
    //   3. Error count decreased to zero.
 | 
						|
    //
 | 
						|
    // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error
 | 
						|
    // bits when corresponding conditions happen. But these
 | 
						|
    // conditions are not deadly, that is a TD can successfully
 | 
						|
    // completes even these bits are set. But it is likely that
 | 
						|
    // upper layer won't distinguish these condtions. So, only
 | 
						|
    // set these bits when TD is actually halted.
 | 
						|
    //
 | 
						|
    if ((State & USBTD_STALLED) != 0) {
 | 
						|
      if ((State & USBTD_BABBLE) != 0) {
 | 
						|
        QhResult->Result |= EFI_USB_ERR_BABBLE;
 | 
						|
 | 
						|
      } else if (TdHw->ErrorCount != 0) {
 | 
						|
        QhResult->Result |= EFI_USB_ERR_STALL;
 | 
						|
      }
 | 
						|
 | 
						|
      if ((State & USBTD_CRC) != 0) {
 | 
						|
        QhResult->Result |= EFI_USB_ERR_CRC;
 | 
						|
      }
 | 
						|
 | 
						|
      if ((State & USBTD_BUFFERR) != 0) {
 | 
						|
        QhResult->Result |= EFI_USB_ERR_BUFFER;
 | 
						|
      }
 | 
						|
 | 
						|
      if ((Td->TdHw.Status & USBTD_BITSTUFF) != 0) {
 | 
						|
        QhResult->Result |= EFI_USB_ERR_BITSTUFF;
 | 
						|
      }
 | 
						|
 | 
						|
      if (TdHw->ErrorCount == 0) {
 | 
						|
        QhResult->Result |= EFI_USB_ERR_TIMEOUT;
 | 
						|
      }
 | 
						|
 | 
						|
      Finished = TRUE;
 | 
						|
      goto ON_EXIT;
 | 
						|
 | 
						|
    } else if ((State & USBTD_ACTIVE) != 0) {
 | 
						|
      //
 | 
						|
      // The TD is still active, no need to check further.
 | 
						|
      //
 | 
						|
      QhResult->Result |= EFI_USB_ERR_NOTEXECUTE;
 | 
						|
 | 
						|
      Finished = FALSE;
 | 
						|
      goto ON_EXIT;
 | 
						|
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // Update the next data toggle, it is always the
 | 
						|
      // next to the last known-good TD's data toggle if
 | 
						|
      // any TD is executed OK
 | 
						|
      //
 | 
						|
      QhResult->NextToggle = (UINT8) (1 - (UINT8)TdHw->DataToggle);
 | 
						|
 | 
						|
      //
 | 
						|
      // This TD is finished OK or met short packet read. Update the
 | 
						|
      // transfer length if it isn't a SETUP.
 | 
						|
      //
 | 
						|
      Len = (TdHw->ActualLen + 1) & 0x7FF;
 | 
						|
 | 
						|
      if (TdHw->PidCode != SETUP_PACKET_ID) {
 | 
						|
        QhResult->Complete += Len;
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // Short packet condition for full speed input TD, also
 | 
						|
      // terminate the transfer
 | 
						|
      //
 | 
						|
      if (!IsLow && (TdHw->ShortPacket == 1) && (Len < Td->DataLen)) {
 | 
						|
        DEBUG ((EFI_D_VERBOSE, "UhciCheckTdStatus: short packet read occured\n"));
 | 
						|
 | 
						|
        Finished = TRUE;
 | 
						|
        goto ON_EXIT;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    Td = Td->NextTd;
 | 
						|
  }
 | 
						|
 | 
						|
ON_EXIT:
 | 
						|
  //
 | 
						|
  // Check whether HC is halted. Don't move this up. It must be
 | 
						|
  // called after data toggle is successfully updated.
 | 
						|
  //
 | 
						|
  if (!UhciIsHcWorking (Uhc->PciIo)) {
 | 
						|
    QhResult->Result |= EFI_USB_ERR_SYSTEM;
 | 
						|
    Finished  = TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Finished) {
 | 
						|
    Uhc->PciIo->Flush (Uhc->PciIo);
 | 
						|
  }
 | 
						|
 | 
						|
  UhciAckAllInterrupt (Uhc);
 | 
						|
  return Finished;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check the result of the transfer.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
  @param  Qh                     The queue head of the transfer.
 | 
						|
  @param  Td                     The first TDs of the transfer.
 | 
						|
  @param  TimeOut                TimeOut value in milliseconds.
 | 
						|
  @param  IsLow                  Is Low Speed Device.
 | 
						|
  @param  QhResult               The variable to return result.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The transfer finished with success.
 | 
						|
  @retval EFI_DEVICE_ERROR       Transfer failed.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
UhciExecuteTransfer (
 | 
						|
  IN  USB_HC_DEV          *Uhc,
 | 
						|
  IN  UHCI_QH_SW          *Qh,
 | 
						|
  IN  UHCI_TD_SW          *Td,
 | 
						|
  IN  UINTN               TimeOut,
 | 
						|
  IN  BOOLEAN             IsLow,
 | 
						|
  OUT UHCI_QH_RESULT      *QhResult
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                   Index;
 | 
						|
  UINTN                   Delay;
 | 
						|
  BOOLEAN                 Finished;
 | 
						|
  EFI_STATUS              Status;
 | 
						|
  BOOLEAN                 InfiniteLoop;
 | 
						|
 | 
						|
  Finished     = FALSE;
 | 
						|
  Status       = EFI_SUCCESS;
 | 
						|
  Delay        = TimeOut * UHC_1_MILLISECOND;
 | 
						|
  InfiniteLoop = FALSE;
 | 
						|
 | 
						|
  //
 | 
						|
  // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller
 | 
						|
  // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR
 | 
						|
  // is returned.
 | 
						|
  //
 | 
						|
  if (TimeOut == 0) {
 | 
						|
    InfiniteLoop = TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  for (Index = 0; InfiniteLoop || (Index < Delay); Index++) {
 | 
						|
    Finished = UhciCheckTdStatus (Uhc, Td, IsLow, QhResult);
 | 
						|
 | 
						|
    //
 | 
						|
    // Transfer is OK or some error occured (TD inactive)
 | 
						|
    //
 | 
						|
    if (Finished) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    gBS->Stall (UHC_1_MICROSECOND);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!Finished) {
 | 
						|
    DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution not finished for %dms\n", (UINT32)TimeOut));
 | 
						|
    UhciDumpQh (Qh);
 | 
						|
    UhciDumpTds (Td);
 | 
						|
 | 
						|
    Status = EFI_TIMEOUT;
 | 
						|
 | 
						|
  } else if (QhResult->Result != EFI_USB_NOERROR) {
 | 
						|
    DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result));
 | 
						|
    UhciDumpQh (Qh);
 | 
						|
    UhciDumpTds (Td);
 | 
						|
 | 
						|
    Status = EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Update Async Request, QH and TDs.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
  @param  AsyncReq               The UHCI asynchronous transfer to update.
 | 
						|
  @param  Result                 Transfer reslut.
 | 
						|
  @param  NextToggle             The toggle of next data.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UhciUpdateAsyncReq (
 | 
						|
  IN USB_HC_DEV          *Uhc,
 | 
						|
  IN UHCI_ASYNC_REQUEST  *AsyncReq,
 | 
						|
  IN UINT32              Result,
 | 
						|
  IN UINT32              NextToggle
 | 
						|
  )
 | 
						|
{
 | 
						|
  UHCI_QH_SW              *Qh;
 | 
						|
  UHCI_TD_SW              *FirstTd;
 | 
						|
  UHCI_TD_SW              *Td;
 | 
						|
 | 
						|
  Qh          = AsyncReq->QhSw;
 | 
						|
  FirstTd     = AsyncReq->FirstTd;
 | 
						|
 | 
						|
  if (Result == EFI_USB_NOERROR) {
 | 
						|
    //
 | 
						|
    // The last transfer succeeds. Then we need to update
 | 
						|
    // the Qh and Td for next round of transfer.
 | 
						|
    // 1. Update the TD's data toggle
 | 
						|
    // 2. Activate all the TDs
 | 
						|
    // 3. Link the TD to the queue head again since during
 | 
						|
    //    execution, queue head's TD pointer is changed by
 | 
						|
    //    hardware.
 | 
						|
    //
 | 
						|
    for (Td = FirstTd; Td != NULL; Td = Td->NextTd) {
 | 
						|
      Td->TdHw.DataToggle = NextToggle;
 | 
						|
      NextToggle         ^= 1;
 | 
						|
      Td->TdHw.Status    |= USBTD_ACTIVE;
 | 
						|
    }
 | 
						|
 | 
						|
    UhciLinkTdToQh (Uhc, Qh, FirstTd);
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Create Async Request node, and Link to List.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
  @param  Qh                     The queue head of the transfer.
 | 
						|
  @param  FirstTd                First TD of the transfer.
 | 
						|
  @param  DevAddr                Device Address.
 | 
						|
  @param  EndPoint               EndPoint Address.
 | 
						|
  @param  DataLen                Data length.
 | 
						|
  @param  Interval               Polling Interval when inserted to frame list.
 | 
						|
  @param  Data                   Data buffer, unmapped.
 | 
						|
  @param  Callback               Callback after interrupt transfeer.
 | 
						|
  @param  Context                Callback Context passed as function parameter.
 | 
						|
  @param  IsLow                  Is Low Speed.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            An asynchronous transfer is created.
 | 
						|
  @retval EFI_INVALID_PARAMETER  Paremeter is error.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES   Failed because of resource shortage.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
UhciCreateAsyncReq (
 | 
						|
  IN USB_HC_DEV                       *Uhc,
 | 
						|
  IN UHCI_QH_SW                       *Qh,
 | 
						|
  IN UHCI_TD_SW                       *FirstTd,
 | 
						|
  IN UINT8                            DevAddr,
 | 
						|
  IN UINT8                            EndPoint,
 | 
						|
  IN UINTN                            DataLen,
 | 
						|
  IN UINTN                            Interval,
 | 
						|
  IN UINT8                            *Data,
 | 
						|
  IN EFI_ASYNC_USB_TRANSFER_CALLBACK  Callback,
 | 
						|
  IN VOID                             *Context,
 | 
						|
  IN BOOLEAN                          IsLow
 | 
						|
  )
 | 
						|
{
 | 
						|
  UHCI_ASYNC_REQUEST      *AsyncReq;
 | 
						|
 | 
						|
  AsyncReq = AllocatePool (sizeof (UHCI_ASYNC_REQUEST));
 | 
						|
 | 
						|
  if (AsyncReq == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill Request field. Data is allocated host memory, not mapped
 | 
						|
  //
 | 
						|
  AsyncReq->Signature   = UHCI_ASYNC_INT_SIGNATURE;
 | 
						|
  AsyncReq->DevAddr     = DevAddr;
 | 
						|
  AsyncReq->EndPoint    = EndPoint;
 | 
						|
  AsyncReq->DataLen     = DataLen;
 | 
						|
  AsyncReq->Interval    = UhciConvertPollRate(Interval);
 | 
						|
  AsyncReq->Data        = Data;
 | 
						|
  AsyncReq->Callback    = Callback;
 | 
						|
  AsyncReq->Context     = Context;
 | 
						|
  AsyncReq->QhSw        = Qh;
 | 
						|
  AsyncReq->FirstTd     = FirstTd;
 | 
						|
  AsyncReq->IsLow       = IsLow;
 | 
						|
 | 
						|
  //
 | 
						|
  // Insert the new interrupt transfer to the head of the list.
 | 
						|
  // The interrupt transfer's monitor function scans the whole
 | 
						|
  // list from head to tail. The new interrupt transfer MUST be
 | 
						|
  // added to the head of the list.
 | 
						|
  //
 | 
						|
  InsertHeadList (&(Uhc->AsyncIntList), &(AsyncReq->Link));
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Free an asynchronous request's resource such as memory.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
  @param  AsyncReq               The asynchronous request to free.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UhciFreeAsyncReq (
 | 
						|
  IN USB_HC_DEV           *Uhc,
 | 
						|
  IN UHCI_ASYNC_REQUEST   *AsyncReq
 | 
						|
  )
 | 
						|
{
 | 
						|
  ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
 | 
						|
 | 
						|
  UhciDestoryTds (Uhc, AsyncReq->FirstTd);
 | 
						|
  UsbHcFreeMem (Uhc->MemPool, AsyncReq->QhSw, sizeof (UHCI_QH_SW));
 | 
						|
 | 
						|
  if (AsyncReq->Data != NULL) {
 | 
						|
    UsbHcFreeMem (Uhc->MemPool, AsyncReq->Data, AsyncReq->DataLen);
 | 
						|
  }
 | 
						|
 | 
						|
  gBS->FreePool (AsyncReq);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Unlink an asynchronous request's from UHC's asynchronus list.
 | 
						|
  also remove the queue head from the frame list. If FreeNow,
 | 
						|
  release its resource also. Otherwise, add the request to the
 | 
						|
  UHC's recycle list to wait for a while before release the memory.
 | 
						|
  Until then, hardware won't hold point to the request.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
  @param  AsyncReq               The asynchronous request to free.
 | 
						|
  @param  FreeNow                If TRUE, free the resource immediately, otherwise
 | 
						|
                                 add the request to recycle wait list.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UhciUnlinkAsyncReq (
 | 
						|
  IN USB_HC_DEV           *Uhc,
 | 
						|
  IN UHCI_ASYNC_REQUEST   *AsyncReq,
 | 
						|
  IN BOOLEAN              FreeNow
 | 
						|
  )
 | 
						|
{
 | 
						|
  ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
 | 
						|
 | 
						|
  RemoveEntryList (&(AsyncReq->Link));
 | 
						|
  UhciUnlinkQhFromFrameList (Uhc, AsyncReq->QhSw);
 | 
						|
 | 
						|
  if (FreeNow) {
 | 
						|
    UhciFreeAsyncReq (Uhc, AsyncReq);
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // To sychronize with hardware, mark the queue head as inactive
 | 
						|
    // then add AsyncReq to UHC's recycle list
 | 
						|
    //
 | 
						|
    AsyncReq->QhSw->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
 | 
						|
    AsyncReq->Recycle = Uhc->RecycleWait;
 | 
						|
    Uhc->RecycleWait  = AsyncReq;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Delete Async Interrupt QH and TDs.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
  @param  DevAddr                Device Address.
 | 
						|
  @param  EndPoint               EndPoint Address.
 | 
						|
  @param  Toggle                 The next data toggle to use.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The request is deleted.
 | 
						|
  @retval EFI_INVALID_PARAMETER  Paremeter is error.
 | 
						|
  @retval EFI_NOT_FOUND          The asynchronous isn't found.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
UhciRemoveAsyncReq (
 | 
						|
  IN  USB_HC_DEV          *Uhc,
 | 
						|
  IN  UINT8               DevAddr,
 | 
						|
  IN  UINT8               EndPoint,
 | 
						|
  OUT UINT8               *Toggle
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS          Status;
 | 
						|
  UHCI_ASYNC_REQUEST  *AsyncReq;
 | 
						|
  UHCI_QH_RESULT      QhResult;
 | 
						|
  LIST_ENTRY          *Link;
 | 
						|
  BOOLEAN             Found;
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
 | 
						|
  //
 | 
						|
  // If no asynchronous interrupt transaction exists
 | 
						|
  //
 | 
						|
  if (IsListEmpty (&(Uhc->AsyncIntList))) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Find the asynchronous transfer to this device/endpoint pair
 | 
						|
  //
 | 
						|
  Found = FALSE;
 | 
						|
  Link  = Uhc->AsyncIntList.ForwardLink;
 | 
						|
 | 
						|
  do {
 | 
						|
    AsyncReq  = UHCI_ASYNC_INT_FROM_LINK (Link);
 | 
						|
    Link      = Link->ForwardLink;
 | 
						|
 | 
						|
    if ((AsyncReq->DevAddr == DevAddr) && (AsyncReq->EndPoint == EndPoint)) {
 | 
						|
      Found = TRUE;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  } while (Link != &(Uhc->AsyncIntList));
 | 
						|
 | 
						|
  if (!Found) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check the result of the async transfer then update it
 | 
						|
  // to get the next data toggle to use.
 | 
						|
  //
 | 
						|
  UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
 | 
						|
  *Toggle = QhResult.NextToggle;
 | 
						|
 | 
						|
  //
 | 
						|
  // Don't release the request now, keep it to synchronize with hardware.
 | 
						|
  //
 | 
						|
  UhciUnlinkAsyncReq (Uhc, AsyncReq, FALSE);
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Recycle the asynchronouse request. When a queue head
 | 
						|
  is unlinked from frame list, host controller hardware
 | 
						|
  may still hold a cached pointer to it. To synchronize
 | 
						|
  with hardware, the request is released in two steps:
 | 
						|
  first it is linked to the UHC's RecycleWait list. At
 | 
						|
  the next time UhciMonitorAsyncReqList is fired, it is
 | 
						|
  moved to UHC's Recylelist. Then, at another timer
 | 
						|
  activation, all the requests on Recycle list is freed.
 | 
						|
  This guarrantes that each unlink queue head keeps
 | 
						|
  existing for at least 50ms, far enough for the hardware
 | 
						|
  to clear its cache.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UhciRecycleAsyncReq (
 | 
						|
  IN USB_HC_DEV           *Uhc
 | 
						|
  )
 | 
						|
{
 | 
						|
  UHCI_ASYNC_REQUEST      *Req;
 | 
						|
  UHCI_ASYNC_REQUEST      *Next;
 | 
						|
 | 
						|
  Req = Uhc->Recycle;
 | 
						|
 | 
						|
  while (Req != NULL) {
 | 
						|
    Next = Req->Recycle;
 | 
						|
    UhciFreeAsyncReq (Uhc, Req);
 | 
						|
    Req  = Next;
 | 
						|
  }
 | 
						|
 | 
						|
  Uhc->Recycle     = Uhc->RecycleWait;
 | 
						|
  Uhc->RecycleWait = NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Release all the asynchronous transfers on the lsit.
 | 
						|
 | 
						|
  @param  Uhc                    The UHCI device.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UhciFreeAllAsyncReq (
 | 
						|
  IN USB_HC_DEV           *Uhc
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY              *Head;
 | 
						|
  UHCI_ASYNC_REQUEST      *AsyncReq;
 | 
						|
 | 
						|
  //
 | 
						|
  // Call UhciRecycleAsyncReq twice. The requests on Recycle
 | 
						|
  // will be released at the first call; The requests on
 | 
						|
  // RecycleWait will be released at the second call.
 | 
						|
  //
 | 
						|
  UhciRecycleAsyncReq (Uhc);
 | 
						|
  UhciRecycleAsyncReq (Uhc);
 | 
						|
 | 
						|
  Head = &(Uhc->AsyncIntList);
 | 
						|
 | 
						|
  if (IsListEmpty (Head)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  while (!IsListEmpty (Head)) {
 | 
						|
    AsyncReq  = UHCI_ASYNC_INT_FROM_LINK (Head->ForwardLink);
 | 
						|
    UhciUnlinkAsyncReq (Uhc, AsyncReq, TRUE);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Interrupt transfer periodic check handler.
 | 
						|
 | 
						|
  @param  Event                  The event of the time.
 | 
						|
  @param  Context                Context of the event, pointer to USB_HC_DEV.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
UhciMonitorAsyncReqList (
 | 
						|
  IN EFI_EVENT            Event,
 | 
						|
  IN VOID                 *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  UHCI_ASYNC_REQUEST      *AsyncReq;
 | 
						|
  LIST_ENTRY              *Link;
 | 
						|
  USB_HC_DEV              *Uhc;
 | 
						|
  VOID                    *Data;
 | 
						|
  BOOLEAN                 Finished;
 | 
						|
  UHCI_QH_RESULT          QhResult;
 | 
						|
 | 
						|
  Uhc = (USB_HC_DEV *) Context;
 | 
						|
 | 
						|
  //
 | 
						|
  // Recycle the asynchronous requests expired, and promote
 | 
						|
  // requests waiting to be recycled the next time when this
 | 
						|
  // timer expires
 | 
						|
  //
 | 
						|
  UhciRecycleAsyncReq (Uhc);
 | 
						|
 | 
						|
  if (IsListEmpty (&(Uhc->AsyncIntList))) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // This loop must be delete safe
 | 
						|
  //
 | 
						|
  Link = Uhc->AsyncIntList.ForwardLink;
 | 
						|
 | 
						|
  do {
 | 
						|
    AsyncReq  = UHCI_ASYNC_INT_FROM_LINK (Link);
 | 
						|
    Link      = Link->ForwardLink;
 | 
						|
 | 
						|
    Finished = UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
 | 
						|
 | 
						|
    if (!Finished) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Copy the data to temporary buffer if there are some
 | 
						|
    // data transferred. We may have zero-length packet
 | 
						|
    //
 | 
						|
    Data = NULL;
 | 
						|
 | 
						|
    if (QhResult.Complete != 0) {
 | 
						|
      Data = AllocatePool (QhResult.Complete);
 | 
						|
 | 
						|
      if (Data == NULL) {
 | 
						|
        return ;
 | 
						|
      }
 | 
						|
 | 
						|
      CopyMem (Data, AsyncReq->FirstTd->Data, QhResult.Complete);
 | 
						|
    }
 | 
						|
 | 
						|
    UhciUpdateAsyncReq (Uhc, AsyncReq, QhResult.Result, QhResult.NextToggle);
 | 
						|
 | 
						|
    //
 | 
						|
    // Now, either transfer is SUCCESS or met errors since
 | 
						|
    // we have skipped to next transfer earlier if current
 | 
						|
    // transfer is still active.
 | 
						|
    //
 | 
						|
    if (QhResult.Result == EFI_USB_NOERROR) {
 | 
						|
      AsyncReq->Callback (Data, QhResult.Complete, AsyncReq->Context, QhResult.Result);
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // Leave error recovery to its related device driver.
 | 
						|
      // A common case of the error recovery is to re-submit
 | 
						|
      // the interrupt transfer. When an interrupt transfer
 | 
						|
      // is re-submitted, its position in the linked list is
 | 
						|
      // changed. It is inserted to the head of the linked
 | 
						|
      // list, while this function scans the whole list from
 | 
						|
      // head to tail. Thus, the re-submitted interrupt transfer's
 | 
						|
      // callback function will not be called again in this round.
 | 
						|
      //
 | 
						|
      AsyncReq->Callback (NULL, 0, AsyncReq->Context, QhResult.Result);
 | 
						|
    }
 | 
						|
 | 
						|
    if (Data != NULL) {
 | 
						|
      gBS->FreePool (Data);
 | 
						|
    }
 | 
						|
  } while (Link != &(Uhc->AsyncIntList));
 | 
						|
}
 |