blob: bd9703dd1320302888653e616f41401b52f9fcc7 [file] [log] [blame]
/** @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;
}