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