예제 #1
0
파일: OhciUrb.c 프로젝트: SunnyKi/bareBoot
/**

  Initialize interrupt list.

  @Param Ohc                    Device private data

  @retval  EFI_SUCCESS          Initialization done

**/
EFI_STATUS
OhciInitializeInterruptList (
  USB_OHCI_HC_DEV          *Ohc
  )
{
  static UINT32     Leaf[32] = {0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17,
                                9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31};
  UINT32            *HccaInterruptTable;
  UINTN             Index;
  UINTN             Level;
  UINTN             Count;
  ED_DESCRIPTOR     *NewEd;

  HccaInterruptTable = Ohc->HccaMemoryBlock->HccaInterruptTable;

  for (Index = 0; Index < 32; Index++) {
    NewEd = OhciCreateED (Ohc);
    if (NewEd == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    HccaInterruptTable[Index] = (UINT32)(UINTN)NewEd;
  }

  for (Index = 0; Index < 32; Index++) {
    Ohc->IntervalList[0][Index] = (ED_DESCRIPTOR *)(UINTN)HccaInterruptTable[Leaf[Index]];
  }

  Count = 32;
  for (Level = 1; Level <= 5; Level++) {
    Count = Count >> 1;

    for (Index = 0; Index < Count; Index++) {
      Ohc->IntervalList[Level][Index] = OhciCreateED (Ohc);
      if (HccaInterruptTable[Index] == 0) {
        return EFI_OUT_OF_RESOURCES;
      }
      Ohc->IntervalList[Level - 1][Index * 2    ]->NextED = (UINT32)(UINTN)Ohc->IntervalList[Level][Index];
      Ohc->IntervalList[Level - 1][Index * 2 + 1]->NextED = (UINT32)(UINTN)Ohc->IntervalList[Level][Index];
    }
  }

  return EFI_SUCCESS;
}
예제 #2
0
파일: OhcPeim.c 프로젝트: shijunjing/edk2
/**
  Submits control transfer to a target USB device.

  @param  PeiServices            The pointer of EFI_PEI_SERVICES.
  @param  This                   The pointer of PEI_USB_HOST_CONTROLLER_PPI.
  @param  DeviceAddress          The target device address.
  @param  DeviceSpeed            Target device speed.
  @param  MaximumPacketLength    Maximum packet size the default control transfer
                                 endpoint is capable of sending or receiving.
  @param  Request                USB device request to send.
  @param  TransferDirection      Specifies the data direction for the data stage.
  @param  Data                   Data buffer to be transmitted or received from USB device.
  @param  DataLength             The size (in bytes) of the data buffer.
  @param  TimeOut                Indicates the maximum timeout, in millisecond.
  @param  TransferResult         Return the result of this control transfer.

  @retval EFI_SUCCESS            Transfer was completed successfully.
  @retval EFI_OUT_OF_RESOURCES   The transfer failed due to lack of resources.
  @retval EFI_INVALID_PARAMETER  Some parameters are invalid.
  @retval EFI_TIMEOUT            Transfer failed due to timeout.
  @retval EFI_DEVICE_ERROR       Transfer failed due to host controller or device error.

**/
EFI_STATUS
EFIAPI
OhciControlTransfer (
  IN  EFI_PEI_SERVICES             **PeiServices,
  IN  PEI_USB_HOST_CONTROLLER_PPI  *This,
  IN  UINT8                        DeviceAddress,
  IN  UINT8                        DeviceSpeed,
  IN  UINT8                        MaxPacketLength,
  IN  EFI_USB_DEVICE_REQUEST       *Request,
  IN  EFI_USB_DATA_DIRECTION       TransferDirection,
  IN  OUT VOID                     *Data,
  IN  OUT UINTN                    *DataLength,
  IN  UINTN                        TimeOut,
  OUT UINT32                       *TransferResult
  )
{
  USB_OHCI_HC_DEV               *Ohc;
  ED_DESCRIPTOR                 *Ed;
  TD_DESCRIPTOR                 *HeadTd;
  TD_DESCRIPTOR                 *SetupTd;
  TD_DESCRIPTOR                 *DataTd;
  TD_DESCRIPTOR                 *StatusTd;
  TD_DESCRIPTOR                 *EmptyTd;
  EFI_STATUS                    Status;
  UINT32                        DataPidDir;
  UINT32                        StatusPidDir;
  UINTN                         TimeCount;
  UINT32                        ErrorCode;

  UINTN                         ActualSendLength;
  UINTN                         LeftLength;
  UINT8                         DataToggle;

  EFI_PHYSICAL_ADDRESS          ReqMapPhyAddr = 0;

  UINTN                         DataMapLength = 0;
  EFI_PHYSICAL_ADDRESS          DataMapPhyAddr = 0;

  HeadTd = NULL;
  DataTd = NULL;

  if ((TransferDirection != EfiUsbDataOut && TransferDirection != EfiUsbDataIn &&
       TransferDirection != EfiUsbNoData) ||
      Request == NULL || DataLength == NULL || TransferResult == NULL ||
      (TransferDirection == EfiUsbNoData && (*DataLength != 0 || Data != NULL)) ||
      (TransferDirection != EfiUsbNoData && (*DataLength == 0 || Data == NULL)) ||
      (DeviceSpeed != EFI_USB_SPEED_LOW && DeviceSpeed != EFI_USB_SPEED_FULL) ||
      (MaxPacketLength != 8 && MaxPacketLength != 16 &&
       MaxPacketLength != 32 && MaxPacketLength != 64)) {
    DEBUG ((EFI_D_INFO, "OhciControlTransfer: EFI_INVALID_PARAMETER\n"));
    return EFI_INVALID_PARAMETER;
  }

  if (*DataLength > MAX_BYTES_PER_TD) {
    DEBUG ((EFI_D_ERROR, "OhciControlTransfer: Request data size is too large\n"));
    return EFI_INVALID_PARAMETER;
  }

  Ohc = PEI_RECOVERY_USB_OHC_DEV_FROM_EHCI_THIS(This);

  if (TransferDirection == EfiUsbDataIn) {
    DataPidDir = TD_IN_PID;
    StatusPidDir = TD_OUT_PID;
  } else {
    DataPidDir = TD_OUT_PID;
    StatusPidDir = TD_IN_PID;
  }

  OhciSetHcControl (Ohc, CONTROL_ENABLE, 0);
  if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 0) {
    MicroSecondDelay (HC_1_MILLISECOND);
    if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 0) {
      *TransferResult = EFI_USB_ERR_SYSTEM;
      DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to disable CONTROL transfer\n"));
      return EFI_DEVICE_ERROR;
    }
  }
  OhciSetMemoryPointer (Ohc, HC_CONTROL_HEAD, NULL);
  Ed = OhciCreateED (Ohc);
  if (Ed == NULL) {
    DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate ED buffer\n"));
    return EFI_OUT_OF_RESOURCES;
  }
  OhciSetEDField (Ed, ED_SKIP, 1);
  OhciSetEDField (Ed, ED_FUNC_ADD, DeviceAddress);
  OhciSetEDField (Ed, ED_ENDPT_NUM, 0);
  OhciSetEDField (Ed, ED_DIR, ED_FROM_TD_DIR);
  OhciSetEDField (Ed, ED_SPEED, DeviceSpeed);
  OhciSetEDField (Ed, ED_FORMAT | ED_HALTED | ED_DTTOGGLE, 0);
  OhciSetEDField (Ed, ED_MAX_PACKET, MaxPacketLength);
  OhciSetEDField (Ed, ED_PDATA, 0);
  OhciSetEDField (Ed, ED_ZERO, 0);
  OhciSetEDField (Ed, ED_TDHEAD_PTR, (UINT32) NULL);
  OhciSetEDField (Ed, ED_TDTAIL_PTR, (UINT32) NULL);
  OhciSetEDField (Ed, ED_NEXT_EDPTR, (UINT32) NULL);
  OhciAttachEDToList (Ohc, CONTROL_LIST, Ed, NULL);
  //
  // Setup Stage
  //
  if(Request != NULL) {
    ReqMapPhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)Request;
  }
  SetupTd = OhciCreateTD (Ohc);
  if (SetupTd == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Setup TD buffer\n"));
    goto FREE_ED_BUFF;
  }
  HeadTd = SetupTd;
  OhciSetTDField (SetupTd, TD_PDATA, 0);
  OhciSetTDField (SetupTd, TD_BUFFER_ROUND, 1);
  OhciSetTDField (SetupTd, TD_DIR_PID, TD_SETUP_PID);
  OhciSetTDField (SetupTd, TD_DELAY_INT, TD_NO_DELAY);
  OhciSetTDField (SetupTd, TD_DT_TOGGLE, 2);
  OhciSetTDField (SetupTd, TD_ERROR_CNT, 0);
  OhciSetTDField (SetupTd, TD_COND_CODE, TD_TOBE_PROCESSED);
  OhciSetTDField (SetupTd, TD_CURR_BUFFER_PTR, (UINTN)ReqMapPhyAddr);
  OhciSetTDField (SetupTd, TD_NEXT_PTR, (UINT32) NULL);
  OhciSetTDField (SetupTd, TD_BUFFER_END_PTR, (UINTN)ReqMapPhyAddr + sizeof (EFI_USB_DEVICE_REQUEST) - 1);
  SetupTd->ActualSendLength = 0;
  SetupTd->DataBuffer = NULL;
  SetupTd->NextTDPointer = NULL;

  DataMapLength = *DataLength;
  if ((Data != NULL) && (DataMapLength != 0)) {
    DataMapPhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)Data;
  }
  //
  //Data Stage
  //
  LeftLength = DataMapLength;
  ActualSendLength = DataMapLength;
  DataToggle = 1;
  while (LeftLength > 0) {
    ActualSendLength = LeftLength;
    if (LeftLength > MaxPacketLength) {
      ActualSendLength = MaxPacketLength;
    }
    DataTd = OhciCreateTD (Ohc);
    if (DataTd == NULL) {
      DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Data TD buffer\n"));
      Status = EFI_OUT_OF_RESOURCES;
      goto FREE_TD_BUFF;
    }
    OhciSetTDField (DataTd, TD_PDATA, 0);
    OhciSetTDField (DataTd, TD_BUFFER_ROUND, 1);
    OhciSetTDField (DataTd, TD_DIR_PID, DataPidDir);
    OhciSetTDField (DataTd, TD_DELAY_INT, TD_NO_DELAY);
    OhciSetTDField (DataTd, TD_DT_TOGGLE, DataToggle);
    OhciSetTDField (DataTd, TD_ERROR_CNT, 0);
    OhciSetTDField (DataTd, TD_COND_CODE, TD_TOBE_PROCESSED);
    OhciSetTDField (DataTd, TD_CURR_BUFFER_PTR, (UINT32) DataMapPhyAddr);
    OhciSetTDField (DataTd, TD_BUFFER_END_PTR, (UINT32) DataMapPhyAddr + ActualSendLength - 1);
    OhciSetTDField (DataTd, TD_NEXT_PTR, (UINT32) NULL);
    DataTd->ActualSendLength = ActualSendLength;
    DataTd->DataBuffer = (UINT8 *)(UINTN)DataMapPhyAddr;
    DataTd->NextTDPointer = 0;
    OhciLinkTD (HeadTd, DataTd);
    DataToggle ^= 1;
    DataMapPhyAddr += ActualSendLength;
    LeftLength -= ActualSendLength;
  }
  //
  // Status Stage
  //
  StatusTd = OhciCreateTD (Ohc);
  if (StatusTd == NULL) {
    DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Status TD buffer\n"));
    Status = EFI_OUT_OF_RESOURCES;
    goto FREE_TD_BUFF;
  }
  OhciSetTDField (StatusTd, TD_PDATA, 0);
  OhciSetTDField (StatusTd, TD_BUFFER_ROUND, 1);
  OhciSetTDField (StatusTd, TD_DIR_PID, StatusPidDir);
  OhciSetTDField (StatusTd, TD_DELAY_INT, 7);
  OhciSetTDField (StatusTd, TD_DT_TOGGLE, 3);
  OhciSetTDField (StatusTd, TD_ERROR_CNT, 0);
  OhciSetTDField (StatusTd, TD_COND_CODE, TD_TOBE_PROCESSED);
  OhciSetTDField (StatusTd, TD_CURR_BUFFER_PTR, (UINT32) NULL);
  OhciSetTDField (StatusTd, TD_NEXT_PTR, (UINT32) NULL);
  OhciSetTDField (StatusTd, TD_BUFFER_END_PTR, (UINT32) NULL);
  StatusTd->ActualSendLength = 0;
  StatusTd->DataBuffer = NULL;
  StatusTd->NextTDPointer = NULL;
  OhciLinkTD (HeadTd, StatusTd);
  //
  // Empty Stage
  //
  EmptyTd = OhciCreateTD (Ohc);
  if (EmptyTd == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to allocate Empty TD buffer\n"));
    goto FREE_TD_BUFF;
  }
  OhciSetTDField (EmptyTd, TD_PDATA, 0);
  OhciSetTDField (EmptyTd, TD_BUFFER_ROUND, 0);
  OhciSetTDField (EmptyTd, TD_DIR_PID, 0);
  OhciSetTDField (EmptyTd, TD_DELAY_INT, 0);
  //OhciSetTDField (EmptyTd, TD_DT_TOGGLE, CurrentToggle);
  EmptyTd->Word0.DataToggle = 0;
  OhciSetTDField (EmptyTd, TD_ERROR_CNT, 0);
  OhciSetTDField (EmptyTd, TD_COND_CODE, 0);
  OhciSetTDField (EmptyTd, TD_CURR_BUFFER_PTR, 0);
  OhciSetTDField (EmptyTd, TD_BUFFER_END_PTR, 0);
  OhciSetTDField (EmptyTd, TD_NEXT_PTR, 0);
  EmptyTd->ActualSendLength = 0;
  EmptyTd->DataBuffer = NULL;
  EmptyTd->NextTDPointer = NULL;
  OhciLinkTD (HeadTd, EmptyTd);
  Ed->TdTailPointer = EmptyTd;
  OhciAttachTDListToED (Ed, HeadTd);
  //
  OhciSetEDField (Ed, ED_SKIP, 0);
  MicroSecondDelay (20 * HC_1_MILLISECOND);
  OhciSetHcCommandStatus (Ohc, CONTROL_LIST_FILLED, 1);
  OhciSetHcControl (Ohc, CONTROL_ENABLE, 1);
  MicroSecondDelay (20 * HC_1_MILLISECOND);
  if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 1) {
  MicroSecondDelay (HC_1_MILLISECOND);
    if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 1) {
      *TransferResult = EFI_USB_ERR_SYSTEM;
      Status = EFI_DEVICE_ERROR;
      DEBUG ((EFI_D_INFO, "OhciControlTransfer: Fail to enable CONTROL transfer\n"));
      goto FREE_TD_BUFF;
    }
  }

  TimeCount = 0;
  Status = CheckIfDone (Ohc, CONTROL_LIST, Ed, HeadTd, &ErrorCode);

  while (Status == EFI_NOT_READY && TimeCount <= TimeOut) {
    MicroSecondDelay (HC_1_MILLISECOND);
    TimeCount++;
    Status = CheckIfDone (Ohc, CONTROL_LIST, Ed, HeadTd, &ErrorCode);
  }
  //
  *TransferResult = ConvertErrorCode (ErrorCode);

  if (ErrorCode != TD_NO_ERROR) {
    if (ErrorCode == TD_TOBE_PROCESSED) {
      DEBUG ((EFI_D_INFO, "Control pipe timeout, > %d mS\r\n", TimeOut));
    } else {
      DEBUG ((EFI_D_INFO, "Control pipe broken\r\n"));
    }

    *DataLength = 0;
  }

  OhciSetHcControl (Ohc, CONTROL_ENABLE, 0);
  if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 0) {
  MicroSecondDelay (HC_1_MILLISECOND);
    if (OhciGetHcControl (Ohc, CONTROL_ENABLE) != 0) {
      *TransferResult = EFI_USB_ERR_SYSTEM;
      DEBUG ((EFI_D_INFO, "OhciControlTransfer: Cannot disable CONTROL_ENABLE transfer\n"));
      goto FREE_TD_BUFF;
    }
  }

