diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c new file mode 100644 index 00000000..485da1c0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c @@ -0,0 +1,701 @@ +/** @file + + The UHCI register operation routines. + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uhci.h" + + +/** + Map address of request structure buffer. + + @param Uhc The UHCI device. + @param Request The user request buffer. + @param MappedAddr Mapped address of request. + @param Map Identificaion of this mapping to return. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail to map the user request. + +**/ +EFI_STATUS +UhciMapUserRequest ( + IN USB_HC_DEV *Uhc, + IN OUT VOID *Request, + OUT UINT8 **MappedAddr, + OUT VOID **Map + ) +{ + EFI_STATUS Status; + UINTN Len; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Len = sizeof (EFI_USB_DEVICE_REQUEST); + Status = Uhc->PciIo->Map ( + Uhc->PciIo, + EfiPciIoOperationBusMasterRead, + Request, + &Len, + &PhyAddr, + Map + ); + + if (!EFI_ERROR (Status)) { + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + } + + return Status; +} + + +/** + Map address of user data buffer. + + @param Uhc The UHCI device. + @param Direction Direction of the data transfer. + @param Data The user data buffer. + @param Len Length of the user data. + @param PktId Packet identificaion. + @param MappedAddr Mapped address to return. + @param Map Identificaion of this mapping to return. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail to map the user data. + +**/ +EFI_STATUS +UhciMapUserData ( + IN USB_HC_DEV *Uhc, + IN EFI_USB_DATA_DIRECTION Direction, + IN VOID *Data, + IN OUT UINTN *Len, + OUT UINT8 *PktId, + OUT UINT8 **MappedAddr, + OUT VOID **Map + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Status = EFI_SUCCESS; + + switch (Direction) { + case EfiUsbDataIn: + // + // BusMasterWrite means cpu read + // + *PktId = INPUT_PACKET_ID; + Status = Uhc->PciIo->Map ( + Uhc->PciIo, + EfiPciIoOperationBusMasterWrite, + Data, + Len, + &PhyAddr, + Map + ); + + if (EFI_ERROR (Status)) { + goto EXIT; + } + + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + break; + + case EfiUsbDataOut: + *PktId = OUTPUT_PACKET_ID; + Status = Uhc->PciIo->Map ( + Uhc->PciIo, + EfiPciIoOperationBusMasterRead, + Data, + Len, + &PhyAddr, + Map + ); + + if (EFI_ERROR (Status)) { + goto EXIT; + } + + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + break; + + case EfiUsbNoData: + if ((Len != NULL) && (*Len != 0)) { + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + + *PktId = OUTPUT_PACKET_ID; + *MappedAddr = NULL; + *Map = NULL; + break; + + default: + Status = EFI_INVALID_PARAMETER; + } + +EXIT: + return Status; +} + + +/** + Link the TD To QH. + + @param Uhc The UHCI device. + @param Qh The queue head for the TD to link to. + @param Td The TD to link. + +**/ +VOID +UhciLinkTdToQh ( + IN USB_HC_DEV *Uhc, + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *Td + ) +{ + EFI_PHYSICAL_ADDRESS PhyAddr; + + PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Td, sizeof (UHCI_TD_HW)); + + ASSERT ((Qh != NULL) && (Td != NULL)); + + Qh->QhHw.VerticalLink = QH_VLINK (PhyAddr, FALSE); + Qh->TDs = (VOID *) Td; +} + + +/** + Unlink TD from the QH. + + @param Qh The queue head to unlink from. + @param Td The TD to unlink. + +**/ +VOID +UhciUnlinkTdFromQh ( + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *Td + ) +{ + ASSERT ((Qh != NULL) && (Td != NULL)); + + Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE); + Qh->TDs = NULL; +} + + +/** + Append a new TD To the previous TD. + + @param Uhc The UHCI device. + @param PrevTd Previous UHCI_TD_SW to be linked to. + @param ThisTd TD to link. + +**/ +VOID +UhciAppendTd ( + IN USB_HC_DEV *Uhc, + IN UHCI_TD_SW *PrevTd, + IN UHCI_TD_SW *ThisTd + ) +{ + EFI_PHYSICAL_ADDRESS PhyAddr; + + PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_HW)); + + ASSERT ((PrevTd != NULL) && (ThisTd != NULL)); + + PrevTd->TdHw.NextLink = TD_LINK (PhyAddr, TRUE, FALSE); + PrevTd->NextTd = (VOID *) ThisTd; +} + + +/** + 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)); + } +} + + +/** + Create an initialize a new queue head. + + @param Uhc The UHCI device. + @param Interval The polling interval for the queue. + + @return The newly created queue header. + +**/ +UHCI_QH_SW * +UhciCreateQh ( + IN USB_HC_DEV *Uhc, + IN UINTN Interval + ) +{ + UHCI_QH_SW *Qh; + + Qh = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_QH_SW)); + + if (Qh == NULL) { + return NULL; + } + + Qh->QhHw.HorizonLink = QH_HLINK (NULL, TRUE); + Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE); + Qh->Interval = UhciConvertPollRate(Interval); + Qh->TDs = NULL; + Qh->NextQh = NULL; + + return Qh; +} + + +/** + Create and intialize a TD. + + @param Uhc The UHCI device. + + @return The newly allocated and initialized TD. + +**/ +UHCI_TD_SW * +UhciCreateTd ( + IN USB_HC_DEV *Uhc + ) +{ + UHCI_TD_SW *Td; + + Td = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_TD_SW)); + if (Td == NULL) { + return NULL; + } + + Td->TdHw.NextLink = TD_LINK (NULL, FALSE, TRUE); + Td->NextTd = NULL; + Td->Data = NULL; + Td->DataLen = 0; + + return Td; +} + + +/** + Create and initialize a TD for Setup Stage of a control transfer. + + @param Uhc The UHCI device. + @param DevAddr Device address. + @param Request A pointer to cpu memory address of Device request. + @param RequestPhy A pointer to pci memory address of Device request. + @param IsLow Full speed or low speed. + + @return The created setup Td Pointer. + +**/ +UHCI_TD_SW * +UhciCreateSetupTd ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 *Request, + IN UINT8 *RequestPhy, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *Td; + + Td = UhciCreateTd (Uhc); + + if (Td == NULL) { + return NULL; + } + + Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE); + Td->TdHw.ShortPacket = FALSE; + Td->TdHw.IsIsoch = FALSE; + Td->TdHw.IntOnCpl = FALSE; + Td->TdHw.ErrorCount = 0x03; + Td->TdHw.Status |= USBTD_ACTIVE; + Td->TdHw.DataToggle = 0; + Td->TdHw.EndPoint = 0; + Td->TdHw.LowSpeed = IsLow ? 1 : 0; + Td->TdHw.DeviceAddr = DevAddr & 0x7F; + Td->TdHw.MaxPacketLen = (UINT32) (sizeof (EFI_USB_DEVICE_REQUEST) - 1); + Td->TdHw.PidCode = SETUP_PACKET_ID; + Td->TdHw.DataBuffer = (UINT32) (UINTN) RequestPhy; + + Td->Data = Request; + Td->DataLen = (UINT16) sizeof (EFI_USB_DEVICE_REQUEST); + + return Td; +} + + +/** + Create a TD for data. + + @param Uhc The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param DataPtr A pointer to cpu memory address of Data buffer. + @param DataPhyPtr A pointer to pci memory address of Data buffer. + @param Len Data length. + @param PktId Packet ID. + @param Toggle Data toggle value. + @param IsLow Full speed or low speed. + + @return Data Td pointer if success, otherwise NULL. + +**/ +UHCI_TD_SW * +UhciCreateDataTd ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 *DataPtr, + IN UINT8 *DataPhyPtr, + IN UINTN Len, + IN UINT8 PktId, + IN UINT8 Toggle, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *Td; + + // + // Code as length - 1, and the max valid length is 0x500 + // + ASSERT (Len <= 0x500); + + Td = UhciCreateTd (Uhc); + + if (Td == NULL) { + return NULL; + } + + Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE); + Td->TdHw.ShortPacket = FALSE; + Td->TdHw.IsIsoch = FALSE; + Td->TdHw.IntOnCpl = FALSE; + Td->TdHw.ErrorCount = 0x03; + Td->TdHw.Status = USBTD_ACTIVE; + Td->TdHw.LowSpeed = IsLow ? 1 : 0; + Td->TdHw.DataToggle = Toggle & 0x01; + Td->TdHw.EndPoint = Endpoint & 0x0F; + Td->TdHw.DeviceAddr = DevAddr & 0x7F; + Td->TdHw.MaxPacketLen = (UINT32) (Len - 1); + Td->TdHw.PidCode = (UINT8) PktId; + Td->TdHw.DataBuffer = (UINT32) (UINTN) DataPhyPtr; + + Td->Data = DataPtr; + Td->DataLen = (UINT16) Len; + + return Td; +} + + +/** + Create TD for the Status Stage of control transfer. + + @param Uhc The UHCI device. + @param DevAddr Device address. + @param PktId Packet ID. + @param IsLow Full speed or low speed. + + @return Status Td Pointer. + +**/ +UHCI_TD_SW * +UhciCreateStatusTd ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 PktId, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *Td; + + Td = UhciCreateTd (Uhc); + + if (Td == NULL) { + return NULL; + } + + Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE); + Td->TdHw.ShortPacket = FALSE; + Td->TdHw.IsIsoch = FALSE; + Td->TdHw.IntOnCpl = FALSE; + Td->TdHw.ErrorCount = 0x03; + Td->TdHw.Status |= USBTD_ACTIVE; + Td->TdHw.MaxPacketLen = 0x7FF; //0x7FF: there is no data (refer to UHCI spec) + Td->TdHw.DataToggle = 1; + Td->TdHw.EndPoint = 0; + Td->TdHw.LowSpeed = IsLow ? 1 : 0; + Td->TdHw.DeviceAddr = DevAddr & 0x7F; + Td->TdHw.PidCode = (UINT8) PktId; + Td->TdHw.DataBuffer = (UINT32) (UINTN) NULL; + + Td->Data = NULL; + Td->DataLen = 0; + + return Td; +} + + +/** + Create Tds list for Control Transfer. + + @param Uhc The UHCI device. + @param DeviceAddr The device address. + @param DataPktId Packet Identification of Data Tds. + @param Request A pointer to cpu memory address of request structure buffer to transfer. + @param RequestPhy A pointer to pci memory address of request structure buffer to transfer. + @param Data A pointer to cpu memory address of user data buffer to transfer. + @param DataPhy A pointer to pci memory address of user data buffer to transfer. + @param DataLen Length of user data to transfer. + @param MaxPacket Maximum packet size for control transfer. + @param IsLow Full speed or low speed. + + @return The Td list head for the control transfer. + +**/ +UHCI_TD_SW * +UhciCreateCtrlTds ( + IN USB_HC_DEV *Uhc, + IN UINT8 DeviceAddr, + IN UINT8 DataPktId, + IN UINT8 *Request, + IN UINT8 *RequestPhy, + IN UINT8 *Data, + IN UINT8 *DataPhy, + IN UINTN DataLen, + IN UINT8 MaxPacket, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *SetupTd; + UHCI_TD_SW *FirstDataTd; + UHCI_TD_SW *DataTd; + UHCI_TD_SW *PrevDataTd; + UHCI_TD_SW *StatusTd; + UINT8 DataToggle; + UINT8 StatusPktId; + UINTN ThisTdLen; + + + DataTd = NULL; + SetupTd = NULL; + FirstDataTd = NULL; + PrevDataTd = NULL; + StatusTd = NULL; + + // + // Create setup packets for the transfer + // + SetupTd = UhciCreateSetupTd (Uhc, DeviceAddr, Request, RequestPhy, IsLow); + + if (SetupTd == NULL) { + return NULL; + } + + // + // Create data packets for the transfer + // + DataToggle = 1; + + while (DataLen > 0) { + // + // PktSize is the data load size in each Td. + // + ThisTdLen = (DataLen > MaxPacket ? MaxPacket : DataLen); + + DataTd = UhciCreateDataTd ( + Uhc, + DeviceAddr, + 0, + Data, //cpu memory address + DataPhy, //Pci memory address + ThisTdLen, + DataPktId, + DataToggle, + IsLow + ); + + if (DataTd == NULL) { + goto FREE_TD; + } + + if (FirstDataTd == NULL) { + FirstDataTd = DataTd; + FirstDataTd->NextTd = NULL; + } else { + UhciAppendTd (Uhc, PrevDataTd, DataTd); + } + + DataToggle ^= 1; + PrevDataTd = DataTd; + Data += ThisTdLen; + DataPhy += ThisTdLen; + DataLen -= ThisTdLen; + } + + // + // Status packet is on the opposite direction to data packets + // + if (OUTPUT_PACKET_ID == DataPktId) { + StatusPktId = INPUT_PACKET_ID; + } else { + StatusPktId = OUTPUT_PACKET_ID; + } + + StatusTd = UhciCreateStatusTd (Uhc, DeviceAddr, StatusPktId, IsLow); + + if (StatusTd == NULL) { + goto FREE_TD; + } + + // + // Link setup Td -> data Tds -> status Td together + // + if (FirstDataTd != NULL) { + UhciAppendTd (Uhc, SetupTd, FirstDataTd); + UhciAppendTd (Uhc, PrevDataTd, StatusTd); + } else { + UhciAppendTd (Uhc, SetupTd, StatusTd); + } + + return SetupTd; + +FREE_TD: + if (SetupTd != NULL) { + UhciDestoryTds (Uhc, SetupTd); + } + + if (FirstDataTd != NULL) { + UhciDestoryTds (Uhc, FirstDataTd); + } + + return NULL; +} + + +/** + Create Tds list for Bulk/Interrupt Transfer. + + @param Uhc USB_HC_DEV. + @param DevAddr Address of Device. + @param EndPoint Endpoint Number. + @param PktId Packet Identification of Data Tds. + @param Data A pointer to cpu memory address of user data buffer to transfer. + @param DataPhy A pointer to pci memory address of user data buffer to transfer. + @param DataLen Length of user data to transfer. + @param DataToggle Data Toggle Pointer. + @param MaxPacket Maximum packet size for Bulk/Interrupt transfer. + @param IsLow Is Low Speed Device. + + @return The Tds list head for the bulk transfer. + +**/ +UHCI_TD_SW * +UhciCreateBulkOrIntTds ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 EndPoint, + IN UINT8 PktId, + IN UINT8 *Data, + IN UINT8 *DataPhy, + IN UINTN DataLen, + IN OUT UINT8 *DataToggle, + IN UINT8 MaxPacket, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *DataTd; + UHCI_TD_SW *FirstDataTd; + UHCI_TD_SW *PrevDataTd; + UINTN ThisTdLen; + + DataTd = NULL; + FirstDataTd = NULL; + PrevDataTd = NULL; + + // + // Create data packets for the transfer + // + while (DataLen > 0) { + // + // PktSize is the data load size that each Td. + // + ThisTdLen = DataLen; + + if (DataLen > MaxPacket) { + ThisTdLen = MaxPacket; + } + + DataTd = UhciCreateDataTd ( + Uhc, + DevAddr, + EndPoint, + Data, + DataPhy, + ThisTdLen, + PktId, + *DataToggle, + IsLow + ); + + if (DataTd == NULL) { + goto FREE_TD; + } + + if (PktId == INPUT_PACKET_ID) { + DataTd->TdHw.ShortPacket = TRUE; + } + + if (FirstDataTd == NULL) { + FirstDataTd = DataTd; + FirstDataTd->NextTd = NULL; + } else { + UhciAppendTd (Uhc, PrevDataTd, DataTd); + } + + *DataToggle ^= 1; + PrevDataTd = DataTd; + Data += ThisTdLen; + DataPhy += ThisTdLen; + DataLen -= ThisTdLen; + } + + return FirstDataTd; + +FREE_TD: + if (FirstDataTd != NULL) { + UhciDestoryTds (Uhc, FirstDataTd); + } + + return NULL; +} |