mirror of https://github.com/acidanthera/audk.git
244 lines
8.0 KiB
C
244 lines
8.0 KiB
C
|
/** @file
|
||
|
Plug an EFI_PCI_IO_PROTOCOL backend into PciCapLib, for config space access.
|
||
|
|
||
|
Copyright (C) 2018, Red Hat, Inc.
|
||
|
|
||
|
This program and the accompanying materials are licensed and made available
|
||
|
under the terms and conditions of the BSD License which accompanies this
|
||
|
distribution. The full text of the license may be found at
|
||
|
http://opensource.org/licenses/bsd-license.php
|
||
|
|
||
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
|
||
|
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||
|
**/
|
||
|
|
||
|
#include <Library/MemoryAllocationLib.h>
|
||
|
|
||
|
#include "UefiPciCapPciIoLib.h"
|
||
|
|
||
|
|
||
|
/**
|
||
|
Transfer bytes between the config space of a given PCI device and a memory
|
||
|
buffer.
|
||
|
|
||
|
ProtoDevTransferConfig() performs as few config space accesses as possible
|
||
|
(without attempting 64-bit wide accesses).
|
||
|
|
||
|
@param[in] PciIo The EFI_PCI_IO_PROTOCOL representation of the
|
||
|
PCI device.
|
||
|
|
||
|
@param[in] TransferFunction The EFI_PCI_IO_PROTOCOL_CONFIG function that
|
||
|
implements the transfer. The direction of the
|
||
|
transfer is inherent to TransferFunction.
|
||
|
TransferFunction() is required to return an
|
||
|
unspecified error if any sub-transfer within
|
||
|
Size bytes from ConfigOffset exceeds the config
|
||
|
space limit of the PCI device.
|
||
|
|
||
|
@param[in] ConfigOffset The offset in the config space of the PCI device
|
||
|
at which the transfer should commence.
|
||
|
|
||
|
@param[in,out] Buffer The memory buffer where the transfer should
|
||
|
occur.
|
||
|
|
||
|
@param[in] Size The number of bytes to transfer.
|
||
|
|
||
|
@retval EFI_SUCCESS Size bytes have been transferred between config space
|
||
|
and Buffer.
|
||
|
|
||
|
@return Error codes propagated from TransferFunction(). Fewer
|
||
|
than Size bytes may have been transferred.
|
||
|
**/
|
||
|
STATIC
|
||
|
EFI_STATUS
|
||
|
ProtoDevTransferConfig (
|
||
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
||
|
IN EFI_PCI_IO_PROTOCOL_CONFIG TransferFunction,
|
||
|
IN UINT16 ConfigOffset,
|
||
|
IN OUT UINT8 *Buffer,
|
||
|
IN UINT16 Size
|
||
|
)
|
||
|
{
|
||
|
while (Size > 0) {
|
||
|
EFI_PCI_IO_PROTOCOL_WIDTH Width;
|
||
|
UINT16 Count;
|
||
|
EFI_STATUS Status;
|
||
|
UINT16 Progress;
|
||
|
|
||
|
//
|
||
|
// Pick the largest access size that is allowed by the remaining transfer
|
||
|
// Size and by the alignment of ConfigOffset.
|
||
|
//
|
||
|
// When the largest access size is available, transfer as many bytes as
|
||
|
// possible in one iteration of the loop. Otherwise, transfer only one
|
||
|
// unit, to improve the alignment.
|
||
|
//
|
||
|
if (Size >= 4 && (ConfigOffset & 3) == 0) {
|
||
|
Width = EfiPciIoWidthUint32;
|
||
|
Count = Size >> Width;
|
||
|
} else if (Size >= 2 && (ConfigOffset & 1) == 0) {
|
||
|
Width = EfiPciIoWidthUint16;
|
||
|
Count = 1;
|
||
|
} else {
|
||
|
Width = EfiPciIoWidthUint8;
|
||
|
Count = 1;
|
||
|
}
|
||
|
Status = TransferFunction (PciIo, Width, ConfigOffset, Count, Buffer);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
Progress = Count << Width;
|
||
|
ConfigOffset += Progress;
|
||
|
Buffer += Progress;
|
||
|
Size -= Progress;
|
||
|
}
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Read the config space of a given PCI device (both normal and extended).
|
||
|
|
||
|
ProtoDevReadConfig() performs as few config space accesses as possible
|
||
|
(without attempting 64-bit wide accesses).
|
||
|
|
||
|
ProtoDevReadConfig() returns an unspecified error if accessing Size bytes
|
||
|
from SourceOffset exceeds the config space limit of the PCI device. Fewer
|
||
|
than Size bytes may have been read in this case.
|
||
|
|
||
|
@param[in] PciDevice Implementation-specific unique representation
|
||
|
of the PCI device in the PCI hierarchy.
|
||
|
|
||
|
@param[in] SourceOffset Source offset in the config space of the PCI
|
||
|
device to start reading from.
|
||
|
|
||
|
@param[out] DestinationBuffer Buffer to store the read data to.
|
||
|
|
||
|
@param[in] Size The number of bytes to transfer.
|
||
|
|
||
|
@retval RETURN_SUCCESS Size bytes have been transferred from config space to
|
||
|
DestinationBuffer.
|
||
|
|
||
|
@return Error codes propagated from
|
||
|
EFI_PCI_IO_PROTOCOL.Pci.Read(). Fewer than Size bytes
|
||
|
may have been read.
|
||
|
**/
|
||
|
STATIC
|
||
|
RETURN_STATUS
|
||
|
EFIAPI
|
||
|
ProtoDevReadConfig (
|
||
|
IN PCI_CAP_DEV *PciDevice,
|
||
|
IN UINT16 SourceOffset,
|
||
|
OUT VOID *DestinationBuffer,
|
||
|
IN UINT16 Size
|
||
|
)
|
||
|
{
|
||
|
PROTO_DEV *ProtoDev;
|
||
|
|
||
|
ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);
|
||
|
return ProtoDevTransferConfig (ProtoDev->PciIo, ProtoDev->PciIo->Pci.Read,
|
||
|
SourceOffset, DestinationBuffer, Size);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Write the config space of a given PCI device (both normal and extended).
|
||
|
|
||
|
ProtoDevWriteConfig() performs as few config space accesses as possible
|
||
|
(without attempting 64-bit wide accesses).
|
||
|
|
||
|
ProtoDevWriteConfig() returns an unspecified error if accessing Size bytes at
|
||
|
DestinationOffset exceeds the config space limit of the PCI device. Fewer
|
||
|
than Size bytes may have been written in this case.
|
||
|
|
||
|
@param[in] PciDevice Implementation-specific unique representation
|
||
|
of the PCI device in the PCI hierarchy.
|
||
|
|
||
|
@param[in] DestinationOffset Destination offset in the config space of the
|
||
|
PCI device to start writing at.
|
||
|
|
||
|
@param[in] SourceBuffer Buffer to read the data to be stored from.
|
||
|
|
||
|
@param[in] Size The number of bytes to transfer.
|
||
|
|
||
|
@retval RETURN_SUCCESS Size bytes have been transferred from SourceBuffer to
|
||
|
config space.
|
||
|
|
||
|
@return Error codes propagated from
|
||
|
EFI_PCI_IO_PROTOCOL.Pci.Write(). Fewer than Size
|
||
|
bytes may have been written.
|
||
|
**/
|
||
|
STATIC
|
||
|
RETURN_STATUS
|
||
|
EFIAPI
|
||
|
ProtoDevWriteConfig (
|
||
|
IN PCI_CAP_DEV *PciDevice,
|
||
|
IN UINT16 DestinationOffset,
|
||
|
IN VOID *SourceBuffer,
|
||
|
IN UINT16 Size
|
||
|
)
|
||
|
{
|
||
|
PROTO_DEV *ProtoDev;
|
||
|
|
||
|
ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);
|
||
|
return ProtoDevTransferConfig (ProtoDev->PciIo, ProtoDev->PciIo->Pci.Write,
|
||
|
DestinationOffset, SourceBuffer, Size);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Create a PCI_CAP_DEV object from an EFI_PCI_IO_PROTOCOL instance. The config
|
||
|
space accessors are based upon EFI_PCI_IO_PROTOCOL.Pci.Read() and
|
||
|
EFI_PCI_IO_PROTOCOL.Pci.Write().
|
||
|
|
||
|
@param[in] PciIo EFI_PCI_IO_PROTOCOL representation of the PCI device.
|
||
|
|
||
|
@param[out] PciDevice The PCI_CAP_DEV object constructed as described above.
|
||
|
PciDevice can be passed to the PciCapLib APIs.
|
||
|
|
||
|
@retval EFI_SUCCESS PciDevice has been constructed and output.
|
||
|
|
||
|
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
PciCapPciIoDeviceInit (
|
||
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
||
|
OUT PCI_CAP_DEV **PciDevice
|
||
|
)
|
||
|
{
|
||
|
PROTO_DEV *ProtoDev;
|
||
|
|
||
|
ProtoDev = AllocatePool (sizeof *ProtoDev);
|
||
|
if (ProtoDev == NULL) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
ProtoDev->Signature = PROTO_DEV_SIG;
|
||
|
ProtoDev->PciIo = PciIo;
|
||
|
ProtoDev->BaseDevice.ReadConfig = ProtoDevReadConfig;
|
||
|
ProtoDev->BaseDevice.WriteConfig = ProtoDevWriteConfig;
|
||
|
|
||
|
*PciDevice = &ProtoDev->BaseDevice;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Free the resources used by PciDevice.
|
||
|
|
||
|
@param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by
|
||
|
PciCapPciIoDeviceInit().
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
PciCapPciIoDeviceUninit (
|
||
|
IN PCI_CAP_DEV *PciDevice
|
||
|
)
|
||
|
{
|
||
|
PROTO_DEV *ProtoDev;
|
||
|
|
||
|
ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);
|
||
|
FreePool (ProtoDev);
|
||
|
}
|