FREE_TD_BUFF:
  while (HeadTd) {
    DataTd = HeadTd;
    HeadTd = HeadTd->NextTDPointer;
    UsbHcFreeMem(Ohc->MemPool, DataTd, sizeof(TD_DESCRIPTOR));
  }

FREE_ED_BUFF:
  UsbHcFreeMem(Ohc->MemPool, Ed, sizeof(ED_DESCRIPTOR));

  return Status;
}
예제 #3
0
파일: OhcPeim.c 프로젝트: shijunjing/edk2
/**
  Submits bulk transfer to a bulk endpoint of a USB device.

  @param  PeiServices           The pointer of EFI_PEI_SERVICES.
  @param  This                  The pointer of PEI_USB_HOST_CONTROLLER_PPI.
  @param  DeviceAddress         Target device address.
  @param  EndPointAddress       Endpoint number and its direction in bit 7.
  @param  MaxiPacketLength      Maximum packet size the endpoint is capable of
                                sending or receiving.
  @param  Data                  A pointers to the buffers of data to transmit
                                from or receive into.
  @param  DataLength            The lenght of the data buffer.
  @param  DataToggle            On input, the initial data toggle for the transfer;
                                On output, it is updated to to next data toggle to use of
                                the subsequent bulk transfer.
  @param  TimeOut               Indicates the maximum time, in millisecond, which the
                                transfer is allowed to complete.
  @param  TransferResult        A pointer to the detailed result information of the
                                bulk transfer.

  @retval EFI_SUCCESS           The transfer was completed successfully.
  @retval EFI_OUT_OF_RESOURCES  The transfer failed due to lack of resource.
  @retval EFI_INVALID_PARAMETER Parameters are invalid.
  @retval EFI_TIMEOUT           The transfer failed due to timeout.
  @retval EFI_DEVICE_ERROR      The transfer failed due to host controller error.

**/
EFI_STATUS
EFIAPI
OhciBulkTransfer (
  IN EFI_PEI_SERVICES             **PeiServices,
  IN PEI_USB_HOST_CONTROLLER_PPI  *This,
  IN  UINT8                       DeviceAddress,
  IN  UINT8                       EndPointAddress,
  IN  UINT8                       MaxPacketLength,
  IN  OUT VOID                    *Data,
  IN  OUT UINTN                   *DataLength,
  IN  OUT UINT8                   *DataToggle,
  IN  UINTN                       TimeOut,
  OUT UINT32                      *TransferResult
  )
{
  USB_OHCI_HC_DEV                *Ohc;
  ED_DESCRIPTOR                  *Ed;
  UINT32                         DataPidDir;
  TD_DESCRIPTOR                  *HeadTd;
  TD_DESCRIPTOR                  *DataTd;
  TD_DESCRIPTOR                  *EmptyTd;
  EFI_STATUS                     Status;
  UINT8                          EndPointNum;
  UINTN                          TimeCount;
  UINT32                         ErrorCode;

  UINT8                          CurrentToggle;
  UINTN                          MapLength;
  EFI_PHYSICAL_ADDRESS           MapPyhAddr;
  UINTN                          LeftLength;
  UINTN                          ActualSendLength;
  BOOLEAN                        FirstTD;

  MapLength = 0;
  MapPyhAddr = 0;
  LeftLength = 0;
  Status = EFI_SUCCESS;

  if (Data == NULL || DataLength == NULL || DataToggle == NULL || TransferResult == NULL ||
      *DataLength == 0 || (*DataToggle != 0 && *DataToggle != 1) ||
      (MaxPacketLength != 8 && MaxPacketLength != 16 &&
       MaxPacketLength != 32 && MaxPacketLength != 64)) {
    return EFI_INVALID_PARAMETER;
  }

  Ohc = PEI_RECOVERY_USB_OHC_DEV_FROM_EHCI_THIS (This);

  if ((EndPointAddress & 0x80) != 0) {
    DataPidDir = TD_IN_PID;
  } else {
    DataPidDir = TD_OUT_PID;
  }

  EndPointNum = (EndPointAddress & 0xF);

  OhciSetHcControl (Ohc, BULK_ENABLE, 0);
  if (OhciGetHcControl (Ohc, BULK_ENABLE) != 0) {
    MicroSecondDelay (HC_1_MILLISECOND);
    if (OhciGetHcControl (Ohc, BULK_ENABLE) != 0) {
      *TransferResult = EFI_USB_ERR_SYSTEM;
      return EFI_DEVICE_ERROR;
    }
  }

  OhciSetMemoryPointer (Ohc, HC_BULK_HEAD, NULL);

  Ed = OhciCreateED (Ohc);
  if (Ed == NULL) {
    DEBUG ((EFI_D_INFO, "OhcBulkTransfer: Fail to allocate ED buffer\r\n"));
    return EFI_OUT_OF_RESOURCES;
  }
  OhciSetEDField (Ed, ED_SKIP, 1);
  OhciSetEDField (Ed, ED_FUNC_ADD, DeviceAddress);
  OhciSetEDField (Ed, ED_ENDPT_NUM, EndPointNum);
  OhciSetEDField (Ed, ED_DIR, ED_FROM_TD_DIR);
  OhciSetEDField (Ed, ED_SPEED, HI_SPEED);
  OhciSetEDField (Ed, ED_FORMAT | ED_HALTED | ED_DTTOGGLE, 0);
  OhciSetEDField (Ed, ED_MAX_PACKET, MaxPacketLength);
  OhciSetEDField (Ed, ED_PDATA, 0);
  OhciSetEDField (Ed, ED_ZERO, 0);
  OhciSetEDField (Ed, ED_TDHEAD_PTR, (UINT32) NULL);
  OhciSetEDField (Ed, ED_TDTAIL_PTR, (UINT32) NULL);
  OhciSetEDField (Ed, ED_NEXT_EDPTR, (UINT32) NULL);
  OhciAttachEDToList (Ohc, BULK_LIST, Ed, NULL);

  if(Data != NULL) {
    MapLength = *DataLength;
    MapPyhAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)Data;
  }
  //
  //Data Stage
  //
  LeftLength = MapLength;
  ActualSendLength = MapLength;
  CurrentToggle = *DataToggle;
  HeadTd = NULL;
  FirstTD = TRUE;
  while (LeftLength > 0) {
    ActualSendLength = LeftLength;
    if (LeftLength > MaxPacketLength) {
      ActualSendLength = MaxPacketLength;
    }
    DataTd = OhciCreateTD (Ohc);
    if (DataTd == NULL) {
      DEBUG ((EFI_D_INFO, "OhcBulkTransfer: Fail to allocate Data TD buffer\r\n"));
      Status = EFI_OUT_OF_RESOURCES;
      goto FREE_TD_BUFF;
    }
    OhciSetTDField (DataTd, TD_PDATA, 0);
    OhciSetTDField (DataTd, TD_BUFFER_ROUND, 1);
    OhciSetTDField (DataTd, TD_DIR_PID, DataPidDir);
    OhciSetTDField (DataTd, TD_DELAY_INT, TD_NO_DELAY);
    OhciSetTDField (DataTd, TD_DT_TOGGLE, CurrentToggle);
    OhciSetTDField (DataTd, TD_ERROR_CNT, 0);
    OhciSetTDField (DataTd, TD_COND_CODE, TD_TOBE_PROCESSED);
    OhciSetTDField (DataTd, TD_CURR_BUFFER_PTR, (UINT32) MapPyhAddr);
    OhciSetTDField (DataTd, TD_BUFFER_END_PTR, (UINT32) MapPyhAddr + ActualSendLength - 1);
    OhciSetTDField (DataTd, TD_NEXT_PTR, (UINT32) NULL);
    DataTd->ActualSendLength = ActualSendLength;
    DataTd->DataBuffer = (UINT8 *)(UINTN)MapPyhAddr;
    DataTd->NextTDPointer = 0;
    if (FirstTD) {
      HeadTd = DataTd;
      FirstTD = FALSE;
    } else {
      OhciLinkTD (HeadTd, DataTd);
    }
    CurrentToggle ^= 1;
    MapPyhAddr += ActualSendLength;
    LeftLength -= ActualSendLength;
  }
  //
  // Empty Stage
  //
  EmptyTd = OhciCreateTD (Ohc);
  if (EmptyTd == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
      DEBUG ((EFI_D_INFO, "OhcBulkTransfer: Fail to allocate Empty TD buffer\r\n"));
    goto FREE_TD_BUFF;
  }
  OhciSetTDField (EmptyTd, TD_PDATA, 0);
  OhciSetTDField (EmptyTd, TD_BUFFER_ROUND, 0);
  OhciSetTDField (EmptyTd, TD_DIR_PID, 0);
  OhciSetTDField (EmptyTd, TD_DELAY_INT, 0);
  //OhciSetTDField (EmptyTd, TD_DT_TOGGLE, CurrentToggle);
  EmptyTd->Word0.DataToggle = 0;
  OhciSetTDField (EmptyTd, TD_ERROR_CNT, 0);
  OhciSetTDField (EmptyTd, TD_COND_CODE, 0);
  OhciSetTDField (EmptyTd, TD_CURR_BUFFER_PTR, 0);
  OhciSetTDField (EmptyTd, TD_BUFFER_END_PTR, 0);
  OhciSetTDField (EmptyTd, TD_NEXT_PTR, 0);
  EmptyTd->ActualSendLength = 0;
  EmptyTd->DataBuffer = NULL;
  EmptyTd->NextTDPointer = NULL;
  OhciLinkTD (HeadTd, EmptyTd);
  Ed->TdTailPointer = EmptyTd;
  OhciAttachTDListToED (Ed, HeadTd);

  OhciSetEDField (Ed, ED_SKIP, 0);
  OhciSetHcCommandStatus (Ohc, BULK_LIST_FILLED, 1);
  OhciSetHcControl (Ohc, BULK_ENABLE, 1);
  if (OhciGetHcControl (Ohc, BULK_ENABLE) != 1) {
    MicroSecondDelay (HC_1_MILLISECOND);
    if (OhciGetHcControl (Ohc, BULK_ENABLE) != 1) {
      *TransferResult = EFI_USB_ERR_SYSTEM;
      goto FREE_TD_BUFF;
    }
  }

  TimeCount = 0;
  Status = CheckIfDone (Ohc, BULK_LIST, Ed, HeadTd, &ErrorCode);

  while (Status == EFI_NOT_READY && TimeCount <= TimeOut) {
    MicroSecondDelay (HC_1_MILLISECOND);
    TimeCount++;
    Status = CheckIfDone (Ohc, BULK_LIST, Ed, HeadTd, &ErrorCode);
  }

  *TransferResult = ConvertErrorCode (ErrorCode);

  if (ErrorCode != TD_NO_ERROR) {
    if (ErrorCode == TD_TOBE_PROCESSED) {
      DEBUG ((EFI_D_INFO, "Bulk pipe timeout, > %d mS\r\n", TimeOut));
    } else {
      DEBUG ((EFI_D_INFO, "Bulk pipe broken\r\n"));
    }
    *DataLength = 0;
  }
    *DataToggle = (UINT8) OhciGetEDField (Ed, ED_DTTOGGLE);

FREE_TD_BUFF:
  while (HeadTd) {
    DataTd = HeadTd;
    HeadTd = HeadTd->NextTDPointer;
    UsbHcFreeMem(Ohc->MemPool, DataTd, sizeof(TD_DESCRIPTOR));
  }
  UsbHcFreeMem(Ohc->MemPool, Ed, sizeof(ED_DESCRIPTOR));

  return Status;
}