PX4 Firmware
PX4 Autopilot Software http://px4.io
uORBDeviceNode.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2016 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 #include "uORBDeviceNode.hpp"
35 
36 #include "uORBUtils.hpp"
37 #include "uORBManager.hpp"
38 
39 #include "SubscriptionCallback.hpp"
40 
41 #ifdef ORB_COMMUNICATOR
42 #include "uORBCommunicator.hpp"
43 #endif /* ORB_COMMUNICATOR */
44 
46 {
47 #ifndef __PX4_NUTTX
48 
49  if (!filp) {
50  return nullptr;
51  }
52 
53 #endif
54  return (SubscriberData *)(filp->f_priv);
55 }
56 
57 uORB::DeviceNode::DeviceNode(const struct orb_metadata *meta, const uint8_t instance, const char *path,
58  uint8_t priority, uint8_t queue_size) :
59  CDev(path),
60  _meta(meta),
61  _instance(instance),
62  _priority(priority),
63  _queue_size(queue_size)
64 {
65 }
66 
68 {
69  delete[] _data;
70 
71  CDev::unregister_driver_and_memory();
72 }
73 
74 int
76 {
77  /* is this a publisher? */
78  if (filp->f_oflags == PX4_F_WRONLY) {
79 
80  lock();
82  unlock();
83 
84  /* now complete the open */
85  return CDev::open(filp);
86  }
87 
88  /* is this a new subscriber? */
89  if (filp->f_oflags == PX4_F_RDONLY) {
90 
91  /* allocate subscriber data */
92  SubscriberData *sd = new SubscriberData{};
93 
94  if (nullptr == sd) {
95  return -ENOMEM;
96  }
97 
98  /* If there were any previous publications, allow the subscriber to read them */
99  const unsigned gen = published_message_count();
100  sd->generation = gen - (_queue_size < gen ? _queue_size : gen);
101 
102  filp->f_priv = (void *)sd;
103 
104  int ret = CDev::open(filp);
105 
107 
108  if (ret != PX4_OK) {
109  PX4_ERR("CDev::open failed");
110  delete sd;
111  }
112 
113  return ret;
114  }
115 
116  if (filp->f_oflags == 0) {
117  return CDev::open(filp);
118  }
119 
120  /* can only be pub or sub, not both */
121  return -EINVAL;
122 }
123 
124 int
126 {
127  if (filp->f_oflags == PX4_F_RDONLY) { /* subscriber */
128  SubscriberData *sd = filp_to_sd(filp);
129 
130  if (sd != nullptr) {
132 
133  delete sd;
134  sd = nullptr;
135  }
136  }
137 
138  return CDev::close(filp);
139 }
140 
141 bool
142 uORB::DeviceNode::copy_locked(void *dst, unsigned &generation)
143 {
144  bool updated = false;
145 
146  if ((dst != nullptr) && (_data != nullptr)) {
147  unsigned current_generation = _generation.load();
148 
149  if (current_generation > generation + _queue_size) {
150  // Reader is too far behind: some messages are lost
151  _lost_messages += current_generation - (generation + _queue_size);
152  generation = current_generation - _queue_size;
153  }
154 
155  if ((current_generation == generation) && (generation > 0)) {
156  /* The subscriber already read the latest message, but nothing new was published yet.
157  * Return the previous message
158  */
159  --generation;
160  }
161 
162  memcpy(dst, _data + (_meta->o_size * (generation % _queue_size)), _meta->o_size);
163 
164  if (generation < current_generation) {
165  ++generation;
166  }
167 
168  updated = true;
169  }
170 
171  return updated;
172 }
173 
174 bool
175 uORB::DeviceNode::copy(void *dst, unsigned &generation)
176 {
177  ATOMIC_ENTER;
178 
179  bool updated = copy_locked(dst, generation);
180 
181  ATOMIC_LEAVE;
182 
183  return updated;
184 }
185 
186 uint64_t
187 uORB::DeviceNode::copy_and_get_timestamp(void *dst, unsigned &generation)
188 {
189  ATOMIC_ENTER;
190 
191  const hrt_abstime update_time = _last_update;
192  copy_locked(dst, generation);
193 
194  ATOMIC_LEAVE;
195 
196  return update_time;
197 }
198 
199 ssize_t
200 uORB::DeviceNode::read(cdev::file_t *filp, char *buffer, size_t buflen)
201 {
202  /* if the object has not been written yet, return zero */
203  if (_data == nullptr) {
204  return 0;
205  }
206 
207  /* if the caller's buffer is the wrong size, that's an error */
208  if (buflen != _meta->o_size) {
209  return -EIO;
210  }
211 
213 
214  /*
215  * Perform an atomic copy & state update
216  */
217  ATOMIC_ENTER;
218 
219  copy_locked(buffer, sd->generation);
220 
221  // if subscriber has an interval track the last update time
222  if (sd->update_interval) {
224  }
225 
226  ATOMIC_LEAVE;
227 
228  return _meta->o_size;
229 }
230 
231 ssize_t
232 uORB::DeviceNode::write(cdev::file_t *filp, const char *buffer, size_t buflen)
233 {
234  /*
235  * Writes are legal from interrupt context as long as the
236  * object has already been initialised from thread context.
237  *
238  * Writes outside interrupt context will allocate the object
239  * if it has not yet been allocated.
240  *
241  * Note that filp will usually be NULL.
242  */
243  if (nullptr == _data) {
244 
245 #ifdef __PX4_NUTTX
246 
247  if (!up_interrupt_context()) {
248 #endif /* __PX4_NUTTX */
249 
250  lock();
251 
252  /* re-check size */
253  if (nullptr == _data) {
254  _data = new uint8_t[_meta->o_size * _queue_size];
255  }
256 
257  unlock();
258 
259 #ifdef __PX4_NUTTX
260  }
261 
262 #endif /* __PX4_NUTTX */
263 
264  /* failed or could not allocate */
265  if (nullptr == _data) {
266  return -ENOMEM;
267  }
268  }
269 
270  /* If write size does not match, that is an error */
271  if (_meta->o_size != buflen) {
272  return -EIO;
273  }
274 
275  /* Perform an atomic copy. */
276  ATOMIC_ENTER;
277  /* wrap-around happens after ~49 days, assuming a publisher rate of 1 kHz */
278  unsigned generation = _generation.fetch_add(1);
279 
280  memcpy(_data + (_meta->o_size * (generation % _queue_size)), buffer, _meta->o_size);
281 
282  /* update the timestamp and generation count */
284 
285 
286  // callbacks
287  for (auto item : _callbacks) {
288  item->call();
289  }
290 
291  ATOMIC_LEAVE;
292 
293  /* notify any poll waiters */
294  poll_notify(POLLIN);
295 
296  return _meta->o_size;
297 }
298 
299 int
300 uORB::DeviceNode::ioctl(cdev::file_t *filp, int cmd, unsigned long arg)
301 {
302  SubscriberData *sd = filp_to_sd(filp);
303 
304  switch (cmd) {
305  case ORBIOCLASTUPDATE: {
306  ATOMIC_ENTER;
307  *(hrt_abstime *)arg = _last_update;
308  ATOMIC_LEAVE;
309  return PX4_OK;
310  }
311 
312  case ORBIOCUPDATED: {
313  ATOMIC_ENTER;
314  *(bool *)arg = appears_updated(sd);
315  ATOMIC_LEAVE;
316  return PX4_OK;
317  }
318 
319  case ORBIOCSETINTERVAL: {
320  int ret = PX4_OK;
321  lock();
322 
323  if (arg == 0) {
324  if (sd->update_interval) {
325  delete (sd->update_interval);
326  sd->update_interval = nullptr;
327  }
328 
329  } else {
330  if (sd->update_interval) {
331  sd->update_interval->interval = arg;
332 
333  } else {
335 
336  if (sd->update_interval) {
337  sd->update_interval->interval = arg;
338 
339  } else {
340  ret = -ENOMEM;
341  }
342  }
343  }
344 
345  unlock();
346  return ret;
347  }
348 
349  case ORBIOCGADVERTISER:
350  *(uintptr_t *)arg = (uintptr_t)this;
351  return PX4_OK;
352 
353  case ORBIOCGPRIORITY:
354  *(int *)arg = get_priority();
355  return PX4_OK;
356 
357  case ORBIOCSETQUEUESIZE: {
358  lock();
359  int ret = update_queue_size(arg);
360  unlock();
361  return ret;
362  }
363 
364  case ORBIOCGETINTERVAL:
365  if (sd->update_interval) {
366  *(unsigned *)arg = sd->update_interval->interval;
367 
368  } else {
369  *(unsigned *)arg = 0;
370  }
371 
372  return OK;
373 
374  case ORBIOCISADVERTISED:
375  *(unsigned long *)arg = _advertised;
376 
377  return OK;
378 
379  default:
380  /* give it to the superclass */
381  return CDev::ioctl(filp, cmd, arg);
382  }
383 }
384 
385 ssize_t
386 uORB::DeviceNode::publish(const orb_metadata *meta, orb_advert_t handle, const void *data)
387 {
388  uORB::DeviceNode *devnode = (uORB::DeviceNode *)handle;
389  int ret;
390 
391  /* check if the device handle is initialized and data is valid */
392  if ((devnode == nullptr) || (meta == nullptr) || (data == nullptr)) {
393  errno = EFAULT;
394  return PX4_ERROR;
395  }
396 
397  /* check if the orb meta data matches the publication */
398  if (devnode->_meta != meta) {
399  errno = EINVAL;
400  return PX4_ERROR;
401  }
402 
403  /* call the devnode write method with no file pointer */
404  ret = devnode->write(nullptr, (const char *)data, meta->o_size);
405 
406  if (ret < 0) {
407  errno = -ret;
408  return PX4_ERROR;
409  }
410 
411  if (ret != (int)meta->o_size) {
412  errno = EIO;
413  return PX4_ERROR;
414  }
415 
416 #ifdef ORB_COMMUNICATOR
417  /*
418  * if the write is successful, send the data over the Multi-ORB link
419  */
420  uORBCommunicator::IChannel *ch = uORB::Manager::get_instance()->get_uorb_communicator();
421 
422  if (ch != nullptr) {
423  if (ch->send_message(meta->o_name, meta->o_size, (uint8_t *)data) != 0) {
424  PX4_ERR("Error Sending [%s] topic data over comm_channel", meta->o_name);
425  return PX4_ERROR;
426  }
427  }
428 
429 #endif /* ORB_COMMUNICATOR */
430 
431  return PX4_OK;
432 }
433 
435 {
436  if (handle == nullptr) {
437  return -EINVAL;
438  }
439 
440  uORB::DeviceNode *devnode = (uORB::DeviceNode *)handle;
441 
442  /*
443  * We are cheating a bit here. First, with the current implementation, we can only
444  * have multiple publishers for instance 0. In this case the caller will have
445  * instance=nullptr and _published has no effect at all. Thus no unadvertise is
446  * necessary.
447  * In case of multiple instances, we have at most 1 publisher per instance and
448  * we can signal an instance as 'free' by setting _published to false.
449  * We never really free the DeviceNode, for this we would need reference counting
450  * of subscribers and publishers. But we also do not have a leak since future
451  * publishers reuse the same DeviceNode object.
452  */
453  devnode->_advertised = false;
454 
455  return PX4_OK;
456 }
457 
458 #ifdef ORB_COMMUNICATOR
459 int16_t uORB::DeviceNode::topic_advertised(const orb_metadata *meta, int priority)
460 {
461  uORBCommunicator::IChannel *ch = uORB::Manager::get_instance()->get_uorb_communicator();
462 
463  if (ch != nullptr && meta != nullptr) {
464  return ch->topic_advertised(meta->o_name);
465  }
466 
467  return -1;
468 }
469 
470 /*
471 //TODO: Check if we need this since we only unadvertise when things all shutdown and it doesn't actually remove the device
472 int16_t uORB::DeviceNode::topic_unadvertised(const orb_metadata *meta, int priority)
473 {
474  uORBCommunicator::IChannel *ch = uORB::Manager::get_instance()->get_uorb_communicator();
475  if (ch != nullptr && meta != nullptr) {
476  return ch->topic_unadvertised(meta->o_name);
477  }
478  return -1;
479 }
480 */
481 #endif /* ORB_COMMUNICATOR */
482 
483 pollevent_t
485 {
486  SubscriberData *sd = filp_to_sd(filp);
487 
488  /*
489  * If the topic appears updated to the subscriber, say so.
490  */
491  if (appears_updated(sd)) {
492  return POLLIN;
493  }
494 
495  return 0;
496 }
497 
498 void
499 uORB::DeviceNode::poll_notify_one(px4_pollfd_struct_t *fds, pollevent_t events)
500 {
501  SubscriberData *sd = filp_to_sd((cdev::file_t *)fds->priv);
502 
503  /*
504  * If the topic looks updated to the subscriber, go ahead and notify them.
505  */
506  if (appears_updated(sd)) {
507  CDev::poll_notify_one(fds, events);
508  }
509 }
510 
511 bool
513 {
514  // check if this topic has been published yet, if not bail out
515  if (_data == nullptr) {
516  return false;
517  }
518 
519  // if subscriber has interval check time since last update
520  if (sd->update_interval != nullptr) {
522  return false;
523  }
524  }
525 
526  // finally, compare the generation
527  return (sd->generation != published_message_count());
528 }
529 
530 bool
532 {
533  if (!_lost_messages) {
534  return false;
535  }
536 
537  lock();
538  //This can be wrong: if a reader never reads, _lost_messages will not be increased either
539  uint32_t lost_messages = _lost_messages;
540 
541  if (reset) {
542  _lost_messages = 0;
543  }
544 
545  unlock();
546 
547  PX4_INFO("%s: %i", _meta->o_name, lost_messages);
548  return true;
549 }
550 
552 {
553  lock();
555 
556 #ifdef ORB_COMMUNICATOR
557  uORBCommunicator::IChannel *ch = uORB::Manager::get_instance()->get_uorb_communicator();
558 
559  if (ch != nullptr && _subscriber_count > 0) {
560  unlock(); //make sure we cannot deadlock if add_subscription calls back into DeviceNode
561  ch->add_subscription(_meta->o_name, 1);
562 
563  } else
564 #endif /* ORB_COMMUNICATOR */
565 
566  {
567  unlock();
568  }
569 }
570 
572 {
573  lock();
575 
576 #ifdef ORB_COMMUNICATOR
577  uORBCommunicator::IChannel *ch = uORB::Manager::get_instance()->get_uorb_communicator();
578 
579  if (ch != nullptr && _subscriber_count == 0) {
580  unlock(); //make sure we cannot deadlock if remove_subscription calls back into DeviceNode
582 
583  } else
584 #endif /* ORB_COMMUNICATOR */
585  {
586  unlock();
587  }
588 }
589 
590 #ifdef ORB_COMMUNICATOR
591 int16_t uORB::DeviceNode::process_add_subscription(int32_t rateInHz)
592 {
593  // if there is already data in the node, send this out to
594  // the remote entity.
595  // send the data to the remote entity.
596  uORBCommunicator::IChannel *ch = uORB::Manager::get_instance()->get_uorb_communicator();
597 
598  if (_data != nullptr && ch != nullptr) { // _data will not be null if there is a publisher.
600  }
601 
602  return PX4_OK;
603 }
604 
605 int16_t uORB::DeviceNode::process_remove_subscription()
606 {
607  return PX4_OK;
608 }
609 
610 int16_t uORB::DeviceNode::process_received_message(int32_t length, uint8_t *data)
611 {
612  int16_t ret = -1;
613 
614  if (length != (int32_t)(_meta->o_size)) {
615  PX4_ERR("Received '%s' with DataLength[%d] != ExpectedLen[%d]", _meta->o_name, (int)length, (int)_meta->o_size);
616  return PX4_ERROR;
617  }
618 
619  /* call the devnode write method with no file pointer */
620  ret = write(nullptr, (const char *)data, _meta->o_size);
621 
622  if (ret < 0) {
623  return PX4_ERROR;
624  }
625 
626  if (ret != (int)_meta->o_size) {
627  errno = EIO;
628  return PX4_ERROR;
629  }
630 
631  return PX4_OK;
632 }
633 #endif /* ORB_COMMUNICATOR */
634 
635 int uORB::DeviceNode::update_queue_size(unsigned int queue_size)
636 {
637  if (_queue_size == queue_size) {
638  return PX4_OK;
639  }
640 
641  //queue size is limited to 255 for the single reason that we use uint8 to store it
642  if (_data || _queue_size > queue_size || queue_size > 255) {
643  return PX4_ERROR;
644  }
645 
646  _queue_size = queue_size;
647  return PX4_OK;
648 }
649 
650 bool
652 {
653  if (callback_sub != nullptr) {
654  ATOMIC_ENTER;
655 
656  // prevent duplicate registrations
657  for (auto existing_callbacks : _callbacks) {
658  if (callback_sub == existing_callbacks) {
659  ATOMIC_LEAVE;
660  return true;
661  }
662  }
663 
664  _callbacks.add(callback_sub);
665  ATOMIC_LEAVE;
666  return true;
667  }
668 
669  return false;
670 }
671 
672 void
674 {
675  ATOMIC_ENTER;
676  _callbacks.remove(callback_sub);
677  ATOMIC_LEAVE;
678 }
static ssize_t publish(const orb_metadata *meta, orb_advert_t handle, const void *data)
Method to publish a data to this node.
bool copy_locked(void *dst, unsigned &generation)
Copies data and the corresponding generation from a node to the buffer provided.
static uORB::Manager * get_instance()
Method to get the singleton instance for the uORB::Manager.
Definition: uORBManager.hpp:89
virtual int16_t topic_advertised(const char *messageName)=0
Interface to notify the remote entity of a topic being advertised.
px4::atomic< unsigned > _generation
object generation count
virtual int16_t send_message(const char *messageName, int32_t length, uint8_t *data)=0
Sends the data message over the communication link.
void unregister_callback(SubscriptionCallback *callback_sub)
pollevent_t poll_state(cdev::file_t *filp) override
Check the current state of the device for poll events from the perspective of the file...
uint64_t last_update
time at which the last update was provided, used when update_interval is nonzero
virtual int16_t remove_subscription(const char *messageName)=0
Interface to notify the remote entity of removal of a subscription.
const orb_metadata * _meta
object metadata information
int update_queue_size(unsigned int queue_size)
Try to change the size of the queue.
void poll_notify_one(px4_pollfd_struct_t *fds, pollevent_t events) override
Internal implementation of poll_notify.
void lock()
Take the driver lock.
Definition: CDev.hpp:264
#define ORBIOCGETINTERVAL
Get the minimum interval at which the topic can be seen to be updated for this subscription.
Definition: drv_orb_dev.h:74
int reset(enum LPS22HB_BUS busid)
Reset the driver.
LidarLite * instance
Definition: ll40ls.cpp:65
int open(cdev::file_t *filp) override
Method to create a subscriber instance and return the struct pointing to the subscriber as a file poi...
#define ORBIOCGPRIORITY
Get the priority for the topic.
Definition: drv_orb_dev.h:68
const char * o_name
unique object name
Definition: uORB.h:51
uint8_t * _data
allocated object buffer
virtual void poll_notify(pollevent_t events)
Report new poll events.
Definition: CDev.cpp:330
#define ORBIOCSETINTERVAL
Set the minimum interval at which the topic can be seen to be updated for this subscription.
Definition: drv_orb_dev.h:62
ssize_t write(cdev::file_t *filp, const char *buffer, size_t buflen) override
writes the published data to the internal buffer to be read by subscribers later. ...
uint8_t _priority
priority of the topic
virtual int16_t add_subscription(const char *messageName, int32_t msgRateInHz)=0
Interface to notify the remote entity of a topic being unadvertised and is no longer publishing messa...
unsigned published_message_count() const
uint32_t _lost_messages
nr of lost messages for all subscribers.
Per-object device instance.
uint8_t * data
Definition: dataman.cpp:149
uint64_t copy_and_get_timestamp(void *dst, unsigned &generation)
Copies data and the corresponding generation from a node to the buffer provided.
int get_priority() const
static hrt_abstime hrt_elapsed_time(const hrt_abstime *then)
Compute the delta between a timestamp taken in the past and now.
Definition: drv_hrt.h:102
void remove_internal_subscriber()
Removes the subscriber from the list.
hrt_abstime _last_update
time the object was last updated
bool appears_updated(SubscriberData *sd)
Check whether a topic appears updated to a subscriber.
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
__BEGIN_DECLS typedef void * orb_advert_t
ORB topic advertiser handle.
Definition: uORB.h:134
#define ORBIOCISADVERTISED
Check whether the topic is advertised, sets *(unsigned long *)arg to 1 if advertised, 0 otherwise.
Definition: drv_orb_dev.h:77
const uint8_t _instance
orb multi instance identifier
#define ATOMIC_LEAVE
UpdateIntervalData * update_interval
if null, no update interval
int close(cdev::file_t *filp) override
Method to close a subscriber for this topic.
static SubscriberData * filp_to_sd(cdev::file_t *filp)
Object metadata.
Definition: uORB.h:50
ssize_t read(cdev::file_t *filp, char *buffer, size_t buflen) override
reads data from a subscriber node to the buffer provided.
#define ORBIOCUPDATED
Check whether the topic has been updated since it was last read, sets *(bool *)arg.
Definition: drv_orb_dev.h:59
#define ORBIOCLASTUPDATE
Fetch the time at which the topic was last updated into *(uint64_t *)arg.
Definition: drv_orb_dev.h:56
void add_internal_subscriber()
Add the subscriber to the node&#39;s list of subscriber.
unsigned interval
if nonzero minimum interval between updates
bool copy(void *dst, unsigned &generation)
Copies data and the corresponding generation from a node to the buffer provided.
bool register_callback(SubscriptionCallback *callback_sub)
CDev(const char *devname)
Constructor.
Definition: CDev.cpp:50
DeviceNode(const struct orb_metadata *meta, const uint8_t instance, const char *path, uint8_t priority, uint8_t queue_size=1)
Interface to enable remote subscriptions.
List< uORB::SubscriptionCallback * > _callbacks
const uint16_t o_size
object size
Definition: uORB.h:52
#define OK
Definition: uavcan_main.cpp:71
#define ORBIOCSETQUEUESIZE
Set the queue size of the topic.
Definition: drv_orb_dev.h:71
static int unadvertise(orb_advert_t handle)
#define ORBIOCGADVERTISER
Get the global advertiser handle for the topic.
Definition: drv_orb_dev.h:65
int ioctl(cdev::file_t *filp, int cmd, unsigned long arg) override
IOCTL control for the subscriber.
bool print_statistics(bool reset)
Print statistics (nr of lost messages)
void unlock()
Release the driver lock.
Definition: CDev.hpp:269
uint8_t _queue_size
maximum number of elements in the queue
#define ATOMIC_ENTER
unsigned generation
last generation the subscriber has seen
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
bool _advertised
has ever been advertised (not necessarily published data yet)