diff --git a/src/lib/drivers/device/CMakeLists.txt b/src/lib/drivers/device/CMakeLists.txt index 4b61c22757..c949db9dd5 100644 --- a/src/lib/drivers/device/CMakeLists.txt +++ b/src/lib/drivers/device/CMakeLists.txt @@ -40,6 +40,10 @@ if (${PX4_PLATFORM} STREQUAL "nuttx") if ("${CONFIG_SPI}" STREQUAL "y") list(APPEND SRCS_PLATFORM nuttx/SPI.cpp) endif() +elseif((${PX4_PLATFORM} MATCHES "qurt")) + list(APPEND SRCS_PLATFORM qurt/I2C.cpp) + list(APPEND SRCS_PLATFORM qurt/SPI.cpp) + list(APPEND SRCS_PLATFORM qurt/uart.c) elseif(UNIX AND NOT APPLE) #TODO: add linux PX4 platform type # Linux I2Cdev and SPIdev list(APPEND SRCS_PLATFORM diff --git a/src/lib/drivers/device/i2c.h b/src/lib/drivers/device/i2c.h index d1d2a10f98..6e4de602fa 100644 --- a/src/lib/drivers/device/i2c.h +++ b/src/lib/drivers/device/i2c.h @@ -34,6 +34,8 @@ #ifdef __PX4_NUTTX #include "nuttx/I2C.hpp" +#elif defined(__PX4_QURT) +#include "qurt/I2C.hpp" #else #include "posix/I2C.hpp" #endif diff --git a/src/lib/drivers/device/qurt/I2C.cpp b/src/lib/drivers/device/qurt/I2C.cpp new file mode 100644 index 0000000000..d3fc6bd04f --- /dev/null +++ b/src/lib/drivers/device/qurt/I2C.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** + * + * Copyright (c) 2016-2020 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 I2C.cpp + * + * Base class for devices attached via the I2C bus. + * + * @todo Bus frequency changes; currently we do nothing with the value + * that is supplied. Should we just depend on the bus knowing? + */ + +#include "I2C.hpp" + +#include +#include + +namespace device +{ + +I2C::_config_i2c_bus_func_t I2C::_config_i2c_bus = NULL; +I2C::_set_i2c_address_func_t I2C::_set_i2c_address = NULL; +I2C::_i2c_transfer_func_t I2C::_i2c_transfer = NULL; + +pthread_mutex_t I2C::_mutex = PTHREAD_MUTEX_INITIALIZER; + +I2C::I2C(uint8_t device_type, const char *name, const int bus, const uint16_t address, const uint32_t frequency) : + CDev(name, nullptr), + _frequency(frequency / 1000) +{ + _device_id.devid = 0; + + // fill in _device_id fields for a I2C device + _device_id.devid_s.devtype = device_type; + _device_id.devid_s.bus_type = DeviceBusType_I2C; + _device_id.devid_s.bus = bus; + _device_id.devid_s.address = address; + + PX4_INFO("*** I2C Device ID 0x%x %d", _device_id.devid, _device_id.devid); +} + +I2C::~I2C() +{ +} + +int +I2C::init() +{ + int ret = PX4_ERROR; + + if (_config_i2c_bus == NULL) { + PX4_ERR("NULL i2c init function"); + goto out; + } + + pthread_mutex_lock(&_mutex); + // Open the actual I2C device + _i2c_fd = _config_i2c_bus(get_device_bus(), get_device_address(), _frequency); + pthread_mutex_unlock(&_mutex); + + if (_i2c_fd == PX4_ERROR) { + PX4_ERR("i2c init failed"); + goto out; + } + + // call the probe function to check whether the device is present + ret = probe(); + + if (ret != OK) { + PX4_ERR("i2c probe failed"); + goto out; + } + + // do base class init, which will create device node, etc + ret = CDev::init(); + + if (ret != OK) { + PX4_ERR("i2c cdev init failed"); + goto out; + } + + // tell the world where we are + // PX4_INFO("on I2C bus %d at 0x%02x", get_device_bus(), get_device_address()); + +out: + + return ret; +} + +void +I2C::set_device_address(int address) +{ + if ((_i2c_fd != PX4_ERROR) && (_set_i2c_address != NULL)) { + PX4_INFO("Set i2c address 0x%x, fd %d", address, _i2c_fd); + + pthread_mutex_lock(&_mutex); + _set_i2c_address(_i2c_fd, address); + pthread_mutex_unlock(&_mutex); + + Device::set_device_address(address); + } +} + + +int +I2C::transfer(const uint8_t *send, const unsigned send_len, uint8_t *recv, const unsigned recv_len) +{ + int ret = PX4_ERROR; + unsigned retry_count = 1; + + if ((_i2c_fd != PX4_ERROR) && (_i2c_transfer != NULL)) { + do { + // PX4_INFO("transfer out %p/%u in %p/%u", send, send_len, recv, recv_len); + + pthread_mutex_lock(&_mutex); + ret = _i2c_transfer(_i2c_fd, send, send_len, recv, recv_len); + pthread_mutex_unlock(&_mutex); + + if (ret != PX4_ERROR) { break; } + + px4_usleep(1000); + + } while (retry_count++ < _retries); + } + + return ret; +} + +} // namespace device diff --git a/src/lib/drivers/device/qurt/I2C.hpp b/src/lib/drivers/device/qurt/I2C.hpp new file mode 100644 index 0000000000..8e2e4c7e7d --- /dev/null +++ b/src/lib/drivers/device/qurt/I2C.hpp @@ -0,0 +1,131 @@ +/**************************************************************************** + * + * Copyright (C) 2016-2020 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 I2C.hpp + * + * Base class for devices connected via I2C. + */ + +#ifndef _DEVICE_I2C_H +#define _DEVICE_I2C_H + +#include +#include "../CDev.hpp" + +namespace device __EXPORT +{ + +/** + * Abstract class for character device on I2C + */ +class __EXPORT I2C : public CDev +{ + +public: + + // no copy, assignment, move, move assignment + I2C(const I2C &) = delete; + I2C &operator=(const I2C &) = delete; + I2C(I2C &&) = delete; + I2C &operator=(I2C &&) = delete; + + virtual int init() override; + + typedef int (*_config_i2c_bus_func_t)(uint8_t, uint8_t, uint32_t); + typedef int (*_set_i2c_address_func_t)(int, uint8_t); + typedef int (*_i2c_transfer_func_t)(int, const uint8_t *, const unsigned, uint8_t *, const unsigned); + + static void configure_callbacks(_config_i2c_bus_func_t config_func, + _set_i2c_address_func_t addr_func, + _i2c_transfer_func_t transfer_func) + { + _config_i2c_bus = config_func; + _set_i2c_address = addr_func; + _i2c_transfer = transfer_func; + } + +protected: + /** + * The number of times a read or write operation will be retried on + * error. + */ + uint8_t _retries{0}; + + /** + * @ Constructor + * + * @param device_type The device type (see drv_sensor.h) + * @param name Driver name + * @param bus I2C bus on which the device lives + * @param address I2C bus address, or zero if set_address will be used + * @param frequency I2C bus frequency for the device (currently not used) + */ + I2C(uint8_t device_type, const char *name, const int bus, const uint16_t address, const uint32_t frequency); + virtual ~I2C(); + + /** + * Check for the presence of the device on the bus. + */ + virtual int probe() { return PX4_OK; } + + virtual void set_device_address(int address); + + /** + * Perform an I2C transaction to the device. + * + * At least one of send_len and recv_len must be non-zero. + * + * @param send Pointer to bytes to send. + * @param send_len Number of bytes to send. + * @param recv Pointer to buffer for bytes received. + * @param recv_len Number of bytes to receive. + * @return OK if the transfer was successful, -errno + * otherwise. + */ + int transfer(const uint8_t *send, const unsigned send_len, uint8_t *recv, const unsigned recv_len); + + virtual bool external() const override { return px4_i2c_bus_external(_device_id.devid_s.bus); } + +private: + uint32_t _frequency{0}; + int _i2c_fd{-1}; + static _config_i2c_bus_func_t _config_i2c_bus; + static _set_i2c_address_func_t _set_i2c_address; + static _i2c_transfer_func_t _i2c_transfer; + static pthread_mutex_t _mutex; +}; + +} // namespace device + +#endif /* _DEVICE_I2C_H */ diff --git a/src/lib/drivers/device/qurt/SPI.cpp b/src/lib/drivers/device/qurt/SPI.cpp new file mode 100644 index 0000000000..66ebb26dd6 --- /dev/null +++ b/src/lib/drivers/device/qurt/SPI.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** + * + * Copyright (C) 2019 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 SPI.cpp + * + * Base class for devices connected via SPI. + * + */ + +#include "SPI.hpp" + +#include +#include +#include +#include +#include + +static int (*register_interrupt_callback_func)(int (*)(int, void *, void *), void *arg) = NULL; + +int px4_arch_gpiosetevent(spi_drdy_gpio_t pin, bool r, bool f, bool e, int (*func)(int, void *, void *), void *arg) +{ + + if ((register_interrupt_callback_func != NULL) && (func != NULL) && (arg != NULL)) { + PX4_INFO("Register interrupt %p %p %p", register_interrupt_callback_func, func, arg); + return register_interrupt_callback_func(func, arg); + } + + return -1; +} + +void register_interrupt_callback_initalizer(int (*func)(int (*)(int, void *, void *), void *arg)) +{ + register_interrupt_callback_func = func; +} + +namespace device +{ + +SPI::_config_spi_bus_func_t SPI::_config_spi_bus = NULL; +SPI::_spi_transfer_func_t SPI::_spi_transfer = NULL; + +pthread_mutex_t SPI::_mutex = PTHREAD_MUTEX_INITIALIZER; + +SPI::SPI(uint8_t device_type, const char *name, int bus, uint32_t device, enum spi_mode_e mode, uint32_t frequency) : + CDev(name, nullptr) + // CDev(name, nullptr), + // _device(device), + // _mode(mode), + // _frequency(frequency) +{ + _device_id.devid = 0; + + _device_id.devid_s.devtype = device_type; + // fill in _device_id fields for a SPI device + _device_id.devid_s.bus_type = DeviceBusType_SPI; + _device_id.devid_s.bus = bus; + _device_id.devid_s.address = (uint8_t)device; + + PX4_INFO("*** SPI Device ID 0x%x %d", _device_id.devid, _device_id.devid); +} + +SPI::SPI(const I2CSPIDriverConfig &config) + : SPI(config.devid_driver_index, config.module_name, config.bus, config.spi_devid, config.spi_mode, + config.bus_frequency) +{ +} + +SPI::~SPI() +{ + if (_fd >= 0) { + ::close(_fd); + _fd = -1; + } +} + +int +SPI::init() +{ + int ret = PX4_ERROR; + + if (_config_spi_bus == NULL) { + PX4_ERR("NULL spi init function"); + return ret; + } + + pthread_mutex_lock(&_mutex); + _fd = _config_spi_bus(); + pthread_mutex_unlock(&_mutex); + + if (_fd == PX4_ERROR) { + PX4_ERR("spi init failed"); + return ret; + } + + /* call the probe function to check whether the device is present */ + ret = probe(); + + if (ret != OK) { + PX4_INFO("SPI probe failed"); + return ret; + } + + /* do base class init, which will create the device node, etc. */ + ret = CDev::init(); + + if (ret != OK) { + PX4_ERR("cdev init failed"); + return ret; + } + + /* tell the world where we are */ + PX4_INFO("on SPI bus %d", get_device_bus()); + + return PX4_OK; +} + +int +SPI::transfer(uint8_t *send, uint8_t *recv, unsigned len) +{ + int ret = PX4_ERROR; + unsigned retry_count = 1; + + if ((_fd != PX4_ERROR) && (_spi_transfer != NULL)) { + do { + // PX4_DEBUG("SPI transfer out %p in %p len %u", send, recv, len); + + if (_spi_transfer != NULL) { + pthread_mutex_lock(&_mutex); + ret = _spi_transfer(_fd, send, recv, len); + pthread_mutex_unlock(&_mutex); + + } else { + PX4_ERR("SPI transfer function is NULL"); + } + + if (ret != PX4_ERROR) { break; } + + } while (retry_count++ < _retries); + } + + return ret; +} + +int +SPI::transferhword(uint16_t *send, uint16_t *recv, unsigned len) +{ + // Not supported on SLPI + return PX4_ERROR; +} + +} // namespace device diff --git a/src/lib/drivers/device/qurt/SPI.hpp b/src/lib/drivers/device/qurt/SPI.hpp new file mode 100644 index 0000000000..44862270d6 --- /dev/null +++ b/src/lib/drivers/device/qurt/SPI.hpp @@ -0,0 +1,198 @@ +/**************************************************************************** + * + * Copyright (C) 2019 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 SPI.hpp + * + * Base class for devices connected via SPI. + */ + +#pragma once + +#include "../CDev.hpp" + +// #include "dev_fs_lib_spi.h" + +#include + +// Perhaps not the best place for these but they are only used for the IMU SPI driver on Qurt +int px4_arch_gpiosetevent(spi_drdy_gpio_t pin, bool r, bool f, bool e, int (*func)(int, void *, void *), void *arg); +void register_interrupt_callback_initalizer(int (*)(int (*)(int, void *, void *), void *arg)); + +enum spi_mode_e { + SPIDEV_MODE0 = 0, /* CPOL=0 CHPHA=0 */ + SPIDEV_MODE1 = 1, /* CPOL=0 CHPHA=1 */ + SPIDEV_MODE2 = 2, /* CPOL=1 CHPHA=0 */ + SPIDEV_MODE3 = 3 /* CPOL=1 CHPHA=1 */ +}; + +struct I2CSPIDriverConfig; + +namespace device __EXPORT +{ + +/** + * Abstract class for character device on SPI + */ +class __EXPORT SPI : public CDev +{ +public: + typedef int (*_config_spi_bus_func_t)(); + typedef int (*_spi_transfer_func_t)(int, const uint8_t *, uint8_t *, const unsigned); + + static void configure_callbacks(_config_spi_bus_func_t config_func, + _spi_transfer_func_t transfer_func) + { + _config_spi_bus = config_func; + _spi_transfer = transfer_func; + } + +protected: + /** + * Constructor + * + * @param device_type The device type (see drv_sensor.h) + * @param name Driver name + * @param bus SPI bus on which the device lives + * @param device Device handle (used by SPI_SELECT) + * @param mode SPI clock/data mode + * @param frequency SPI clock frequency + */ + SPI(uint8_t device_type, const char *name, int bus, uint32_t device, enum spi_mode_e mode, uint32_t frequency); + + SPI(const I2CSPIDriverConfig &config); + + virtual ~SPI(); + + /** + * Locking modes supported by the driver. + */ + enum LockMode { + LOCK_PREEMPTION, /**< the default; lock against all forms of preemption. */ + LOCK_THREADS, /**< lock only against other threads, using SPI_LOCK */ + LOCK_NONE /**< perform no locking, only safe if the bus is entirely private */ + }; + + virtual int init(); + + /** + * Check for the presence of the device on the bus. + */ + virtual int probe() { return PX4_OK; } + + /** + * Perform a SPI transfer. + * + * If called from interrupt context, this interface does not lock + * the bus and may interfere with non-interrupt-context callers. + * + * Clients in a mixed interrupt/non-interrupt configuration must + * ensure appropriate interlocking. + * + * At least one of send or recv must be non-null. + * + * @param send Bytes to send to the device, or nullptr if + * no data is to be sent. + * @param recv Buffer for receiving bytes from the device, + * or nullptr if no bytes are to be received. + * @param len Number of bytes to transfer. + * @return OK if the exchange was successful, -errno + * otherwise. + */ + int transfer(uint8_t *send, uint8_t *recv, unsigned len); + + /** + * Perform a SPI 16 bit transfer. + * + * If called from interrupt context, this interface does not lock + * the bus and may interfere with non-interrupt-context callers. + * + * Clients in a mixed interrupt/non-interrupt configuration must + * ensure appropriate interlocking. + * + * At least one of send or recv must be non-null. + * + * @param send Words to send to the device, or nullptr if + * no data is to be sent. + * @param recv Words for receiving bytes from the device, + * or nullptr if no bytes are to be received. + * @param len Number of words to transfer. + * @return OK if the exchange was successful, -errno + * otherwise. + */ + int transferhword(uint16_t *send, uint16_t *recv, unsigned len); + + /** + * Set the SPI bus frequency + * This is used to change frequency on the fly. Some sensors + * (such as the MPU6000) need a lower frequency for setup + * registers and can handle higher frequency for sensor + * value registers + * + * @param frequency Frequency to set (Hz) + */ + void set_frequency(uint32_t frequency) {} + uint32_t get_frequency() { return 0; } + + /** + * Set the SPI bus locking mode + * + * This set the SPI locking mode. For devices competing with NuttX SPI + * drivers on a bus the right lock mode is LOCK_THREADS. + * + * @param mode Locking mode + */ + void set_lockmode(enum LockMode mode) {} + +private: + int _fd{-1}; + + static _config_spi_bus_func_t _config_spi_bus; + static _spi_transfer_func_t _spi_transfer; + + static pthread_mutex_t _mutex; + +protected: + + /** + * The number of times a read or write operation will be retried on + * error. + */ + uint8_t _retries{0}; + + // bool external() { return px4_spi_bus_external(get_device_bus()); } + // bool external() { return false; } + +}; + +} // namespace device diff --git a/src/lib/drivers/device/qurt/uart.c b/src/lib/drivers/device/qurt/uart.c new file mode 100644 index 0000000000..798d205344 --- /dev/null +++ b/src/lib/drivers/device/qurt/uart.c @@ -0,0 +1,116 @@ + +#include +#include +#include "uart.h" + +#define UART_READ_POLL_INTERVAL_US 500 + +// Static variables +static bool _callbacks_configured = false; + +static open_uart_func_t _open_uart = NULL; +static write_uart_func_t _write_uart = NULL; +static read_uart_func_t _read_uart = NULL; + +void configure_uart_callbacks(open_uart_func_t open_func, + write_uart_func_t write_func, + read_uart_func_t read_func) +{ + _open_uart = open_func; + _write_uart = write_func; + _read_uart = read_func; + + if (_open_uart && _write_uart && _read_uart) { + _callbacks_configured = true; + } +} + +int qurt_uart_open(const char *dev, speed_t speed) +{ + if (_callbacks_configured) { + // Convert device string into a uart port number + char *endptr = NULL; + uint8_t port_number = (uint8_t) strtol(dev, &endptr, 10); + + if ((port_number == 0) && (endptr == dev)) { + PX4_ERR("Could not convert %s into a valid uart port number", dev); + return -1; + } + + return _open_uart(port_number, speed); + + } else { + PX4_ERR("Cannot open uart until callbacks have been configured"); + } + + return -1; +} + +int qurt_uart_write(int fd, const char *buf, size_t len) +{ + if (fd < 0) { + PX4_ERR("invalid fd %d for %s", fd, __FUNCTION__); + return -1; + } + + if (buf == NULL) { + PX4_ERR("NULL buffer pointer in %s", fd, __FUNCTION__); + return -1; + } + + if (len == 0) { + PX4_ERR("Zero length buffer in %s", __FUNCTION__); + return -1; + } + + if (_callbacks_configured) { + return _write_uart(fd, buf, len); + + } else { + PX4_ERR("Cannot write to uart until callbacks have been configured"); + } + + return -1; +} + +int qurt_uart_read(int fd, char *buf, size_t len, uint32_t timeout_us) +{ + if (fd < 0) { + PX4_ERR("invalid fd %d for %s", fd, __FUNCTION__); + return -1; + } + + if (buf == NULL) { + PX4_ERR("NULL buffer pointer in %s", fd, __FUNCTION__); + return -1; + } + + if (len == 0) { + PX4_ERR("Zero length buffer in %s", __FUNCTION__); + return -1; + } + + if (_callbacks_configured) { + uint32_t interval_counter = (timeout_us + (UART_READ_POLL_INTERVAL_US - 1)) / UART_READ_POLL_INTERVAL_US; + // PX4_INFO("UART interval counter = %d", interval_counter); + int read_len = 0; + + do { + read_len = _read_uart(fd, buf, len); + + if (read_len > 0) { break; } + + interval_counter--; + px4_usleep(UART_READ_POLL_INTERVAL_US); + } while (interval_counter); + + // if (read_len <= 0) PX4_INFO("Warning, UART read timed out"); + // else PX4_INFO("UART read %d bytes, counter = %d", read_len, interval_counter); + return read_len; + + } else { + PX4_ERR("Cannot read from uart until callbacks have been configured"); + } + + return -1; +} diff --git a/src/lib/drivers/device/qurt/uart.h b/src/lib/drivers/device/qurt/uart.h new file mode 100644 index 0000000000..0305596189 --- /dev/null +++ b/src/lib/drivers/device/qurt/uart.h @@ -0,0 +1,22 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +int qurt_uart_open(const char *dev, speed_t speed); +int qurt_uart_write(int fd, const char *buf, size_t len); +int qurt_uart_read(int fd, char *buf, size_t len, uint32_t timeout_us); + +typedef int (*open_uart_func_t)(uint8_t, speed_t); +typedef int (*write_uart_func_t)(int, const void *, size_t); +typedef int (*read_uart_func_t)(int, void *, size_t); + +void configure_uart_callbacks(open_uart_func_t, write_uart_func_t, read_uart_func_t); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/src/lib/drivers/device/spi.h b/src/lib/drivers/device/spi.h index 9ca739d180..c2cca8597d 100644 --- a/src/lib/drivers/device/spi.h +++ b/src/lib/drivers/device/spi.h @@ -34,6 +34,8 @@ #ifdef __PX4_NUTTX #include "nuttx/SPI.hpp" +#elif defined(__PX4_QURT) +#include "qurt/SPI.hpp" #else #include "posix/SPI.hpp" #endif