You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
830 lines
19 KiB
830 lines
19 KiB
/**************************************************************************** |
|
* |
|
* 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; |
|
} |
|
|
|
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; |
|
|
|
while (iterator.next()) { |
|
if (iterator.instance()) { |
|
I2CSPIDriverBase *instance = (I2CSPIDriverBase *)iterator.instance(); |
|
instance->request_stop_and_wait(); |
|
delete iterator.instance(); |
|
iterator.removeInstance(); |
|
is_running = true; |
|
} |
|
} |
|
|
|
if (!is_running) { |
|
PX4_ERR("Not running"); |
|
return -1; |
|
} |
|
|
|
return 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 |
|
} |
|
|
|
void 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()); |
|
|
|
if (i >= 100) { |
|
PX4_ERR("Module did not respond to stop request"); |
|
} |
|
}
|
|
|