Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
DmaInputFilter.cc 2.96 KiB
#include "DmaInputFilter.h"

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <cassert>
#include <cerrno>
#include <cstdio>
#include <iostream>
#include <sstream>
#include <system_error>

#include "log.h"

DmaInputFilter::DmaInputFilter(const std::string &deviceFileName, size_t packetBufferSize,
                               size_t nbPacketBuffers, ctrl &control)
    : InputFilter(packetBufferSize, nbPacketBuffers, control) {
  dma_fd = open(deviceFileName.c_str(), O_RDWR | O_NONBLOCK);
  if (dma_fd < 0) {
    throw std::system_error(errno, std::system_category(),
                            "Cannot open DMA device: " + deviceFileName);
  }

  LOG(TRACE) << "Created DMA input filter";
}

DmaInputFilter::~DmaInputFilter() {
  close(dma_fd);
  LOG(TRACE) << "Destroyed DMA input filter";
}

/*
 * man 2 write:
 * On Linux, write() (and similar system calls) will transfer at most
 * 	0x7ffff000 (2,147,479,552) bytes, returning the number of bytes
 *	actually transferred.  (This is true on both 32-bit and 64-bit
 *	systems.)
 */

#define RW_MAX_SIZE 0x7ffff000

static inline ssize_t read_axi_packet_to_buffer(int fd, char *buffer, uint64_t size) {
  ssize_t rc;
  uint64_t to_read = size;

  if (to_read > RW_MAX_SIZE) {
    to_read = RW_MAX_SIZE;
  }

  /* read data from file into memory buffer */
  rc = read(fd, buffer, to_read);

  if (rc <= 0) {
    return rc;
  }
  return rc;
}

ssize_t DmaInputFilter::readPacketFromDMA(char **buffer, size_t bufferSize) {
  // Read from DMA

  ssize_t bytesRead = 0;
  int skip = 0;

  while (true) {
    // Read from DMA
    bytesRead = read_axi_packet_to_buffer(dma_fd, *buffer, bufferSize);

    if (bytesRead < 0) {
      skip++;
      // Check for errors and then skip
      if (errno == EIO || errno == EMSGSIZE) {
        if (errno == EIO) {
          stats.nbDmaErrors++;
          LOG(ERROR) << "#" << nbReads() << ": DMA I/O ERROR. Skipping packet #" << skip << '.';
        } else {
          stats.nbDmaOversizedPackets++;
          LOG(ERROR) << "#" << nbReads() << ": DMA read returned oversized packet. DMA returned "
                     << bytesRead << ", buffer size is " << bufferSize << ". Skipping packet #"
                     << skip << '.';
        }
        continue;
      }

      // Some fatal error occurred
      std::ostringstream os;
      os << "Iteration: " << nbReads() << "  ERROR: DMA read failed.";
      throw std::system_error(errno, std::system_category(), os.str());
    }

    // We have some data
    break;
  }

  return bytesRead;
}

/**************************************************************************
 * Entry points are here
 * Overriding virtual functions
 */

// Print some additional info
void DmaInputFilter::print(std::ostream &out) const {
  out << ", DMA errors " << stats.nbDmaErrors << ", oversized " << stats.nbDmaOversizedPackets;
}

// Read a packet from DMA
ssize_t DmaInputFilter::readInput(char **buffer, size_t bufferSize) {
  return readPacketFromDMA(buffer, bufferSize);
}