mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-07-24 15:27:41 +08:00
* fix(i2c_spi): prevent use-after-free when stopping driver instances module_stop() deleted each instance before unlinking it from the global i2c_spi_module_instances list. The instance *is* the intrusive list node, so removeInstance()/List::remove() then dereferenced the freed node to fix up the links. If another thread reused that heap block in the window, the list got corrupted, sporadically leaving instances linked (a later start reports "already running") or hard-faulting. This regressed in the 2020 array->linked-list refactor: the old array version only nulled an array slot after delete, which was safe. Unlink each instance before freeing it. Also only delete/unlink when the task actually exited: if request_stop_and_wait() times out the work queue may still reference the object, so leave it allocated and listed rather than freeing it. module_stop() now returns -1 when an instance failed to stop. Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com> * Update platforms/common/i2c_spi_buses.cpp * Update platforms/common/i2c_spi_buses.cpp --------- Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
854 lines
20 KiB
C++
854 lines
20 KiB
C++
/****************************************************************************
|
|
*
|
|
* Copyright (C) 2020-2021 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <board_config.h>
|
|
|
|
#ifndef MODULE_NAME
|
|
#define MODULE_NAME "SPI_I2C"
|
|
#endif
|
|
|
|
#include <lib/drivers/device/Device.hpp>
|
|
#include <px4_platform_common/i2c_spi_buses.h>
|
|
#include <px4_platform_common/px4_work_queue/WorkItemSingleShot.hpp>
|
|
#include <px4_platform_common/log.h>
|
|
#include <px4_platform_common/getopt.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
static List<I2CSPIInstance *> i2c_spi_module_instances; ///< list of currently running instances
|
|
static pthread_mutex_t i2c_spi_module_instances_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
I2CSPIDriverConfig::I2CSPIDriverConfig(const BusCLIArguments &cli, const BusInstanceIterator &iterator,
|
|
const px4::wq_config_t &wq_config_)
|
|
: module_name(iterator.moduleName()),
|
|
devid_driver_index(iterator.devidDriverIndex()),
|
|
bus_option(iterator.configuredBusOption()),
|
|
bus_type(iterator.busType()),
|
|
bus(iterator.bus()),
|
|
#if defined(CONFIG_I2C)
|
|
i2c_address(cli.i2c_address),
|
|
#endif // CONFIG_I2C
|
|
bus_frequency(cli.bus_frequency),
|
|
#if defined(CONFIG_SPI)
|
|
drdy_gpio(iterator.DRDYGPIO()),
|
|
spi_mode(cli.spi_mode),
|
|
spi_devid(iterator.devid()),
|
|
#endif // CONFIG_SPI
|
|
bus_device_index(iterator.busDeviceIndex()),
|
|
rotation(cli.rotation),
|
|
quiet_start(cli.quiet_start),
|
|
keep_running(cli.keep_running),
|
|
custom1(cli.custom1),
|
|
custom2(cli.custom2),
|
|
custom_data(cli.custom_data),
|
|
wq_config(wq_config_)
|
|
{
|
|
|
|
}
|
|
|
|
const char *BusCLIArguments::parseDefaultArguments(int argc, char *argv[])
|
|
{
|
|
if (getOpt(argc, argv, "") == EOF) {
|
|
return optArg();
|
|
}
|
|
|
|
// unexpected arguments
|
|
return nullptr;
|
|
}
|
|
|
|
int BusCLIArguments::getOpt(int argc, char *argv[], const char *options)
|
|
{
|
|
if (_options[0] == 0) { // need to initialize
|
|
if (!validateConfiguration()) {
|
|
return EOF;
|
|
}
|
|
|
|
char *p = (char *)&_options;
|
|
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (_i2c_support) {
|
|
*(p++) = 'X'; // external
|
|
*(p++) = 'I'; // internal
|
|
|
|
if (i2c_address != 0) {
|
|
*(p++) = 'a'; *(p++) = ':'; // I2C address
|
|
}
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (_spi_support) {
|
|
*(p++) = 'S'; // external
|
|
*(p++) = 's'; // internal
|
|
*(p++) = 'c'; *(p++) = ':'; // chip-select
|
|
*(p++) = 'm'; *(p++) = ':'; // spi mode
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
|
|
if (support_keep_running) {
|
|
*(p++) = 'k';
|
|
}
|
|
|
|
*(p++) = 'b'; *(p++) = ':'; // bus
|
|
*(p++) = 'f'; *(p++) = ':'; // frequency
|
|
*(p++) = 'q'; // quiet flag
|
|
|
|
// copy all options
|
|
const char *option = options;
|
|
|
|
while (p != _options + sizeof(_options) && *option) {
|
|
if (*option != ':') {
|
|
// check for duplicates
|
|
for (const char *c = _options; c != p; ++c) {
|
|
if (*c == *option) {
|
|
PX4_ERR("conflicting option: %c", *c);
|
|
_options[0] = 0;
|
|
return EOF;
|
|
}
|
|
}
|
|
}
|
|
|
|
*(p++) = *(option++);
|
|
}
|
|
|
|
if (p == _options + sizeof(_options)) {
|
|
PX4_ERR("too many options");
|
|
_options[0] = 0;
|
|
return EOF;
|
|
}
|
|
|
|
*p = '\0';
|
|
}
|
|
|
|
int ch;
|
|
|
|
while ((ch = px4_getopt(argc, argv, _options, &_optind, &_optarg)) != EOF) {
|
|
switch (ch) {
|
|
#if defined(CONFIG_I2C)
|
|
|
|
case 'X':
|
|
bus_option = I2CSPIBusOption::I2CExternal;
|
|
break;
|
|
|
|
case 'I':
|
|
bus_option = I2CSPIBusOption::I2CInternal;
|
|
break;
|
|
|
|
case 'a':
|
|
if (i2c_address == 0) {
|
|
return ch;
|
|
}
|
|
|
|
i2c_address = (int)strtol(_optarg, nullptr, 0);
|
|
break;
|
|
#endif // CONFIG_I2C
|
|
#if defined(CONFIG_SPI)
|
|
|
|
case 'S':
|
|
bus_option = I2CSPIBusOption::SPIExternal;
|
|
break;
|
|
|
|
case 's':
|
|
bus_option = I2CSPIBusOption::SPIInternal;
|
|
break;
|
|
|
|
case 'c':
|
|
chipselect = atoi(_optarg);
|
|
break;
|
|
#endif // CONFIG_SPI
|
|
|
|
case 'b':
|
|
requested_bus = atoi(_optarg);
|
|
break;
|
|
|
|
case 'f':
|
|
bus_frequency = 1000 * atoi(_optarg);
|
|
break;
|
|
#if defined(CONFIG_SPI)
|
|
|
|
case 'm':
|
|
spi_mode = (spi_mode_e)atoi(_optarg);
|
|
break;
|
|
#endif // CONFIG_SPI
|
|
|
|
case 'q':
|
|
quiet_start = true;
|
|
break;
|
|
|
|
case 'k':
|
|
if (!support_keep_running) {
|
|
return ch;
|
|
}
|
|
|
|
keep_running = true;
|
|
break;
|
|
|
|
default:
|
|
if (ch == '?') {
|
|
// abort further parsing on unknown arguments
|
|
_optarg = nullptr;
|
|
return EOF;
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
}
|
|
|
|
if (ch == EOF) {
|
|
_optarg = argv[_optind];
|
|
|
|
// apply defaults if not provided
|
|
if (bus_frequency == 0) {
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (bus_option == I2CSPIBusOption::I2CExternal || bus_option == I2CSPIBusOption::I2CInternal) {
|
|
bus_frequency = default_i2c_frequency;
|
|
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (bus_option == I2CSPIBusOption::SPIExternal || bus_option == I2CSPIBusOption::SPIInternal) {
|
|
bus_frequency = default_spi_frequency;
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
}
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
|
|
bool BusCLIArguments::validateConfiguration()
|
|
{
|
|
bool success = true;
|
|
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (_i2c_support && default_i2c_frequency == -1) {
|
|
PX4_ERR("Bug: driver %s does not set default_i2c_frequency", px4_get_taskname());
|
|
success = false;
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (_spi_support && default_spi_frequency == -1) {
|
|
PX4_ERR("Bug: driver %s does not set default_spi_frequency", px4_get_taskname());
|
|
success = false;
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
return success;
|
|
}
|
|
|
|
|
|
BusInstanceIterator::BusInstanceIterator(const char *module_name,
|
|
const BusCLIArguments &cli_arguments, uint16_t devid_driver_index)
|
|
: _module_name(module_name), _bus_option(cli_arguments.bus_option), _devid_driver_index(devid_driver_index),
|
|
#if defined(CONFIG_I2C)
|
|
_i2c_address(cli_arguments.i2c_address),
|
|
#endif // CONFIG_I2C
|
|
#if defined(CONFIG_SPI)
|
|
_spi_bus_iterator(spiFilter(cli_arguments.bus_option),
|
|
devid_driver_index, cli_arguments.chipselect,
|
|
cli_arguments.requested_bus),
|
|
#endif // CONFIG_SPI
|
|
#if defined(CONFIG_I2C)
|
|
_i2c_bus_iterator(i2cFilter(cli_arguments.bus_option), cli_arguments.requested_bus),
|
|
#endif // CONFIG_I2C
|
|
_current_instance(i2c_spi_module_instances.end())
|
|
{
|
|
// We lock the module instance list as long as this object is alive, since we iterate over the list.
|
|
// Locking could be a bit more fine-grained, but the iterator is mostly only used sequentially, so not an issue.
|
|
pthread_mutex_lock(&i2c_spi_module_instances_mutex);
|
|
_current_instance = i2c_spi_module_instances.end();
|
|
}
|
|
|
|
BusInstanceIterator::~BusInstanceIterator()
|
|
{
|
|
pthread_mutex_unlock(&i2c_spi_module_instances_mutex);
|
|
}
|
|
|
|
bool BusInstanceIterator::next()
|
|
{
|
|
int bus = -1;
|
|
|
|
if (busType() == BOARD_INVALID_BUS) {
|
|
if (_current_instance == i2c_spi_module_instances.end()) { // either not initialized, or the first instance was removed
|
|
_current_instance = i2c_spi_module_instances.begin();
|
|
|
|
} else {
|
|
++_current_instance;
|
|
}
|
|
|
|
while (_current_instance != i2c_spi_module_instances.end()) {
|
|
if (strcmp((*_current_instance)->_module_name, _module_name) == 0 &&
|
|
_devid_driver_index == (*_current_instance)->_devid_driver_index) {
|
|
return true;
|
|
}
|
|
|
|
++_current_instance;
|
|
}
|
|
|
|
return false;
|
|
|
|
#if defined(CONFIG_SPI)
|
|
|
|
} else if (busType() == BOARD_SPI_BUS) {
|
|
if (_spi_bus_iterator.next()) {
|
|
bus = _spi_bus_iterator.bus().bus;
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
#if defined(CONFIG_I2C)
|
|
|
|
} else if (busType() == BOARD_I2C_BUS) {
|
|
if (_i2c_bus_iterator.next()) {
|
|
bus = _i2c_bus_iterator.bus().bus;
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
}
|
|
|
|
if (bus != -1) {
|
|
// find matching runtime instance
|
|
#if defined(CONFIG_I2C)
|
|
bool is_i2c = busType() == BOARD_I2C_BUS;
|
|
#else
|
|
bool is_i2c = false;
|
|
#endif
|
|
|
|
for (_current_instance = i2c_spi_module_instances.begin(); _current_instance != i2c_spi_module_instances.end();
|
|
++_current_instance) {
|
|
if (strcmp((*_current_instance)->_module_name, _module_name) != 0) {
|
|
continue;
|
|
}
|
|
|
|
if (_bus_option == (*_current_instance)->_bus_option && bus == (*_current_instance)->_bus &&
|
|
_devid_driver_index == (*_current_instance)->_devid_driver_index &&
|
|
busDeviceIndex() == (*_current_instance)->_bus_device_index &&
|
|
(!is_i2c
|
|
#if defined(CONFIG_I2C)
|
|
|| _i2c_address == (*_current_instance)->_i2c_address
|
|
#endif // CONFIG_I2C
|
|
)
|
|
) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int BusInstanceIterator::runningInstancesCount() const
|
|
{
|
|
int num_instances = 0;
|
|
|
|
for (const auto &modules : i2c_spi_module_instances) {
|
|
if (strcmp(modules->_module_name, _module_name) == 0) {
|
|
++num_instances;
|
|
}
|
|
}
|
|
|
|
return num_instances;
|
|
}
|
|
|
|
int BusInstanceIterator::runningInstancesOnBusCount() const
|
|
{
|
|
int num_instances = 0;
|
|
|
|
for (const auto &modules : i2c_spi_module_instances) {
|
|
if (modules->_bus == bus() && strcmp(modules->_module_name, _module_name) == 0) {
|
|
++num_instances;
|
|
}
|
|
}
|
|
|
|
return num_instances;
|
|
}
|
|
|
|
I2CSPIInstance *BusInstanceIterator::instance() const
|
|
{
|
|
if (_current_instance == i2c_spi_module_instances.end()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return *_current_instance;
|
|
}
|
|
|
|
void BusInstanceIterator::removeInstance()
|
|
{
|
|
// find previous node
|
|
List<I2CSPIInstance *>::Iterator previous = i2c_spi_module_instances.begin();
|
|
|
|
while (previous != i2c_spi_module_instances.end() && (*previous)->getSibling() != *_current_instance) {
|
|
++previous;
|
|
}
|
|
|
|
i2c_spi_module_instances.remove(*_current_instance);
|
|
_current_instance = previous; // previous can be i2c_spi_module_instances.end(), which means we removed the first item
|
|
}
|
|
|
|
void BusInstanceIterator::addInstance(I2CSPIInstance *instance)
|
|
{
|
|
i2c_spi_module_instances.add(instance);
|
|
}
|
|
|
|
board_bus_types BusInstanceIterator::busType() const
|
|
{
|
|
switch (_bus_option) {
|
|
case I2CSPIBusOption::All:
|
|
return BOARD_INVALID_BUS;
|
|
|
|
#if defined(CONFIG_I2C)
|
|
|
|
case I2CSPIBusOption::I2CInternal:
|
|
case I2CSPIBusOption::I2CExternal:
|
|
return BOARD_I2C_BUS;
|
|
#endif // CONFIG_I2C
|
|
|
|
#if defined(CONFIG_SPI)
|
|
|
|
case I2CSPIBusOption::SPIInternal:
|
|
case I2CSPIBusOption::SPIExternal:
|
|
return BOARD_SPI_BUS;
|
|
#endif // CONFIG_SPI
|
|
}
|
|
|
|
return BOARD_INVALID_BUS;
|
|
}
|
|
|
|
int BusInstanceIterator::bus() const
|
|
{
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (busType() == BOARD_SPI_BUS) {
|
|
return _spi_bus_iterator.bus().bus;
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (busType() == BOARD_I2C_BUS) {
|
|
return _i2c_bus_iterator.bus().bus;
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
|
|
return -1;
|
|
}
|
|
|
|
uint32_t BusInstanceIterator::devid() const
|
|
{
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (busType() == BOARD_SPI_BUS) {
|
|
return _spi_bus_iterator.devid();
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_SPI)
|
|
spi_drdy_gpio_t BusInstanceIterator::DRDYGPIO() const
|
|
{
|
|
if (busType() == BOARD_SPI_BUS) {
|
|
return _spi_bus_iterator.DRDYGPIO();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif // CONFIG_SPI
|
|
|
|
bool BusInstanceIterator::external() const
|
|
{
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (busType() == BOARD_SPI_BUS) {
|
|
return _spi_bus_iterator.external();
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (busType() == BOARD_I2C_BUS) {
|
|
return _i2c_bus_iterator.external();
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
|
|
return false;
|
|
}
|
|
|
|
int BusInstanceIterator::externalBusIndex() const
|
|
{
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (busType() == BOARD_SPI_BUS) {
|
|
return _spi_bus_iterator.externalBusIndex();
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (busType() == BOARD_I2C_BUS) {
|
|
return _i2c_bus_iterator.externalBusIndex();
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
|
|
return 0;
|
|
}
|
|
|
|
int BusInstanceIterator::busDeviceIndex() const
|
|
{
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (busType() == BOARD_SPI_BUS) {
|
|
return _spi_bus_iterator.busDeviceIndex();
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
|
|
return -1;
|
|
}
|
|
|
|
#if defined(CONFIG_I2C)
|
|
I2CBusIterator::FilterType BusInstanceIterator::i2cFilter(I2CSPIBusOption bus_option)
|
|
{
|
|
switch (bus_option) {
|
|
case I2CSPIBusOption::All: return I2CBusIterator::FilterType::All;
|
|
|
|
case I2CSPIBusOption::I2CExternal: return I2CBusIterator::FilterType::ExternalBus;
|
|
|
|
case I2CSPIBusOption::I2CInternal: return I2CBusIterator::FilterType::InternalBus;
|
|
|
|
default: break;
|
|
}
|
|
|
|
return I2CBusIterator::FilterType::All;
|
|
}
|
|
#endif // CONFIG_I2C
|
|
|
|
#if defined(CONFIG_SPI)
|
|
SPIBusIterator::FilterType BusInstanceIterator::spiFilter(I2CSPIBusOption bus_option)
|
|
{
|
|
switch (bus_option) {
|
|
case I2CSPIBusOption::SPIExternal: return SPIBusIterator::FilterType::ExternalBus;
|
|
|
|
case I2CSPIBusOption::SPIInternal: return SPIBusIterator::FilterType::InternalBus;
|
|
|
|
default: break;
|
|
}
|
|
|
|
return SPIBusIterator::FilterType::InternalBus;
|
|
}
|
|
#endif // CONFIG_SPI
|
|
|
|
struct I2CSPIDriverInitializing {
|
|
const I2CSPIDriverConfig &config;
|
|
I2CSPIDriverBase::instantiate_method instantiate;
|
|
int runtime_instance;
|
|
I2CSPIDriverBase *instance{nullptr};
|
|
};
|
|
|
|
static void initializer_trampoline(void *argument)
|
|
{
|
|
I2CSPIDriverInitializing *data = (I2CSPIDriverInitializing *)argument;
|
|
data->instance = data->instantiate(data->config, data->runtime_instance);
|
|
}
|
|
|
|
int I2CSPIDriverBase::module_start(const BusCLIArguments &cli, BusInstanceIterator &iterator,
|
|
void(*print_usage)(), instantiate_method instantiate)
|
|
{
|
|
if (iterator.configuredBusOption() == I2CSPIBusOption::All) {
|
|
PX4_ERR("need to specify a bus type");
|
|
print_usage();
|
|
return -1;
|
|
}
|
|
|
|
bool started = false;
|
|
|
|
while (iterator.next()) {
|
|
if (iterator.instance()) {
|
|
PX4_WARN("Already running on bus %i", iterator.bus());
|
|
continue;
|
|
}
|
|
|
|
|
|
device::Device::DeviceId device_id{};
|
|
device_id.devid_s.bus = iterator.bus();
|
|
|
|
switch (iterator.busType()) {
|
|
#if defined(CONFIG_I2C)
|
|
|
|
case BOARD_I2C_BUS: device_id.devid_s.bus_type = device::Device::DeviceBusType_I2C; break;
|
|
#endif // CONFIG_I2C
|
|
|
|
#if defined(CONFIG_SPI)
|
|
|
|
case BOARD_SPI_BUS: device_id.devid_s.bus_type = device::Device::DeviceBusType_SPI; break;
|
|
#endif // CONFIG_SPI
|
|
|
|
case BOARD_INVALID_BUS: device_id.devid_s.bus_type = device::Device::DeviceBusType_UNKNOWN; break;
|
|
}
|
|
|
|
|
|
const px4::wq_config_t &wq_config = px4::device_bus_to_wq(device_id.devid);
|
|
I2CSPIDriverConfig driver_config{cli, iterator, wq_config};
|
|
const int runtime_instance = iterator.runningInstancesCount();
|
|
I2CSPIDriverInitializing initializer_data{driver_config, instantiate, runtime_instance};
|
|
// initialize the object and bus on the work queue thread - this will also probe for the device
|
|
px4::WorkItemSingleShot initializer(wq_config, initializer_trampoline, &initializer_data);
|
|
initializer.ScheduleNow();
|
|
initializer.wait();
|
|
I2CSPIDriverBase *instance = initializer_data.instance;
|
|
|
|
if (!instance) {
|
|
PX4_DEBUG("instantiate failed (no device on bus %i (devid 0x%x)?)", iterator.bus(), iterator.devid());
|
|
continue;
|
|
}
|
|
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (cli.i2c_address != 0 && instance->_i2c_address == 0) {
|
|
PX4_ERR("Bug: driver %s does not pass the I2C address to I2CSPIDriverBase", instance->ItemName());
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
|
|
iterator.addInstance(instance);
|
|
started = true;
|
|
|
|
// print some info that we are running
|
|
switch (iterator.busType()) {
|
|
#if defined(CONFIG_I2C)
|
|
|
|
case BOARD_I2C_BUS:
|
|
PX4_INFO_RAW("%s #%i on I2C bus %d", instance->ItemName(), runtime_instance, iterator.bus());
|
|
|
|
if (iterator.external()) {
|
|
PX4_INFO_RAW(" (external)");
|
|
}
|
|
|
|
if (cli.i2c_address != 0) {
|
|
PX4_INFO_RAW(" address 0x%X", cli.i2c_address);
|
|
}
|
|
|
|
if (cli.rotation != 0) {
|
|
PX4_INFO_RAW(" rotation %d", cli.rotation);
|
|
}
|
|
|
|
PX4_INFO_RAW("\n");
|
|
|
|
break;
|
|
#endif // CONFIG_I2C
|
|
#if defined(CONFIG_SPI)
|
|
|
|
case BOARD_SPI_BUS:
|
|
PX4_INFO_RAW("%s #%i on SPI bus %d", instance->ItemName(), runtime_instance, iterator.bus());
|
|
|
|
if (iterator.external()) {
|
|
PX4_INFO_RAW(" (external, equal to '-b %i')", iterator.externalBusIndex());
|
|
}
|
|
|
|
if (cli.rotation != 0) {
|
|
PX4_INFO_RAW(" rotation %d", cli.rotation);
|
|
}
|
|
|
|
PX4_INFO_RAW("\n");
|
|
|
|
break;
|
|
#endif // CONFIG_SPI
|
|
|
|
case BOARD_INVALID_BUS:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!started && !cli.quiet_start) {
|
|
static constexpr char no_instance_started[] {"no instance started (no device on bus?)"};
|
|
|
|
if (iterator.external()) {
|
|
PX4_WARN("%s: %s", px4_get_taskname(), no_instance_started);
|
|
|
|
} else {
|
|
PX4_ERR("%s: %s", px4_get_taskname(), no_instance_started);
|
|
}
|
|
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (iterator.busType() == BOARD_I2C_BUS && cli.i2c_address == 0) {
|
|
PX4_ERR("%s: driver does not set i2c address", px4_get_taskname());
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
}
|
|
|
|
return started ? 0 : -1;
|
|
}
|
|
|
|
int I2CSPIDriverBase::module_stop(BusInstanceIterator &iterator)
|
|
{
|
|
bool is_running = false;
|
|
bool stop_failed = false;
|
|
|
|
while (iterator.next()) {
|
|
if (iterator.instance()) {
|
|
is_running = true;
|
|
I2CSPIDriverBase *instance = (I2CSPIDriverBase *)iterator.instance();
|
|
|
|
if (instance->request_stop_and_wait()) {
|
|
iterator.removeInstance();
|
|
delete instance;
|
|
|
|
} else {
|
|
stop_failed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_running) {
|
|
PX4_ERR("Not running");
|
|
return -1;
|
|
}
|
|
|
|
return stop_failed ? -1 : 0;
|
|
}
|
|
|
|
int I2CSPIDriverBase::module_status(BusInstanceIterator &iterator)
|
|
{
|
|
bool is_running = false;
|
|
|
|
while (iterator.next()) {
|
|
if (iterator.instance()) {
|
|
I2CSPIDriverBase *instance = (I2CSPIDriverBase *)iterator.instance();
|
|
instance->print_status();
|
|
is_running = true;
|
|
}
|
|
}
|
|
|
|
if (!is_running) {
|
|
PX4_INFO("Not running");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct custom_method_data_t {
|
|
I2CSPIDriverBase *instance;
|
|
const BusCLIArguments &cli;
|
|
};
|
|
|
|
void I2CSPIDriverBase::custom_method_trampoline(void *argument)
|
|
{
|
|
custom_method_data_t *data = (custom_method_data_t *)argument;
|
|
data->instance->custom_method(data->cli);
|
|
}
|
|
|
|
int I2CSPIDriverBase::module_custom_method(const BusCLIArguments &cli, BusInstanceIterator &iterator,
|
|
bool run_on_work_queue)
|
|
{
|
|
while (iterator.next()) {
|
|
if (iterator.instance()) {
|
|
I2CSPIDriverBase *instance = (I2CSPIDriverBase *)iterator.instance();
|
|
|
|
if (run_on_work_queue) {
|
|
custom_method_data_t data{instance, cli};
|
|
px4::WorkItemSingleShot runner(*instance, custom_method_trampoline, &data);
|
|
runner.ScheduleNow();
|
|
runner.wait();
|
|
|
|
} else {
|
|
instance->custom_method(cli);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void I2CSPIDriverBase::print_status()
|
|
{
|
|
#if defined(CONFIG_I2C)
|
|
|
|
if (_bus_option == I2CSPIBusOption::I2CExternal || _bus_option == I2CSPIBusOption::I2CInternal) {
|
|
PX4_INFO("Running on I2C Bus %i, Address 0x%02X", _bus, get_i2c_address());
|
|
return;
|
|
}
|
|
|
|
#endif // CONFIG_I2C
|
|
|
|
#if defined(CONFIG_SPI)
|
|
|
|
if (_bus_option == I2CSPIBusOption::SPIExternal || _bus_option == I2CSPIBusOption::SPIInternal) {
|
|
PX4_INFO("Running on SPI Bus %i", _bus);
|
|
return;
|
|
}
|
|
|
|
#endif // CONFIG_SPI
|
|
}
|
|
|
|
bool I2CSPIDriverBase::request_stop_and_wait()
|
|
{
|
|
_task_should_exit.store(true);
|
|
ScheduleNow(); // wake up the task (in case it is not scheduled anymore or just to be faster)
|
|
unsigned int i = 0;
|
|
|
|
do {
|
|
px4_usleep(20000); // 20 ms
|
|
// wait at most 2 sec
|
|
} while (++i < 100 && !_task_exited.load());
|
|
|
|
const bool exited = _task_exited.load();
|
|
|
|
if (!exited) {
|
|
PX4_ERR("Module did not respond to stop request");
|
|
}
|
|
|
|
return exited;
|
|
}
|