PX4 Firmware
PX4 Autopilot Software http://px4.io
uavcan_virtual_can_driver.hpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2015 PX4 Development Team. All rights reserved.
4  * Pavel Kirienko <pavel.kirienko@zubax.com>
5  * David Sidrane <david_s5@nscdg.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  * 3. Neither the name PX4 nor the names of its contributors may be
18  * used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  ****************************************************************************/
35 
36 #pragma once
37 
38 #include <nuttx/config.h>
39 
40 #include <cstdlib>
41 #include <cstdint>
42 #include <cstring>
43 #include <cstdio>
44 #include <fcntl.h>
45 #include <pthread.h>
46 #include <semaphore.h>
47 #include <debug.h>
48 
49 #include <uavcan/node/sub_node.hpp>
50 #include <uavcan/protocol/node_status_monitor.hpp>
51 
52 /*
53  * General purpose wrapper around os's mutual exclusion
54  * mechanism.
55  *
56  * It supports the
57  *
58  * Note the naming of thier_mutex_ implies that the underlying
59  * mutex is owned by class using the Lock.
60  * and this wrapper provides service to initialize and de initialize
61  * the mutex.
62  */
63 class Lock
64 {
65  pthread_mutex_t &thier_mutex_;
66 
67 public:
68 
69  Lock(pthread_mutex_t &m) :
70  thier_mutex_(m)
71  {
72  (void)pthread_mutex_lock(&m);
73  }
74 
76  {
77  (void)pthread_mutex_unlock(&thier_mutex_);
78  }
79 
80  static int init(pthread_mutex_t &thier_mutex_)
81  {
82  return pthread_mutex_init(&thier_mutex_, NULL);
83  }
84 
85  static int deinit(pthread_mutex_t &thier_mutex_)
86  {
87  return pthread_mutex_destroy(&thier_mutex_);
88  }
89 
90 };
91 
92 /**
93  * Generic queue based on the linked list class defined in libuavcan.
94  * This class does not use heap memory.
95  */
96 template <typename T>
97 class Queue
98 {
99  struct Item : public uavcan::LinkedListNode<Item> {
101 
102  template <typename... Args>
103  Item(Args... args) : payload(args...) { }
104  };
105 
106  uavcan::LimitedPoolAllocator allocator_;
107  uavcan::LinkedListRoot<Item> list_;
108 
109 public:
110  Queue(uavcan::IPoolAllocator &arg_allocator, std::size_t block_allocation_quota) :
111  allocator_(arg_allocator, block_allocation_quota)
112  {
113  uavcan::IsDynamicallyAllocatable<Item>::check();
114  }
115 
117  {
118  while (!isEmpty()) {
119  pop();
120  }
121  }
122 
123  bool isEmpty() const { return list_.isEmpty(); }
124 
125  /**
126  * Creates one item in-place at the end of the list.
127  * Returns true if the item was appended successfully, false if there's not enough memory.
128  * Complexity is O(N) where N is queue length.
129  */
130  template <typename... Args>
131  bool tryEmplace(Args... args)
132  {
133  // Allocating memory
134  void *const ptr = allocator_.allocate(sizeof(Item));
135 
136  if (ptr == nullptr) {
137  return false;
138  }
139 
140  // Constructing the new item
141  Item *const item = new (ptr) Item(args...);
142  assert(item != nullptr);
143 
144  // Inserting the new item at the end of the list
145  Item *p = list_.get();
146 
147  if (p == nullptr) {
148  list_.insert(item);
149 
150  } else {
151  while (p->getNextListNode() != nullptr) {
152  p = p->getNextListNode();
153  }
154 
155  assert(p->getNextListNode() == nullptr);
156  p->setNextListNode(item);
157  assert(p->getNextListNode()->getNextListNode() == nullptr);
158  }
159 
160  return true;
161  }
162 
163  /**
164  * Accesses the first element.
165  * Nullptr will be returned if the queue is empty.
166  * Complexity is O(1).
167  */
168  T *peek() { return isEmpty() ? nullptr : &list_.get()->payload; }
169  const T *peek() const { return isEmpty() ? nullptr : &list_.get()->payload; }
170 
171  /**
172  * Removes the first element.
173  * If the queue is empty, nothing will be done and assertion failure will be triggered.
174  * Complexity is O(1).
175  */
176  void pop()
177  {
178  Item *const item = list_.get();
179  assert(item != nullptr);
180 
181  if (item != nullptr) {
182  list_.remove(item);
183  item->~Item();
184  allocator_.deallocate(item);
185  }
186  }
187 };
188 
189 /**
190  * Objects of this class are owned by the sub-node thread.
191  * This class does not use heap memory.
192  */
193 class VirtualCanIface : public uavcan::ICanIface,
194  uavcan::Noncopyable
195 {
196  /**
197  * This class re-defines uavcan::RxCanFrame with flags.
198  * Simple inheritance or composition won't work here, because the 40 byte limit will be exceeded,
199  * rendering this class unusable with Queue<>.
200  */
201  struct RxItem: public uavcan::CanFrame {
202  const uavcan::MonotonicTime ts_mono;
203  const uavcan::UtcTime ts_utc;
204  const uavcan::CanIOFlags flags;
205  const uint8_t iface_index;
206 
207  RxItem(const uavcan::CanRxFrame &arg_frame, uavcan::CanIOFlags arg_flags) :
208  uavcan::CanFrame(arg_frame),
209  ts_mono(arg_frame.ts_mono),
210  ts_utc(arg_frame.ts_utc),
211  flags(arg_flags),
212  iface_index(arg_frame.iface_index)
213  {
214  // Making sure it will fit into a pool block with a pointer prefix
215  static_assert(sizeof(RxItem) <= (uavcan::MemPoolBlockSize - 8), "Bad coder, no coffee");
216  }
217  };
218 
219  pthread_mutex_t &common_driver_mutex_;
220 
221  uavcan::CanTxQueue prioritized_tx_queue_;
223 
224  int16_t send(const uavcan::CanFrame &frame, uavcan::MonotonicTime tx_deadline, uavcan::CanIOFlags flags) override
225  {
226  Lock lock(common_driver_mutex_);
227  prioritized_tx_queue_.push(frame, tx_deadline, uavcan::CanTxQueue::Volatile, flags);
228  return 1;
229  }
230 
231  int16_t receive(uavcan::CanFrame &out_frame, uavcan::MonotonicTime &out_ts_monotonic,
232  uavcan::UtcTime &out_ts_utc, uavcan::CanIOFlags &out_flags) override
233  {
234  Lock lock(common_driver_mutex_);
235 
236  if (rx_queue_.isEmpty()) {
237  return 0;
238  }
239 
240  const auto item = *rx_queue_.peek();
241  rx_queue_.pop();
242 
243  out_frame = item;
244  out_ts_monotonic = item.ts_mono;
245  out_ts_utc = item.ts_utc;
246  out_flags = item.flags;
247 
248  return 1;
249  }
250 
251  int16_t configureFilters(const uavcan::CanFilterConfig *, std::uint16_t) override { return -uavcan::ErrDriver; }
252  uint16_t getNumFilters() const override { return 0; }
253  uint64_t getErrorCount() const override { return 0; }
254 
255 public:
256  VirtualCanIface(uavcan::IPoolAllocator &allocator, uavcan::ISystemClock &clock,
257  pthread_mutex_t &arg_mutex, unsigned quota_per_queue) :
258  common_driver_mutex_(arg_mutex),
259  prioritized_tx_queue_(allocator, clock, quota_per_queue),
260  rx_queue_(allocator, quota_per_queue)
261  {
262  }
263 
265  {
266  }
267 
268  /**
269  * Note that RX queue overwrites oldest items when overflowed.
270  * Call this from the main thread only.
271  * No additional locking is required.
272  */
273  void addRxFrame(const uavcan::CanRxFrame &frame, uavcan::CanIOFlags flags)
274  {
275  Lock lock(common_driver_mutex_);
276 
277  if (!rx_queue_.tryEmplace(frame, flags) && !rx_queue_.isEmpty()) {
278  rx_queue_.pop();
279  (void)rx_queue_.tryEmplace(frame, flags);
280  }
281  }
282 
283  /**
284  * Call this from the main thread only.
285  * No additional locking is required.
286  */
287  void flushTxQueueTo(uavcan::INode &main_node, std::uint8_t iface_index)
288  {
289  Lock lock(common_driver_mutex_);
290  const std::uint8_t iface_mask = static_cast<std::uint8_t>(1U << iface_index);
291 
292  while (auto e = prioritized_tx_queue_.peek()) {
293  UAVCAN_TRACE("VirtualCanIface", "TX injection [iface=0x%02x]: %s",
294  unsigned(iface_mask), e->toString().c_str());
295 
296  const int res = main_node.injectTxFrame(e->frame, e->deadline, iface_mask,
297  uavcan::CanTxQueue::Qos(e->qos), e->flags);
298 
299  prioritized_tx_queue_.remove(e);
300 
301  if (res <= 0) {
302  break;
303  }
304 
305  }
306  }
307 
308  /**
309  * Call this from the sub-node thread only.
310  * No additional locking is required.
311  */
313  {
314  Lock lock(common_driver_mutex_);
315  return !rx_queue_.isEmpty();
316  }
317 };
318 
319 /**
320  * This interface defines one method that will be called by the main node thread periodically in order to
321  * transfer contents of TX queue of the sub-node into the TX queue of the main node.
322  */
324 {
325 public:
326  virtual ~ITxQueueInjector() { }
327 
328  /**
329  * Flush contents of TX queues into the main node.
330  * @param main_node Reference to the main node.
331  */
332  virtual void injectTxFramesInto(uavcan::INode &main_node) = 0;
333 };
334 
335 /**
336  * Objects of this class are owned by the sub-node thread.
337  * This class does not use heap memory.
338  */
339 class VirtualCanDriver : public uavcan::ICanDriver,
340  public uavcan::IRxFrameListener,
341  public ITxQueueInjector,
342  uavcan::Noncopyable
343 {
344  class Event
345  {
346  FAR px4_sem_t sem;
347 
348 
349  public:
350 
351  int init()
352  {
353  int rv = px4_sem_init(&sem, 0, 0);
354 
355  if (rv == 0) {
356  px4_sem_setprotocol(&sem, SEM_PRIO_NONE);
357  }
358 
359  return rv;
360  }
361 
362  int deinit()
363  {
364  return px4_sem_destroy(&sem);
365  }
366 
367 
369  {
370  }
371 
373  {
374  }
375 
376 
377  /**
378  */
379 
380  void waitFor(uavcan::MonotonicDuration duration)
381  {
382  static const int NsPerSec = 1000000000;
383 
384  if (duration.isPositive()) {
385  auto abstime = ::timespec();
386 
387  if (clock_gettime(CLOCK_REALTIME, &abstime) >= 0) {
388  abstime.tv_nsec += duration.toUSec() * 1000;
389 
390  if (abstime.tv_nsec >= NsPerSec) {
391  abstime.tv_sec++;
392  abstime.tv_nsec -= NsPerSec;
393  }
394 
395  (void)px4_sem_timedwait(&sem, &abstime);
396  }
397  }
398  }
399 
400  void signal()
401  {
402  int count;
403  int rv = px4_sem_getvalue(&sem, &count);
404 
405  if (rv == 0 && count <= 0) {
406  px4_sem_post(&sem);
407  }
408  }
409  };
410 
411  Event event_; ///< Used to unblock the select() call when IO happens.
412  pthread_mutex_t driver_mutex_; ///< Shared across all ifaces
413  uavcan::LazyConstructor<VirtualCanIface> ifaces_[uavcan::MaxCanIfaces];
414  const unsigned num_ifaces_;
415  uavcan::ISystemClock &clock_;
416 
417  uavcan::ICanIface *getIface(uint8_t iface_index) override
418  {
419  return (iface_index < num_ifaces_) ? ifaces_[iface_index].operator VirtualCanIface * () : nullptr;
420  }
421 
422  uint8_t getNumIfaces() const override { return num_ifaces_; }
423 
424  /**
425  * This and other methods of ICanDriver will be invoked by the sub-node thread.
426  */
427  int16_t select(uavcan::CanSelectMasks &inout_masks,
428  const uavcan::CanFrame * (&)[uavcan::MaxCanIfaces],
429  uavcan::MonotonicTime blocking_deadline) override
430  {
431  bool need_block = (inout_masks.write == 0); // Write queue is infinite
432 
433  for (unsigned i = 0; need_block && (i < num_ifaces_); i++) {
434  const bool need_read = inout_masks.read & (1U << i);
435 
436  if (need_read && ifaces_[i]->hasDataInRxQueue()) {
437  need_block = false;
438  }
439  }
440 
441  if (need_block) {
442  event_.waitFor(blocking_deadline - clock_.getMonotonic());
443  }
444 
445  inout_masks = uavcan::CanSelectMasks();
446 
447  for (unsigned i = 0; i < num_ifaces_; i++) {
448  const std::uint8_t iface_mask = 1U << i;
449  inout_masks.write |= iface_mask; // Always ready to write
450 
451  if (ifaces_[i]->hasDataInRxQueue()) {
452  inout_masks.read |= iface_mask;
453  }
454  }
455 
456  return num_ifaces_; // We're always ready to write, hence > 0.
457  }
458 
459  /**
460  * This handler will be invoked by the main node thread.
461  */
462  void handleRxFrame(const uavcan::CanRxFrame &frame, uavcan::CanIOFlags flags) override
463  {
464  UAVCAN_TRACE("VirtualCanDriver", "RX [flags=%u]: %s", unsigned(flags), frame.toString().c_str());
465 
466  if (frame.iface_index < num_ifaces_) {
467  ifaces_[frame.iface_index]->addRxFrame(frame, flags);
468  event_.signal();
469 
470  }
471  }
472 
473  /**
474  * This method will be invoked by the main node thread.
475  */
476  void injectTxFramesInto(uavcan::INode &main_node) override
477  {
478  for (unsigned i = 0; i < num_ifaces_; i++) {
479  ifaces_[i]->flushTxQueueTo(main_node, i);
480  }
481 
482  event_.signal();
483  }
484 
485 public:
486  VirtualCanDriver(unsigned arg_num_ifaces,
487  uavcan::ISystemClock &system_clock,
488  uavcan::IPoolAllocator &allocator,
489  unsigned virtual_iface_block_allocation_quota) :
490  num_ifaces_(arg_num_ifaces),
491  clock_(system_clock)
492  {
493  Lock::init(driver_mutex_);
494  event_.init();
495 
496  assert(num_ifaces_ > 0 && num_ifaces_ <= uavcan::MaxCanIfaces);
497 
498  const unsigned quota_per_queue = virtual_iface_block_allocation_quota; // 2x overcommit
499 
500  for (unsigned i = 0; i < num_ifaces_; i++) {
501  ifaces_[i].construct<uavcan::IPoolAllocator &, uavcan::ISystemClock &, pthread_mutex_t &,
502  unsigned>(allocator, clock_, driver_mutex_, quota_per_queue);
503  }
504  }
505 
507  {
508  Lock::deinit(driver_mutex_);
509  event_.deinit();
510  }
511 
512 };
Objects of this class are owned by the sub-node thread.
void injectTxFramesInto(uavcan::INode &main_node) override
This method will be invoked by the main node thread.
int16_t send(const uavcan::CanFrame &frame, uavcan::MonotonicTime tx_deadline, uavcan::CanIOFlags flags) override
T * peek()
Accesses the first element.
void flushTxQueueTo(uavcan::INode &main_node, std::uint8_t iface_index)
Call this from the main thread only.
bool isEmpty() const
uint64_t getErrorCount() const override
RxItem(const uavcan::CanRxFrame &arg_frame, uavcan::CanIOFlags arg_flags)
pthread_mutex_t & thier_mutex_
uavcan::CanTxQueue prioritized_tx_queue_
static int deinit(pthread_mutex_t &thier_mutex_)
uavcan::ICanIface * getIface(uint8_t iface_index) override
uint16_t getNumFilters() const override
const uavcan::MonotonicTime ts_mono
void waitFor(uavcan::MonotonicDuration duration)
uavcan::ISystemClock & clock_
int16_t receive(uavcan::CanFrame &out_frame, uavcan::MonotonicTime &out_ts_monotonic, uavcan::UtcTime &out_ts_utc, uavcan::CanIOFlags &out_flags) override
VirtualCanIface(uavcan::IPoolAllocator &allocator, uavcan::ISystemClock &clock, pthread_mutex_t &arg_mutex, unsigned quota_per_queue)
const uavcan::CanIOFlags flags
Event event_
Used to unblock the select() call when IO happens.
int16_t configureFilters(const uavcan::CanFilterConfig *, std::uint16_t) override
pthread_mutex_t & common_driver_mutex_
static int init(pthread_mutex_t &thier_mutex_)
void addRxFrame(const uavcan::CanRxFrame &frame, uavcan::CanIOFlags flags)
Note that RX queue overwrites oldest items when overflowed.
bool hasDataInRxQueue()
Call this from the sub-node thread only.
bool tryEmplace(Args... args)
Creates one item in-place at the end of the list.
uavcan::LimitedPoolAllocator allocator_
void pop()
Removes the first element.
const T * peek() const
pthread_mutex_t driver_mutex_
Shared across all ifaces.
Lock(pthread_mutex_t &m)
int16_t select(uavcan::CanSelectMasks &inout_masks, const uavcan::CanFrame *(&)[uavcan::MaxCanIfaces], uavcan::MonotonicTime blocking_deadline) override
This and other methods of ICanDriver will be invoked by the sub-node thread.
Queue(uavcan::IPoolAllocator &arg_allocator, std::size_t block_allocation_quota)
uavcan::LinkedListRoot< Item > list_
uint8_t getNumIfaces() const override
Generic queue based on the linked list class defined in libuavcan.
void handleRxFrame(const uavcan::CanRxFrame &frame, uavcan::CanIOFlags flags) override
This handler will be invoked by the main node thread.
VirtualCanDriver(unsigned arg_num_ifaces, uavcan::ISystemClock &system_clock, uavcan::IPoolAllocator &allocator, unsigned virtual_iface_block_allocation_quota)
This class re-defines uavcan::RxCanFrame with flags.
Objects of this class are owned by the sub-node thread.
This interface defines one method that will be called by the main node thread periodically in order t...