REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the MdeModulePkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			460 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			460 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
 | 
						|
which is used to enable recovery function from USB Drivers.
 | 
						|
 | 
						|
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
 | 
						|
Copyright (c) Microsoft Corporation.<BR>
 | 
						|
 | 
						|
SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "EhcPeim.h"
 | 
						|
 | 
						|
/**
 | 
						|
  Create helper QTD/QH for the EHCI device.
 | 
						|
 | 
						|
  @param  Ehc         The EHCI device.
 | 
						|
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource for helper QTD/QH.
 | 
						|
  @retval EFI_SUCCESS           Helper QH/QTD are created.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EhcCreateHelpQ (
 | 
						|
  IN PEI_USB2_HC_DEV  *Ehc
 | 
						|
  )
 | 
						|
{
 | 
						|
  USB_ENDPOINT  Ep;
 | 
						|
  PEI_EHC_QH    *Qh;
 | 
						|
  QH_HW         *QhHw;
 | 
						|
  PEI_EHC_QTD   *Qtd;
 | 
						|
 | 
						|
  //
 | 
						|
  // Create an inactive Qtd to terminate the short packet read.
 | 
						|
  //
 | 
						|
  Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64);
 | 
						|
 | 
						|
  if (Qtd == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Qtd->QtdHw.Status  = QTD_STAT_HALTED;
 | 
						|
  Ehc->ShortReadStop = Qtd;
 | 
						|
 | 
						|
  //
 | 
						|
  // Create a QH to act as the EHC reclamation header.
 | 
						|
  // Set the header to loopback to itself.
 | 
						|
  //
 | 
						|
  Ep.DevAddr   = 0;
 | 
						|
  Ep.EpAddr    = 1;
 | 
						|
  Ep.Direction = EfiUsbDataIn;
 | 
						|
  Ep.DevSpeed  = EFI_USB_SPEED_HIGH;
 | 
						|
  Ep.MaxPacket = 64;
 | 
						|
  Ep.HubAddr   = 0;
 | 
						|
  Ep.HubPort   = 0;
 | 
						|
  Ep.Toggle    = 0;
 | 
						|
  Ep.Type      = EHC_BULK_TRANSFER;
 | 
						|
  Ep.PollRate  = 1;
 | 
						|
 | 
						|
  Qh = EhcCreateQh (Ehc, &Ep);
 | 
						|
 | 
						|
  if (Qh == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  QhHw              = &Qh->QhHw;
 | 
						|
  QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE);
 | 
						|
  QhHw->Status      = QTD_STAT_HALTED;
 | 
						|
  QhHw->ReclaimHead = 1;
 | 
						|
  Ehc->ReclaimHead  = Qh;
 | 
						|
 | 
						|
  //
 | 
						|
  // Create a dummy QH to act as the terminator for periodical schedule
 | 
						|
  //
 | 
						|
  Ep.EpAddr = 2;
 | 
						|
  Ep.Type   = EHC_INT_TRANSFER_SYNC;
 | 
						|
 | 
						|
  Qh = EhcCreateQh (Ehc, &Ep);
 | 
						|
 | 
						|
  if (Qh == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Qh->QhHw.Status = QTD_STAT_HALTED;
 | 
						|
  Ehc->PeriodOne  = Qh;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Initialize the schedule data structure such as frame list.
 | 
						|
 | 
						|
  @param  Ehc                   The EHCI device to init schedule data for.
 | 
						|
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to init schedule data.
 | 
						|
  @retval EFI_SUCCESS           The schedule data is initialized.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EhcInitSched (
 | 
						|
  IN PEI_USB2_HC_DEV  *Ehc
 | 
						|
  )
 | 
						|
{
 | 
						|
  VOID                  *Buf;
 | 
						|
  EFI_PHYSICAL_ADDRESS  PhyAddr;
 | 
						|
  VOID                  *Map;
 | 
						|
  UINTN                 Index;
 | 
						|
  UINT32                *Desc;
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  EFI_PHYSICAL_ADDRESS  PciAddr;
 | 
						|
 | 
						|
  //
 | 
						|
  // First initialize the periodical schedule data:
 | 
						|
  // 1. Allocate and map the memory for the frame list
 | 
						|
  // 2. Create the help QTD/QH
 | 
						|
  // 3. Initialize the frame entries
 | 
						|
  // 4. Set the frame list register
 | 
						|
  //
 | 
						|
  //
 | 
						|
  // The Frame List ocupies 4K bytes,
 | 
						|
  // and must be aligned on 4-Kbyte boundaries.
 | 
						|
  //
 | 
						|
  Status = IoMmuAllocateBuffer (
 | 
						|
             Ehc->IoMmu,
 | 
						|
             1,
 | 
						|
             &Buf,
 | 
						|
             &PhyAddr,
 | 
						|
             &Map
 | 
						|
             );
 | 
						|
 | 
						|
  if (EFI_ERROR (Status) || (Buf == NULL)) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Ehc->PeriodFrame    = Buf;
 | 
						|
  Ehc->PeriodFrameMap = Map;
 | 
						|
  Ehc->High32bitAddr  = EHC_HIGH_32BIT (PhyAddr);
 | 
						|
 | 
						|
  //
 | 
						|
  // Init memory pool management then create the helper
 | 
						|
  // QTD/QH. If failed, previously allocated resources
 | 
						|
  // will be freed by EhcFreeSched
 | 
						|
  //
 | 
						|
  Ehc->MemPool = UsbHcInitMemPool (
 | 
						|
                   Ehc,
 | 
						|
                   EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),
 | 
						|
                   Ehc->High32bitAddr
 | 
						|
                   );
 | 
						|
 | 
						|
  if (Ehc->MemPool == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = EhcCreateHelpQ (Ehc);
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Initialize the frame list entries then set the registers
 | 
						|
  //
 | 
						|
  Desc    = (UINT32 *)Ehc->PeriodFrame;
 | 
						|
  PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
 | 
						|
  for (Index = 0; Index < EHC_FRAME_LEN; Index++) {
 | 
						|
    Desc[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
 | 
						|
  }
 | 
						|
 | 
						|
  EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (PhyAddr));
 | 
						|
 | 
						|
  //
 | 
						|
  // Second initialize the asynchronous schedule:
 | 
						|
  // Only need to set the AsynListAddr register to
 | 
						|
  // the reclamation header
 | 
						|
  //
 | 
						|
  PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
 | 
						|
  EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Free the schedule data. It may be partially initialized.
 | 
						|
 | 
						|
  @param  Ehc   The EHCI device.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EhcFreeSched (
 | 
						|
  IN PEI_USB2_HC_DEV  *Ehc
 | 
						|
  )
 | 
						|
{
 | 
						|
  EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);
 | 
						|
  EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);
 | 
						|
 | 
						|
  if (Ehc->PeriodOne != NULL) {
 | 
						|
    UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
 | 
						|
    Ehc->PeriodOne = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Ehc->ReclaimHead != NULL) {
 | 
						|
    UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
 | 
						|
    Ehc->ReclaimHead = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Ehc->ShortReadStop != NULL) {
 | 
						|
    UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));
 | 
						|
    Ehc->ShortReadStop = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Ehc->MemPool != NULL) {
 | 
						|
    UsbHcFreeMemPool (Ehc, Ehc->MemPool);
 | 
						|
    Ehc->MemPool = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Ehc->PeriodFrame != NULL) {
 | 
						|
    IoMmuFreeBuffer (Ehc->IoMmu, 1, Ehc->PeriodFrame, Ehc->PeriodFrameMap);
 | 
						|
    Ehc->PeriodFrame = NULL;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Link the queue head to the asynchronous schedule list.
 | 
						|
  UEFI only supports one CTRL/BULK transfer at a time
 | 
						|
  due to its interfaces. This simplifies the AsynList
 | 
						|
  management: A reclamation header is always linked to
 | 
						|
  the AsyncListAddr, the only active QH is appended to it.
 | 
						|
 | 
						|
  @param  Ehc   The EHCI device.
 | 
						|
  @param  Qh    The queue head to link.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EhcLinkQhToAsync (
 | 
						|
  IN PEI_USB2_HC_DEV  *Ehc,
 | 
						|
  IN PEI_EHC_QH       *Qh
 | 
						|
  )
 | 
						|
{
 | 
						|
  PEI_EHC_QH  *Head;
 | 
						|
 | 
						|
  //
 | 
						|
  // Append the queue head after the reclaim header, then
 | 
						|
  // fix the hardware visiable parts (EHCI R1.0 page 72).
 | 
						|
  // ReclaimHead is always linked to the EHCI's AsynListAddr.
 | 
						|
  //
 | 
						|
  Head = Ehc->ReclaimHead;
 | 
						|
 | 
						|
  Qh->NextQh   = Head->NextQh;
 | 
						|
  Head->NextQh = Qh;
 | 
						|
 | 
						|
  Qh->QhHw.HorizonLink   = QH_LINK (Head, EHC_TYPE_QH, FALSE);
 | 
						|
  Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Unlink a queue head from the asynchronous schedule list.
 | 
						|
  Need to synchronize with hardware.
 | 
						|
 | 
						|
  @param  Ehc   The EHCI device.
 | 
						|
  @param  Qh    The queue head to unlink.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EhcUnlinkQhFromAsync (
 | 
						|
  IN PEI_USB2_HC_DEV  *Ehc,
 | 
						|
  IN PEI_EHC_QH       *Qh
 | 
						|
  )
 | 
						|
{
 | 
						|
  PEI_EHC_QH  *Head;
 | 
						|
 | 
						|
  ASSERT (Ehc->ReclaimHead->NextQh == Qh);
 | 
						|
 | 
						|
  //
 | 
						|
  // Remove the QH from reclamation head, then update the hardware
 | 
						|
  // visiable part: Only need to loopback the ReclaimHead. The Qh
 | 
						|
  // is pointing to ReclaimHead (which is staill in the list).
 | 
						|
  //
 | 
						|
  Head = Ehc->ReclaimHead;
 | 
						|
 | 
						|
  Head->NextQh = Qh->NextQh;
 | 
						|
  Qh->NextQh   = NULL;
 | 
						|
 | 
						|
  Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);
 | 
						|
 | 
						|
  //
 | 
						|
  // Set and wait the door bell to synchronize with the hardware
 | 
						|
  //
 | 
						|
  EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);
 | 
						|
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check the URB's execution result and update the URB's
 | 
						|
  result accordingly.
 | 
						|
 | 
						|
  @param Ehc   The EHCI device.
 | 
						|
  @param Urb   The URB to check result.
 | 
						|
 | 
						|
  @retval TRUE    URB transfer is finialized.
 | 
						|
  @retval FALSE   URB transfer is not finialized.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
EhcCheckUrbResult (
 | 
						|
  IN  PEI_USB2_HC_DEV  *Ehc,
 | 
						|
  IN  PEI_URB          *Urb
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_LIST_ENTRY  *Entry;
 | 
						|
  PEI_EHC_QTD     *Qtd;
 | 
						|
  QTD_HW          *QtdHw;
 | 
						|
  UINT8           State;
 | 
						|
  BOOLEAN         Finished;
 | 
						|
 | 
						|
  ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));
 | 
						|
 | 
						|
  Finished       = TRUE;
 | 
						|
  Urb->Completed = 0;
 | 
						|
 | 
						|
  Urb->Result = EFI_USB_NOERROR;
 | 
						|
 | 
						|
  if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
 | 
						|
    Urb->Result |= EFI_USB_ERR_SYSTEM;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  BASE_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
 | 
						|
    Qtd   = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
 | 
						|
    QtdHw = &Qtd->QtdHw;
 | 
						|
    State = (UINT8)QtdHw->Status;
 | 
						|
 | 
						|
    if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {
 | 
						|
      //
 | 
						|
      // EHCI will halt the queue head when met some error.
 | 
						|
      // If it is halted, the result of URB is finialized.
 | 
						|
      //
 | 
						|
      if ((State & QTD_STAT_ERR_MASK) == 0) {
 | 
						|
        Urb->Result |= EFI_USB_ERR_STALL;
 | 
						|
      }
 | 
						|
 | 
						|
      if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {
 | 
						|
        Urb->Result |= EFI_USB_ERR_BABBLE;
 | 
						|
      }
 | 
						|
 | 
						|
      if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {
 | 
						|
        Urb->Result |= EFI_USB_ERR_BUFFER;
 | 
						|
      }
 | 
						|
 | 
						|
      if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {
 | 
						|
        Urb->Result |= EFI_USB_ERR_TIMEOUT;
 | 
						|
      }
 | 
						|
 | 
						|
      Finished = TRUE;
 | 
						|
      goto ON_EXIT;
 | 
						|
    } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {
 | 
						|
      //
 | 
						|
      // The QTD is still active, no need to check furthur.
 | 
						|
      //
 | 
						|
      Urb->Result |= EFI_USB_ERR_NOTEXECUTE;
 | 
						|
 | 
						|
      Finished = FALSE;
 | 
						|
      goto ON_EXIT;
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // This QTD is finished OK or met short packet read. Update the
 | 
						|
      // transfer length if it isn't a setup.
 | 
						|
      //
 | 
						|
      if (QtdHw->Pid != QTD_PID_SETUP) {
 | 
						|
        Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;
 | 
						|
      }
 | 
						|
 | 
						|
      if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {
 | 
						|
        // EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
 | 
						|
 | 
						|
        //
 | 
						|
        // Short packet read condition. If it isn't a setup transfer,
 | 
						|
        // no need to check furthur: the queue head will halt at the
 | 
						|
        // ShortReadStop. If it is a setup transfer, need to check the
 | 
						|
        // Status Stage of the setup transfer to get the finial result
 | 
						|
        //
 | 
						|
        if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {
 | 
						|
          Finished = TRUE;
 | 
						|
          goto ON_EXIT;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
ON_EXIT:
 | 
						|
  //
 | 
						|
  // Return the data toggle set by EHCI hardware, bulk and interrupt
 | 
						|
  // transfer will use this to initialize the next transaction. For
 | 
						|
  // Control transfer, it always start a new data toggle sequence for
 | 
						|
  // new transfer.
 | 
						|
  //
 | 
						|
  // NOTICE: don't move DT update before the loop, otherwise there is
 | 
						|
  // a race condition that DT is wrong.
 | 
						|
  //
 | 
						|
  Urb->DataToggle = (UINT8)Urb->Qh->QhHw.DataToggle;
 | 
						|
 | 
						|
  return Finished;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Execute the transfer by polling the URB. This is a synchronous operation.
 | 
						|
 | 
						|
  @param  Ehc               The EHCI device.
 | 
						|
  @param  Urb               The URB to execute.
 | 
						|
  @param  TimeOut           The time to wait before abort, in millisecond.
 | 
						|
 | 
						|
  @retval EFI_DEVICE_ERROR  The transfer failed due to transfer error.
 | 
						|
  @retval EFI_TIMEOUT       The transfer failed due to time out.
 | 
						|
  @retval EFI_SUCCESS       The transfer finished OK.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EhcExecTransfer (
 | 
						|
  IN  PEI_USB2_HC_DEV  *Ehc,
 | 
						|
  IN  PEI_URB          *Urb,
 | 
						|
  IN  UINTN            TimeOut
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  UINTN       Index;
 | 
						|
  UINTN       Loop;
 | 
						|
  BOOLEAN     Finished;
 | 
						|
  BOOLEAN     InfiniteLoop;
 | 
						|
 | 
						|
  Status       = EFI_SUCCESS;
 | 
						|
  Loop         = TimeOut * EHC_1_MILLISECOND;
 | 
						|
  Finished     = FALSE;
 | 
						|
  InfiniteLoop = FALSE;
 | 
						|
 | 
						|
  //
 | 
						|
  // 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 < Loop); Index++) {
 | 
						|
    Finished = EhcCheckUrbResult (Ehc, Urb);
 | 
						|
 | 
						|
    if (Finished) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    MicroSecondDelay (EHC_1_MICROSECOND);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!Finished) {
 | 
						|
    Status = EFI_TIMEOUT;
 | 
						|
  } else if (Urb->Result != EFI_USB_NOERROR) {
 | 
						|
    Status = EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 |