MotionLib  1.0.0
SamBuCa motion library
EdgeGrblDeviceBase.cpp
1 /*
2  * Copyright (C) 2022 CERN
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty. In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  * claim that you wrote the original software. If you use this software
14  * in a product, an acknowledgment in the product documentation would be
15  * appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  * misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  *
20  * Created on: 2022-03-08
21  * Author: Michal Mysior <mmysior> <michal.mysior@cern.ch>
22  *
23  */
24 
25 #include "EdgeGrblDeviceBase.hpp"
26 
27 #include <algorithm>
28 #include <cerrno>
29 #include <chrono>
30 #include <cstring>
31 #include <thread>
32 
33 #include <ccut/Regex.hpp>
34 #include <ccut/async.hpp>
35 #include <dlfcn.h>
36 #include <fcntl.h>
37 #include <fmc_mfe/libfmc_mfe.h>
38 #include <logger/Logger.hpp>
39 #include <poll.h>
40 #include <pthread.h>
41 #include <unistd.h>
42 
43 #include "Exception.hpp"
44 
45 using namespace logger;
46 using namespace std::chrono;
47 
48 static const size_t s_edgeFifoTxSize{512};
49 static constexpr uint32_t s_edgeFifoTxNullCharMask{(uint32_t) (1 << 18)};
50 static constexpr uint32_t s_edgeFifoRxEmptyMask{(uint32_t) (1 << 8)};
51 static const std::chrono::milliseconds s_edgeFifoRxFreeTimeout{10};
52 
53 namespace edge {
54 
55 const std::string EdgeGrblDeviceBase::loggerCat{"edge:edgegrbldevice"};
56 
57 EdgeGrblDeviceBase::EdgeGrblDeviceBase(int lun, const std::string &driverPath)
58 {
59  init(lun, driverPath);
60 }
61 
62 EdgeGrblDeviceBase::EdgeGrblDeviceBase() {}
63 
64 void EdgeGrblDeviceBase::init(int lun, const std::string &driverPath)
65 {
66  bool simMode{!driverPath.empty()};
67 
68  drvOpen(lun, driverPath, simMode);
69  m_fifoBytes.resize(m_edgeDrvHdl->reg_map->grbl_fifo.tx_dr->reg_attr->depth);
70 
71  /* flush fifo */
72  EdgeGrblDeviceBase::read();
73 
74  ::pipe(m_wakeFd);
75  ::fcntl(m_wakeFd[0], F_SETFL, ::fcntl(m_wakeFd[0], F_GETFL, 0) | O_NONBLOCK);
76  ::fcntl(m_wakeFd[1], F_SETFL, ::fcntl(m_wakeFd[1], F_GETFL, 0) | O_NONBLOCK);
77 }
78 
79 EdgeGrblDeviceBase::~EdgeGrblDeviceBase()
80 {
81  ::close(m_wakeFd[0]);
82  ::close(m_wakeFd[1]);
83  drvClose();
84 }
85 
86 void EdgeGrblDeviceBase::read()
87 {
88  uint32_t txCnt;
89  struct edge_reg *txCntReg = m_edgeDrvHdl->reg_map->grbl_fifo.tx_counter;
90  if (edge_get(m_edgeDrvHdl->edge_hdl, txCntReg, &txCnt, 1))
91  {
92  error(loggerCat) << "Failed getting tx_counter register";
93  throw smc::Exception{smc::ErrorCode::InternalError,
94  std::string{edge_strerror(m_edgeDrvHdl->edge_hdl)}};
95  }
96  if (txCnt > 0)
97  {
98  if (!m_buffer.remaining())
99  {
100  error(loggerCat) << "flushing buffer";
101  m_buffer.reset();
102  }
103 
104  struct edge_reg *txDataReg = m_edgeDrvHdl->reg_map->grbl_fifo.tx_dr;
105  size_t bytesToRead = std::min(m_buffer.remaining(),
106  static_cast<size_t>(txCnt));
107  m_fifoBytes.reserve(bytesToRead);
108 
109  if (edge_get(m_edgeDrvHdl->edge_hdl, txDataReg, m_fifoBytes.data(),
110  static_cast<uint32_t>(bytesToRead)))
111  {
112  error(loggerCat) << "Failed getting " << bytesToRead
113  << " bytes from tx_dr register";
114  throw smc::Exception{
115  smc::ErrorCode::InternalError,
116  std::string{edge_strerror(m_edgeDrvHdl->edge_hdl)}};
117  }
118 
119  // Add non-null chars to buffer
120  for (size_t i = 0; i < bytesToRead; ++i)
121  {
122  const char byte = *(m_fifoBytes.data() + i);
123  if (byte != '\0')
124  m_buffer.add(byte);
125  }
126  }
127 }
128 
129 void EdgeGrblDeviceBase::write(const std::string &data)
130 {
131  uint32_t rxByteCount;
132  if (edge_get(m_edgeDrvHdl->edge_hdl,
133  m_edgeDrvHdl->reg_map->grbl_fifo.rx_counter, &rxByteCount, 1))
134  {
135  error(loggerCat) << "Failed getting data from rx_count register";
136  throw smc::Exception{smc::ErrorCode::InternalError,
137  std::string{edge_strerror(m_edgeDrvHdl->edge_hdl)}};
138  }
139  struct edge_reg *rxDataReg = m_edgeDrvHdl->reg_map->grbl_fifo.rx_dr;
140  size_t rxFreeBytes = rxDataReg->reg_attr->depth - rxByteCount;
141 
142  size_t bytesWritten = 0;
143  while (bytesWritten < data.size())
144  {
145  size_t bytesToWrite = std::min((data.size() - bytesWritten),
146  rxFreeBytes);
147  if (edge_set(m_edgeDrvHdl->edge_hdl, rxDataReg,
148  const_cast<void *>(reinterpret_cast<const void *>(
149  data.c_str() + bytesWritten)),
150  bytesToWrite))
151  {
152  error(loggerCat) << "Failed writing " << bytesToWrite
153  << " bytes to rx_dr register";
154  throw smc::Exception{
155  smc::ErrorCode::InternalError,
156  std::string{edge_strerror(m_edgeDrvHdl->edge_hdl)}};
157  }
158 
159  if ((bytesWritten = bytesToWrite) < data.size())
160  {
161  if (!waitIrq(s_edgeFifoRxFreeTimeout,
162  m_edgeDrvHdl->reg_map->grbl_fifo.intc_sr,
163  s_edgeFifoRxEmptyMask))
164  {
165  error(loggerCat)
166  << "Failed waiting for Rx FIFO empty interrupt";
167  throw smc::Exception{smc::ErrorCode::InternalError,
168  "Rx FIFO interrupt timeout"};
169  }
170  rxFreeBytes = rxDataReg->reg_attr->depth;
171  }
172  }
173 }
174 
175 bool EdgeGrblDeviceBase::wait(const std::chrono::milliseconds &timeout)
176 {
177  return waitIrq(timeout, m_edgeDrvHdl->reg_map->grbl_fifo.intc_sr,
178  s_edgeFifoTxNullCharMask);
179 }
180 
181 void EdgeGrblDeviceBase::wake()
182 {
183  ::write(m_wakeFd[1], "X", 1);
184 }
185 
186 bool EdgeGrblDeviceBase::waitIrq(const std::chrono::milliseconds &timeout,
187  const struct edge_reg *const expectedReg,
188  uint32_t expectedBit)
189 {
190  struct pollfd pfds[2] = {{m_wakeFd[0], POLLIN, 0},
191  {m_edgeDrvHdl->edge_hdl->fd, POLLIN | POLLNVAL, 0}};
192  const steady_clock::time_point deadline = steady_clock::now() + timeout;
193  bool irqMatched{false};
194  bool waked{false};
195  int remainingIrqs = 0;
196  edge_irq irqRes;
197 
198  while (!irqMatched && !waked)
199  {
200  milliseconds remaining = duration_cast<milliseconds>(
201  deadline - steady_clock::now());
202  if (remaining.count() < 0)
203  break;
204 
205  int res = ::poll(pfds, 2, remaining.count() + 1);
206  if (res > 0)
207  {
208  // Check for wake event
209  if (pfds[0].revents & POLLIN)
210  {
211  std::vector<char> buffer(10);
212  ::read(m_wakeFd[0], buffer.data(), buffer.size());
213  waked = true;
214  }
215 
216  // Check for IRQ event
217  if (pfds[1].revents & POLLIN)
218  {
219  // Read IRQ from device's FD using edge function
220  if (edge_wait_irq(m_edgeDrvHdl->edge_hdl, &irqRes) < 0)
221  {
222  error(loggerCat)
223  << "edge_wait_irq failed: " << ::strerror(errno);
224  throw make_errno_exception(smc::ErrorCode::IO);
225  }
226  // Update how many IRQs remain in queue
227  remainingIrqs = irqRes.queued;
228  // Check if IRQ is equal to expected
229  if ((irqRes.irq_src == expectedReg) &&
230  (irqRes.irq_value & expectedBit))
231  irqMatched = true;
232  }
233  // Check if poll error on edge fd
234  if (pfds[1].revents & POLLNVAL)
235  {
236  error(loggerCat) << "IRQ poll() failed - wrong poll request";
237  throw smc::Exception(smc::ErrorCode::InternalError,
238  "IRQ poll() failed - wrong poll request");
239  }
240  }
241  else if (res == 0)
242  {
243  /* timeout */
244  waked = true;
245  }
246  else
247  {
248  // Error
249  error(loggerCat) << "poll() failed waiting for IRQ";
250  throw smc::Exception(smc::ErrorCode::InternalError,
251  "IRQ poll() failed");
252  }
253  }
254 
255  // Flush remaining IRQs in FD
256  while (remainingIrqs--)
257  {
258  if (edge_wait_irq(m_edgeDrvHdl->edge_hdl, &irqRes) < 0)
259  {
260  error(loggerCat)
261  << "edge_wait_irq (flush) failed: " << ::strerror(errno);
262  break;
263  }
264  }
265  return irqMatched;
266 }
267 
268 void EdgeGrblDeviceBase::read(struct edge_reg *reg,
269  size_t offset,
270  uint32_t &value)
271 {
272  std::string err;
273  if (!m_edgeDrvHdl)
274  err = "driver not initialized";
275  else if (reg && reg->reg_attr->dwidth != 32)
276  err = "invalid register size";
277  else if (edge_get_range(m_edgeDrvHdl->edge_hdl, reg, offset, &value, 1) != 0)
278  err = edge_strerror(m_edgeDrvHdl->edge_hdl);
279 
280  if (!err.empty())
281  {
282  err = "Failed to read register: " + err;
283  error(loggerCat) << err;
284  throw smc::Exception{smc::ErrorCode::IO, err};
285  }
286 }
287 
288 void EdgeGrblDeviceBase::read(struct edge_reg *reg,
289  std::vector<uint32_t> &values)
290 {
291  std::string err;
292  if (!m_edgeDrvHdl)
293  err = "driver not initialized";
294  else if (reg && reg->reg_attr->dwidth != 32)
295  err = "invalid register size";
296  else if (edge_get_range(m_edgeDrvHdl->edge_hdl, reg, 0, values.data(),
297  values.size()) != 0)
298  err = edge_strerror(m_edgeDrvHdl->edge_hdl);
299 
300  if (!err.empty())
301  {
302  err = "Failed to read register: " + err;
303  error(loggerCat) << err;
304  throw smc::Exception{smc::ErrorCode::IO, err};
305  }
306 }
307 
308 void EdgeGrblDeviceBase::write(struct edge_reg *reg,
309  size_t offset,
310  uint32_t value)
311 {
312  std::string err;
313  if (!m_edgeDrvHdl)
314  err = "driver not initialized";
315  else if (reg && reg->reg_attr->dwidth != 32)
316  err = "invalid register size";
317  else if (edge_set_range(m_edgeDrvHdl->edge_hdl, reg, offset, &value, 1) != 0)
318  err = edge_strerror(m_edgeDrvHdl->edge_hdl);
319 
320  if (!err.empty())
321  {
322  err = "Failed to write register: " + err;
323  error(loggerCat) << err;
324  throw smc::Exception{smc::ErrorCode::IO, err};
325  }
326 }
327 
328 void EdgeGrblDeviceBase::drvOpen(int lun, const std::string &simPath, bool isSim)
329 {
330  if (isSim)
331  {
332  {
333  // CentOS7 workarround, see
334  // https://gitlab.cern.ch/be-cem-edl/fec/utilities/edge/-/issues/68
335  void *lib = dlopen((simPath + "/libfmc_mfe_sim.so").c_str(),
336  RTLD_LAZY | RTLD_NODELETE);
337  if (lib)
338  dlclose(lib);
339  }
340  m_edgeDrvHdl = fmc_mfe_sim_open(lun, simPath.c_str());
341  }
342  else
343  {
344  m_edgeDrvHdl = fmc_mfe_drv_open(lun);
345  }
346 
347  if (m_edgeDrvHdl == NULL)
348  {
349  error(loggerCat) << "Opening fmc_mfe driver failed: "
350  << std::string{edge_strerror(NULL)};
351  throw smc::Exception(smc::ErrorCode::InternalError,
352  std::string{edge_strerror(NULL)});
353  }
354 
355  struct edge_vers version;
356  if (edge_get_version(m_edgeDrvHdl->edge_hdl, &version) == 0)
357  info(loggerCat) << "fmc_mfe: " << version.hw_lif_vers;
358  else /* unlikely */
359  error(loggerCat) << "Failed to retrieve driver version";
360 
361  // Enable irq queue
362  struct edge_irq_cfg cfg;
363  cfg.irq_src_reg = m_edgeDrvHdl->reg_map->grbl_fifo.intc_sr;
364  cfg.irq_mask = s_edgeFifoTxNullCharMask | s_edgeFifoRxEmptyMask;
365  cfg.queue_flg = 1;
366  /* very large fallback timeout, should not be used since ::poll is used on
367  * the file-descriptor */
368  cfg.timeout = 10000;
369 
370  if (edge_set_irq_cfg(m_edgeDrvHdl->edge_hdl, &cfg,
371  EDGE_IRQ_SRC | EDGE_IRQ_MASK | EDGE_IRQ_QUEUE |
372  EDGE_IRQ_TMO) < 0)
373  {
374  error(loggerCat) << "Enabling IRQ queue in edge failed: "
375  << std::string{edge_strerror(m_edgeDrvHdl->edge_hdl)};
376  throw smc::Exception(smc::ErrorCode::InternalError,
377  std::string{edge_strerror(m_edgeDrvHdl->edge_hdl)});
378  }
379 
380  if (isSim)
381  notice(loggerCat)
382  << "fmc_mfe driver opened in sim mode on path " << simPath;
383  else
384  notice(loggerCat) << "fmc_mfe driver opened";
385 }
386 
387 void EdgeGrblDeviceBase::drvClose()
388 {
389  if (m_edgeDrvHdl != NULL)
390  {
391  fmc_mfe_close(m_edgeDrvHdl);
392  notice(loggerCat) << "fmc_mfe driver closed";
393  }
394 }
395 
396 } /* namespace edge */
Exception thrown by MotionController in case of issues with command.
Definition: Exception.hpp:61