18.5.6 DMA Bus Master Read Operation

The general algorithm for performing a bus master read operation is as follows:

  • The processor initializes the contents of the DMA using HostAddress.

  • Call Map() with an Operation of EfiPciOperationBusMasterRead.

  • Program the DMA bus master with the DeviceAddress returned by Map().

  • Program the DMA bus master with the NumberOfBytes returned by Map().

  • Start the DMA bus master.

  • Wait for DMA bus master to complete the bus master read operation.

  • Call Unmap().

The following example shows a function for performing a bus master read operation on a PCI controller. The PCI controller is accessed through the parameter PciIo. The system memory buffer read by the bus master is specified by HostAddress and Length. This function performs one or more bus master read operations until either Length bytes have been read by the bus master or an error is detected. The PCI controller in this example has three MMIO registers in BAR #1 The MMIO register at offset 0x10 is a status register the function uses to check if the DMA operation is complete or not. The function writes the start of the DMA transaction to the MMIO register at offset 0x20 and the length of the DMA transaction to the MMIO register at offset 0x24 The write operation to offset 0x24 also starts the DMA read operation. The services of the PCI I/O Protocol used in this example include Map(), Unmap(), Mem.Write(), and PollMem(). The example below is for a 32-bit PCI bus master.

A 64-bit PCI bus master instance uses two 32-bit MMIO registers to specify the start address and two 32-bit MMIO registers to specify the length. If the PCI bus master supports 64-bit DMA addressing, the EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE attribute must be set in the Driver Binding Protocol Start() service of the PCI driver.

Example 178-Bus master read operation
#include <Uefi.h>
#include <Protocol/PciIo.h>
#include <Library/UefiLib.h>

EFI_STATUS
EFIAPI
DoBusMasterRead (
  IN EFI_PCI_IO_PROTOCOL  *PciIo,
  IN UINT8                *HostAddress,
  IN UINTN                *Length
  )
{
  EFI_STATUS              Status;
  UINTN                   NumberOfBytes;
  EFI_PHYSICAL_ADDRESS    DeviceAddress;
  VOID                    *Mapping;
  UINT32                  DmaStartAddress;
  UINT64                  ControllerStatus;

  //
  // Loop until the entire buffer specified by HostAddress and
  // Length has been read from the PCI DMA bus master
  // do {
    //
    // Call Map() to retrieve the DeviceAddress to use for the bus
    // master read operation. The Map() function may not support
    // performing a DMA operation for the entire length, so it may
    // be broken up into smaller DMA operations.
    //
    NumberOfBytes = *Length;
    Status = PciIo->Map (
                    PciIo, // This
                    EfiPciIoOperationBusMasterRead, // Operation
                    (VOID *)HostAddress,            // HostAddress
                    &NumberOfBytes,                 // NumberOfBytes
                    &DeviceAddress,                 // DeviceAddress
                    &Mapping                        // Mapping
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    //
    // Write the DMA start address to MMIO Register 0x20 of Bar #1
    //
    DmaStartAddress = (UINT32)DeviceAddress;
    Status = PciIo->Mem.Write (
                        PciIo,                      // This
                        EfiPciIoWidthUint32,        // Width
                        1,                          // BarIndex
                        0x20,                       // Offset
                        1,                          // Count
                        &DmaStartAddress            // Buffer
                        );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    //
    // Write the length of the DMA to MMIO Register 0x24 of Bar #1
    // This write operation also starts the DMA transaction
    //
    Status = PciIo->Mem.Write (
                        PciIo, // This
                        EfiPciIoWidthUint32,        // Width
                        1,                          // BarIndex
                        0x24,                       // Offset
                        1,                          // Count
                        &NumberOfBytes              // Buffer
                        );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    //
    // Call PollMem() to poll for Bit #0 in MMIO register 0x10 of
    // Bar #1
    //
    Status = PciIo->PollMem (
                    PciIo,                          // This
                    EfiPciIoWidthUint32,            // Width
                    1,                              // BarIndex
                    0x10,                           // Offset
                    BIT0,                           // Mask
                    BIT0,                           // Value
                    EFI_TIMER_PERIOD_SECONDS (1),   // Timeout
                    &ControllerStatus // Result
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    //
    // Call Unmap() to complete the bus master read operation
    //
    Status = PciIo->Unmap (PciIo, Mapping);
    if (EFI_ERROR (Status)) {
    return Status;
    }

    //
    // Update the HostAddress and Length remaining based upon the
    // number of bytes transferred
    //
    HostAddress += NumberOfBytes;
    *Length -= NumberOfBytes;
  } while (*Length != 0);
  return Status;
}