/** 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); }
/** 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; }
EFI_STATUS OhciFreeED ( IN USB_OHCI_HC_DEV *Ohc, IN ED_DESCRIPTOR *Ed ) { if (Ed == NULL) { return EFI_SUCCESS; } UsbHcFreeMem(Ohc->MemPool, Ed, sizeof(ED_DESCRIPTOR)); return EFI_SUCCESS; }
/** Free a TD @Param Ohc UHC private data @Param Td Pointer to a TD to free @retval EFI_SUCCESS TD freed **/ EFI_STATUS OhciFreeTD ( IN USB_OHCI_HC_DEV *Ohc, IN TD_DESCRIPTOR *Td ) { if (Td == NULL) { return EFI_SUCCESS; } UsbHcFreeMem(Ohc->MemPool, Td, sizeof(TD_DESCRIPTOR)); return EFI_SUCCESS; }
/** Free a list of QTDs. @param Ehc The EHCI device. @param Qtds The list head of the QTD. **/ VOID EhcFreeQtds ( IN PEI_USB2_HC_DEV *Ehc, IN EFI_LIST_ENTRY *Qtds ) { EFI_LIST_ENTRY *Entry; EFI_LIST_ENTRY *Next; PEI_EHC_QTD *Qtd; EFI_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) { Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList); RemoveEntryList (&Qtd->QtdList); UsbHcFreeMem (Ehc->MemPool, Qtd, sizeof (PEI_EHC_QTD)); }
/** Free the Ed that were initilized during driver was starting, those memory were used as interrupt ED head @Param Ohc Device private data **/ VOID OhciFreeFixedIntMemory ( IN USB_OHCI_HC_DEV *Ohc ) { static UINT32 Leaf[] = {32,16,8,4,2,1}; UINTN Index; UINTN Level; for (Level = 0; Level < 6; Level++) { for (Index = 0; Index < Leaf[Level]; Index++) { if (Ohc->IntervalList[Level][Index] != NULL) { UsbHcFreeMem(Ohc->MemPool, Ohc->IntervalList[Level][Index], sizeof(ED_DESCRIPTOR)); } } } }
/** Delete a list of TDs. @param Uhc The UHCI device. @param FirstTd TD link list head. @return None. **/ VOID UhciDestoryTds ( IN USB_HC_DEV *Uhc, IN UHCI_TD_SW *FirstTd ) { UHCI_TD_SW *NextTd; UHCI_TD_SW *ThisTd; NextTd = FirstTd; while (NextTd != NULL) { ThisTd = NextTd; NextTd = ThisTd->NextTd; UsbHcFreeMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_SW)); } }
/** Free a interrupt context entry @param Ohc UHC private data @param Entry Pointer to an interrupt context entry @retval EFI_SUCCESS Entry freed @retval EFI_INVALID_PARAMETER Entry is NULL **/ EFI_STATUS OhciFreeInterruptContextEntry ( IN USB_OHCI_HC_DEV *Ohc, IN INTERRUPT_CONTEXT_ENTRY *Entry ) { TD_DESCRIPTOR *Td; if (Entry == NULL) { return EFI_INVALID_PARAMETER; } if (Entry->UCBufferMapping != NULL) { Ohc->PciIo->Unmap(Ohc->PciIo, Entry->UCBufferMapping); } if (Entry->UCBuffer != NULL) { FreePool(Entry->UCBuffer); } while (Entry->DataTd) { Td = Entry->DataTd; Entry->DataTd = (TD_DESCRIPTOR *)(UINTN)(Entry->DataTd->NextTDPointer); UsbHcFreeMem(Ohc->MemPool, Td, sizeof(TD_DESCRIPTOR)); } FreePool(Entry); return EFI_SUCCESS; }
/** 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; }
/** 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; }
/** 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; }