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