diff --git a/drivers/Kconfig.net b/drivers/Kconfig.net index 04b6b27e16..afe39e97c5 100644 --- a/drivers/Kconfig.net +++ b/drivers/Kconfig.net @@ -16,6 +16,7 @@ rsource "ncv7356/Kconfig" rsource "pn532/Kconfig" rsource "rn2xx3/Kconfig" rsource "slipdev/Kconfig" +rsource "sx126x/Kconfig" rsource "sx127x/Kconfig" rsource "tja1042/Kconfig" source "$(RIOTCPU)/nrf52/radio/nrf802154/Kconfig" diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 92e57c4494..2c9c47ab66 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -68,6 +68,10 @@ ifneq (,$(filter lis2dh12%,$(USEMODULE))) USEMODULE += lis2dh12 endif +ifneq (,$(filter llcc68,$(USEMODULE))) + USEMODULE += sx126x +endif + ifneq (,$(filter lps331ap lps2%hb,$(USEMODULE))) USEMODULE += lpsxxx endif @@ -147,6 +151,10 @@ ifneq (,$(filter slipdev_%,$(USEMODULE))) USEMODULE += slipdev endif +ifneq (,$(filter sx126%,$(USEMODULE))) + USEMODULE += sx126x +endif + ifneq (,$(filter sx127%,$(USEMODULE))) USEMODULE += sx127x endif diff --git a/drivers/include/net/netdev.h b/drivers/include/net/netdev.h index 2a1b2b040d..588326c28a 100644 --- a/drivers/include/net/netdev.h +++ b/drivers/include/net/netdev.h @@ -318,6 +318,7 @@ typedef enum { NETDEV_ESP_NOW, NETDEV_NRF24L01P_NG, NETDEV_SOCKET_ZEP, + NETDEV_SX126X, /* add more if needed */ } netdev_type_t; /** @} */ diff --git a/drivers/include/sx126x.h b/drivers/include/sx126x.h new file mode 100644 index 0000000000..2a61318af3 --- /dev/null +++ b/drivers/include/sx126x.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2021 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_sx126x SX1261/2/8 and LLCC68 LoRa radio driver + * @ingroup drivers_netdev + * @brief Driver for the SX1261/2/8 and LLCC68 LoRa radio device + * + * @{ + * + * @file + * + * @author Alexandre Abadie + */ + +#ifndef SX126X_H +#define SX126X_H + +#include "sx126x_driver.h" + +#include "net/netdev.h" + +#include "periph/gpio.h" +#include "periph/spi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Device initialization parameters + */ +typedef struct { + spi_t spi; /**< SPI device */ + gpio_t nss_pin; /**< SPI NSS pin */ + gpio_t reset_pin; /**< Reset pin */ + gpio_t busy_pin; /**< Busy pin */ + gpio_t dio1_pin; /**< Dio1 pin */ + sx126x_reg_mod_t regulator; /**< Power regulator mode */ +} sx126x_params_t; + +/** + * @brief Device descriptor for the driver + */ +typedef struct { + netdev_t netdev; /**< Netdev parent struct */ + sx126x_params_t *params; /**< Initialization parameters */ + sx126x_pkt_params_lora_t pkt_params; /**< Lora packet parameters */ + sx126x_mod_params_lora_t mod_params; /**< Lora modulation parameters */ + uint32_t channel; /**< Current channel frequency (in Hz) */ + uint32_t rx_timeout; /**< RX timeout in ms */ +} sx126x_t; + +/** + * @brief Setup the radio device + * + * @param[in] dev Device descriptor + * @param[in] params Parameters for device initialization + * @param[in] index Index of @p params in a global parameter struct array. + * If initialized manually, pass a unique identifier instead. + */ +void sx126x_setup(sx126x_t *dev, const sx126x_params_t *params, uint8_t index); + +/** + * @brief Initialize the given device + * + * @param[inout] dev Device descriptor of the driver + * + * @return 0 on success + */ +int sx126x_init(sx126x_t *dev); + +/** + * @brief Gets the channel RF frequency. + * + * @param[in] dev Device descriptor of the driver + * + * @return The channel frequency + */ +uint32_t sx126x_get_channel(const sx126x_t *dev); + +/** + * @brief Sets the channel RF frequency. + * + * @param[in] dev Device descriptor of the driver + * @param[in] freq Channel RF frequency + */ +void sx126x_set_channel(sx126x_t *dev, uint32_t freq); + +/** + * @brief Gets the LoRa bandwidth + * + * @param[in] dev Device descriptor of the driver + * + * @return the bandwidth + */ +uint8_t sx126x_get_bandwidth(const sx126x_t *dev); + +/** + * @brief Sets the LoRa bandwidth + * + * @param[in] dev Device descriptor of the driver + * @param[in] bandwidth The new bandwidth + */ +void sx126x_set_bandwidth(sx126x_t *dev, uint8_t bandwidth); + +/** + * @brief Gets the LoRa spreading factor + * + * @param[in] dev Device descriptor of the driver + * + * @return the spreading factor + */ +uint8_t sx126x_get_spreading_factor(const sx126x_t *dev); + +/** + * @brief Sets the LoRa spreading factor + * + * @param[in] dev Device descriptor of the driver + * @param[in] sf The spreading factor + */ +void sx126x_set_spreading_factor(sx126x_t *dev, uint8_t sf); + +/** + * @brief Gets the LoRa coding rate + * + * @param[in] dev Device descriptor of the driver + * + * @return the current LoRa coding rate + */ +uint8_t sx126x_get_coding_rate(const sx126x_t *dev); + +/** + * @brief Sets the LoRa coding rate + * + * @param[in] dev Device descriptor of the driver + * @param[in] cr The LoRa coding rate + */ +void sx126x_set_coding_rate(sx126x_t *dev, uint8_t cr); + +/** + * @brief Gets the payload length + * + * @param[in] dev Device descriptor of the driver + * + * @return the payload length + */ +uint8_t sx126x_get_lora_payload_length(const sx126x_t *dev); + +/** + * @brief Sets the payload length + * + * @param[in] dev Device descriptor of the driver + * @param[in] len The payload len + */ +void sx126x_set_lora_payload_length(sx126x_t *dev, uint8_t len); + +/** + * @brief Checks if CRC verification mode is enabled + * + * @param[in] dev Device descriptor of the driver + * + * @return the LoRa single mode + */ +bool sx126x_get_lora_crc(const sx126x_t *dev); + +/** + * @brief Enable/Disable CRC verification mode + * + * @param[in] dev Device descriptor of the driver + * @param[in] crc The CRC check mode + */ +void sx126x_set_lora_crc(sx126x_t *dev, bool crc); + +/** + * @brief Gets the LoRa implicit header mode + * + * @param[in] dev Device descriptor of the driver + * + * @return the LoRa implicit mode + */ +bool sx126x_get_lora_implicit_header(const sx126x_t *dev); + +/** + * @brief Sets LoRa implicit header mode + * + * @param[in] dev Device descriptor of the driver + * @param[in] mode The header mode + */ +void sx126x_set_lora_implicit_header(sx126x_t *dev, bool mode); + +/** + * @brief Gets the LoRa preamble length + * + * @param[in] dev Device descriptor of the driver + * + * @return the preamble length + */ +uint16_t sx126x_get_lora_preamble_length(const sx126x_t *dev); + +/** + * @brief Sets the LoRa preamble length + * + * @param[in] dev Device descriptor of the driver + * @param[in] preamble The LoRa preamble length + */ +void sx126x_set_lora_preamble_length(sx126x_t *dev, uint16_t preamble); + +/** + * @brief Checks if the LoRa inverted IQ mode is enabled/disabled + * + * @param[in] dev Device descriptor of the driver + * + * @return the LoRa IQ inverted mode + */ +bool sx126x_get_lora_iq_invert(const sx126x_t *dev); + +/** + * @brief Enable/disable the LoRa IQ inverted mode + * + * @param[in] dev Device descriptor of the driver + * @param[in] iq_invert The LoRa IQ inverted mode + */ +void sx126x_set_lora_iq_invert(sx126x_t *dev, bool iq_invert); + +#ifdef __cplusplus +} +#endif + +#endif /* SX126X_H */ +/** @} */ diff --git a/drivers/sx126x/Kconfig b/drivers/sx126x/Kconfig new file mode 100644 index 0000000000..f682c31920 --- /dev/null +++ b/drivers/sx126x/Kconfig @@ -0,0 +1,14 @@ +# Copyright (c) 2021 Inria +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +config MODULE_SX126X + bool "SX126X LoRa Sub-GHz radio" + depends on HAS_PERIPH_GPIO_IRQ + depends on PACKAGE_DRIVER_SX126X + depends on TEST_KCONFIG + select MODULE_PERIPH_GPIO_IRQ + select MODULE_IOLIST diff --git a/drivers/sx126x/Makefile b/drivers/sx126x/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/sx126x/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/sx126x/Makefile.dep b/drivers/sx126x/Makefile.dep new file mode 100644 index 0000000000..c3de4b4ce3 --- /dev/null +++ b/drivers/sx126x/Makefile.dep @@ -0,0 +1,3 @@ +USEMODULE += iolist +USEPKG += driver_sx126x +FEATURES_REQUIRED += periph_gpio_irq diff --git a/drivers/sx126x/Makefile.include b/drivers/sx126x/Makefile.include new file mode 100644 index 0000000000..2d0b8c8b18 --- /dev/null +++ b/drivers/sx126x/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_sx126x := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_sx126x) diff --git a/drivers/sx126x/include/sx126x_netdev.h b/drivers/sx126x/include/sx126x_netdev.h new file mode 100644 index 0000000000..c3de0a4987 --- /dev/null +++ b/drivers/sx126x/include/sx126x_netdev.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_sx126x + * @{ + * @file + * @brief Netdev driver definitions for SX1261/2/8 and LLCC68 driver + * + * @author Alexandre Abadie + */ + +#ifndef SX126X_NETDEV_H +#define SX126X_NETDEV_H + +#include "net/netdev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Reference to the netdev device driver struct + */ +extern const netdev_driver_t sx126x_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* SX126X_NETDEV_H */ +/** @} */ diff --git a/drivers/sx126x/include/sx126x_params.h b/drivers/sx126x/include/sx126x_params.h new file mode 100644 index 0000000000..6c4b95b6b2 --- /dev/null +++ b/drivers/sx126x/include/sx126x_params.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_sx126x + * + * @{ + * @file + * @brief Default configuration + * + * @author Alexandre Abadie + */ + +#ifndef SX126X_PARAMS_H +#define SX126X_PARAMS_H + +#include "board.h" +#include "sx126x.h" +#include "sx126x_driver.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters + * + * Default values are adapted for mbed shield used with to nucleo64 boards + * @{ + */ +#ifndef SX126X_PARAM_SPI +#define SX126X_PARAM_SPI SPI_DEV(0) +#endif + +#ifndef SX126X_PARAM_SPI_NSS +#define SX126X_PARAM_SPI_NSS GPIO_PIN(0, 8) /* D7 */ +#endif + +#ifndef SX126X_PARAM_RESET +#define SX126X_PARAM_RESET GPIO_PIN(0, 0) /* A0 */ +#endif + +#ifndef SX126X_PARAM_BUSY +#define SX126X_PARAM_BUSY GPIO_PIN(1, 3) /* D3 */ +#endif + +#ifndef SX126X_PARAM_DIO1 +#define SX126X_PARAM_DIO1 GPIO_PIN(1, 4) /* D5 */ +#endif + +#ifndef SX126X_PARAM_REGULATOR +#define SX126X_PARAM_REGULATOR SX126X_REG_MODE_DCDC +#endif + +#define SX126X_PARAMS { .spi = SX126X_PARAM_SPI, \ + .nss_pin = SX126X_PARAM_SPI_NSS, \ + .reset_pin = SX126X_PARAM_RESET, \ + .busy_pin = SX126X_PARAM_BUSY, \ + .dio1_pin = SX126X_PARAM_DIO1, \ + .regulator = SX126X_PARAM_REGULATOR } +/**@}*/ + +/** + * @brief Configuration struct + */ +static const sx126x_params_t sx126x_params[] = +{ + SX126X_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SX126X_PARAMS_H */ +/** @} */ diff --git a/drivers/sx126x/sx126x.c b/drivers/sx126x/sx126x.c new file mode 100644 index 0000000000..6567a93061 --- /dev/null +++ b/drivers/sx126x/sx126x.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2021 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_sx126x + * @{ + * + * @file + * @brief Device driver implementation for the SX1261/2/8 and LLCC68 LoRa radio driver + * + * @author Alexandre Abadie + * + * @} + */ + +#include + +#include "sx126x_netdev.h" + +#include "net/lora.h" +#include "periph/spi.h" + +#include "sx126x_driver.h" +#include "sx126x.h" +#include "sx126x_params.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#ifndef CONFIG_SX126X_PKT_TYPE_DEFAULT +#define CONFIG_SX126X_PKT_TYPE_DEFAULT (SX126X_PKT_TYPE_LORA) +#endif + +#ifndef CONFIG_SX126X_CHANNEL_DEFAULT +#define CONFIG_SX126X_CHANNEL_DEFAULT (868300000UL) /* in Hz */ +#endif + +#ifndef CONFIG_SX126X_TX_POWER_DEFAULT +#define CONFIG_SX126X_TX_POWER_DEFAULT (14U) /* in dBm */ +#endif + +#ifndef CONFIG_SX126X_RAMP_TIME_DEFAULT +#define CONFIG_SX126X_RAMP_TIME_DEFAULT (SX126X_RAMP_10_US) +#endif + +void sx126x_setup(sx126x_t *dev, const sx126x_params_t *params, uint8_t index) +{ + netdev_t *netdev = (netdev_t *)dev; + + netdev->driver = &sx126x_driver; + dev->params = (sx126x_params_t *)params; + netdev_register(&dev->netdev, NETDEV_SX126X, index); +} + +static const uint16_t _bw_khz[3] = { + [LORA_BW_125_KHZ] = 125, + [LORA_BW_250_KHZ] = 250, + [LORA_BW_500_KHZ] = 500, +}; + +static uint8_t _compute_ldro(sx126x_t *dev) +{ + uint32_t symbol_len = (uint32_t)(1 << dev->mod_params.sf) / _bw_khz[dev->mod_params.bw - SX126X_LORA_BW_125]; + if (symbol_len >= 16) { + return 0x01; + } + + return 0x00; +} + +static void sx126x_init_default_config(sx126x_t *dev) +{ + /* packet type must be set first */ + sx126x_set_pkt_type(dev, SX126X_PKT_TYPE_LORA); + sx126x_set_channel(dev, CONFIG_SX126X_CHANNEL_DEFAULT); + + /* Configure PA optimal settings for maximum output power + * Values used here comes from the datasheet, section 13.1.14 SetPaConfig + * and are optimal for a TX output power of 14dBm. + */ + if (IS_USED(MODULE_LLCC68) || IS_USED(MODULE_SX1262)) { + sx126x_pa_cfg_params_t pa_cfg = { + .pa_duty_cycle = 0x02, + .hp_max = 0x02, + .device_sel = 0x00, + .pa_lut = 0x01 + }; + sx126x_set_pa_cfg(dev, &pa_cfg); + } + else if (IS_USED(MODULE_SX1268)) { + sx126x_pa_cfg_params_t pa_cfg = { + .pa_duty_cycle = 0x04, + .hp_max = 0x06, + .device_sel = 0x00, + .pa_lut = 0x01 + }; + sx126x_set_pa_cfg(dev, &pa_cfg); + } + else { /* IS_USED(MODULE_SX1261) */ + sx126x_pa_cfg_params_t pa_cfg = { + .pa_duty_cycle = 0x04, + .hp_max = 0x00, + .device_sel = 0x01, + .pa_lut = 0x01 + }; + sx126x_set_pa_cfg(dev, &pa_cfg); + } + sx126x_set_tx_params(dev, CONFIG_SX126X_TX_POWER_DEFAULT, CONFIG_SX126X_RAMP_TIME_DEFAULT); + + dev->mod_params.bw = (sx126x_lora_bw_t)(CONFIG_LORA_BW_DEFAULT + SX126X_LORA_BW_125); + dev->mod_params.sf = (sx126x_lora_sf_t)CONFIG_LORA_SF_DEFAULT; + dev->mod_params.cr = (sx126x_lora_cr_t)CONFIG_LORA_CR_DEFAULT; + dev->mod_params.ldro = _compute_ldro(dev); + sx126x_set_lora_mod_params(dev, &dev->mod_params); + + dev->pkt_params.pld_len_in_bytes = 0; + dev->pkt_params.crc_is_on = LORA_PAYLOAD_CRC_ON_DEFAULT; + dev->pkt_params.header_type = ( + IS_ACTIVE(CONFIG_LORA_FIXED_HEADER_LEN_MODE_DEFAULT) ? true : false + ); + dev->pkt_params.preamble_len_in_symb = CONFIG_LORA_PREAMBLE_LENGTH_DEFAULT; + dev->pkt_params.invert_iq_is_on = ( + IS_ACTIVE(CONFIG_LORA_IQ_INVERTED_DEFAULT) ? true : false + ); + sx126x_set_lora_pkt_params(dev, &dev->pkt_params); +} + +static void _dio1_isr(void *arg) +{ + netdev_trigger_event_isr((netdev_t *)arg); +} + +int sx126x_init(sx126x_t *dev) +{ + /* Setup SPI for SX126X */ + int res = spi_init_cs(dev->params->spi, dev->params->nss_pin); + + if (res != SPI_OK) { + DEBUG("[sx126x] error: failed to initialize SPI_%i device (code %i)\n", + dev->params->spi, res); + return -1; + } + + DEBUG("[sx126x] init: SPI_%i initialized with success\n", dev->params->spi); + + gpio_init(dev->params->reset_pin, GPIO_OUT); + gpio_init(dev->params->busy_pin, GPIO_IN_PD); + + /* Initialize DIOs */ + if (gpio_is_valid(dev->params->dio1_pin)) { + res = gpio_init_int(dev->params->dio1_pin, GPIO_IN, GPIO_RISING, _dio1_isr, dev); + if (res < 0) { + DEBUG("[sx126x] error: failed to initialize DIO1 pin\n"); + return res; + } + } + else { + DEBUG("[sx126x] error: no DIO1 pin defined\n"); + return -EIO; + } + + /* Reset the device */ + sx126x_reset(dev); + + /* Configure the power regulator mode */ + sx126x_set_reg_mode(dev, dev->params->regulator); + + /* Initialize radio with the default parameters */ + sx126x_init_default_config(dev); + + /* Configure available IRQs */ + const uint16_t irq_mask = ( + SX126X_IRQ_TX_DONE | + SX126X_IRQ_RX_DONE | + SX126X_IRQ_PREAMBLE_DETECTED | + SX126X_IRQ_HEADER_VALID | + SX126X_IRQ_HEADER_ERROR | + SX126X_IRQ_CRC_ERROR | + SX126X_IRQ_CAD_DONE | + SX126X_IRQ_CAD_DETECTED | + SX126X_IRQ_TIMEOUT + ); + + sx126x_set_dio_irq_params(dev, irq_mask, irq_mask, 0, 0); + + if (IS_ACTIVE(ENABLE_DEBUG)) { + sx126x_pkt_type_t pkt_type; + sx126x_get_pkt_type(dev, &pkt_type); + DEBUG("[sx126x] init radio: pkt type: %d\n", pkt_type); + + sx126x_chip_status_t radio_status; + sx126x_get_status(dev, &radio_status); + DEBUG("[sx126x] init: chip mode %d\n", radio_status.chip_mode); + DEBUG("[sx126x] init: cmd status %d\n", radio_status.cmd_status); + } + + return res; +} + +uint32_t sx126x_get_channel(const sx126x_t *dev) +{ + return dev->channel; +} + +void sx126x_set_channel(sx126x_t *dev, uint32_t freq) +{ + dev->channel = freq; + sx126x_set_rf_freq(dev, dev->channel); +} + +uint8_t sx126x_get_bandwidth(const sx126x_t *dev) +{ + return dev->mod_params.bw - SX126X_LORA_BW_125; +} + +void sx126x_set_bandwidth(sx126x_t *dev, uint8_t bandwidth) +{ + dev->mod_params.bw = bandwidth + SX126X_LORA_BW_125; + dev->mod_params.ldro = _compute_ldro(dev); + sx126x_set_lora_mod_params(dev, &dev->mod_params); +} + +uint8_t sx126x_get_spreading_factor(const sx126x_t *dev) +{ + return dev->mod_params.sf; +} + +void sx126x_set_spreading_factor(sx126x_t *dev, uint8_t sf) +{ + dev->mod_params.sf = (sx126x_lora_sf_t)sf; + dev->mod_params.ldro = _compute_ldro(dev); + sx126x_set_lora_mod_params(dev, &dev->mod_params); +} + +uint8_t sx126x_get_coding_rate(const sx126x_t *dev) +{ + return dev->mod_params.cr; +} + +void sx126x_set_coding_rate(sx126x_t *dev, uint8_t cr) +{ + dev->mod_params.cr = (sx126x_lora_cr_t)cr; + sx126x_set_lora_mod_params(dev, &dev->mod_params); +} + +uint8_t sx126x_get_lora_payload_length(const sx126x_t *dev) +{ + sx126x_rx_buffer_status_t rx_buffer_status; + + sx126x_get_rx_buffer_status(dev, &rx_buffer_status); + return rx_buffer_status.pld_len_in_bytes; +} + +void sx126x_set_lora_payload_length(sx126x_t *dev, uint8_t len) +{ + dev->pkt_params.pld_len_in_bytes = len; + sx126x_set_lora_pkt_params(dev, &dev->pkt_params); +} + +bool sx126x_get_lora_crc(const sx126x_t *dev) +{ + return dev->pkt_params.crc_is_on; +} + +void sx126x_set_lora_crc(sx126x_t *dev, bool crc) +{ + dev->pkt_params.crc_is_on = crc; + sx126x_set_lora_pkt_params(dev, &dev->pkt_params); +} + +bool sx126x_get_lora_implicit_header(const sx126x_t *dev) +{ + return dev->pkt_params.header_type == SX126X_LORA_PKT_IMPLICIT; +} + +void sx126x_set_lora_implicit_header(sx126x_t *dev, bool mode) +{ + dev->pkt_params.header_type = (mode ? SX126X_LORA_PKT_IMPLICIT : SX126X_LORA_PKT_EXPLICIT); + sx126x_set_lora_pkt_params(dev, &dev->pkt_params); +} + +uint16_t sx126x_get_lora_preamble_length(const sx126x_t *dev) +{ + return dev->pkt_params.preamble_len_in_symb; +} + +void sx126x_set_lora_preamble_length(sx126x_t *dev, uint16_t preamble) +{ + dev->pkt_params.preamble_len_in_symb = preamble; + sx126x_set_lora_pkt_params(dev, &dev->pkt_params); +} + +bool sx126x_get_lora_iq_invert(const sx126x_t *dev) +{ + return dev->pkt_params.invert_iq_is_on; +} + +void sx126x_set_lora_iq_invert(sx126x_t *dev, bool iq_invert) +{ + dev->pkt_params.invert_iq_is_on = iq_invert; + sx126x_set_lora_pkt_params(dev, &dev->pkt_params); +} diff --git a/drivers/sx126x/sx126x_netdev.c b/drivers/sx126x/sx126x_netdev.c new file mode 100644 index 0000000000..0e93e75ef3 --- /dev/null +++ b/drivers/sx126x/sx126x_netdev.c @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2021 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_sx126x + * @{ + * @file + * @brief Netdev adaptation for the SX1261/2/8 and LLCC68 driver + * + * @author Alexandre Abadie + * @} + */ + +#include +#include +#include +#include + +#include "iolist.h" +#include "net/netopt.h" +#include "net/netdev.h" +#include "net/netdev/lora.h" +#include "net/lora.h" + +#include "sx126x.h" +#include "sx126x_netdev.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#if IS_USED(MODULE_LLCC68) +#define SX126X_MAX_SF LORA_SF11 +#else +#define SX126X_MAX_SF LORA_SF12 +#endif + +static int _send(netdev_t *netdev, const iolist_t *iolist) +{ + sx126x_t *dev = (sx126x_t *)netdev; + + netopt_state_t state; + + netdev->driver->get(netdev, NETOPT_STATE, &state, sizeof(uint8_t)); + if (state == NETOPT_STATE_TX) { + DEBUG("[sx126x] netdev: cannot send packet, radio is already transmitting.\n"); + return -ENOTSUP; + } + + uint8_t size = iolist_size(iolist); + + /* Ignore send if packet size is 0 */ + if (!size) { + return 0; + } + + DEBUG("[sx126x] netdev: sending packet now (size: %d).\n", size); + /* Write payload buffer */ + for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { + if (iol->iol_len > 0) { + sx126x_set_lora_payload_length(dev, iol->iol_len); + sx126x_write_buffer(dev, 0, iol->iol_base, iol->iol_len); + DEBUG("[sx126x] netdev: send: wrote data to payload buffer.\n"); + } + } + + state = NETOPT_STATE_TX; + netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(uint8_t)); + DEBUG("[sx126x] netdev: send: transmission in progress.\n"); + + return 0; +} + +static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) +{ + DEBUG("[sx126x] netdev: read received data.\n"); + + sx126x_t *dev = (sx126x_t *)netdev; + uint8_t size = 0; + + netdev_lora_rx_info_t *packet_info = (netdev_lora_rx_info_t *)info; + + if (packet_info) { + sx126x_pkt_status_lora_t pkt_status; + sx126x_get_lora_pkt_status(dev, &pkt_status); + packet_info->snr = pkt_status.snr_pkt_in_db; + packet_info->rssi = pkt_status.rssi_pkt_in_dbm; + } + + sx126x_rx_buffer_status_t rx_buffer_status; + + sx126x_get_rx_buffer_status(dev, &rx_buffer_status); + + size = rx_buffer_status.pld_len_in_bytes; + + if (buf == NULL) { + return size; + } + + if (size > len) { + return -ENOBUFS; + } + + sx126x_read_buffer(dev, rx_buffer_status.buffer_start_pointer, buf, size); + + return 0; +} + +static int _init(netdev_t *netdev) +{ + sx126x_t *dev = (sx126x_t *)netdev; + + /* Launch initialization of driver and device */ + DEBUG("[sx126x] netdev: initializing driver...\n"); + if (sx126x_init(dev) != 0) { + DEBUG("[sx126x] netdev: initialization failed\n"); + return -1; + } + + DEBUG("[sx126x] netdev: initialization successful\n"); + return 0; +} + +static void _isr(netdev_t *netdev) +{ + sx126x_t *dev = (sx126x_t *)netdev; + + sx126x_irq_mask_t irq_mask; + + sx126x_get_and_clear_irq_status(dev, &irq_mask); + + if (irq_mask & SX126X_IRQ_TX_DONE) { + DEBUG("[sx126x] netdev: SX126X_IRQ_TX_DONE\n"); + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); + } + else if (irq_mask & SX126X_IRQ_RX_DONE) { + DEBUG("[sx126x] netdev: SX126X_IRQ_RX_DONE\n"); + netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); + } + else if (irq_mask & SX126X_IRQ_PREAMBLE_DETECTED) { + DEBUG("[sx126x] netdev: SX126X_IRQ_PREAMBLE_DETECTED\n"); + } + else if (irq_mask & SX126X_IRQ_SYNC_WORD_VALID) { + DEBUG("[sx126x] netdev: SX126X_IRQ_SYNC_WORD_VALID\n"); + } + else if (irq_mask & SX126X_IRQ_HEADER_VALID) { + DEBUG("[sx126x] netdev: SX126X_IRQ_HEADER_VALID\n"); + netdev->event_callback(netdev, NETDEV_EVENT_RX_STARTED); + } + else if (irq_mask & SX126X_IRQ_HEADER_ERROR) { + DEBUG("[sx126x] netdev: SX126X_IRQ_HEADER_ERROR\n"); + } + else if (irq_mask & SX126X_IRQ_CRC_ERROR) { + DEBUG("[sx126x] netdev: SX126X_IRQ_CRC_ERROR\n"); + netdev->event_callback(netdev, NETDEV_EVENT_CRC_ERROR); + } + else if (irq_mask & SX126X_IRQ_CAD_DONE) { + DEBUG("[sx126x] netdev: SX126X_IRQ_CAD_DONE\n"); + netdev->event_callback(netdev, NETDEV_EVENT_CAD_DONE); + } + else if (irq_mask & SX126X_IRQ_CAD_DETECTED) { + DEBUG("[sx126x] netdev: SX126X_IRQ_CAD_DETECTED\n"); + } + else if (irq_mask & SX126X_IRQ_TIMEOUT) { + DEBUG("[sx126x] netdev: SX126X_IRQ_TIMEOUT\n"); + netdev->event_callback(netdev, NETDEV_EVENT_RX_TIMEOUT); + } + else { + DEBUG("[sx126x] netdev: SX126X_IRQ_NONE\n"); + } +} + +static int _get_state(sx126x_t *dev, void *val) +{ + sx126x_chip_status_t radio_status; + + sx126x_get_status(dev, &radio_status); + netopt_state_t state = NETOPT_STATE_OFF; + + switch (radio_status.chip_mode) { + case SX126X_CHIP_MODE_RFU: + case SX126X_CHIP_MODE_STBY_RC: + case SX126X_CHIP_MODE_STBY_XOSC: + state = NETOPT_STATE_STANDBY; + break; + + case SX126X_CHIP_MODE_TX: + state = NETOPT_STATE_TX; + break; + + case SX126X_CHIP_MODE_RX: + state = NETOPT_STATE_RX; + break; + + default: + break; + } + memcpy(val, &state, sizeof(netopt_state_t)); + return sizeof(netopt_state_t); +} + +static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) +{ + (void)max_len; /* unused when compiled without debug, assert empty */ + sx126x_t *dev = (sx126x_t *)netdev; + + if (dev == NULL) { + return -ENODEV; + } + + switch (opt) { + case NETOPT_STATE: + assert(max_len >= sizeof(netopt_state_t)); + return _get_state(dev, val); + + case NETOPT_DEVICE_TYPE: + assert(max_len >= sizeof(uint16_t)); + sx126x_pkt_type_t pkt_type; + sx126x_get_pkt_type(dev, &pkt_type); + *((uint16_t *)val) = + (pkt_type == SX126X_PKT_TYPE_LORA) ? NETDEV_TYPE_LORA : NETDEV_TYPE_UNKNOWN; + return sizeof(uint16_t); + + case NETOPT_CHANNEL_FREQUENCY: + assert(max_len >= sizeof(uint32_t)); + *((uint32_t *)val) = sx126x_get_channel(dev); + return sizeof(uint32_t); + + case NETOPT_BANDWIDTH: + assert(max_len >= sizeof(uint8_t)); + *((uint8_t *)val) = sx126x_get_bandwidth(dev); + return sizeof(uint8_t); + + case NETOPT_SPREADING_FACTOR: + assert(max_len >= sizeof(uint8_t)); + *((uint8_t *)val) = sx126x_get_spreading_factor(dev); + return sizeof(uint8_t); + + case NETOPT_CODING_RATE: + assert(max_len >= sizeof(uint8_t)); + *((uint8_t *)val) = sx126x_get_coding_rate(dev); + return sizeof(uint8_t); + + case NETOPT_PDU_SIZE: + assert(max_len >= sizeof(uint8_t)); + *((uint8_t *)val) = sx126x_get_lora_payload_length(dev); + return sizeof(uint8_t); + + case NETOPT_INTEGRITY_CHECK: + assert(max_len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)val) = sx126x_get_lora_crc(dev) ? NETOPT_ENABLE : NETOPT_DISABLE; + return sizeof(netopt_enable_t); + + case NETOPT_RANDOM: + assert(max_len >= sizeof(uint32_t)); + sx126x_get_random_numbers(dev, val, 1); + return sizeof(uint32_t); + + case NETOPT_IQ_INVERT: + assert(max_len >= sizeof(uint8_t)); + *((netopt_enable_t *)val) = sx126x_get_lora_iq_invert(dev) ? NETOPT_ENABLE : NETOPT_DISABLE; + return sizeof(netopt_enable_t); + + case NETOPT_RSSI: + assert(max_len >= sizeof(int8_t)); + sx126x_get_rssi_inst(dev, ((int16_t *)val)); + return sizeof(int8_t); + + default: + break; + } + + return -ENOTSUP; +} + +static int _set_state(sx126x_t *dev, netopt_state_t state) +{ + switch (state) { + case NETOPT_STATE_STANDBY: + DEBUG("[sx126x] netdev: set NETOPT_STATE_STANDBY state\n"); + sx126x_set_standby(dev, SX126X_CHIP_MODE_STBY_XOSC); + break; + + case NETOPT_STATE_IDLE: + case NETOPT_STATE_RX: + DEBUG("[sx126x] netdev: set NETOPT_STATE_RX state\n"); + sx126x_cfg_rx_boosted(dev, true); + if (dev->rx_timeout != 0) { + sx126x_set_rx(dev, dev->rx_timeout); + } + else { + sx126x_set_rx(dev, SX126X_RX_SINGLE_MODE); + } + break; + + case NETOPT_STATE_TX: + DEBUG("[sx126x] netdev: set NETOPT_STATE_TX state\n"); + sx126x_set_tx(dev, 0); + break; + + case NETOPT_STATE_RESET: + DEBUG("[sx126x] netdev: set NETOPT_STATE_RESET state\n"); + sx126x_reset(dev); + break; + + default: + return -ENOTSUP; + } + return sizeof(netopt_state_t); +} + +static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len) +{ + (void)len; /* unused when compiled without debug, assert empty */ + sx126x_t *dev = (sx126x_t *)netdev; + int res = -ENOTSUP; + + if (dev == NULL) { + return -ENODEV; + } + + switch (opt) { + case NETOPT_STATE: + assert(len == sizeof(netopt_state_t)); + return _set_state(dev, *((const netopt_state_t *)val)); + + case NETOPT_DEVICE_TYPE: + assert(len <= sizeof(uint16_t)); + /* Only LoRa modem is supported for the moment */ + if (*(const uint16_t *)val == NETDEV_TYPE_LORA) { + sx126x_set_pkt_type(dev, SX126X_PKT_TYPE_LORA); + return sizeof(uint16_t); + } + else { + return -EINVAL; + } + + case NETOPT_CHANNEL_FREQUENCY: + assert(len <= sizeof(uint32_t)); + sx126x_set_channel(dev, *((const uint32_t *)val)); + return sizeof(uint32_t); + + case NETOPT_BANDWIDTH: + assert(len <= sizeof(uint8_t)); + uint8_t bw = *((const uint8_t *)val); + if (bw > LORA_BW_500_KHZ) { + res = -EINVAL; + break; + } + sx126x_set_bandwidth(dev, bw); + return sizeof(uint8_t); + + case NETOPT_SPREADING_FACTOR: + assert(len <= sizeof(uint8_t)); + uint8_t sf = *((const uint8_t *)val); + if ((sf < LORA_SF6) || (sf > SX126X_MAX_SF)) { + res = -EINVAL; + break; + } + sx126x_set_spreading_factor(dev, sf); + return sizeof(uint8_t); + + case NETOPT_CODING_RATE: + assert(len <= sizeof(uint8_t)); + uint8_t cr = *((const uint8_t *)val); + if ((cr < LORA_CR_4_5) || (cr > LORA_CR_4_8)) { + res = -EINVAL; + break; + } + sx126x_set_coding_rate(dev, cr); + return sizeof(uint8_t); + + case NETOPT_PDU_SIZE: + assert(len <= sizeof(uint8_t)); + sx126x_set_lora_payload_length(dev, *((const uint8_t *)val)); + return sizeof(uint8_t); + + case NETOPT_INTEGRITY_CHECK: + assert(len <= sizeof(netopt_enable_t)); + sx126x_set_lora_crc(dev, *((const netopt_enable_t *)val) ? true : false); + return sizeof(netopt_enable_t); + + case NETOPT_RX_TIMEOUT: + assert(len <= sizeof(uint32_t)); + dev->rx_timeout = *(const uint32_t *)val; + return sizeof(uint32_t); + + case NETOPT_TX_POWER: + assert(len <= sizeof(int16_t)); + int16_t power = *((const int16_t *)val); + if ((power < INT8_MIN) || (power > INT8_MAX)) { + res = -EINVAL; + break; + } + sx126x_set_tx_params(dev, power, SX126X_RAMP_10_US); + return sizeof(int16_t); + + case NETOPT_FIXED_HEADER: + assert(len <= sizeof(netopt_enable_t)); + sx126x_set_lora_implicit_header(dev, *((const netopt_enable_t *)val) ? true : false); + return sizeof(netopt_enable_t); + + case NETOPT_PREAMBLE_LENGTH: + assert(len <= sizeof(uint16_t)); + sx126x_set_lora_preamble_length(dev, *((const uint16_t *)val)); + return sizeof(uint16_t); + + case NETOPT_SYNCWORD: + assert(len <= sizeof(uint8_t)); + sx126x_set_lora_sync_word(dev, *((uint8_t *)val)); + return sizeof(uint8_t); + + case NETOPT_IQ_INVERT: + assert(len <= sizeof(netopt_enable_t)); + sx126x_set_lora_iq_invert(dev, *((const netopt_enable_t *)val) ? true : false); + return sizeof(bool); + + default: + break; + } + + return res; +} + +const netdev_driver_t sx126x_driver = { + .send = _send, + .recv = _recv, + .init = _init, + .isr = _isr, + .get = _get, + .set = _set, +};