/** @file The UHCI register operation routines. Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
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; }