Jukka Laitinen 36d440f895 Add IOCTL interface to uORBManager for nuttx protected/kernel build split
When building uORB for NuttX flat build, or for some other target, everything
works as before.

When building uORB for NuttX protected or kernel build, this does the following:
- The kernel side uORB library reigsters a boardctl handler for calls from userspace
  and services the boardctl_ioctls by calling the actual uORB functions
- For user mode binaries, the uORBManager acts as a proxy, making boardctl_ioctl calls to the
  kernel side

Signed-off-by: Jukka Laitinen <jukkax@ssrc.tii.ae>
2022-02-14 09:10:49 +01:00

495 lines
14 KiB
C++

/****************************************************************************
*
* Copyright (c) 2012-2015 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file uORB.cpp
* A lightweight object broker.
*/
#include "uORB.h"
#include "uORBManager.hpp"
#include "uORBCommon.hpp"
#include <lib/drivers/device/Device.hpp>
#include <matrix/Quaternion.hpp>
#include <mathlib/mathlib.h>
#ifdef __PX4_NUTTX
#include <sys/boardctl.h>
#endif
static uORB::DeviceMaster *g_dev = nullptr;
int uorb_start(void)
{
if (g_dev != nullptr) {
PX4_WARN("already loaded");
/* user wanted to start uorb, its already running, no error */
return 0;
}
if (!uORB::Manager::initialize()) {
PX4_ERR("uorb manager alloc failed");
return -ENOMEM;
}
#if !defined(__PX4_NUTTX) || defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
/* create the driver */
g_dev = uORB::Manager::get_instance()->get_device_master();
if (g_dev == nullptr) {
return -errno;
}
#endif
return OK;
}
int uorb_status(void)
{
#if !defined(__PX4_NUTTX) || defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
if (g_dev != nullptr) {
g_dev->printStatistics();
} else {
PX4_INFO("uorb is not running");
}
#else
boardctl(ORBIOCDEVMASTERCMD, ORB_DEVMASTER_STATUS);
#endif
return OK;
}
int uorb_top(char **topic_filter, int num_filters)
{
#if !defined(__PX4_NUTTX) || defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
if (g_dev != nullptr) {
g_dev->showTop(topic_filter, num_filters);
} else {
PX4_INFO("uorb is not running");
}
#else
boardctl(ORBIOCDEVMASTERCMD, ORB_DEVMASTER_TOP);
#endif
return OK;
}
orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
{
return uORB::Manager::get_instance()->orb_advertise(meta, data);
}
orb_advert_t orb_advertise_queue(const struct orb_metadata *meta, const void *data, unsigned int queue_size)
{
return uORB::Manager::get_instance()->orb_advertise(meta, data, queue_size);
}
orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance)
{
return uORB::Manager::get_instance()->orb_advertise_multi(meta, data, instance);
}
orb_advert_t orb_advertise_multi_queue(const struct orb_metadata *meta, const void *data, int *instance,
unsigned int queue_size)
{
return uORB::Manager::get_instance()->orb_advertise_multi(meta, data, instance, queue_size);
}
int orb_unadvertise(orb_advert_t handle)
{
return uORB::Manager::get_instance()->orb_unadvertise(handle);
}
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
{
return uORB::Manager::get_instance()->orb_publish(meta, handle, data);
}
int orb_subscribe(const struct orb_metadata *meta)
{
return uORB::Manager::get_instance()->orb_subscribe(meta);
}
int orb_subscribe_multi(const struct orb_metadata *meta, unsigned instance)
{
return uORB::Manager::get_instance()->orb_subscribe_multi(meta, instance);
}
int orb_unsubscribe(int handle)
{
return uORB::Manager::get_instance()->orb_unsubscribe(handle);
}
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
{
return uORB::Manager::get_instance()->orb_copy(meta, handle, buffer);
}
int orb_check(int handle, bool *updated)
{
return uORB::Manager::get_instance()->orb_check(handle, updated);
}
int orb_exists(const struct orb_metadata *meta, int instance)
{
return uORB::Manager::get_instance()->orb_exists(meta, instance);
}
int orb_group_count(const struct orb_metadata *meta)
{
unsigned instance = 0;
while (uORB::Manager::get_instance()->orb_exists(meta, instance) == OK) {
++instance;
};
return instance;
}
int orb_set_interval(int handle, unsigned interval)
{
return uORB::Manager::get_instance()->orb_set_interval(handle, interval);
}
int orb_get_interval(int handle, unsigned *interval)
{
return uORB::Manager::get_instance()->orb_get_interval(handle, interval);
}
const char *orb_get_c_type(unsigned char short_type)
{
// this matches with the uorb o_fields generator
switch (short_type) {
case 0x82: return "int8_t";
case 0x83: return "int16_t";
case 0x84: return "int32_t";
case 0x85: return "int64_t";
case 0x86: return "uint8_t";
case 0x87: return "uint16_t";
case 0x88: return "uint32_t";
case 0x89: return "uint64_t";
case 0x8a: return "float";
case 0x8b: return "double";
case 0x8c: return "bool";
case 0x8d: return "char";
}
return nullptr;
}
void orb_print_message_internal(const orb_metadata *meta, const void *data, bool print_topic_name)
{
if (print_topic_name) {
PX4_INFO_RAW(" %s\n", meta->o_name);
}
const hrt_abstime now = hrt_absolute_time();
hrt_abstime topic_timestamp = 0;
const uint8_t *data_ptr = (const uint8_t *)data;
int data_offset = 0;
for (int format_idx = 0; meta->o_fields[format_idx] != 0;) {
const char *end_field = strchr(meta->o_fields + format_idx, ';');
if (!end_field) {
PX4_ERR("Format error in %s", meta->o_fields);
return;
}
const char *c_type = orb_get_c_type(meta->o_fields[format_idx]);
const int end_field_idx = end_field - meta->o_fields;
int array_idx = -1;
int field_name_idx = -1;
for (int field_idx = format_idx; field_idx != end_field_idx; ++field_idx) {
if (meta->o_fields[field_idx] == '[') {
array_idx = field_idx + 1;
} else if (meta->o_fields[field_idx] == ' ') {
field_name_idx = field_idx + 1;
break;
}
}
int array_size = 1;
if (array_idx >= 0) {
array_size = strtol(meta->o_fields + array_idx, nullptr, 10);
}
char field_name[80];
size_t field_name_len = end_field_idx - field_name_idx;
if (field_name_len >= sizeof(field_name)) {
PX4_ERR("field name too long %s (max: %u)", meta->o_fields, (unsigned)sizeof(field_name));
return;
}
memcpy(field_name, meta->o_fields + field_name_idx, field_name_len);
field_name[field_name_len] = '\0';
if (c_type) { // built-in type
bool dont_print = false;
// handle special cases
if (strncmp(field_name, "_padding", 8) == 0) {
dont_print = true;
} else if (strcmp(c_type, "char") == 0 && array_size > 1) { // string
PX4_INFO_RAW(" %s: \"%.*s\"\n", field_name, array_size, (char *)(data_ptr + data_offset));
dont_print = true;
}
if (!dont_print) {
PX4_INFO_RAW(" %s: ", field_name);
}
if (!dont_print && array_size > 1) {
PX4_INFO_RAW("[");
}
const int previous_data_offset = data_offset;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align" // the caller ensures data is aligned
for (int i = 0; i < array_size; ++i) {
if (strcmp(c_type, "int8_t") == 0) {
if (!dont_print) { PX4_INFO_RAW("%" PRIi8, *(int8_t *)(data_ptr + data_offset)); }
data_offset += sizeof(int8_t);
} else if (strcmp(c_type, "int16_t") == 0) {
if (!dont_print) { PX4_INFO_RAW("%" PRIi16, *(int16_t *)(data_ptr + data_offset)); }
data_offset += sizeof(int16_t);
} else if (strcmp(c_type, "int32_t") == 0) {
if (!dont_print) { PX4_INFO_RAW("%" PRIi32, *(int32_t *)(data_ptr + data_offset)); }
data_offset += sizeof(int32_t);
} else if (strcmp(c_type, "int64_t") == 0) {
if (!dont_print) { PX4_INFO_RAW("%" PRIi64, *(int64_t *)(data_ptr + data_offset)); }
data_offset += sizeof(int64_t);
} else if (strcmp(c_type, "uint8_t") == 0) {
if (!dont_print) { PX4_INFO_RAW("%" PRIu8, *(uint8_t *)(data_ptr + data_offset)); }
data_offset += sizeof(uint8_t);
} else if (strcmp(c_type, "uint16_t") == 0) {
if (!dont_print) { PX4_INFO_RAW("%" PRIu16, *(uint16_t *)(data_ptr + data_offset)); }
data_offset += sizeof(uint16_t);
} else if (strcmp(c_type, "uint32_t") == 0) {
if (!dont_print) { PX4_INFO_RAW("%" PRIu32, *(uint32_t *)(data_ptr + data_offset)); }
data_offset += sizeof(uint32_t);
} else if (strcmp(c_type, "uint64_t") == 0) {
if (!dont_print) { PX4_INFO_RAW("%" PRIu64, *(uint64_t *)(data_ptr + data_offset)); }
data_offset += sizeof(uint64_t);
} else if (strcmp(c_type, "float") == 0) {
if (!dont_print) { PX4_INFO_RAW("%.4f", (double) * (float *)(data_ptr + data_offset)); }
data_offset += sizeof(float);
} else if (strcmp(c_type, "double") == 0) {
if (!dont_print) { PX4_INFO_RAW("%.4f", *(double *)(data_ptr + data_offset)); }
data_offset += sizeof(double);
} else if (strcmp(c_type, "bool") == 0) {
if (!dont_print) { PX4_INFO_RAW("%s", *(bool *)(data_ptr + data_offset) ? "True" : "False"); }
data_offset += sizeof(bool);
} else if (strcmp(c_type, "char") == 0) {
if (!dont_print) { PX4_INFO_RAW("%i", (int) * (char *)(data_ptr + data_offset)); }
data_offset += sizeof(char);
} else {
PX4_ERR("unknown type: %s", c_type);
return;
}
if (!dont_print && i < array_size - 1) {
PX4_INFO_RAW(", ");
}
}
if (!dont_print && array_size > 1) {
PX4_INFO_RAW("]");
}
// handle special cases
if (array_size == 1) {
if (strcmp(c_type, "uint64_t") == 0 && strcmp(field_name, "timestamp") == 0) {
topic_timestamp = *(uint64_t *)(data_ptr + previous_data_offset);
if (topic_timestamp != 0) {
PX4_INFO_RAW(" (%.6f seconds ago)", (double)((now - topic_timestamp) / 1e6f));
}
} else if (strcmp(c_type, "uint64_t") == 0 && strcmp(field_name, "timestamp_sample") == 0) {
hrt_abstime timestamp = *(uint64_t *)(data_ptr + previous_data_offset);
if (topic_timestamp != 0 && timestamp != 0) {
PX4_INFO_RAW(" (%i us before timestamp)", (int)(topic_timestamp - timestamp));
}
} else if (strstr(field_name, "flags") != nullptr) {
// bitfield
unsigned field_size = 0;
unsigned long value = 0;
if (strcmp(c_type, "uint8_t") == 0) {
field_size = sizeof(uint8_t);
value = *(uint8_t *)(data_ptr + previous_data_offset);
} else if (strcmp(c_type, "uint16_t") == 0) {
field_size = sizeof(uint16_t);
value = *(uint16_t *)(data_ptr + previous_data_offset);
} else if (strcmp(c_type, "uint32_t") == 0) {
field_size = sizeof(uint32_t);
value = *(uint32_t *)(data_ptr + previous_data_offset);
}
if (field_size > 0) {
PX4_INFO_RAW(" (0b");
for (int i = (field_size * 8) - 1; i >= 0; i--) {
PX4_INFO_RAW("%lu%s", (value >> i) & 1, ((unsigned)i < (field_size * 8) - 1 && i % 4 == 0 && i > 0) ? "'" : "");
}
PX4_INFO_RAW(")");
}
} else if (strcmp(c_type, "uint32_t") == 0 && strstr(field_name, "device_id") != nullptr) {
// Device ID
uint32_t device_id = *(uint32_t *)(data_ptr + previous_data_offset);
char device_id_buffer[80];
device::Device::device_id_print_buffer(device_id_buffer, sizeof(device_id_buffer), device_id);
PX4_INFO_RAW(" (%s)", device_id_buffer);
}
} else if (array_size == 4 && strcmp(c_type, "float") == 0 && (strcmp(field_name, "q") == 0
|| strncmp(field_name, "q_", 2) == 0)) {
// attitude
float *attitude = (float *)(data_ptr + previous_data_offset);
matrix::Eulerf euler{matrix::Quatf{attitude}};
PX4_INFO_RAW(" (Roll: %.1f deg, Pitch: %.1f deg, Yaw: %.1f deg)",
(double)math::degrees(euler(0)), (double)math::degrees(euler(1)), (double)math::degrees(euler(2)));
}
#pragma GCC diagnostic pop
PX4_INFO_RAW("\n");
} else {
// extract the topic name
char topic_name[80];
const size_t topic_name_len = array_size > 1 ? array_idx - format_idx - 1 : field_name_idx - format_idx - 1;
if (topic_name_len >= sizeof(topic_name)) {
PX4_ERR("topic name too long in %s (max: %u)", meta->o_name, (unsigned)sizeof(topic_name));
return;
}
memcpy(topic_name, meta->o_fields + format_idx, topic_name_len);
topic_name[topic_name_len] = '\0';
// find the metadata
const orb_metadata *const *topics = orb_get_topics();
const orb_metadata *found_topic = nullptr;
for (size_t i = 0; i < orb_topics_count(); i++) {
if (strcmp(topics[i]->o_name, topic_name) == 0) {
found_topic = topics[i];
break;
}
}
if (!found_topic) {
PX4_ERR("Topic %s did not match any known topics", topic_name);
return;
}
// print recursively
for (int i = 0; i < array_size; ++i) {
PX4_INFO_RAW(" %s", field_name);
if (array_size > 1) {
PX4_INFO_RAW("[%i]", i);
}
PX4_INFO_RAW(" (%s):\n", topic_name);
orb_print_message_internal(found_topic, data_ptr + data_offset, false);
data_offset += found_topic->o_size;
}
}
format_idx = end_field_idx + 1;
}
}