ChimeraTK-DeviceAccess  03.18.00
UioAccess.cc
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 
4 #include "UioAccess.h"
5 
6 #include "Exception.h"
7 #include <sys/mman.h>
8 
9 #include <cerrno>
10 #include <fcntl.h>
11 #include <fstream>
12 #include <limits>
13 #include <poll.h>
14 
15 namespace ChimeraTK {
16 
17  UioAccess::UioAccess(const std::string& deviceFilePath) : _deviceFilePath(deviceFilePath.c_str()) {}
18 
20  close();
21  }
22 
23  void UioAccess::open() {
24  if(boost::filesystem::is_symlink(_deviceFilePath)) {
25  _deviceFilePath = boost::filesystem::canonical(_deviceFilePath);
26  }
27  std::string fileName = _deviceFilePath.filename().string();
28  _deviceKernelBase = (void*)readUint64HexFromFile("/sys/class/uio/" + fileName + "/maps/map0/addr");
29  _deviceMemSize = readUint64HexFromFile("/sys/class/uio/" + fileName + "/maps/map0/size");
30  _lastInterruptCount = readUint32FromFile("/sys/class/uio/" + fileName + "/event");
31 
32  // Open UIO device file here, so that interrupt thread can run before calling open()
33  _deviceFileDescriptor = ::open(_deviceFilePath.c_str(), O_RDWR);
34  if(_deviceFileDescriptor < 0) {
35  throw ChimeraTK::runtime_error("UIO: Failed to open device file '" + getDeviceFilePath() + "'");
36  }
37  UioMMap();
38  _opened = true;
39  }
40 
42  if(_opened) {
43  UioUnmap();
44  ::close(_deviceFileDescriptor);
45  _opened = false;
46  }
47  }
48 
49  void UioAccess::read(uint64_t map, uint64_t address, int32_t* __restrict__ data, size_t sizeInBytes) {
50  if(map > 0) {
51  throw ChimeraTK::logic_error("UIO: Multiple memory regions are not supported");
52  }
53 
54  // This is a temporary work around, because register nodes of current map use absolute bus addresses.
55  address = address % reinterpret_cast<uint64_t>(_deviceKernelBase);
56 
57  if(address + sizeInBytes > _deviceMemSize) {
58  throw ChimeraTK::logic_error("UIO: Read request exceeds device memory region");
59  }
60 
61  volatile int32_t* rptr = static_cast<volatile int32_t*>(_deviceUserBase) + address / sizeof(int32_t);
62  while(sizeInBytes >= sizeof(int32_t)) {
63  *(data++) = *(rptr++);
64  sizeInBytes -= sizeof(int32_t);
65  }
66  }
67 
68  void UioAccess::write(uint64_t map, uint64_t address, int32_t const* data, size_t sizeInBytes) {
69  if(map > 0) {
70  throw ChimeraTK::logic_error("UIO: Multiple memory regions are not supported");
71  }
72 
73  // This is a temporary work around, because register nodes of current map use absolute bus addresses.
74  address = address % reinterpret_cast<uint64_t>(_deviceKernelBase);
75 
76  if(address + sizeInBytes > _deviceMemSize) {
77  throw ChimeraTK::logic_error("UIO: Write request exceeds device memory region");
78  }
79 
80  volatile int32_t* __restrict__ wptr = static_cast<volatile int32_t*>(_deviceUserBase) + address / sizeof(int32_t);
81  while(sizeInBytes >= sizeof(int32_t)) {
82  *(wptr++) = *(data++);
83  sizeInBytes -= sizeof(int32_t);
84  }
85  }
86 
87  uint32_t UioAccess::waitForInterrupt(int timeoutMs) {
88  // Represents the total interrupt count since system uptime.
89  uint32_t totalInterruptCount = 0;
90  // Will hold the number of new interrupts
91  uint32_t occurredInterruptCount = 0;
92 
93  struct pollfd pfd;
94  pfd.fd = _deviceFileDescriptor;
95  pfd.events = POLLIN;
96 
97  int ret = poll(&pfd, 1, timeoutMs);
98 
99  if(ret >= 1) {
100  // No timeout, start reading
101  ret = ::read(_deviceFileDescriptor, &totalInterruptCount, sizeof(totalInterruptCount));
102 
103  if(ret != (ssize_t)sizeof(totalInterruptCount)) {
104  throw ChimeraTK::runtime_error("UIO - Reading interrupt failed: " + std::string(std::strerror(errno)));
105  }
106 
107  // Prevent overflow of interrupt count value
108  occurredInterruptCount = subtractUint32OverflowSafe(totalInterruptCount, _lastInterruptCount);
109  _lastInterruptCount = totalInterruptCount;
110  }
111  else if(ret == 0) {
112  // Timeout
113  occurredInterruptCount = 0;
114  }
115  else {
116  throw ChimeraTK::runtime_error("UIO - Waiting for interrupt failed: " + std::string(std::strerror(errno)));
117  }
118  return occurredInterruptCount;
119  }
120 
122  uint32_t unmask = 1;
123  ssize_t ret = ::write(_deviceFileDescriptor, &unmask, sizeof(unmask));
124 
125  if(ret != (ssize_t)sizeof(unmask)) {
126  throw ChimeraTK::runtime_error("UIO - Waiting for interrupt failed: " + std::string(std::strerror(errno)));
127  }
128  }
129 
131  return _deviceFilePath.string();
132  }
133 
134  void UioAccess::UioMMap() {
135  _deviceUserBase = mmap(NULL, _deviceMemSize, PROT_READ | PROT_WRITE, MAP_SHARED, _deviceFileDescriptor, 0);
136  if(_deviceUserBase == MAP_FAILED) {
137  ::close(_deviceFileDescriptor);
138  throw ChimeraTK::runtime_error("UIO: Cannot allocate memory for UIO device '" + getDeviceFilePath() + "'");
139  }
140  return;
141  }
142 
143  void UioAccess::UioUnmap() {
144  munmap(_deviceUserBase, _deviceMemSize);
145  }
146 
147  uint32_t UioAccess::subtractUint32OverflowSafe(uint32_t minuend, uint32_t subtrahend) {
148  if(subtrahend > minuend) {
149  return minuend +
150  (uint32_t)(static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) - static_cast<uint64_t>(subtrahend));
151  }
152  else {
153  return minuend - subtrahend;
154  }
155  }
156 
157  uint32_t UioAccess::readUint32FromFile(std::string fileName) {
158  uint64_t value = 0;
159  std::ifstream inputFile(fileName);
160 
161  if(inputFile.is_open()) {
162  inputFile >> value;
163  inputFile.close();
164  }
165  return (uint32_t)value;
166  }
167 
168  uint64_t UioAccess::readUint64HexFromFile(std::string fileName) {
169  uint64_t value = 0;
170  std::ifstream inputFile(fileName);
171 
172  if(inputFile.is_open()) {
173  inputFile >> std::hex >> value;
174  inputFile.close();
175  }
176  return value;
177  }
178 } // namespace ChimeraTK
ChimeraTK::UioAccess::open
void open()
Opens UIO device for read and write operations and interrupt handling.
Definition: UioAccess.cc:23
ChimeraTK::UioAccess::UioAccess
UioAccess(const std::string &deviceFilePath)
Definition: UioAccess.cc:17
ChimeraTK::runtime_error
Exception thrown when a runtime error has occured.
Definition: Exception.h:18
ChimeraTK::UioAccess::read
void read(uint64_t map, uint64_t address, int32_t *data, size_t sizeInBytes)
Read data from the specified memory offset address.
Definition: UioAccess.cc:49
ChimeraTK::UioAccess::write
void write(uint64_t map, uint64_t address, int32_t const *data, size_t sizeInBytes)
Write data to the specified memory offset address.
Definition: UioAccess.cc:68
ChimeraTK::UioAccess::close
void close()
Closes UIO device.
Definition: UioAccess.cc:41
ChimeraTK::UioAccess::getDeviceFilePath
std::string getDeviceFilePath()
Return UIO device file path.
Definition: UioAccess.cc:130
ChimeraTK::UioAccess::waitForInterrupt
uint32_t waitForInterrupt(int timeoutMs)
Wait for hardware interrupt to occur within specified timeout period.
Definition: UioAccess.cc:87
ChimeraTK::UioAccess::~UioAccess
~UioAccess()
Definition: UioAccess.cc:19
UioAccess.h
ChimeraTK::UioAccess::clearInterrupts
void clearInterrupts()
Clear all pending interrupts.
Definition: UioAccess.cc:121
Exception.h
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51