ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
testUioBackendUnified.cpp
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 <filesystem>
5#define BOOST_TEST_DYN_LINK
6#define BOOST_TEST_MODULE UioBackendUnifiedTest
7#include <boost/test/unit_test.hpp>
8using namespace boost::unit_test_framework;
9
10#include "MapFileParser.h"
11#include "UnifiedBackendTest.h"
12
13#include <sys/file.h>
14#include <sys/mman.h>
15
16#include <fcntl.h>
17#include <poll.h>
18#include <unistd.h>
19
20#include <fstream>
21#include <iostream>
22
23using namespace ChimeraTK;
24
25/**********************************************************************************************************************/
26
27// Use a file lock to ensure we are not running concurrent tests in
28// parallel using the same kernel dummy driver.
29//
30// Note: The lock is automatically released when the process terminates!
31struct TestLocker {
32 const std::string lockfile{"/var/run/lock/uiodummy.lock"};
34 int fd = open(lockfile.c_str(), O_WRONLY | O_CREAT, 0777);
35 if(fd == -1) {
36 exit(1);
37 }
38
39 // obtain lock
40 int res = flock(fd, LOCK_EX);
41 if(res == -1) {
42 exit(1);
43 }
44 }
45
47 // FIXME: It would be nice to unlink the file here, so it does not unnecessarily remain in the file system.
48 // Unfortunately, this somehow spoils the locking. Completely unclear why.
49
50 // unlink(lockfile.c_str());
51 }
52};
53static TestLocker testLocker;
54
55/**********************************************************************************************************************/
56
57BOOST_AUTO_TEST_SUITE(UioBackendUnifiedTestSuite)
58
59/**********************************************************************************************************************/
60
61static const std::string cdd("(uio:ctkuiodummy?map=uioBackendTest.mapp)");
62
63/**********************************************************************************************************************/
64
66 public:
67 explicit RawUioAccess(const std::string& filePath, const std::string& mapFile);
69 void sendInterrupt() const;
70 [[nodiscard]] size_t getMemorySize() const;
71 void* data();
72 template<typename T>
73 T read(const std::string& name);
74 template<typename T>
75 void write(const std::string& name, T value);
76
77 private:
78 int _uioFileDescriptor;
79 int _uioProcFd;
80 std::filesystem::path _deviceFilePath;
81 size_t _deviceMemSize = 0;
82 void* _memoryPointer{nullptr};
84
85 static uint64_t readUint64HexFromFile(const std::string& filePath);
86};
87
88/**********************************************************************************************************************/
89
90RawUioAccess::RawUioAccess(const std::string& filePath, const std::string& mapFile)
91: _deviceFilePath(filePath.c_str()) {
92 if(std::filesystem::is_symlink(_deviceFilePath)) {
93 _deviceFilePath = std::filesystem::canonical(_deviceFilePath);
94 }
95 _uioFileDescriptor = open(filePath.c_str(), O_RDWR);
96
97 if(_uioFileDescriptor < 0) {
98 throw std::runtime_error("failed to open UIO device '" + filePath + "'");
99 }
100
101 _uioProcFd = open("/proc/uio-dummy", O_RDWR);
102 if(_uioProcFd < 0) {
103 throw std::runtime_error("failed to open UIO device '" + filePath + "'");
104 }
105
106 // Determine size of UIO memory region
107 std::string fileName = _deviceFilePath.filename().string();
108 _deviceMemSize = readUint64HexFromFile("/sys/class/uio/" + fileName + "/maps/map0/size");
109
110 _memoryPointer = mmap(nullptr, _deviceMemSize, PROT_READ | PROT_WRITE, MAP_SHARED, _uioFileDescriptor, 0);
111
112 if(_memoryPointer == MAP_FAILED) {
113 throw std::runtime_error("UioMmap construction failed");
114 }
116 auto [cat, metaCat] = p.parse(mapFile);
117 _catalogue = std::move(cat);
118}
119
120/**********************************************************************************************************************/
121/* Helper implementations */
122/**********************************************************************************************************************/
123
125 munmap(_memoryPointer, _deviceMemSize);
126 close(_uioFileDescriptor);
127 close(_uioProcFd);
128}
129
130/**********************************************************************************************************************/
131
133 static int enable{1};
134 std::ignore = ::write(_uioProcFd, &enable, sizeof(enable));
135}
136
137/**********************************************************************************************************************/
138
140 return _deviceMemSize;
141}
142
143/**********************************************************************************************************************/
144
146 return _memoryPointer;
147}
148
149/**********************************************************************************************************************/
150
151template<typename T>
152T RawUioAccess::read(const std::string& name) {
153 auto r = _catalogue.getBackendRegister(name);
154 return *reinterpret_cast<T*>(reinterpret_cast<std::byte*>(data()) + r.address);
155}
156
157/**********************************************************************************************************************/
158
159template<typename T>
160void RawUioAccess::write(const std::string& name, T value) {
161 auto r = _catalogue.getBackendRegister(name);
162 *reinterpret_cast<T*>(reinterpret_cast<std::byte*>(data()) + r.address) = value;
164}
165
166/**********************************************************************************************************************/
167
168uint64_t RawUioAccess::readUint64HexFromFile(const std::string& filePath) {
169 uint64_t value = 0;
170 std::ifstream inputFile(filePath.c_str());
171
172 if(inputFile.is_open()) {
173 inputFile >> std::hex >> value;
174 inputFile.close();
175 }
176 return value;
177}
178
179/**********************************************************************************************************************/
180/* The test descriptors */
181/**********************************************************************************************************************/
182
183template<typename Derived>
185 Derived* derived = static_cast<Derived*>(this);
186 AccessModeFlags supportedFlags() { return {AccessMode::raw}; }
187 size_t nChannels() { return 1; }
188 size_t nElementsPerChannel() { return 1; }
189 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
190 size_t nRuntimeErrorCases() { return 0; }
191 using minimumUserType = int32_t;
193
194 static constexpr auto capabilities = TestCapabilities<>()
196 .disableSwitchReadOnly()
197 .disableSwitchWriteOnly()
198 .disableTestWriteNeverLosesData()
199 .disableAsyncReadInconsistency()
200 .enableTestRawTransfer();
201
202 std::shared_ptr<RawUioAccess> remote{new RawUioAccess("/dev/ctkuiodummy", "uioBackendTest.mapp")};
203
204 // Type can be raw type or user type
205 template<typename Type>
206 std::vector<std::vector<Type>> generateValue(bool raw = false) {
207 auto rawVal00 = remote->read<rawUserType>(derived->path());
208 rawVal00 += 3;
209 Type val00 = (raw ? rawVal00 : rawToCooked<Type, rawUserType>(rawVal00));
210
211 return {{val00}};
212 }
213
214 // Type can be raw type or user type
215 template<typename Type>
216 std::vector<std::vector<Type>> getRemoteValue(bool raw = false) {
217 auto rawVal00 = remote->read<rawUserType>(derived->path());
218 Type val00 = (raw ? rawVal00 : rawToCooked<Type, rawUserType>(rawVal00));
219
220 return {{val00}};
221 }
222
224 auto x = generateValue<rawUserType>(true)[0][0];
225 remote->write<rawUserType>(derived->path(), x);
226 }
227
228 // default implementation just casting. Re-implement in derived classes if needed.
229 template<typename UserType, typename RawType>
230 RawType cookedToRaw(UserType val) {
231 return static_cast<RawType>(val);
232 }
233
234 // default implementation just casting. Re-implement in derived classes if needed.
235 template<typename UserType, typename RawType>
236 UserType rawToCooked(RawType val) {
237 return static_cast<UserType>(val);
238 }
239
240 // we need this because it's expected in template, but unused
241 void setForceRuntimeError([[maybe_unused]] bool enable, [[maybe_unused]] size_t type) {}
242};
243
244/**********************************************************************************************************************/
245
246struct Scalar32 : ScalarDescriptor<Scalar32> {
247 static std::string path() { return "TIMING.WORD_ID"; }
248 static bool isReadable() { return true; }
249 static bool isWriteable() { return false; }
250};
251
252/**********************************************************************************************************************/
253
254struct Scalar32Async : ScalarDescriptor<Scalar32Async> {
255 static std::string path() { return "MOTOR_CONTROL.MOTOR_POSITION"; }
256 static bool isReadable() { return true; }
257 static bool isWriteable() { return false; }
261};
262
263/**********************************************************************************************************************/
264
266 UnifiedBackendTest<>().addRegister<Scalar32>().addRegister<Scalar32Async>().runTests(cdd);
267}
268
269/**********************************************************************************************************************/
270
271BOOST_AUTO_TEST_SUITE_END()
Set of AccessMode flags with additional functionality for an easier handling.
Definition AccessMode.h:48
static std::pair< NumericAddressedRegisterCatalogue, MetadataCatalogue > parse(const std::string &fileName)
Performs parsing of specified MAP file, resulting in catalogue objects describing all registers and m...
NumericAddressedRegisterInfo getBackendRegister(const RegisterPath &registerPathName) const override
Note: Override this function if backend has "hidden" registers which are not added to the map and hen...
Class to test any backend for correct behaviour.
UnifiedBackendTest< typename boost::mpl::push_back< VECTOR_OF_REGISTERS_T, REG_T >::type > addRegister()
Add a register to be used by the test.
void write(const std::string &name, T value)
RawUioAccess(const std::string &filePath, const std::string &mapFile)
T read(const std::string &name)
size_t getMemorySize() const
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
Descriptor for the test capabilities for each register.
constexpr TestCapabilities< _syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion, _testPartialAccessor > disableForceDataLossWrite() const
static std::string path()
static ChimeraTK::AccessModeFlags supportedFlags()
static bool isWriteable()
static std::string path()
static bool isReadable()
std::vector< std::vector< Type > > getRemoteValue(bool raw=false)
UserType rawToCooked(RawType val)
void setForceRuntimeError(bool enable, size_t type)
std::vector< std::vector< Type > > generateValue(bool raw=false)
AccessModeFlags supportedFlags()
RawType cookedToRaw(UserType val)
static constexpr auto capabilities
std::shared_ptr< RawUioAccess > remote
const std::string lockfile
std::string cdd
BOOST_AUTO_TEST_CASE(testUnified)