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