From a7d4abd8d222734289835efd96dbcd5eb590aa01 Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sun, 20 Jan 2019 13:03:11 +0000 Subject: [PATCH] Merged in raiden00/nuttx_lora (pull request #821) Initial support for sx127x radio Approved-by: GregoryN --- configs/b-l072z-lrwan1/include/board.h | 21 +- configs/b-l072z-lrwan1/src/Makefile | 10 +- configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h | 51 + configs/b-l072z-lrwan1/src/stm32_appinit.c | 72 +- configs/b-l072z-lrwan1/src/stm32_boot.c | 29 + configs/b-l072z-lrwan1/src/stm32_bringup.c | 128 + configs/b-l072z-lrwan1/src/stm32_spi.c | 195 + configs/b-l072z-lrwan1/src/stm32_sx127x.c | 165 + configs/nucleo-l073rz/src/Makefile | 4 + configs/nucleo-l073rz/src/nucleo-l073rz.h | 23 + configs/nucleo-l073rz/src/stm32_bringup.c | 8 + configs/nucleo-l073rz/src/stm32_spi.c | 34 +- configs/nucleo-l073rz/src/stm32_sx127x.c | 158 + drivers/wireless/Kconfig | 9 + drivers/wireless/Make.defs | 6 + drivers/wireless/lpwan/Kconfig | 65 + drivers/wireless/lpwan/Make.defs | 42 + drivers/wireless/lpwan/sx127x/Make.defs | 49 + drivers/wireless/lpwan/sx127x/sx127x.c | 4322 +++++++++++++++++++ drivers/wireless/lpwan/sx127x/sx127x.h | 869 ++++ include/nuttx/spi/spi.h | 2 + include/nuttx/wireless/ioctl.h | 5 + include/nuttx/wireless/lpwan/sx127x.h | 270 ++ 23 files changed, 6458 insertions(+), 79 deletions(-) create mode 100644 configs/b-l072z-lrwan1/src/stm32_bringup.c create mode 100644 configs/b-l072z-lrwan1/src/stm32_spi.c create mode 100644 configs/b-l072z-lrwan1/src/stm32_sx127x.c create mode 100644 configs/nucleo-l073rz/src/stm32_sx127x.c create mode 100644 drivers/wireless/lpwan/Kconfig create mode 100644 drivers/wireless/lpwan/Make.defs create mode 100644 drivers/wireless/lpwan/sx127x/Make.defs create mode 100644 drivers/wireless/lpwan/sx127x/sx127x.c create mode 100644 drivers/wireless/lpwan/sx127x/sx127x.h create mode 100644 include/nuttx/wireless/lpwan/sx127x.h diff --git a/configs/b-l072z-lrwan1/include/board.h b/configs/b-l072z-lrwan1/include/board.h index 7b6a52901a..c55cbe95d4 100644 --- a/configs/b-l072z-lrwan1/include/board.h +++ b/configs/b-l072z-lrwan1/include/board.h @@ -179,14 +179,15 @@ * PA6 | SX1276_MISO * PB3 | SX1276_SCK * PA15 | SX1276_NSS - * ? | SX1276_DIO0 - * ? | SX1276_DIO2 - * ? | SX1276_DIO3 - * PA5 | SX1276_DIO4 optional + * PB4 | SX1276_DIO0 + * PB1 | SX1276_DIO1 + * PB0 | SX1276_DIO2 + * PC13 | SX1276_DIO3 + * PA5 | SX1276_DIO4 optional / LED5 * PA4 | SX1276_DIO5 optional - * ? | CRF1 - * ? | CRF2 - * ? | CRF3 + * PA1 | CRF1 + * PC1 | CRF2 + * PC2 | CRF3 * PA3 | STLINK Virtual COM RX * PA2 | STLINK Virtual COM TX * PA10 | USART1_RX @@ -195,9 +196,9 @@ * PB14 | SPI2_MISO * PB13 | SPI2_SCK * PB12 | SPI2_NSS - * PB5 | LPTIM1_INI - * PB6 | LPTIM1_ETR - * PB7 | LPTIM1_IN2 + * PB5 | LPTIM1_INI / LED2 + * PB6 | LPTIM1_ETR / LED3 + * PB7 | LPTIM1_IN2 / LED4 * PB2 | LPTIM1_OUT / BUTTON * PA0 | BUTTON (optional) * PB9 | I2C1_SDA diff --git a/configs/b-l072z-lrwan1/src/Makefile b/configs/b-l072z-lrwan1/src/Makefile index af8f9c9d5c..e862eb9238 100644 --- a/configs/b-l072z-lrwan1/src/Makefile +++ b/configs/b-l072z-lrwan1/src/Makefile @@ -36,7 +36,7 @@ -include $(TOPDIR)/Make.defs ASRCS = -CSRCS = stm32_boot.c +CSRCS = stm32_boot.c stm32_bringup.c ifeq ($(CONFIG_ARCH_LEDS),y) CSRCS += stm32_autoleds.c @@ -48,8 +48,16 @@ ifeq ($(CONFIG_ARCH_BUTTONS),y) CSRCS += stm32_buttons.c endif +ifeq ($(CONFIG_STM32F0L0_SPI),y) +CSRCS += stm32_spi.c +endif + ifeq ($(CONFIG_LIB_BOARDCTL),y) CSRCS += stm32_appinit.c endif +ifeq ($(CONFIG_LPWAN_SX127X),y) +CSRCS += stm32_sx127x.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h b/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h index 4ce45e8283..9f8c0c8127 100644 --- a/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h +++ b/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h @@ -92,6 +92,18 @@ #define GPIO_BTN_USER (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_PORTC|GPIO_PIN13) +/* SX1276 + * RESET - PC0 (active low) + * CS - PA15 + * DIO0 - PB4 + */ + +#define GPIO_SX127X_RESET (GPIO_PORTC | GPIO_PIN0) +#define GPIO_SX127X_CS (GPIO_OUTPUT | GPIO_SPEED_HIGH | \ + GPIO_OUTPUT_SET | GPIO_PORTA | GPIO_PIN15) +#define GPIO_SX127X_DIO0 (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI | \ + GPIO_PORTB | GPIO_PIN4) + /**************************************************************************** * Public Data ****************************************************************************/ @@ -100,4 +112,43 @@ * Public Function Prototypes ****************************************************************************/ +/************************************************************************************ + * Name: stm32_bringup + * + * Description: + * Perform architecture-specific initialization + * + * CONFIG_BOARD_INITIALIZE=y : + * Called from board_initialize(). + * + * CONFIG_BOARD_INITIALIZE=y && CONFIG_LIB_BOARDCTL=y : + * Called from the NSH library + * + ************************************************************************************/ + +int stm32_bringup(void); + +/************************************************************************************ + * Name: stm32_spidev_initialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the Nucleo-H743ZI board. + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI +void stm32_spidev_initialize(void); +#endif + +/***************************************************************************** + * Name: stm32_lpwaninitialize + * + * Description: + * Initialize SX127X LPWAN interaface. + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X +int stm32_lpwaninitialize(void); +#endif + #endif /* __CONFIGS_B_L072Z_LRWAN1_SRC_B_L072Z_LRWAN1_H */ diff --git a/configs/b-l072z-lrwan1/src/stm32_appinit.c b/configs/b-l072z-lrwan1/src/stm32_appinit.c index dcba3b386b..09c3501044 100644 --- a/configs/b-l072z-lrwan1/src/stm32_appinit.c +++ b/configs/b-l072z-lrwan1/src/stm32_appinit.c @@ -51,18 +51,6 @@ * Pre-processor Definitions ****************************************************************************/ -#undef HAVE_LEDS -#undef HAVE_DAC - -#if !defined(CONFIG_ARCH_LEDS) && defined(CONFIG_USERLED_LOWER) -# define HAVE_LEDS 1 -#endif - -#if defined(CONFIG_DAC) -# define HAVE_DAC1 1 -# define HAVE_DAC2 1 -#endif - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -94,59 +82,13 @@ int board_app_initialize(uintptr_t arg) { - int ret; +#ifdef CONFIG_BOARD_INITIALIZE + /* Board initialization already performed by board_initialize() */ -#ifdef HAVE_LEDS - /* Register the LED driver */ - - ret = userled_lower_initialize(LED_DRIVER_PATH); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: userled_lower_initialize() failed: %d\n", ret); - return ret; - } -#endif - -#ifdef CONFIG_ADC - /* Initialize ADC and register the ADC driver. */ - - ret = stm32_adc_setup(); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: stm32_adc_setup failed: %d\n", ret); - } -#endif - -#ifdef CONFIG_DAC - /* Initialize DAC and register the DAC driver. */ - - ret = stm32_dac_setup(); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: stm32_dac_setup failed: %d\n", ret); - } -#endif - -#ifdef CONFIG_COMP - /* Initialize COMP and register the COMP driver. */ - - ret = stm32_comp_setup(); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: stm32_comp_setup failed: %d\n", ret); - } -#endif - -#ifdef CONFIG_OPAMP - /* Initialize OPAMP and register the OPAMP driver. */ - - ret = stm32_opamp_setup(); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: stm32_opamp_setup failed: %d\n", ret); - } -#endif - - UNUSED(ret); return OK; +#else + /* Perform board-specific initialization */ + + return stm32_bringup(); +#endif } diff --git a/configs/b-l072z-lrwan1/src/stm32_boot.c b/configs/b-l072z-lrwan1/src/stm32_boot.c index 87aba8bff2..3106e3a580 100644 --- a/configs/b-l072z-lrwan1/src/stm32_boot.c +++ b/configs/b-l072z-lrwan1/src/stm32_boot.c @@ -84,4 +84,33 @@ void stm32_boardinitialize(void) board_autoled_initialize(); #endif +#ifdef CONFIG_STM32F0L0_SPI + /* Configure SPI chip selects */ + + stm32_spidev_initialize(); +#endif } + +/************************************************************************************ + * Name: board_initialize + * + * Description: + * If CONFIG_BOARD_INITIALIZE is selected, then an additional initialization call + * will be performed in the boot-up sequence to a function called + * board_initialize(). board_initialize() will be called immediately after + * up_initialize() is called and just before the initial application is started. + * This additional initialization phase may be used, for example, to initialize + * board-specific device drivers. + * + ************************************************************************************/ + +#ifdef CONFIG_BOARD_INITIALIZE +void board_initialize(void) +{ +#if defined(CONFIG_NSH_LIBRARY) && !defined(CONFIG_LIB_BOARDCTL) + /* Perform board bring-up here instead of from the board_app_initialize(). */ + + (void)stm32_bringup(); +#endif +} +#endif diff --git a/configs/b-l072z-lrwan1/src/stm32_bringup.c b/configs/b-l072z-lrwan1/src/stm32_bringup.c new file mode 100644 index 0000000000..ca10239914 --- /dev/null +++ b/configs/b-l072z-lrwan1/src/stm32_bringup.c @@ -0,0 +1,128 @@ +/**************************************************************************** + * configs/b-l072z-lrwan1/src/stm32_bringup.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Authors: Mateusz Szafoni + * + * 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 NuttX 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "b-l072z-lrwan1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#undef HAVE_LEDS +#undef HAVE_DAC + +#if !defined(CONFIG_ARCH_LEDS) && defined(CONFIG_USERLED_LOWER) +# define HAVE_LEDS 1 +#endif + +#if defined(CONFIG_DAC) +# define HAVE_DAC1 1 +# define HAVE_DAC2 1 +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: stm32_bringup + * + * Description: + * Perform architecture-specific initialization + * + * CONFIG_BOARD_INITIALIZE=y : + * Called from board_initialize(). + * + * CONFIG_BOARD_INITIALIZE=n && CONFIG_LIB_BOARDCTL=y && CONFIG_NSH_ARCHINIT: + * Called from the NSH library + * + ****************************************************************************/ + +int stm32_bringup(void) +{ + int ret; + +#ifdef HAVE_LEDS + /* Register the LED driver */ + + ret = userled_lower_initialize(LED_DRIVER_PATH); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: userled_lower_initialize() failed: %d\n", ret); + return ret; + } +#endif + +#ifdef CONFIG_ADC + /* Initialize ADC and register the ADC driver. */ + + ret = stm32_adc_setup(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_adc_setup failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_DAC + /* Initialize DAC and register the DAC driver. */ + + ret = stm32_dac_setup(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_dac_setup failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_LPWAN_SX127X + ret = stm32_lpwaninitialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize wireless driver: %d\n", ret); + } +#endif /* CONFIG_LPWAN_SX127X */ + + UNUSED(ret); + return OK; +} diff --git a/configs/b-l072z-lrwan1/src/stm32_spi.c b/configs/b-l072z-lrwan1/src/stm32_spi.c new file mode 100644 index 0000000000..1e9a455214 --- /dev/null +++ b/configs/b-l072z-lrwan1/src/stm32_spi.c @@ -0,0 +1,195 @@ +/**************************************************************************** + * configs/b-l072z-lrwan1/src/stm32_spi.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * 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 NuttX 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "up_arch.h" +#include "chip.h" +#include "stm32_gpio.h" +#include "stm32_spi.h" + +#include "b-l072z-lrwan1.h" +#include + +#ifdef CONFIG_STM32F0L0_SPI + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/************************************************************************************ + * Name: stm32_spidev_initialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the Nucleo-144 board. + * + ************************************************************************************/ + +void stm32_spidev_initialize(void) +{ + /* NOTE: Clocking for SPI1 and/or SPI2 was already provided in stm32_rcc.c. + * Configurations of SPI pins is performed in stm32_spi.c. + * Here, we only initialize chip select pins unique to the board + * architecture. + */ + +#ifdef CONFIG_STM32F0L0_SPI1 +# ifdef CONFIG_LPWAN_SX127X + /* Configure the SPI-based SX127X chip select GPIO */ + + spiinfo("Configure GPIO for SX127X SPI1/CS\n"); + + stm32_configgpio(GPIO_SX127X_CS); + stm32_gpiowrite(GPIO_SX127X_CS, true); +# endif +#endif +} + +/**************************************************************************** + * Name: stm32_spi1/2/select and stm32_spi1/2/status + * + * Description: + * The external functions, stm32_spi1/2select and stm32_spi1/2status + * must be provided by board-specific logic. They are implementations of + * the select and status methods of the SPI interface defined by struct + * spi_ops_s (see include/nuttx/spi/spi.h). All other methods (including + * stm32_spibus_initialize()) are provided by common STM32 logic. To use this + * common SPI logic on your board: + * + * 1. Provide logic in stm32_boardinitialize() to configure SPI chip select + * pins. + * 2. Provide stm32_spi1/2select() and stm32_spi1/2status() functions + * in your board-specific logic. These functions will perform chip + * selection and status operations using GPIOs in the way your board is + * configured. + * 3. Add a calls to stm32_spibus_initialize() in your low level application + * initialization logic + * 4. The handle returned by stm32_spibus_initialize() may then be used to bind + * the SPI driver to higher level logic (e.g., calling + * mmcsd_spislotinitialize(), for example, will bind the SPI driver to + * the SPI MMC/SD driver). + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI1 +void stm32_spi1select(FAR struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + + switch (devid) + { +#ifdef CONFIG_LPWAN_SX127X + case SPIDEV_LPWAN(0): + { + spiinfo("SX127X device %s\n", selected ? "asserted" : "de-asserted"); + + /* Set the GPIO low to select and high to de-select */ + + stm32_gpiowrite(GPIO_SX127X_CS, !selected); + break; + } +#endif + default: + { + break; + } + } +} + +uint8_t stm32_spi1status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + uint8_t status = 0; + + switch (devid) + { +#ifdef CONFIG_LPWAN_SX127X + case SPIDEV_LPWAN(0): + { + status |= SPI_STATUS_PRESENT; + break; + } +#endif + default: + { + break; + } + } + + return status; +} +#endif /* CONFIG_STM32F0L0_SPI1 */ + +#ifdef CONFIG_STM32F0L0_SPI2 +void stm32_spi2select(FAR struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); +} + +uint8_t stm32_spi2status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} +#endif /* CONFIG_STM32F0L0_SPI2 */ + +#endif /* CONFIG_STM32F0L0_SPI */ diff --git a/configs/b-l072z-lrwan1/src/stm32_sx127x.c b/configs/b-l072z-lrwan1/src/stm32_sx127x.c new file mode 100644 index 0000000000..4ee0f6eec5 --- /dev/null +++ b/configs/b-l072z-lrwan1/src/stm32_sx127x.c @@ -0,0 +1,165 @@ +/**************************************************************************** + * configs/b-l072z-lrwan1/src/stm32_boot.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Authors: Mateusz Szafoni + * + * 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 NuttX 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "stm32_gpio.h" +#include "stm32_exti.h" +#include "stm32_spi.h" + +#include "b-l072z-lrwan1.h" + +/* WARNING: SX1276 on my CMWX1ZZABZ-091 module suddenly stopped + * working (no SPI communication), so there might be a bug here, + * something is missing in the configuration or something else. + */ + +#warning SX127X driver support for B-L072Z-LRWAN1 needs some additional verification! + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* SX127X on SPI1 bus */ + +#define SX127X_SPI 1 + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void sx127x_chip_reset(void); +static int sx127x_irq0_attach(xcpt_t isr, FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct sx127x_lower_s lower = +{ + .irq0attach = sx127x_irq0_attach, + .reset = sx127x_chip_reset +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int sx127x_irq0_attach(xcpt_t isr, FAR void *arg) +{ + wlinfo("Attach DIO0 IRQ\n"); + + /* IRQ on rising edge */ + + (void)stm32_gpiosetevent(GPIO_SX127X_DIO0, false, true, false, isr, arg); + return OK; +} + +static void sx127x_chip_reset(void) +{ + wlinfo("SX127X RESET\n"); + + /* Configure reset as output */ + + stm32_configgpio(GPIO_SX127X_RESET|GPIO_OUTPUT|GPIO_SPEED_HIGH|GPIO_OUTPUT_SET); + + /* Set pin to zero */ + + stm32_gpiowrite(GPIO_SX127X_RESET, false); + + /* Wait 1 ms */ + + usleep(1000); + + /* Configure reset as input */ + + stm32_configgpio(GPIO_SX127X_RESET|GPIO_INPUT|GPIO_FLOAT); + + /* Wait 10 ms */ + + usleep(10000); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int stm32_lpwaninitialize(void) +{ + FAR struct spi_dev_s *spidev; + int ret = OK; + + wlinfo("Register the sx127x module\n"); + + /* Setup DIO0 */ + + stm32_configgpio(GPIO_SX127X_DIO0); + + /* Init SPI bus */ + + spidev = stm32_spibus_initialize(SX127X_SPI); + if (!spidev) + { + wlerr("ERROR: Failed to initialize SPI %d bus\n", SX127X_SPI); + ret = -ENODEV; + goto errout; + } + + /* Initialize SX127X */ + + ret = sx127x_register(spidev, &lower); + if (ret < 0) + { + wlerr("ERROR: Failed to register sx127x\n"); + goto errout; + } + +errout: + return ret; +} diff --git a/configs/nucleo-l073rz/src/Makefile b/configs/nucleo-l073rz/src/Makefile index 3057ce5adc..1d2a20c705 100644 --- a/configs/nucleo-l073rz/src/Makefile +++ b/configs/nucleo-l073rz/src/Makefile @@ -60,4 +60,8 @@ ifeq ($(CONFIG_WL_NRF24L01),y) CSRCS += stm32_nrf24l01.c endif +ifeq ($(CONFIG_LPWAN_SX127X),y) +CSRCS += stm32_sx127x.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/nucleo-l073rz/src/nucleo-l073rz.h b/configs/nucleo-l073rz/src/nucleo-l073rz.h index b89aedfb47..13ec9ba61b 100644 --- a/configs/nucleo-l073rz/src/nucleo-l073rz.h +++ b/configs/nucleo-l073rz/src/nucleo-l073rz.h @@ -99,6 +99,18 @@ GPIO_OUTPUT_CLEAR | GPIO_PORTA | GPIO_PIN9) #define GPIO_NRF24L01_IRQ (GPIO_INPUT | GPIO_FLOAT | GPIO_PORTC | GPIO_PIN7) +/* Dragino LORA shield (v1.4) - RF98 module (based on SX127X) + * RESET - PC7 (PD9) + * CS - PB6 (PD10) + * DIO0 - PA10 (D2) + */ + +#define GPIO_SX127X_RESET (GPIO_PORTC | GPIO_PIN7) +#define GPIO_SX127X_CS (GPIO_OUTPUT | GPIO_SPEED_HIGH | \ + GPIO_OUTPUT_SET | GPIO_PORTB | GPIO_PIN6) +#define GPIO_SX127X_DIO0 (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI | \ + GPIO_PORTA | GPIO_PIN10) + /**************************************************************************** * Public Data ****************************************************************************/ @@ -146,4 +158,15 @@ void stm32_spidev_initialize(void); int stm32_wlinitialize(void); #endif +/***************************************************************************** + * Name: stm32_lpwaninitialize + * + * Description: + * Initialize SX127X LPWAN interaface. + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X +int stm32_lpwaninitialize(void); +#endif + #endif /* __CONFIGS_NUCLEO_L073RZ_SRC_NUCLEO_L073RZ_H */ diff --git a/configs/nucleo-l073rz/src/stm32_bringup.c b/configs/nucleo-l073rz/src/stm32_bringup.c index 9d9749548f..78453a4031 100644 --- a/configs/nucleo-l073rz/src/stm32_bringup.c +++ b/configs/nucleo-l073rz/src/stm32_bringup.c @@ -143,6 +143,14 @@ int stm32_bringup(void) } #endif /* CONFIG_WL_NRF24L01 */ +#ifdef CONFIG_LPWAN_SX127X + ret = stm32_lpwaninitialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize wireless driver: %d\n", ret); + } +#endif /* CONFIG_LPWAN_SX127X */ + UNUSED(ret); return OK; } diff --git a/configs/nucleo-l073rz/src/stm32_spi.c b/configs/nucleo-l073rz/src/stm32_spi.c index 54848125c7..2d377898ea 100644 --- a/configs/nucleo-l073rz/src/stm32_spi.c +++ b/configs/nucleo-l073rz/src/stm32_spi.c @@ -37,7 +37,6 @@ * Included Files ****************************************************************************/ - #include #include @@ -94,15 +93,26 @@ void stm32_spidev_initialize(void) */ #ifdef CONFIG_STM32F0L0_SPI1 + # ifdef CONFIG_WL_NRF24L01 /* Configure the SPI-based NRF24L01 chip select GPIO */ - spiinfo("Configure GPIO for SPI1/CS\n"); + spiinfo("Configure GPIO for NRF24L01 SPI1/CS\n"); stm32_configgpio(GPIO_NRF24L01_CS); stm32_gpiowrite(GPIO_NRF24L01_CS, true); # endif -#endif + +# ifdef CONFIG_LPWAN_SX127X + /* Configure the SPI-based SX127X chip select GPIO */ + + spiinfo("Configure GPIO for SX127X SPI1/CS\n"); + + stm32_configgpio(GPIO_SX127X_CS); + stm32_gpiowrite(GPIO_SX127X_CS, true); +# endif + +#endif /* CONFIG_STM32F0L0_SPI1 */ } /**************************************************************************** @@ -149,6 +159,17 @@ void stm32_spi1select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) stm32_gpiowrite(GPIO_NRF24L01_CS, !selected); break; } +#endif +#ifdef CONFIG_LPWAN_SX127X + case SPIDEV_LPWAN(0): + { + spiinfo("SX127X device %s\n", selected ? "asserted" : "de-asserted"); + + /* Set the GPIO low to select and high to de-select */ + + stm32_gpiowrite(GPIO_SX127X_CS, !selected); + break; + } #endif default: { @@ -169,6 +190,13 @@ uint8_t stm32_spi1status(FAR struct spi_dev_s *dev, uint32_t devid) status |= SPI_STATUS_PRESENT; break; } +#endif +#ifdef CONFIG_LPWAN_SX127X + case SPIDEV_LPWAN(0): + { + status |= SPI_STATUS_PRESENT; + break; + } #endif default: { diff --git a/configs/nucleo-l073rz/src/stm32_sx127x.c b/configs/nucleo-l073rz/src/stm32_sx127x.c new file mode 100644 index 0000000000..0ac0c47666 --- /dev/null +++ b/configs/nucleo-l073rz/src/stm32_sx127x.c @@ -0,0 +1,158 @@ +/**************************************************************************** + * configs/nucleo-l073rz/src/stm32_rfm9x.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Authors: Mateusz Szafoni + * + * 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 NuttX 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "stm32_gpio.h" +#include "stm32_exti.h" +#include "stm32_spi.h" + +#include "nucleo-l073rz.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* SX127X on SPI1 bus */ + +#define SX127X_SPI 1 + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void sx127x_chip_reset(void); +static int sx127x_irq0_attach(xcpt_t isr, FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct sx127x_lower_s lower = +{ + .irq0attach = sx127x_irq0_attach, + .reset = sx127x_chip_reset +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int sx127x_irq0_attach(xcpt_t isr, FAR void *arg) +{ + wlinfo("Attach DIO0 IRQ\n"); + + /* IRQ on rising edge */ + + (void)stm32_gpiosetevent(GPIO_SX127X_DIO0, true, false, false, isr, arg); + return OK; +} + +static void sx127x_chip_reset(void) +{ + wlinfo("SX127X RESET\n"); + + /* Configure reset as output */ + + stm32_configgpio(GPIO_SX127X_RESET|GPIO_OUTPUT|GPIO_SPEED_HIGH|GPIO_OUTPUT_SET); + + /* Set pin to zero */ + + stm32_gpiowrite(GPIO_SX127X_RESET, false); + + /* Wait 1 ms */ + + usleep(1000); + + /* Configure reset as input */ + + stm32_configgpio(GPIO_SX127X_RESET|GPIO_INPUT|GPIO_FLOAT); + + /* Wait 10 ms */ + + usleep(10000); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int stm32_lpwaninitialize(void) +{ + FAR struct spi_dev_s *spidev; + int ret = OK; + + wlinfo("Register the sx127x module\n"); + + /* Setup DIO0 */ + + stm32_configgpio(GPIO_SX127X_DIO0); + + /* Init SPI bus */ + + spidev = stm32_spibus_initialize(SX127X_SPI); + if (!spidev) + { + wlerr("ERROR: Failed to initialize SPI %d bus\n", SX127X_SPI); + ret = -ENODEV; + goto errout; + } + + /* Initialize SX127X */ + + ret = sx127x_register(spidev, &lower); + if (ret < 0) + { + wlerr("ERROR: Failed to register sx127x\n"); + goto errout; + } + +errout: + return ret; +} diff --git a/drivers/wireless/Kconfig b/drivers/wireless/Kconfig index 202f9d50cc..8be349fe90 100644 --- a/drivers/wireless/Kconfig +++ b/drivers/wireless/Kconfig @@ -50,6 +50,14 @@ menuconfig DRIVERS_BLUETOOTH source drivers/wireless/bluetooth/Kconfig +menuconfig DRIVERS_LPWAN + bool "LPWAN Device Support" + default n + ---help--- + Select to enable building of LPWAN device drivers. + +source drivers/wireless/lpwan/Kconfig + config WL_NRF24L01 bool "nRF24l01+ transceiver support" default n @@ -96,4 +104,5 @@ config WL_NRF24L01_RXFIFO_LEN endif # WL_NRF24L01_RXSUPPORT endif # WL_NRF24L01 + endif # DRIVERS_WIRELESS diff --git a/drivers/wireless/Make.defs b/drivers/wireless/Make.defs index e9a67ec2b6..c8649b84b2 100644 --- a/drivers/wireless/Make.defs +++ b/drivers/wireless/Make.defs @@ -53,6 +53,12 @@ ifeq ($(CONFIG_DRIVERS_BLUETOOTH),y) include wireless$(DELIM)bluetooth$(DELIM)Make.defs endif +# Include LPWAN support + +ifeq ($(CONFIG_DRIVERS_LPWAN),y) +include wireless$(DELIM)lpwan$(DELIM)Make.defs +endif + # Include wireless drivers ifeq ($(CONFIG_WL_CC1101),y) diff --git a/drivers/wireless/lpwan/Kconfig b/drivers/wireless/lpwan/Kconfig new file mode 100644 index 0000000000..3f4063db2b --- /dev/null +++ b/drivers/wireless/lpwan/Kconfig @@ -0,0 +1,65 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +if DRIVERS_LPWAN + +config LPWAN_SX127X + bool "SX127X Low Power Long Range transceiver support" + default n + select SPI + depends on EXPERIMENTAL + ---help--- + This options adds driver support for the Samtech SX127X chip. + +if LPWAN_SX127X + +config LPWAN_SX127X_RXSUPPORT + bool "SX127X RX support" + default n + +if LPWAN_SX127X_RXSUPPORT + +config LPWAN_SX127X_RXFIFO_LEN + int "SX127X RX FIFO length" + default 5 + +config LPWAN_SX127X_RXFIFO_DATA_LEN + int "SX127X RX FIFO data length" + default 64 + +endif #LPWAN_SX127X_RXSUPPORT + +config LPWAN_SX127X_TXSUPPORT + bool "SX127X TX support" + default n + +config LPWAN_SX127X_LORA + bool "SX127X LORA support" + default y + +config LPWAN_SX127X_FSKOOK + bool "SX127X FSK/OOK support" + default y + +choice + prompt "SX127X modulation default" + +config LPWAN_SX127X_DEFAULT_LORA + bool "SX127X default LORA modualtion" + depends on LPWAN_SX127X_LORA + +config LPWAN_SX127X_DEFAULT_FSK + bool "SX127X default FSK modulation" + depends on LPWAN_SX127X_FSKOOK + +config LPWAN_SX127X_DEFAULT_OOK + bool "SX127X default OOK modulation" + depends on LPWAN_SX127X_FSKOOK + +endchoice + +endif # WL_SX127X + +endif # DRIVERS_LPWAN diff --git a/drivers/wireless/lpwan/Make.defs b/drivers/wireless/lpwan/Make.defs new file mode 100644 index 0000000000..90563b7ac0 --- /dev/null +++ b/drivers/wireless/lpwan/Make.defs @@ -0,0 +1,42 @@ +############################################################################ +# drivers/lpwan/Make.defs +# +# Copyright (C) 2019 Gregory Nutt. All rights reserved. +# Author: Mateusz Szafoni +# +# 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 NuttX 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 nothing if LPWAN is disabled + +ifeq ($(CONFIG_DRIVERS_LPWAN),y) + +include wireless$(DELIM)lpwan$(DELIM)sx127x$(DELIM)Make.defs + +endif # CONFIG_DRIVERS_LPWAN diff --git a/drivers/wireless/lpwan/sx127x/Make.defs b/drivers/wireless/lpwan/sx127x/Make.defs new file mode 100644 index 0000000000..6fd2cd3cf8 --- /dev/null +++ b/drivers/wireless/lpwan/sx127x/Make.defs @@ -0,0 +1,49 @@ +############################################################################ +# drivers/lpwan/sx127x/Make.defs +# +# Copyright (C) 2019 Gregory Nutt. All rights reserved. +# Author: Mateusz Szafoni +# +# 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 NuttX 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 SX127X drivers into the build + +ifeq ($(CONFIG_LPWAN_SX127X),y) + +CSRCS += sx127x.c + +# Include SX127X build support + +DEPPATH += --dep-path wireless$(DELIM)lpwan$(DELIM)sx127x +VPATH += :wireless$(DELIM)lpwan$(DELIM)sx127x +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)wireless$(DELIM)lpwan$(DELIM)sx127x} + +endif # CONFIG_LPWAN_SX127X + diff --git a/drivers/wireless/lpwan/sx127x/sx127x.c b/drivers/wireless/lpwan/sx127x/sx127x.c new file mode 100644 index 0000000000..05cf22b6f1 --- /dev/null +++ b/drivers/wireless/lpwan/sx127x/sx127x.c @@ -0,0 +1,4322 @@ +/**************************************************************************** + * drivers/wireless/sx127x.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * 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 NuttX 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "sx127x.h" + +/* This driver is WIP and depends on EXPERIMENTA flag mainly because + * the communication betweew modules (RX+TX) hasn't been fully tested. + * + * TODO: + * - Channel Activity Detection (CAD) for LORA + * - frequency hopping for LORA and FSK/OOK + * - modulation shaping for FSK/OOK + * - support for long payload for FSK/OOK (len > FIFO size) + * - transmitter/receiver configuration + */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if !defined(CONFIG_SCHED_HPWORK) +# error SX127X requires CONFIG_SCHED_HPWORK +#endif + +/* Configuration ************************************************************/ + +/* Default SPI bus frequency (in Hz) up to 10MHz */ + +#define SX127X_SPIFREQ (1000000) +#define SX127X_MODULATION_DEFAULT SX127X_MODULATION_FSK + +/* Device name */ + +#define SX127X_DEV_NAME "/dev/sx127x" + +/* Default modulation */ + +#define SX127X_MODULATION_DEFAULT SX127X_MODULATION_FSK + +/* Payload fixlen default */ + +#define SX127X_RX_FIXLEN_DEFAULT (0xff) + +/* Calibration frequency (TODO: Kconfig) */ + +#define SX127X_FREQ_CALIBRATION (868000000) + +/* FSK/OOK bandwidth default */ + +#define SX127X_FSKOOK_RXBW_DEFAULT FSKOOK_BANDWIDTH_2p6kHz +#define SX127X_FSKOOK_AFCBW_DEFAULT FSKOOK_BANDWIDTH_2p6kHz + +/* Default LORA bandwidth */ + +#define SX127X_LRM_BW_DEFAULT LORA_BANDWIDTH_7p8kHz + +/* Default SF for LORA */ + +#define SX127X_LRM_SF_DEFAULT (7) + +/* Disable implict header for LORA at default */ + +#define SX127X_LRM_IMPLICTHDR_DEFAULT (false) + +/* FSK/OOK RX/TX FIFO size (two separate FIFOs) */ + +#define SX127X_FOM_FIFO_LEN (64) + +/* LORA RX/TX FIFO size (one FIFO) */ + +#define SX127X_LRM_FIFO_LEN (256) + +/* FSK default frequency deviation is 5 kHz */ + +#define SX127X_FREQ_DEV_DEFAULT (5000) + +/* Default preamble length for LORA and FSK/OOK */ + +#define SX127X_PREAMBLE_LEN_DEFAULT (8) + +/* LORA maximum payload length */ + +#define SX127X_LRM_PAYLOADMAX_DEFAULT (0xff) + +/* FSK/OOK default shaping configuration */ + +#define SX127X_FSKOOK_SHAPING_DEFAULT SX127X_CMN_PARAMP_SHAPING_NONE + +/* FSK/OOK default PARAMP configuration */ + +#define SX127X_FSKOOK_PARAMP_DEFAULT SX127X_CMN_PARAMP_PARAMP_40us + +/* Default code rate for LORA */ + +#define SX127X_LRM_CR_DEFAULT LORA_CR_4d5 + +/* Default IDLE mode */ + +#define SX127X_IDLE_OPMODE SX127X_OPMODE_STANDBY + +/* Total size for local RX FIFO */ + +#define SX127X_RXFIFO_TOTAL_SIZE (SX127X_RXFIFO_ITEM_SIZE*CONFIG_LPWAN_SX127X_RXFIFO_LEN) + +/* Some assertions */ + +#if CONFIG_LPWAN_SX127X_RXFIFO_DATA_LEN > SX127X_FOM_FIFO_LEN +# warning RX data length limited by chip RX FIFO size (FSK/OOK = 64, LORA = 256) +#endif + +/**************************************************************************** + * Private Data Types + ****************************************************************************/ + +/* SPI access mode */ + +typedef enum +{ + MODE_READ, + MODE_WRITE +} sx127x_access_mode_t; + +/* SX127X modulation specific ops */ + +struct sx127x_dev_s; +struct sx127x_priv_ops_s +{ + /* Initialize configuration for modulation */ + + void (*init)(FAR struct sx127x_dev_s *dev); + + /* Process IRQ 0 */ + + int (*isr0_process)(FAR struct sx127x_dev_s *dev); + + /* Operation mode initialization */ + + int (*opmode_init)(FAR struct sx127x_dev_s *dev, uint8_t opmode); + + /* Change operation mode */ + + int (*opmode_set)(FAR struct sx127x_dev_s *dev, uint8_t opmode); + + /* Set preamble length */ + + void (*preamble_set)(FAR struct sx127x_dev_s *dev, uint32_t len); + + /* Get preamble length */ + + int (*preamble_get)(FAR struct sx127x_dev_s *dev); + + /* Get current RSSI */ + + int16_t (*rssi_get)(FAR struct sx127x_dev_s *dev); + + /* Set sync word */ + + int (*syncword_set)(FAR struct sx127x_dev_s *dev, uint8_t *sw, uint8_t len); + + /* Get sync word */ + + void (*syncword_get)(FAR struct sx127x_dev_s *dev, uint8_t *sw, uint8_t *len); + +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + /* Send packet */ + + int (*send)(FAR struct sx127x_dev_s *dev, FAR const uint8_t *data, + size_t datalen); +#endif +#ifdef CONFIG_DEBUG_WIRELESS_INFO + /* Dump registers for given modulation */ + + void (*dumpregs)(FAR struct sx127x_dev_s *dev); +#endif +}; + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + +/* FSK/OOK private data */ + +struct sx127x_fskook_s +{ + uint32_t bitrate; /* Bitrate */ + uint32_t fdev; /* Frequency deviation */ + uint8_t rx_bw; /* RX bandwidth */ + uint8_t afc_bw; /* AFC bandwidth */ + bool fixlen; /* Fix length */ + bool seqon; /* Sequencer enabled */ +}; +#endif + +#ifdef CONFIG_LPWAN_SX127X_LORA + +/* LORA private data */ + +struct sx127x_lora_s +{ + uint32_t freqhop; /* Frequency hopping (not supported) */ + uint8_t bw; /* LORA banwidth */ + uint8_t sf; /* Spreading factor */ + uint8_t cr; /* Coding rate */ + bool implicthdr; /* Implict header mode ON */ + bool invert_iq; /* Invert I and Q signals */ +}; +#endif + +/* SX127X private data */ + +struct sx127x_dev_s +{ + /* Reference to SPI bus device */ + + FAR struct spi_dev_s *spi; + + /* Low-level MCU-specific support */ + + FAR const struct sx127x_lower_s *lower; + + /* Operations specific for selected modulation scheme */ + + struct sx127x_priv_ops_s ops; + struct work_s irq0_work; /* Interrupt DIO0 handling "bottom half" */ + + uint32_t freq; /* RF carrier frequency */ + uint8_t modulation; /* Current modulation (LORA/FSK/OOK) */ + uint8_t opmode; /* Current operation mode */ + uint8_t idle; /* IDLE opmode */ + bool crcon; /* TX/RX CRC enable */ + bool rx_cont; /* RX in continuous mode (not supported) */ + bool tx_cont; /* TX in continuous mode (not supported) */ + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + struct sx127x_fskook_s fskook; /* FSK/OOK modulation specific data */ +#endif +#ifdef CONFIG_LPWAN_SX127X_LORA + struct sx127x_lora_s lora; /* LORA modulation specific data */ +#endif + +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + sem_t tx_sem; /* Wait for availability of send data */ + uint32_t tx_timeout; /* TX timeout (not supported) */ +#endif +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + uint32_t rx_timeout; /* RX timeout (not supported) */ + uint16_t rx_fifo_len; /* Number of bytes stored in fifo */ + uint16_t nxt_read; /* Next read index */ + uint16_t nxt_write; /* Next write index */ + + /* Circular RX packet buffer */ + + uint8_t rx_buffer[SX127X_RXFIFO_TOTAL_SIZE]; + sem_t rx_sem; /* Wait for availability of received data */ + sem_t rx_buffer_sem; /* Protect access to rx fifo */ +#endif + + uint8_t nopens; /* Number of times the device has been opened */ + sem_t dev_sem; /* Ensures exclusive access to this structure */ +#ifndef CONFIG_DISABLE_POLL + FAR struct pollfd *pfd; /* Polled file descr (or NULL if any) */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Low-level SPI helpres */ + +static void sx127x_lock(FAR struct spi_dev_s *spi); +static void sx127x_unlock(FAR struct spi_dev_s *spi); +static uint8_t sx127x_readregbyte(FAR struct sx127x_dev_s *dev, uint8_t reg); +static void sx127x_writeregbyte(FAR struct sx127x_dev_s *dev, uint8_t reg, + uint8_t value); +static uint8_t sx127x_modregbyte(FAR struct sx127x_dev_s *dev, uint8_t reg, + uint8_t setbits, uint8_t clrbits); + +/* LORA specific functions */ + +#ifdef CONFIG_LPWAN_SX127X_LORA +static void sx127x_lora_init(FAR struct sx127x_dev_s *dev); +static int16_t sx127x_lora_rssi_get(FAR struct sx127x_dev_s *dev); +static int16_t sx127x_lora_rssi_correct(FAR struct sx127x_dev_s *dev, + uint32_t freq, int8_t snr, uint8_t regval); +static void sx127x_lora_preamble_set(FAR struct sx127x_dev_s *dev, uint32_t len); +static int sx127x_lora_preamble_get(FAR struct sx127x_dev_s *dev); +static int sx127x_lora_opmode_set(FAR struct sx127x_dev_s *dev, uint8_t opmode); +static int sx127x_lora_opmode_init(FAR struct sx127x_dev_s *dev, uint8_t opmode); +static int sx127x_lora_syncword_set(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t len); +static void sx127x_lora_syncword_get(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t *len); + +# ifdef CONFIG_LPWAN_SX127X_RXSUPPORT +static int8_t sx127x_lora_snr_get(FAR struct sx127x_dev_s *dev); +static int16_t sx127x_lora_pckrssi_get(FAR struct sx127x_dev_s *dev, int8_t snr); +static int sx127x_lora_rxhandle(FAR struct sx127x_dev_s *dev); +# endif +# ifdef CONFIG_LPWAN_SX127X_TXSUPPORT +static int sx127x_lora_send(FAR struct sx127x_dev_s *dev, FAR const uint8_t *data, + size_t datalen); +# endif +# ifdef CONFIG_DEBUG_WIRELESS_INFO +static void sx127x_lora_dumpregs(FAR struct sx127x_dev_s *dev); +# endif +#endif + +/* FSK/OOK specific functions */ + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK +static void sx127x_fskook_init(FAR struct sx127x_dev_s *dev); +static int sx127x_fskook_fdev_set(FAR struct sx127x_dev_s *dev, uint32_t freq); +static int16_t sx127x_fskook_rssi_get(FAR struct sx127x_dev_s *dev); +static int sx127x_fskook_bitrate_set(FAR struct sx127x_dev_s *dev, + uint32_t bitrate); +static void sx127x_fskook_preamble_set(FAR struct sx127x_dev_s *dev, + uint32_t len); +static int sx127x_fskook_preamble_get(FAR struct sx127x_dev_s *dev); +static int sx127x_fskook_opmode_init(FAR struct sx127x_dev_s *dev, uint8_t opmode); +static int sx127x_fskook_syncword_set(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t len); +static void sx127x_fskook_syncword_get(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t *len); +# ifdef CONFIG_LPWAN_SX127X_RXSUPPORT +static int sx127x_fskook_rxhandle(FAR struct sx127x_dev_s *dev); +# endif +# ifdef CONFIG_LPWAN_SX127X_TXSUPPORT +static int sx127x_fskook_send(FAR struct sx127x_dev_s *dev, FAR const uint8_t *data, + size_t datalen); +# endif +# ifdef CONFIG_DEBUG_WIRELESS_INFO +static void sx127x_fskook_dumpregs(FAR struct sx127x_dev_s *dev); +# endif +#endif + +/* Common for FSK/OOK and LORA */ + +static int sx127x_fskook_opmode_set(FAR struct sx127x_dev_s *dev, uint8_t opmode); +static int sx127x_init(FAR struct sx127x_dev_s *dev); +static int sx127x_deinit(FAR struct sx127x_dev_s *dev); +static int sx127x_unregister(FAR struct sx127x_dev_s *dev); +static inline int sx127x_attachirq0(FAR struct sx127x_dev_s *dev, xcpt_t isr, + FAR void *arg); +static int sx127x_irq0handler(int irq, FAR void *context, FAR void *arg); + +static int sx127x_modulation_set(FAR struct sx127x_dev_s *dev, + uint8_t modulation); +static uint8_t sx127x_modulation_get(FAR struct sx127x_dev_s *dev); +static int16_t sx127x_rssi_get(FAR struct sx127x_dev_s *dev); +static int sx127x_frequency_set(FAR struct sx127x_dev_s *dev, uint32_t freq); +static uint32_t sx127x_frequency_get(FAR struct sx127x_dev_s *dev); +static void sx127x_preamble_set(FAR struct sx127x_dev_s *dev, uint32_t len); +static int sx127x_preamble_get(FAR struct sx127x_dev_s *dev); +static int sx127x_opmode_set(FAR struct sx127x_dev_s *dev, uint8_t opmode); +static uint8_t sx127x_opmode_get(FAR struct sx127x_dev_s *dev); +static int sx127x_opmode_init(FAR struct sx127x_dev_s *dev, uint8_t opmode); +static int sx127x_syncword_set(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t len); +static void sx127x_syncword_get(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t *len); +#ifdef CONFIG_DEBUG_WIRELESS_INFO +static void sx127x_dumpregs(FAR struct sx127x_dev_s *dev); +#else +# define sx127x_dumpregs(x) +#endif + +static bool sx127x_channel_scan(FAR struct sx127x_dev_s *dev, + FAR struct sx127x_chanscan_ioc_s *chanscan); +static uint32_t sx127x_random_get(FAR struct sx127x_dev_s *dev); + +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT +static int sx127x_txfifo_write(FAR struct sx127x_dev_s *dev, + FAR const uint8_t *data, size_t datalen); +#endif +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT +static ssize_t sx127x_rxfifo_get(struct sx127x_dev_s *dev, uint8_t *buffer, + size_t buflen); +static void sx127x_rxfifo_put(struct sx127x_dev_s *dev, uint8_t *buffer, + size_t buflen); +#endif + +/* POSIX API */ + +static int sx127x_open(FAR struct file *filep); +static int sx127x_close(FAR struct file *filep); +static ssize_t sx127x_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t sx127x_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int sx127x_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +#ifndef CONFIG_DISABLE_POLL +static int sx127x_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Only one device is supported for now */ + +static struct sx127x_dev_s g_sx127x_devices[1]; + +/* File ops */ + +static const struct file_operations sx127x_fops = +{ + sx127x_open, /* open */ + sx127x_close, /* close */ + sx127x_read, /* read */ + sx127x_write, /* write */ + NULL, /* seek */ + sx127x_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , sx127x_poll /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sx127x_lock + * + * Description: + * Acquire exclusive access to the shared SPI bus. + * + ****************************************************************************/ + +static void sx127x_lock(FAR struct spi_dev_s *spi) +{ + SPI_LOCK(spi, 1); + SPI_SETBITS(spi, 8); + SPI_SETMODE(spi, SPIDEV_MODE0); + SPI_SETFREQUENCY(spi, SX127X_SPIFREQ); +} + +/**************************************************************************** + * Name: sx127x_unlock + * + * Description: + * Release exclusive access to the shared SPI bus. + * + ****************************************************************************/ + +static void sx127x_unlock(FAR struct spi_dev_s *spi) +{ + SPI_LOCK(spi, 0); +} + +/**************************************************************************** + * Name: sx127x_select + ****************************************************************************/ + +static inline void sx127x_select(struct sx127x_dev_s * dev) +{ + SPI_SELECT(dev->spi, SPIDEV_LPWAN(0), true); +} + +/**************************************************************************** + * Name: sx127x_deselect + ****************************************************************************/ + +static inline void sx127x_deselect(struct sx127x_dev_s * dev) +{ + SPI_SELECT(dev->spi, SPIDEV_LPWAN(0), false); +} + +/**************************************************************************** + * Name: sx127x_access + ****************************************************************************/ + +static uint8_t sx127x_access(FAR struct sx127x_dev_s *dev, + sx127x_access_mode_t mode, uint8_t cmd, + FAR uint8_t *buf, int length) +{ + uint8_t status = 0; + + /* Prepare SPI */ + + sx127x_select(dev); + + /* Transfer */ + + status = SPI_SEND(dev->spi, cmd); + + switch (mode) + { + case MODE_WRITE: + { + if (length > 0) + { + SPI_SNDBLOCK(dev->spi, buf, length); + } + break; + } + + case MODE_READ: + { + SPI_RECVBLOCK(dev->spi, buf, length); + break; + } + + default: + { + wlerr("ERROR: unknown SPI access mode %d!\n", mode); + break; + } + } + + sx127x_deselect(dev); + + return status; +} + +/**************************************************************************** + * Name: sx127x_readreg + * + * Description: + * Read register from sx127x + * + ****************************************************************************/ + +static inline uint8_t sx127x_readreg(struct sx127x_dev_s *dev, uint8_t reg, + FAR uint8_t *value, int len) +{ + return sx127x_access(dev, MODE_READ, reg | SX127X_R_REGISTER, value, len); +} + +/**************************************************************************** + * Name: sx127x_readregbyte + * + * Description: + * Read single byte value from a register of sx127x + * + ****************************************************************************/ + +static inline uint8_t sx127x_readregbyte(struct sx127x_dev_s *dev, uint8_t reg) +{ + uint8_t val = 0; + + sx127x_readreg(dev, reg, &val, 1); + + return val; +} + +/**************************************************************************** + * Name: sx127x_writereg + * + * Description: + * Write value to a register of sx127x + * + ****************************************************************************/ + +static inline int sx127x_writereg(FAR struct sx127x_dev_s *dev, uint8_t reg, + FAR const uint8_t *value, int len) +{ + return sx127x_access(dev, MODE_WRITE, reg | SX127X_W_REGISTER, + (FAR uint8_t *)value, len); +} + +/**************************************************************************** + * Name: sx127x_writeregbyte + * + * Description: + * Write single byte value to a register of sx127x + * + ****************************************************************************/ + +static inline void sx127x_writeregbyte(FAR struct sx127x_dev_s *dev, + uint8_t reg, uint8_t value) +{ + sx127x_writereg(dev, reg, &value, 1); +} + +/**************************************************************************** + * Name: sx127x_modreg + * + * Description: + * Modify register value of sx127x + * + ****************************************************************************/ + +static uint8_t sx127x_modregbyte(FAR struct sx127x_dev_s *dev, uint8_t reg, + uint8_t setbits, uint8_t clrbits) +{ + uint8_t val = 0; + + sx127x_readreg(dev, reg, &val, 1); + + val &= ~clrbits; + val |= setbits; + + sx127x_writereg(dev, reg, &val, 1); + return val; +} + +/**************************************************************************** + * Name: sx127x_attachirq0 + ****************************************************************************/ + +static inline int sx127x_attachirq0(FAR struct sx127x_dev_s *dev, xcpt_t isr, + FAR void *arg) +{ + return dev->lower->irq0attach(isr, arg); +} + +/**************************************************************************** + * Name: sx127x_attachirq1 + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_DIO1 +static inline int sx127x_attachirq1(FAR struct sx127x_dev_s *dev, xcpt_t isr, + FAR void *arg) +{ + return dev->lower->irq1attach(isr, arg); +} +#endif + +/**************************************************************************** + * Name: sx127x_attachirq2 + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_DIO2 +static inline int sx127x_attachirq2(FAR struct sx127x_dev_s *dev, xcpt_t isr, + FAR void *arg) +{ + return dev->lower->irq2attach(isr, arg); +} +#endif + +/**************************************************************************** + * Name: sx127x_attachirq3 + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_DIO3 +static inline int sx127x_attachirq3(FAR struct sx127x_dev_s *dev, xcpt_t isr, + FAR void *arg) +{ + return dev->lower->irq3attach(isr, arg); +} +#endif + +/**************************************************************************** + * Name: sx127x_attachirq4 + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_DIO4 +static inline int sx127x_attachirq4(FAR struct sx127x_dev_s *dev, xcpt_t isr, + FAR void *arg) +{ + return dev->lower->irq4attach(isr, arg); +} +#endif + +/**************************************************************************** + * Name: sx127x_attachirq5 + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_DIO5 +static inline int sx127x_attachirq5(FAR struct sx127x_dev_s *dev, xcpt_t isr, + FAR void *arg) +{ + return dev->lower->irq5attach(isr, arg); +} +#endif + +/**************************************************************************** + * Name: sx127x_reset + * + * Description: + * Reset radio + * + ****************************************************************************/ + +static void sx127x_reset(FAR struct sx127x_dev_s *dev) +{ + dev->lower->reset(); +} + +/**************************************************************************** + * Name: sx127x_open + * + * Description: + * This function is called whenever the SX127X device is opened. + * + ****************************************************************************/ + +static int sx127x_open(FAR struct file *filep) +{ + FAR struct sx127x_dev_s *dev = NULL; + FAR struct inode *inode = NULL; + int ret = 0; + + wlinfo("Opening sx127x dev\n"); + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = (FAR struct sx127x_dev_s *)inode->i_private; + + /* Get exclusive access to the driver data structure */ + + ret = nxsem_wait(&dev->dev_sem); + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return ret; + } + + /* Check if device is not already used */ + + if (dev->nopens > 0) + { + ret = -EBUSY; + goto errout; + } + + /* Initialize device */ + + ret = sx127x_init(dev); + if (ret < 0) + { + wlerr("ERROR: failed to initialize sx127x\n"); + goto errout; + } + + /* Dump registers after initial configuration */ + + sx127x_dumpregs(dev); + + dev->nopens++; + +errout: + nxsem_post(&dev->dev_sem); + + return ret; +} + +/**************************************************************************** + * Name: sx127x_close + * + * Description: + * This routine is called when the SX127X device is closed. + * It waits for the last remaining data to be sent. + * + ****************************************************************************/ + +static int sx127x_close(FAR struct file *filep) +{ + FAR struct sx127x_dev_s *dev = NULL; + FAR struct inode *inode = NULL; + int ret = 0; + + wlinfo("Closing sx127x dev\n"); + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = (FAR struct sx127x_dev_s *)inode->i_private; + + /* Get exclusive access to the driver data structure */ + + ret = nxsem_wait(&dev->dev_sem); + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return ret; + } + + ret = sx127x_deinit(dev); + if (ret < 0) + { + wlerr("ERROR: failed to deinit sx127x\n"); + } + + dev->nopens--; + + nxsem_post(&dev->dev_sem); + + return OK; +} + +/**************************************************************************** + * Name: sx127x_read + * + * Description: + * Standard driver read method + * + ****************************************************************************/ + +static ssize_t sx127x_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ +#ifndef CONFIG_LPWAN_SX127X_RXSUPPORT + return -ENOSYS; +#else + FAR struct sx127x_dev_s *dev = NULL; + FAR struct inode *inode = NULL; + int ret = 0; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = (FAR struct sx127x_dev_s *)inode->i_private; + + ret = nxsem_wait(&dev->dev_sem); + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return ret; + } + + if ((filep->f_oflags & O_NONBLOCK) != 0) + { + nxsem_trywait(&dev->rx_sem); + ret = 0; + } + else + { + ret = nxsem_wait(&dev->rx_sem); + } + + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return ret; + } + + /* Get RX data from fifo */ + + ret = sx127x_rxfifo_get(dev, (uint8_t *)buffer, buflen); + + nxsem_post(&dev->dev_sem); + + return ret; +#endif +} + +/**************************************************************************** + * Name: sx127x_write + * + * Description: + * Standard driver write method. + * + ****************************************************************************/ + +static ssize_t sx127x_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ +#ifndef CONFIG_LPWAN_SX127X_TXSUPPORT + return -ENOSYS; +#else + FAR struct sx127x_dev_s *dev = NULL; + FAR struct inode *inode = NULL; + int ret = 0; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = (FAR struct sx127x_dev_s *)inode->i_private; + + ret = nxsem_wait(&dev->dev_sem); + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return ret; + } + + ret = dev->ops.send(dev, (uint8_t *)buffer, buflen); + + nxsem_post(&dev->dev_sem); + + return ret; +#endif +} + +/**************************************************************************** + * Name: sx127x_ioctl + * + * Description: + * Standard driver ioctl method. + * + ****************************************************************************/ + +static int sx127x_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct sx127x_dev_s *dev = NULL; + FAR struct inode *inode = NULL; + int ret = 0; + + wlinfo("cmd: %d arg: %ld\n", cmd, arg); + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = (FAR struct sx127x_dev_s *)inode->i_private; + + /* Get exclusive access to the driver data structure */ + + ret = nxsem_wait(&dev->dev_sem); + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return ret; + } + + /* Process the IOCTL by command */ + + switch (cmd) + { + /* Set radio frequency. Arg: Pointer to + * uint32_t frequency value in Hz ! */ + + case WLIOC_SETRADIOFREQ: + + { + FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + sx127x_frequency_set(dev, *ptr); + break; + } + + /* Get current radio frequency. arg: Pointer + * to uint32_t frequency value in Hz !*/ + + case WLIOC_GETRADIOFREQ: + + { + FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + *ptr = sx127x_frequency_get(dev); + break; + } + + /* Get RSSI */ + + case SX127XIOC_RSSIGET: + { + FAR int16_t *ptr = (FAR int16_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + *ptr = sx127x_rssi_get(dev); + + break; + } + + /* Set modulaton */ + + case SX127XIOC_MODULATIONSET: + { + FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + ret = sx127x_modulation_set(dev, *ptr); + break; + } + + /* Get modulaton */ + + case SX127XIOC_MODULATIONGET: + { + FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + *ptr = sx127x_modulation_get(dev); + break; + } + + /* Operation mode set */ + + case SX127XIOC_OPMODESET: + { + FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + ret = sx127x_opmode_set(dev, *ptr); + break; + } + + /* Operation mode get */ + + case SX127XIOC_OPMODEGET: + { + FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + *ptr = sx127x_opmode_get(dev); + break; + } + + /* Channel scan */ + + case SX127XIOC_CHANSCAN: + { + FAR struct sx127x_chanscan_ioc_s *ptr + = (FAR struct sx127x_chanscan_ioc_s *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + (void)sx127x_channel_scan(dev, ptr); + break; + } + + /* Preamble length set */ + + case SX127XIOC_PREAMBLESET: + { + FAR int *ptr = (FAR int *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + sx127x_preamble_set(dev, *ptr); + break; + } + + /* Preamble length get */ + + case SX127XIOC_PREAMBLEGET: + { + FAR int *ptr = (FAR int *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + *ptr = sx127x_preamble_get(dev); + break; + } + + /* SyncWord set */ + + case SX127XIOC_SYNCWORDSET: + { + ASSERT(0); + sx127x_syncword_set(dev, NULL, 0); + break; + } + + /* SyncWord get */ + + case SX127XIOC_SYNCWORDGET: + { + ASSERT(0); + sx127x_syncword_get(dev, NULL, 0); + break; + } + + /* Get random number based on RSSI */ + + case SX127XIOC_RANDOMGET: + { + FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + *ptr = sx127x_random_get(dev); + break; + } + + default: + { + ret = -ENOTTY; + break; + } + } + + nxsem_post(&dev->dev_sem); + return ret; +} + +/**************************************************************************** + * Name: sx127x_poll + * + * Description: + * Standard driver poll method. + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int sx127x_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + return -ENOSYS; +#else + + FAR struct sx127x_dev_s *dev = NULL; + FAR struct inode *inode = NULL; + int ret = 0; + + wlinfo("setup: %d\n", (int)setup); + DEBUGASSERT(filep && fds); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = (FAR struct sx127x_dev_s *)inode->i_private; + + /* Exclusive access */ + + ret = nxsem_wait(&dev->devsem); + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return ret; + } + + /* Are we setting up the poll? Or tearing it down? */ + + if (setup) + { + /* Ignore waits that do not include POLLIN */ + + if ((fds->events & POLLIN) == 0) + { + ret = -EDEADLK; + goto errout; + } + + /* Check if we can accept this poll. + * For now, only one thread can poll the device at any time + * (shorter / simpler code) + */ + + if (dev->pfd) + { + ret = -EBUSY; + goto errout; + } + + dev->pfd = fds; + + /* Is there is already data in the fifo? then trigger POLLIN now - + * don't wait for RX. + */ + + (void)nxsem_wait(&dev->rx_buffer_sem); + if (dev->rx_fifo_len > 0) + { + /* Data available for input */ + + dev->pfd->revents |= POLLIN; + nxsem_post(dev->pfd->sem); + } + + nxsem_post(&dev->rx_buffer_sem); + } + else /* Tear it down */ + { + dev->pfd = NULL; + } + +errout: + nxsem_post(&dev->devsem); + return ret; +#endif +} +#endif + +/**************************************************************************** + * Name: sx127x_lora_isr0_process + * + * Description: + * Handle DIO0 interrupt for LORA radio + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_LORA +static int sx127x_lora_isr0_process(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + bool data_valid = true; +#endif + uint8_t irq = 0; + int ret = OK; + + /* Get IRQ */ + + sx127x_lock(dev->spi); + irq = sx127x_readregbyte(dev, SX127X_LRM_IRQ); + sx127x_unlock(dev->spi); + + wlinfo("ISR0: IRQ = 0x%02x\n", irq); + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + /* RX data valid ? */ + + if (dev->opmode == SX127X_OPMODE_TX && dev->crcon == true && + (irq & SX127X_LRM_IRQ_PAYLOADCRCERR) != 0) + { + data_valid = false; + } +#endif + + switch (dev->opmode) + { +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + /* TX DONE for FSK/OOK and LORA */ + + case SX127X_OPMODE_TX: + { + /* Release TX sem */ + + nxsem_post(&dev->tx_sem); + + /* Clear TX interrupt */ + + irq = SX127X_LRM_IRQ_TXDONE; + + break; + } +#endif /* CONFIG_LPWAN_SX127X_TXSUPPORT */ + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + /* RX DONE */ + + case SX127X_OPMODE_RX: + case SX127X_OPMODE_RXSINGLE: + { + if (data_valid) + { + sx127x_lora_rxhandle(dev); + +#ifndef CONFIG_DISABLE_POLL + if (dev->pfd) + { + /* Data available for input */ + + dev->pfd->revents |= POLLIN; + + wlinfo("Wake up polled fd\n"); + nxsem_post(dev->pfd->sem); + } +#endif /* CONFIG_DISABLE_POLL */ + + /* Wake-up any thread waiting in recv */ + + nxsem_post(&dev->rx_sem); + } + else + { + /* RX Data invalid */ + + wlinfo("Invalid LORA RX data!\n"); + } + + /* After receiving the data in RXSINGLE mode the chip goes into + * STANBY mode + */ + + if (dev->opmode == SX127X_OPMODE_RXSINGLE) + { + dev->opmode = SX127X_OPMODE_STANDBY; + } + + /* Clear RX interrupts */ + + irq = SX127X_LRM_IRQ_RXDONE | SX127X_LRM_IRQ_PAYLOADCRCERR | SX127X_LRM_IRQ_VALIDHDR; + + break; + } +#endif /* CONFIG_LPWAN_SX127X_RXSUPPORT */ + + /* Only LORA - CAD DONE */ + + case SX127X_OPMODE_CAD: + { + /* TODO */ + + wlerr("TODO: ISR0 in CAD mode not implemented yet!\n"); + + /* Clear CAD interrupt */ + + irq = SX127X_LRM_IRQ_CADDONE; + + break; + } + + default: + { + wlwarn("WARNING: Interrupt not processed\n"); + ret = -EINVAL; + break; + } + } + + /* Clear interrupts */ + + sx127x_lock(dev->spi); + sx127x_writeregbyte(dev, SX127X_LRM_IRQ, irq); + sx127x_unlock(dev->spi); + + return ret; +} +#endif /* CONFIG_LPWAN_SX127X_LORA */ + +/**************************************************************************** + * Name: sx127x_fskook_isr0_process + * + * Description: + * Handle DIO0 interrupt for FSK/OOK radio + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK +static int sx127x_fskook_isr0_process(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + bool data_valid = true; +#endif + uint8_t irq1 = 0; + uint8_t irq2 = 0; + int ret = OK; + + /* Get IRQ1 and IRQ2 */ + + sx127x_lock(dev->spi); + irq1 = sx127x_readregbyte(dev, SX127X_FOM_IRQ1); + irq2 = sx127x_readregbyte(dev, SX127X_FOM_IRQ2); + sx127x_unlock(dev->spi); + + wlinfo("ISR0: IRQ1 = 0x%02x, IRQ2 = 0x%02x\n", irq1, irq2); + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + /* RX data valid ? */ + + if (dev->opmode == SX127X_OPMODE_RX && dev->crcon == true && + (irq2 & SX127X_FOM_IRQ2_CRCOK) == 0) + { + data_valid = false; + } +#endif + + switch (dev->opmode) + { +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + /* TX DONE for FSK/OOK and LORA */ + + case SX127X_OPMODE_TX: + { + /* Release TX sem */ + + nxsem_post(&dev->tx_sem); + + break; + } +#endif /* CONFIG_LPWAN_SX127X_TXSUPPORT */ + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + /* RX DONE */ + + case SX127X_OPMODE_RX: + { + if (data_valid == true) + { + /* RX data valid */ + + sx127x_fskook_rxhandle(dev); + +#ifndef CONFIG_DISABLE_POLL + if (dev->pfd) + { + /* Data available for input */ + + dev->pfd->revents |= POLLIN; + + wlinfo("Wake up polled fd\n"); + nxsem_post(dev->pfd->sem); + } +#endif /* CONFIG_DISABLE_POLL */ + + /* Wake-up any thread waiting in recv */ + + nxsem_post(&dev->rx_sem); + } + else + { + /* RX Data invalid */ + + wlinfo("Invalid FSK/OOK RX data!\n"); + } + + /* TODO: restart RX if continuous mode */ + + break; + } +#endif /* CONFIG_LPWAN_SX127X_RXSUPPORT */ + + default: + { + wlwarn("WARNING: Interrupt not processed\n"); + ret = -EINVAL; + break; + } + } + + /* REVISIT: clear interrupts */ + + irq1 = (SX127X_FOM_IRQ1_RSSI | SX127X_FOM_IRQ1_PREAMBE | + SX127X_FOM_IRQ1_SYNCADDRMATCH); + irq2 = SX127X_FOM_IRQ2_FIFOOVR; + + sx127x_lock(dev->spi); + sx127x_writeregbyte(dev, SX127X_FOM_IRQ1, irq1); + sx127x_writeregbyte(dev, SX127X_FOM_IRQ2, irq2); + sx127x_unlock(dev->spi); + + return ret; +} +#endif /* CONFIG_LPWAN_SX127X_FSKOOK */ + +/**************************************************************************** + * Name: sx127x_isr0_process + * + * Description: + * Handle DIO0 interrupt for LORA radio + * + ****************************************************************************/ + +static void sx127x_isr0_process(FAR void *arg) +{ + DEBUGASSERT(arg); + + FAR struct sx127x_dev_s *dev = (struct sx127x_dev_s *)arg; + int ret = OK; + + ret = dev->ops.isr0_process(dev); + if (ret < 0) + { + wlerr("Failed to process ISR0 %d\n", ret); + } +} + +/**************************************************************************** + * Name: sx127x_irq0handler + ****************************************************************************/ + +static int sx127x_irq0handler(int irq, FAR void *context, FAR void *arg) +{ + FAR struct sx127x_dev_s *dev = (struct sx127x_dev_s *)arg; + + DEBUGASSERT(arg); + + work_queue(HPWORK, &dev->irq0_work, sx127x_isr0_process, dev, 0); + + return 0; +} + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + +/**************************************************************************** + * Name: sx127x_fskook_rxhandle + * + * Description: + * Receive data from FIFO for FSK/OOK radio + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK +static int sx127x_fskook_rxhandle(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + struct sx127x_read_hdr_s rxdata; + uint8_t datalen = 0; + uint8_t len = 0; + + /* Get data from chip fifo */ + + if (dev->fskook.fixlen == true) + { + /* Fixed packet length hardcoded */ + + datalen = SX127X_RX_FIXLEN_DEFAULT; + } + else + { + /* First byte is payload length */ + + datalen = sx127x_readregbyte(dev, SX127X_CMN_FIFO); + } + + /* Read payload and store */ + + sx127x_readreg(dev, SX127X_CMN_FIFO, rxdata.data, datalen); + + /* No RX SNR data for FSK/OOK */ + + rxdata.snr = 0; + + /* Store last RSSI */ + + rxdata.rssi = sx127x_fskook_rssi_get(dev); + + /* Store packet length */ + + rxdata.datalen = datalen; + + /* Total length */ + + len = datalen + SX127X_READ_DATA_HEADER_LEN; + + /* Put data on local fifo */ + + sx127x_rxfifo_put(dev, (uint8_t *)&rxdata, len); + + /* Return total length */ + + return len; +} +#endif /* CONFIG_LPWAN_SX127X_FSKOOK */ + +/**************************************************************************** + * Name: sx127x_lora_rxhandle + * + * Description: + * Receive data from FIFO for LORA radio + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_LORA +static int sx127x_lora_rxhandle(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + struct sx127x_read_hdr_s rxdata; + uint8_t datalen = 0; + uint8_t len = 0; + uint8_t rx_ptr = 0; + + /* Get payload length */ + + datalen = sx127x_readregbyte(dev, SX127X_LRM_RXBYTES); + + /* Get start address of last packet received */ + + rx_ptr = sx127x_readregbyte(dev, SX127X_LRM_RXCURR); + + /* Set FIFO pointer */ + + sx127x_writeregbyte(dev, SX127X_LRM_ADDRPTR, rx_ptr); + + /* Read payload */ + + sx127x_readreg(dev, SX127X_CMN_FIFO, rxdata.data, datalen); + + /* Store last RX SNR */ + + rxdata.snr = sx127x_lora_snr_get(dev); + + /* Store last RX RSSI */ + + rxdata.rssi = sx127x_lora_pckrssi_get(dev, rxdata.snr); + + /* Store packet length */ + + rxdata.datalen = datalen; + + /* Total length */ + + len = datalen + SX127X_READ_DATA_HEADER_LEN; + + /* Put data on local fifo */ + + sx127x_rxfifo_put(dev, (uint8_t *)&rxdata, len); + + /* Return total length */ + + return len; +} +#endif /* CONFIG_LPWAN_SX127X_LORA */ + +/**************************************************************************** + * Name: sx127x_rxfifo_get + * + * Description: + * Get data from RX FIFO + * + ****************************************************************************/ + +static ssize_t sx127x_rxfifo_get(struct sx127x_dev_s *dev, uint8_t *buffer, + size_t buflen) +{ + struct sx127x_read_hdr_s *pkt = NULL; + uint8_t pktlen = 0; + uint8_t i = 0; + int ret = 0; + + ret = nxsem_wait(&dev->rx_buffer_sem); + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return ret; + } + + /* No data on RX FIFO */ + + if (dev->rx_fifo_len == 0) + { + pktlen = 0; + goto no_data; + } + + /* Get packet header */ + + pkt = (struct sx127x_read_hdr_s *)(dev->rx_buffer + + dev->nxt_read * SX127X_RXFIFO_ITEM_SIZE); + + /* Packet length is data length + header length */ + + pktlen = pkt->datalen + SX127X_READ_DATA_HEADER_LEN; + + /* Get packet from FIFO */ + + for (i = 0; i < pktlen && i < SX127X_RXFIFO_ITEM_SIZE; i+=1) + { + buffer[i] = dev->rx_buffer[dev->nxt_read * SX127X_RXFIFO_ITEM_SIZE + i]; + } + + dev->nxt_read = (dev->nxt_read + 1) % CONFIG_LPWAN_SX127X_RXFIFO_LEN; + dev->rx_fifo_len--; + + ret = pktlen; + +no_data: + nxsem_post(&dev->rx_buffer_sem); + return ret; +} + +/**************************************************************************** + * Name: sx127x_rxfifo_put + * + * Description: + * Put packet data on RX FIFO + * + ****************************************************************************/ + +static void sx127x_rxfifo_put(struct sx127x_dev_s *dev, uint8_t *buffer, + size_t buflen) +{ + uint8_t i = 0; + int ret = 0; + + ret = nxsem_wait(&dev->rx_buffer_sem); + if (ret < 0) + { + /* This should only happen if the wait was canceled by an signal */ + + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + return; + } + + dev->rx_fifo_len++; + if (dev->rx_fifo_len > CONFIG_LPWAN_SX127X_RXFIFO_LEN) + { + dev->rx_fifo_len = CONFIG_LPWAN_SX127X_RXFIFO_LEN; + dev->nxt_read = (dev->nxt_read + 1) % CONFIG_LPWAN_SX127X_RXFIFO_LEN; + } + + /* Put packet on fifo */ + + for (i = 0; i < (buflen + 1) && i < SX127X_RXFIFO_ITEM_SIZE; i+=1) + { + dev->rx_buffer[i + dev->nxt_write * SX127X_RXFIFO_ITEM_SIZE] = buffer[i]; + } + + dev->nxt_write = (dev->nxt_write + 1) % CONFIG_LPWAN_SX127X_RXFIFO_LEN; + nxsem_post(&dev->rx_buffer_sem); +} + +#endif /* CONFIG_LPWAN_SX127X_RXSUPPORT */ + +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + +/**************************************************************************** + * Name: sx127x_txfifo_write + * + * Description: + * Write data to the SX127X TX FIFO + * + ****************************************************************************/ + +static int sx127x_txfifo_write(FAR struct sx127x_dev_s *dev, + FAR const uint8_t *data, size_t datalen) +{ + /* NOTE: Do not lock SPI here, it should be already locked! */ + + /* Write buffer to FIFO */ + + sx127x_writereg(dev, SX127X_CMN_FIFO, data, datalen); + + return OK; +} + +/**************************************************************************** + * Name: sx127x_fskook_send + * + * Description: + * Send data in FSK/OOK radio mode + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK +static int sx127x_fskook_send(FAR struct sx127x_dev_s *dev, + FAR const uint8_t *data, size_t datalen) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + int ret = 0; + + /* Check payload length */ + + if (datalen > SX127X_FOM_PAYLOADLEN_MAX) + { + wlerr("Not supported data len!\n"); + ret = -EINVAL; + goto errout; + } + +#if 1 + /* For now we don't support datalen > FIFO_LEN for FSK/OOK */ + + if (datalen > 64) + { + wlerr("Not supported data len!\n"); + ret = -EINVAL; + goto errout; + } +#endif + + /* Change mode to STANDBY */ + + sx127x_opmode_set(dev, SX127X_OPMODE_STANDBY); + + /* Initialize TX mode */ + + ret = sx127x_opmode_init(dev, SX127X_OPMODE_TX); + if (ret < 0) + { + wlerr("Failed to initialize TX mode!\n"); + ret = -EINVAL; + goto errout; + } + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + if (dev->fskook.fixlen == true) + { + /* Write payload length reigster (only LSB for now) */ + + sx127x_writeregbyte(dev, SX127X_FOM_PAYLOADLEN, datalen); + } + else + { + /* First byte is length */ + + ret = sx127x_txfifo_write(dev, (uint8_t *)&datalen, 1); + } + + /* Write payload */ + + sx127x_txfifo_write(dev, data, datalen); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Change mode to TX to start data transfer */ + + sx127x_opmode_set(dev, SX127X_OPMODE_TX); + + /* Wait for payload send IRQ */ + + nxsem_wait(&dev->tx_sem); + + /* Change mode to IDLE after transfer + * NOTE: if sequencer is on - this should be done automatically + */ + + sx127x_opmode_set(dev, dev->idle); + +errout: + return ret; +} +#endif /* CONFIG_LPWAN_SX127X_FSKOOK */ + +/**************************************************************************** + * Name: sx127x_lora_send + * + * Description: + * Send data in LORA radio mode + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_LORA +static int sx127x_lora_send(FAR struct sx127x_dev_s *dev, + FAR const uint8_t *data, size_t datalen) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + int ret = 0; + + /* Check payload length */ + + if (datalen > SX127X_LRM_PAYLOADLEN_MAX) + { + wlerr("Not supported data len!\n"); + ret = -EINVAL; + goto errout; + } + + /* Change mode to STANDBY */ + + sx127x_opmode_set(dev, SX127X_OPMODE_STANDBY); + + /* Initialize TX mode */ + + ret = sx127x_opmode_init(dev, SX127X_OPMODE_TX); + if (ret < 0) + { + wlerr("Failed to initialize TX mode!\n"); + ret = -EINVAL; + goto errout; + } + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Configure payload length */ + + sx127x_writeregbyte(dev, SX127X_LRM_PAYLOADLEN, datalen); + + /* Write payload */ + + sx127x_txfifo_write(dev, data, datalen); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Change mode to TX to start data transfer */ + + sx127x_opmode_set(dev, SX127X_OPMODE_TX); + + /* Wait for TXDONE */ + + nxsem_wait(&dev->tx_sem); + + /* Change mode to IDLE after transfer */ + + /* sx127x_opmode_set(dev, dev->idle); */ + dev->opmode = SX127X_OPMODE_STANDBY; + +errout: + return ret; +} +#endif /* CONFIG_LPWAN_SX127X_LORA */ +#endif /* CONFIG_LPWAN_SX127X_TXSUPPORT */ + +/**************************************************************************** + * Name: sx127x_opmode_init + * + * Description: + * Initialize operation mode + * + ****************************************************************************/ + +static int sx127x_opmode_init(FAR struct sx127x_dev_s *dev, uint8_t opmode) +{ + int ret = OK; + + if (opmode == dev->opmode) + { + goto errout; + } + + dev->ops.opmode_init(dev, opmode); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_opmode_set + * + * Description: + * Set operation mode + * + ****************************************************************************/ + +static int sx127x_opmode_set(FAR struct sx127x_dev_s *dev, uint8_t opmode) +{ + int ret = OK; + + wlinfo("opmode_set %d->%d\n", dev->opmode, opmode); + + if (opmode == dev->opmode) + { + goto errout; + } + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + /* REVISIT: TX is initialized before data send, + * but where we should initialize RX ? + */ + + if (opmode == SX127X_OPMODE_RX || opmode == SX127X_OPMODE_RXSINGLE) + { + ret = sx127x_opmode_init(dev, opmode); + } +#endif + + /* Change mode */ + + dev->ops.opmode_set(dev, opmode); + + /* Update local variable */ + + dev->opmode = opmode; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_opmode_get + * + * Description: + * Get current operation mode + * + ****************************************************************************/ + +static uint8_t sx127x_opmode_get(FAR struct sx127x_dev_s *dev) +{ + wlerr("TODO: sx127x_opmode_get not implemented yet\n"); + return 0; +} + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + +/**************************************************************************** + * Name: sx127x_lora_opmode_init + * + * Description: + * Initialize operation mode for FSK/OOK + * + ****************************************************************************/ + +static int sx127x_fskook_opmode_init(FAR struct sx127x_dev_s *dev, uint8_t opmode) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + uint8_t dio0map = 0; + uint8_t setbits = 0; + uint8_t clrbits = 0; + int ret = OK; + + sx127x_lock(dev->spi); + + /* Get mode specific configuration */ + + switch (opmode) + { + case SX127X_OPMODE_SLEEP: + case SX127X_OPMODE_STANDBY: + case SX127X_OPMODE_FSRX: + case SX127X_OPMODE_FSTX: + { + break; + } + + case SX127X_OPMODE_TX: + { + /* Remap DIO0 to RXTX DONE */ + + dio0map = SX127X_FOM_DIOMAP1_DIO0_RXTX; + + /* TX start condition on FIFO not empty */ + + sx127x_writeregbyte(dev, SX127X_FOM_FIFOTHR, + SX127X_FOM_FIFOTHR_TXSTARTCOND); + + break; + } + + case SX127X_OPMODE_RX: + { + /* Remap DIO0 to RXTX DONE */ + + dio0map = SX127X_FOM_DIOMAP1_DIO0_RXTX; + + break; + } + + default: + { + wlerr("ERROR: invalid mode %d\n", opmode); + ret = -EINVAL; + goto errout; + } + } + + /* Configure DIO0 pin */ + + setbits = dio0map; + clrbits = SX127X_CMN_DIOMAP1_DIO0_MASK; + sx127x_modregbyte(dev, SX127X_CMN_DIOMAP1, setbits, clrbits); + + sx127x_unlock(dev->spi); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_fskook_opmode_set + * + * Description: + * Set operation mode for FSK/OOK + * + ****************************************************************************/ + +static int sx127x_fskook_opmode_set(FAR struct sx127x_dev_s *dev, uint8_t opmode) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + uint8_t regval = 0; + uint8_t setbits = 0; + uint8_t clrbits = 0; + int ret = OK; + + switch (opmode) + { + case SX127X_OPMODE_SLEEP: + case SX127X_OPMODE_STANDBY: + case SX127X_OPMODE_FSRX: + case SX127X_OPMODE_FSTX: + case SX127X_OPMODE_TX: + case SX127X_OPMODE_RX: + { + /* Do nothing */ + + break; + } + + default: + { + wlerr("ERROR: invalid FSK/OOK mode %d\n", opmode); + ret = -EINVAL; + goto errout; + } + } + + sx127x_lock(dev->spi); + + /* Update mode */ + + setbits = ((opmode-1) << SX127X_CMN_OPMODE_MODE_SHIFT); + clrbits = SX127X_CMN_OPMODE_MODE_MASK; + sx127x_modregbyte(dev, SX127X_CMN_OPMODE, setbits, clrbits); + + /* Wait for mode ready */ + + do + { + usleep(1000); + + if (opmode == SX127X_OPMODE_SLEEP) + { + /* REVISIT: Somehow MODERDY doesnt work for SLEEP, + * so we just break here + */ + break; + } + + regval = sx127x_readregbyte(dev, SX127X_FOM_IRQ1); + } + while (!(regval & SX127X_FOM_IRQ1_MODERDY)); + + sx127x_unlock(dev->spi); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_fskook_rxbw_set + * + * Description: + * Set RX BW for FSK/OOK + * + ****************************************************************************/ + +static int sx127x_fskook_rxbw_set(FAR struct sx127x_dev_s *dev, uint8_t rx_bw) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + int ret = OK; + + if (rx_bw == dev->fskook.rx_bw) + { + goto errout; + } + + switch (rx_bw) + { + case FSKOOK_BANDWIDTH_2p6kHz: + case FSKOOK_BANDWIDTH_3p1kHz: + case FSKOOK_BANDWIDTH_3p9kHz: + case FSKOOK_BANDWIDTH_5p2kHz: + case FSKOOK_BANDWIDTH_6p3kHz: + case FSKOOK_BANDWIDTH_7p8kHz: + case FSKOOK_BANDWIDTH_10p4kHz: + case FSKOOK_BANDWIDTH_12p5kHz: + case FSKOOK_BANDWIDTH_15p6kHz: + case FSKOOK_BANDWIDTH_20p8kHz: + case FSKOOK_BANDWIDTH_25kHz: + case FSKOOK_BANDWIDTH_31p3kHz: + case FSKOOK_BANDWIDTH_41p7kHz: + case FSKOOK_BANDWIDTH_50kHz: + case FSKOOK_BANDWIDTH_62p5kHz: + case FSKOOK_BANDWIDTH_83p3kHz: + case FSKOOK_BANDWIDTH_100kHz: + case FSKOOK_BANDWIDTH_125kHz: + case FSKOOK_BANDWIDTH_166p7kHz: + case FSKOOK_BANDWIDTH_200kHz: + case FSKOOK_BANDWIDTH_250kHz: + { + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Write register */ + + sx127x_writeregbyte(dev, SX127X_FOM_RXBW, rx_bw); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + break; + } + + default: + { + wlerr("Unsupported bandwidth %d\n", rx_bw); + ret = -EINVAL; + goto errout; + } + } + + /* Update local */ + + dev->fskook.rx_bw = rx_bw; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_fskook_afcbw_set + * + * Description: + * Set AFC BW for FSK/OOK + * + ****************************************************************************/ + +static int sx127x_fskook_afcbw_set(FAR struct sx127x_dev_s *dev, uint8_t afc_bw) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + int ret = OK; + + if (afc_bw == dev->fskook.afc_bw) + { + goto errout; + } + + switch (afc_bw) + { + case FSKOOK_BANDWIDTH_2p6kHz: + case FSKOOK_BANDWIDTH_3p1kHz: + case FSKOOK_BANDWIDTH_3p9kHz: + case FSKOOK_BANDWIDTH_5p2kHz: + case FSKOOK_BANDWIDTH_6p3kHz: + case FSKOOK_BANDWIDTH_7p8kHz: + case FSKOOK_BANDWIDTH_10p4kHz: + case FSKOOK_BANDWIDTH_12p5kHz: + case FSKOOK_BANDWIDTH_15p6kHz: + case FSKOOK_BANDWIDTH_20p8kHz: + case FSKOOK_BANDWIDTH_25kHz: + case FSKOOK_BANDWIDTH_31p3kHz: + case FSKOOK_BANDWIDTH_41p7kHz: + case FSKOOK_BANDWIDTH_50kHz: + case FSKOOK_BANDWIDTH_62p5kHz: + case FSKOOK_BANDWIDTH_83p3kHz: + case FSKOOK_BANDWIDTH_100kHz: + case FSKOOK_BANDWIDTH_125kHz: + case FSKOOK_BANDWIDTH_166p7kHz: + case FSKOOK_BANDWIDTH_200kHz: + case FSKOOK_BANDWIDTH_250kHz: + { + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Write register */ + + sx127x_writeregbyte(dev, SX127X_FOM_AFCBW, afc_bw); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + break; + } + + default: + { + wlerr("Unsupported bandwidth %d\n", afc_bw); + ret = -EINVAL; + goto errout; + } + } + + /* Update local */ + + dev->fskook.afc_bw = afc_bw; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_fskook_seq_start + ****************************************************************************/ + +static void sx127x_fskook_seq_start(FAR struct sx127x_dev_s *dev, bool state) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + if (state == true) + { + /* Start sequencer */ + + sx127x_modregbyte(dev, SX127X_FOM_SEQCFG1, SX127X_FOM_SEQCFG1_SEQSTART, 0); + } + else + { + /* Stop sequencer */ + + sx127x_modregbyte(dev, SX127X_FOM_SEQCFG1, SX127X_FOM_SEQCFG1_SEQSTOP, 0); + } + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Store sequencer state */ + + dev->fskook.seqon = state; +} + + +/**************************************************************************** + * Name: sx127x_fskook_seq_init + * + * Description: + * Initialize FSK/OOK sequencer. + * This can be used to automate transistions between operation modes and + * thus further reduce energy consumtion. + * + ****************************************************************************/ + +static int sx127x_fskook_seq_init(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + uint8_t seq1 = 0; + uint8_t seq2 = 0; + int ret = OK; + + /* Need sleep mode or standby mode */ + + if (dev->opmode > SX127X_OPMODE_STANDBY) + { + sx127x_opmode_set(dev, SX127X_OPMODE_STANDBY); + } + + /* Nothing here */ + + seq1 = 0; + seq2 = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Write registers */ + + sx127x_writeregbyte(dev, SX127X_FOM_SEQCFG1, seq1); + sx127x_writeregbyte(dev, SX127X_FOM_SEQCFG2, seq2); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + return ret; +} + +/**************************************************************************** + * Name: sx127x_fskook_syncword_get + ****************************************************************************/ + +static void sx127x_fskook_syncword_get(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t *len) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + wlerr("sx127x_fskook_syncword_get not implemented yet\n"); +} + +/**************************************************************************** + * Name: sx127x_syncword_get + ****************************************************************************/ + +static void sx127x_syncword_get(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t *len) +{ + dev->ops.syncword_get(dev, sw, len); +} + +/**************************************************************************** + * Name: sx127x_fskook_syncword_set + * + * Description: + * Set SyncWord for FSK/OOK + * + ****************************************************************************/ + +static int sx127x_fskook_syncword_set(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t len) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + uint8_t setbits = 0; + uint8_t clrbits = 0; + uint8_t offset = 0; + int ret = OK; + int i = 0; + + if (len > SX127X_FOM_SYNCSIZE_MAX) + { + wlerr("Unsupported sync word length %d!", len); + ret = -EINVAL; + goto errout; + } + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + if (len == 0) + { + /* Disable sync word generation and detection */ + + clrbits = SX127X_FOM_SYNCCFG_SYNCSIZE_MASK | SX127X_FOM_SYNCCFG_SYNCON; + setbits = 0; + + sx127x_modregbyte(dev, SX127X_FOM_SYNCCFG, setbits, clrbits); + + } + else + { + /* Configure sync word length */ + + clrbits = SX127X_FOM_SYNCCFG_SYNCSIZE_MASK; + setbits = SX127X_FOM_SYNCCFG_SYNCON | SX127X_FOM_SYNCCFG_SYNCSIZE(len-1); + + sx127x_modregbyte(dev, SX127X_FOM_SYNCCFG, setbits, clrbits); + + /* Write sync words */ + + for (i = 0; i < len; i+=1) + { + offset = SX127X_FOM_SYNCVAL1 + i; + sx127x_writeregbyte(dev, offset, sw[i]); + } + } + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_syncword_set + ****************************************************************************/ + +static int sx127x_syncword_set(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t len) +{ + return dev->ops.syncword_set(dev, sw, len); +} + +/**************************************************************************** + * Name: sx127x_fskook_init + * + * Description: + * Initialization specific for FSK/OOK modulation + * + ****************************************************************************/ + +static void sx127x_fskook_init(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + uint8_t setbits = 0; + uint8_t clrbits = 0; + + /* Set FDEV */ + + sx127x_fskook_fdev_set(dev, SX127X_FREQ_DEV_DEFAULT); + + /* Set bitrate */ + + sx127x_fskook_bitrate_set(dev, SX127X_FOM_BITRATE_DEFAULT); + + /* Configure sequencer + * WARNING: sequencer is OFF for now! + */ + + sx127x_fskook_seq_init(dev); + sx127x_fskook_seq_start(dev, false); + + /* Configure Sync Word - disable */ + + sx127x_fskook_syncword_set(dev, NULL, 0); + + /* Configure bandwidth */ + + sx127x_fskook_rxbw_set(dev, SX127X_FSKOOK_RXBW_DEFAULT); + sx127x_fskook_afcbw_set(dev, SX127X_FSKOOK_AFCBW_DEFAULT); + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Configure packet mode settings 1: + * - fixlen + * - RX/TX CRC + */ + + setbits = 0; + clrbits = 0; + setbits |= (dev->fskook.fixlen == true ? SX127X_FOM_PKTCFG1_PCKFORMAT : 0); + setbits |= (dev->crcon == true ? SX127X_FOM_PKTCFG1_CRCON : 0); + + /* Write packet mode settings 1 */ + + sx127x_modregbyte(dev, SX127X_FOM_PKTCFG1, setbits, clrbits); + + /* Configure packet mode settings 2: + * - packet mode on + */ + + setbits = 0; + clrbits = 0; + setbits |= SX127X_FOM_PKTCFG2_DATAMODE; + + /* Write packet mode settings 2 */ + + sx127x_modregbyte(dev, SX127X_FOM_PKTCFG1, setbits, clrbits); + + /* Configure PARAMP register */ + + setbits = SX127X_FSKOOK_SHAPING_DEFAULT | SX127X_FSKOOK_PARAMP_DEFAULT; + clrbits = SX127X_CMN_PARAMP_PARAMP_MASK | SX127X_CMN_PARAMP_SHAPING_MASK; + + /* Write PARAMP register */ + + sx127x_modregbyte(dev, SX127X_CMN_PARAMP, setbits, clrbits); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); +} + +/**************************************************************************** + * Name: sx127x_fskook_rssi_get + * + * Description: + * Get current RSSI for FSK/OOK modem + * + ****************************************************************************/ + +static int16_t sx127x_fskook_rssi_get(FAR struct sx127x_dev_s *dev) +{ + uint8_t regval = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Get register value */ + + regval = sx127x_readregbyte(dev, SX127X_FOM_RSSIVAL); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Return decoded RSSI value */ + + return SX127X_FOM_RSSIVAL_GET(regval); +} + +/**************************************************************************** + * Name: sx127x_fskook_fdev_set + * + * Description: + * Set frequency deviation + * + ****************************************************************************/ + +static int sx127x_fskook_fdev_set(FAR struct sx127x_dev_s *dev, uint32_t freq) +{ + uint32_t fdev = 0; + int ret = OK; + + /* Only for FSK modulation */ + + if (dev->modulation != SX127X_MODULATION_FSK) + { + ret = -EINVAL; + goto errout; + } + + if (freq == dev->fskook.fdev) + { + goto errout; + } + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Get FDEV value */ + + fdev = SX127X_FDEV_FROM_FREQ(freq); + + /* Write FDEV MSB */ + + sx127x_writeregbyte(dev, SX127X_FOM_FDEVMSB, SX127X_FOM_FDEV_MSB(fdev)); + + /* Write FDEV LSB */ + + sx127x_writeregbyte(dev, SX127X_FOM_FDEVLSB, SX127X_FOM_FDEV_LSB(fdev)); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Update local variable */ + + dev->fskook.fdev = freq; + + errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_fskook_bitrate_set + * + * Description: + * Set bitrate for FSK/OOK modulation + * + ****************************************************************************/ + +static int sx127x_fskook_bitrate_set(FAR struct sx127x_dev_s *dev, + uint32_t bitrate) +{ + uint32_t br = 0; + int ret = OK; + + if (bitrate == dev->fskook.bitrate) + { + goto errout; + } + + /* Get bitrate register value */ + + br = SX127X_FXOSC / bitrate; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Set fractial part to 0 */ + + sx127x_writeregbyte(dev, SX127X_FOM_BITRATEFRAC, 0); + + /* Write MSB */ + + sx127x_writeregbyte(dev, SX127X_FOM_BITRATEMSB, + SX127X_FOM_BITRATE_MSB(br)); + + /* Write LSB */ + + sx127x_writeregbyte(dev, SX127X_FOM_BITRATELSB, + SX127X_FOM_BITRATE_LSB(br)); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Update local variable */ + + dev->fskook.bitrate = bitrate; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_fskook_preamble_set + * + * Description: + * Set preamble for FSK/OOK modulation + * + ****************************************************************************/ + +static void sx127x_fskook_preamble_set(FAR struct sx127x_dev_s *dev, uint32_t len) +{ + uint8_t regval = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + if (len == 0) + { + /* Disable detector */ + + regval = 0; + sx127x_writeregbyte(dev, SX127X_FOM_PREDET, regval); + } + else + { + /* Configure preamble length */ + + regval = SX127X_FOM_PRE_MSB(len); + sx127x_writeregbyte(dev, SX127X_FOM_PREMSB, regval); + regval = SX127X_FOM_PRE_LSB(len); + sx127x_writeregbyte(dev, SX127X_FOM_PRELSB, regval); + + /* Configure preamble polarity to 0xAA */ + + regval = SX127X_FOM_SYNCCFG_PREPOL; + sx127x_modregbyte(dev, SX127X_FOM_SYNCCFG, regval, 0); + + /* Configure and enable preamble detector: + * - tolerance = 10 + * - detector size = 2B + */ + + regval = (SX127X_FOM_PREDET_ON | SX127X_FOM_PREDET_SIZE_2B | + SX127X_FOM_PREDET_TOL(10)); + sx127x_writeregbyte(dev, SX127X_FOM_PREDET, regval); + } + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); +} + +/**************************************************************************** + * Name: sx127x_fskook_preamble_get + * + * Description: + * Get current preamble configuration for FSK/OOK + * + ****************************************************************************/ + +static int sx127x_fskook_preamble_get(FAR struct sx127x_dev_s *dev) +{ + wlerr("sx127x_fskook_preamble_get\n"); + return 0; +} + +#endif /* CONFIG_LPWAN_SX127X_FSKOOK */ + +#ifdef CONFIG_LPWAN_SX127X_LORA + +/**************************************************************************** + * Name: sx127x_lora_opmode_init + * + * Description: + * Initialize operation mode for LORA + * + ****************************************************************************/ + +static int sx127x_lora_opmode_init(FAR struct sx127x_dev_s *dev, uint8_t opmode) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + uint8_t dio0map = 0; + uint8_t setbits = 0; + uint8_t clrbits = 0; + int ret = OK; + + sx127x_lock(dev->spi); + + /* Get mode specific configuration */ + + switch (opmode) + { + case SX127X_OPMODE_SLEEP: + case SX127X_OPMODE_STANDBY: + case SX127X_OPMODE_FSRX: + case SX127X_OPMODE_FSTX: + { + break; + } + + case SX127X_OPMODE_TX: + { + /* DIO0 is TX DONE */ + + dio0map = SX127X_LRM_DIOMAP1_DIO0_TXDONE; + + /* Full buffer for TX */ + + sx127x_writeregbyte(dev, SX127X_LRM_TXBASE, 0); + + /* Reset FIFO pointer */ + + sx127x_writeregbyte(dev, SX127X_LRM_ADDRPTR, 0); + + break; + } + + case SX127X_OPMODE_RX: + case SX127X_OPMODE_RXSINGLE: + { + /* DIO0 is RX DONE */ + + dio0map = SX127X_LRM_DIOMAP1_DIO0_RXDONE; + + /* Full buffer for RX */ + + sx127x_writeregbyte(dev, SX127X_LRM_RXBASE, 0); + + /* Reset FIFO pointer */ + + sx127x_writeregbyte(dev, SX127X_LRM_ADDRPTR, 0); + + break; + } + + case SX127X_OPMODE_CAD: + { + /* DIO0 is CAD DONE */ + + dio0map = SX127X_LRM_DIOMAP1_DIO0_CADDONE; + + break; + } + + default: + { + wlerr("ERROR: invalid mode %d\n", opmode); + ret = -EINVAL; + goto errout; + } + } + + /* Configure DIO0 pin */ + + setbits = dio0map; + clrbits = SX127X_CMN_DIOMAP1_DIO0_MASK; + sx127x_modregbyte(dev, SX127X_CMN_DIOMAP1, setbits, clrbits); + + sx127x_unlock(dev->spi); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_opmode_set + * + * Description: + * Set operation mode for LORA + * + ****************************************************************************/ + +static int sx127x_lora_opmode_set(FAR struct sx127x_dev_s *dev, uint8_t opmode) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + int ret = OK; + + sx127x_lock(dev->spi); + + switch (opmode) + { + case SX127X_OPMODE_SLEEP: + case SX127X_OPMODE_STANDBY: + case SX127X_OPMODE_FSRX: + case SX127X_OPMODE_FSTX: + case SX127X_OPMODE_TX: + case SX127X_OPMODE_RX: + case SX127X_OPMODE_RXSINGLE: + case SX127X_OPMODE_CAD: + { + /* Do nothing */ + + break; + } + + default: + { + wlerr("ERROR: invalid LORA mode %d\n", opmode); + ret = -EINVAL; + goto errout; + } + } + + /* Update mode */ + + sx127x_modregbyte(dev, SX127X_CMN_OPMODE, + ((opmode-1) << SX127X_CMN_OPMODE_MODE_SHIFT), + SX127X_CMN_OPMODE_MODE_MASK); + + sx127x_unlock(dev->spi); + + /* Wait for mode ready. REVISIT: do we need this ? */ + + usleep(250); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_syncword_get + ****************************************************************************/ + +static void sx127x_lora_syncword_get(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t *len) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + wlerr("sx127x_lora_syncword_get not implemented yet\n"); +} + +/**************************************************************************** + * Name: sx127x_lora_syncword_set + * + * Description: + * Set SyncWord for LORA + * + ****************************************************************************/ + +static int sx127x_lora_syncword_set(FAR struct sx127x_dev_s *dev, uint8_t *sw, + uint8_t len) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + int ret = OK; + + if (len != 1) + { + wlerr("LORA support sync word with len = 1 but len = %d\n", len); + ret = -EINVAL; + goto errout; + } + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Write sync word */ + + sx127x_writeregbyte(dev, SX127X_LRM_SYNCWORD, sw[0]); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_bw_set + * + * Description: + * Configure LORA bandwidth + * + ****************************************************************************/ + +static int sx127x_lora_bw_set(FAR struct sx127x_dev_s *dev, uint8_t bw) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + uint8_t clrbits = 0; + uint8_t setbits = 0; + int ret = OK; + + if (bw == dev->lora.bw) + { + goto errout; + } + + switch(bw) + { + case LORA_BANDWIDTH_7p8kHz: + case LORA_BANDWIDTH_10p4kHz: + case LORA_BANDWIDTH_15p6kHz: + case LORA_BANDWIDTH_20p8kHz: + case LORA_BANDWIDTH_31p2kHz: + case LORA_BANDWIDTH_41p4kHz: + case LORA_BANDWIDTH_62p5kHz: + case LORA_BANDWIDTH_125kHz: + case LORA_BANDWIDTH_250kHz: + { + /* Lock SPI */ + + sx127x_lock(dev->spi); + + setbits = bw << SX127X_LRM_MDMCFG1_BW_SHIFT; + clrbits = SX127X_LRM_MDMCFG1_BW_MASK; + sx127x_modregbyte(dev, SX127X_LRM_MDMCFG1, setbits, clrbits); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + break; + } + + default: + { + ret = -EINVAL; + wlerr("Unsupported bandwidth %d\n", bw); + goto errout; + } + } + + dev->lora.bw = bw; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_cr_set + * + * Description: + * Configure LORA coding rate + * + ****************************************************************************/ + +static int sx127x_lora_cr_set(FAR struct sx127x_dev_s *dev, uint8_t cr) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + uint8_t clrbits = 0; + uint8_t setbits = 0; + int ret = OK; + + if (cr == dev->lora.cr) + { + goto errout; + } + + switch(cr) + { + case LORA_CR_4d5: + case LORA_CR_4d6: + case LORA_CR_4d7: + case LORA_CR_4d8: + { + /* Lock SPI */ + + sx127x_lock(dev->spi); + + setbits = cr << SX127X_LRM_MDMCFG1_CDRATE_SHIFT; + clrbits = SX127X_LRM_MDMCFG1_CDRATE_MASK; + sx127x_modregbyte(dev, SX127X_LRM_MDMCFG1, setbits, clrbits); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + break; + } + + default: + { + ret = -EINVAL; + wlerr("Unsupported code rate %d\n", cr); + goto errout; + } + } + + dev->lora.cr = cr; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_sf_set + * + * Description: + * Configure LORA SF + * + ****************************************************************************/ + +static int sx127x_lora_sf_set(FAR struct sx127x_dev_s *dev, uint8_t sf) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + uint8_t dopt = SX127X_LRM_DETECTOPT_DO_SF7SF12; + uint8_t dthr = SX127X_LRM_DETECTTHR_SF7SF12; + uint8_t setbits = 0; + uint8_t clrbits = 0; + int ret = OK; + + if (dev->lora.sf == sf) + { + goto errout; + } + + /* Special configuration required by SF6 (highest data rate transmission): + * - implict header mode ON + * - Detection optimise for SF6 + * - Detection threshold for SF6 + */ + + if (dev->lora.sf == 6) + { + if (dev->lora.implicthdr == true) + { + wlerr("SF6 needs implict header ON!\n"); + ret = -EINVAL; + goto errout; + } + + dopt = SX127X_LRM_DETECTOPT_DO_SF6; + dthr = SX127X_LRM_DETECTTHR_SF6; + } + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Write spreading factor */ + + clrbits = SX127X_LRM_MDMCFG2_SPRFACT_MASK; + setbits = (sf << SX127X_LRM_MDMCFG2_SPRFACT_SHIFT); + sx127x_modregbyte(dev, SX127X_LRM_MDMCFG2, setbits, clrbits); + + sx127x_writeregbyte(dev, SX127X_LRM_DETECTOPT, dopt); + sx127x_writeregbyte(dev, SX127X_LRM_DETECTTHR, dthr); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Update local variable */ + + dev->lora.sf = sf; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_implicthdr_set + * + * Description: + * Enable/disable implict header for LORA + * + ****************************************************************************/ + +static int sx127x_lora_implicthdr_set(FAR struct sx127x_dev_s *dev, bool enable) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + uint8_t setbits = 0; + uint8_t clrbits = 0; + int ret = OK; + + if (dev->lora.sf == 6 && enable == false) + { + wlerr("SF=6 requires implict header ON\n"); + ret = -EINVAL; + goto errout; + } + + if (enable == dev->lora.implicthdr) + { + goto errout; + } + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Modify MDMCFG1 register */ + + clrbits = 0; + setbits = SX127X_LRM_MDMCFG1_IMPLHDRON; + + sx127x_modregbyte(dev, SX127X_LRM_MDMCFG1, setbits, clrbits); + + sx127x_modregbyte(dev, SX127X_LRM_HOPCHAN, setbits, clrbits); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Update local variable */ + + dev->lora.implicthdr = enable; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_init + * + * Description: + * Initialization specific for LORA modulation + * + ****************************************************************************/ + +static void sx127x_lora_init(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + uint8_t setbits = 0; + uint8_t clrbits = 0; + + /* Configure sync word for LORA modulation */ + + setbits = SX127X_LRM_SYNCWORD_DEFAULT; + sx127x_lora_syncword_set(dev, &setbits, 1); + + /* Configure bandwidth */ + + sx127x_lora_bw_set(dev, SX127X_LRM_BW_DEFAULT); + + /* Configure coding rate */ + + sx127x_lora_cr_set(dev, SX127X_LRM_CR_DEFAULT); + + /* TODO: Configure frequency hopping */ + + /* sx127x_lora_fhop_set(dev,) */ + + /* Configure spreading factor */ + + sx127x_lora_sf_set(dev, SX127X_LRM_SF_DEFAULT); + + /* Configure LORA header */ + + sx127x_lora_implicthdr_set(dev, SX127X_LRM_IMPLICTHDR_DEFAULT); + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Configure maximum payload */ + + sx127x_writeregbyte(dev, SX127X_LRM_PAYLOADMAX, SX127X_LRM_PAYLOADMAX_DEFAULT); + + /* Modem PHY config 2: + * - TX crc ON + * - packet mode + */ + + setbits = (dev->crcon == true ? SX127X_LRM_MDMCFG2_RXCRCON : 0); + clrbits = SX127X_LRM_MDMCFG2_TXCONT; + sx127x_modregbyte(dev, SX127X_LRM_MDMCFG2, setbits, clrbits); + + /* Invert I and Q signals if configured */ + + setbits = (dev->lora.invert_iq == true ? SX127X_LRM_INVERTIQ_IIQ : 0); + clrbits = 0; + sx127x_modregbyte(dev, SX127X_LRM_INVERTIQ, setbits, clrbits); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); +} + +/**************************************************************************** + * Name: sx127x_lora_rssi_correct + * + * Description: + * Correct RSSI for LORA radio according to datasheet + * + ****************************************************************************/ + +static int16_t sx127x_lora_rssi_correct(FAR struct sx127x_dev_s *dev, + uint32_t freq, int8_t snr, uint8_t regval) +{ + int16_t offset = 0; + int16_t ret = 0; + + /* Ignore SNR if >= 0 */ + + if (snr >= 0) + { + snr = 0; + } + + /* RSSI offset depends on RF frequency */ + + offset = (freq > SX127X_HFBAND_THR ? + SX127X_LRM_RSSIVAL_HF_OFFSET : SX127X_LRM_RSSIVAL_LF_OFFSET); + + /* Get corrected RSSI value */ + + ret = regval + offset + snr; + + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_snr_get + * + * Description: + * Get estimation of SNR on last packet received for LORA modem + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT +static int8_t sx127x_lora_snr_get(FAR struct sx127x_dev_s *dev) +{ + uint8_t regval = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Get register value */ + + regval = sx127x_readregbyte(dev, SX127X_LRM_PKTSNR); + + /* Get SNR */ + + regval = regval/4; + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Return corrected RSSI */ + + return (int8_t)regval; +} + +/**************************************************************************** + * Name: sx127x_lora_pckrssi_get + * + * Description: + * Get RSSI of the last received LORA packet + * + ****************************************************************************/ + +static int16_t sx127x_lora_pckrssi_get(FAR struct sx127x_dev_s *dev, int8_t snr) +{ + uint8_t regval = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Get register value */ + + regval = sx127x_readregbyte(dev, SX127X_LRM_PKTRSSI); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Return corrected RSSI */ + + return sx127x_lora_rssi_correct(dev, dev->freq, snr, regval); +} +#endif + +/**************************************************************************** + * Name: sx127x_lora_rssi_get + * + * Description: + * Get current RSSI for LORA modem + * + ****************************************************************************/ + +static int16_t sx127x_lora_rssi_get(FAR struct sx127x_dev_s *dev) +{ + uint8_t regval = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Get register value */ + + regval = sx127x_readregbyte(dev, SX127X_LRM_RSSIVAL); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Return corrected RSSI */ + + return sx127x_lora_rssi_correct(dev, dev->freq, 0, regval); +} + +/**************************************************************************** + * Name: sx127x_lora_preamble_set + * + * Description: + * Set preamble for LORA modulation + * + ****************************************************************************/ + +static void sx127x_lora_preamble_set(FAR struct sx127x_dev_s *dev, uint32_t len) +{ + uint8_t regval = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Configure preamble len */ + + regval = SX127X_LRM_PRE_MSB(len); + sx127x_writeregbyte(dev, SX127X_LRM_PREMSB, regval); + regval = SX127X_LRM_PRE_LSB(len); + sx127x_writeregbyte(dev, SX127X_LRM_PRELSB, regval); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); +} + +/**************************************************************************** + * Name: sx127x_lora_preamble_get + * + * Description: + * + ****************************************************************************/ + +static int sx127x_lora_preamble_get(FAR struct sx127x_dev_s *dev) +{ + wlerr("sx127x_lora_preamble_get\n"); + return 0; +} + +#endif /* CONFIG_LPWAN_SX127X_LORA */ + +/**************************************************************************** + * Name: sx127x_modulation_get + * + * Description: + * Get current radio modulation + * + ****************************************************************************/ + +static uint8_t sx127x_modulation_get(FAR struct sx127x_dev_s *dev) +{ + uint8_t regval = 0; + uint8_t ret = 0; + + /* Get OPMODE register */ + + regval = sx127x_readregbyte(dev, SX127X_CMN_OPMODE); + + if (regval & SX127X_CMN_OPMODE_LRMODE) + { + /* LORA modulation */ + + ret = SX127X_MODULATION_LORA; + } + else + { + /* FSK or OOK modulation */ + + ret = (regval & SX127X_CMN_OPMODE_MODTYPE_FSK ? + SX127X_MODULATION_FSK : SX127X_MODULATION_OOK); + } + + return ret; +} + +/**************************************************************************** + * Name: sx127x_ops_set + ****************************************************************************/ + +static void sx127x_ops_set(FAR struct sx127x_dev_s *dev, uint8_t modulation) +{ +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + if (modulation <= SX127X_MODULATION_OOK) + { + dev->ops.init = sx127x_fskook_init; + dev->ops.isr0_process = sx127x_fskook_isr0_process; + dev->ops.opmode_init = sx127x_fskook_opmode_init; + dev->ops.opmode_set = sx127x_fskook_opmode_set; + dev->ops.preamble_set = sx127x_fskook_preamble_set; + dev->ops.preamble_get = sx127x_fskook_preamble_get; + dev->ops.rssi_get = sx127x_fskook_rssi_get; + dev->ops.syncword_set = sx127x_fskook_syncword_set; + dev->ops.syncword_get = sx127x_fskook_syncword_get; +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + dev->ops.send = sx127x_fskook_send; +#endif +#ifdef CONFIG_DEBUG_WIRELESS_INFO + dev->ops.dumpregs = sx127x_fskook_dumpregs; +#endif + } +#endif +#ifdef CONFIG_LPWAN_SX127X_LORA + if (modulation == SX127X_MODULATION_LORA) + { + dev->ops.init = sx127x_lora_init; + dev->ops.isr0_process = sx127x_lora_isr0_process; + dev->ops.opmode_init = sx127x_lora_opmode_init; + dev->ops.opmode_set = sx127x_lora_opmode_set; + dev->ops.preamble_set = sx127x_lora_preamble_set; + dev->ops.preamble_get = sx127x_lora_preamble_get; + dev->ops.rssi_get = sx127x_lora_rssi_get; + dev->ops.syncword_set = sx127x_lora_syncword_set; + dev->ops.syncword_get = sx127x_lora_syncword_get; +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + dev->ops.send = sx127x_lora_send; +#endif +#ifdef CONFIG_DEBUG_WIRELESS_INFO + dev->ops.dumpregs = sx127x_lora_dumpregs; +#endif + } +#endif +} + +/**************************************************************************** + * Name: sx127x_modulation_set + * + * Description: + * Set radio modulation and configure + * + ****************************************************************************/ + +static int sx127x_modulation_set(FAR struct sx127x_dev_s *dev, + uint8_t modulation) +{ + uint8_t setbits = 0; + uint8_t clrbits = 0; + int ret = OK; + + wlinfo("modulation_set %d->%d\n", dev->modulation, modulation); + + if (modulation == dev->modulation) + { + goto errout; + } + + /* Modualtion can be only changed in SLEEP mode */ + + sx127x_opmode_set(dev, SX127X_OPMODE_SLEEP); + + /* Change modulation */ + + switch (modulation) + { +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + case SX127X_MODULATION_FSK: + { + clrbits = (SX127X_CMN_OPMODE_LRMODE|SX127X_CMN_OPMODE_MODTYPE_MASK); + setbits = SX127X_CMN_OPMODE_MODTYPE_FSK; + + break; + } + + case SX127X_MODULATION_OOK: + { + clrbits = (SX127X_CMN_OPMODE_LRMODE|SX127X_CMN_OPMODE_MODTYPE_MASK); + setbits = SX127X_CMN_OPMODE_MODTYPE_OOK; + + break; + } +#endif /* CONFIG_LPWAN_SX127X_FSKOOK */ + +#ifdef CONFIG_LPWAN_SX127X_LORA + case SX127X_MODULATION_LORA: + { + clrbits = SX127X_CMN_OPMODE_MODTYPE_MASK; + setbits = SX127X_CMN_OPMODE_LRMODE; + + break; + } +#endif /* CONFIG_LPWAN_SX127X_LORA */ + + default: + { + wlerr("ERROR: Unsupported modulation type %d\n", modulation); + ret = -EINVAL; + goto errout; + } + } + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Modify register */ + + sx127x_modregbyte(dev, SX127X_CMN_OPMODE, setbits, clrbits); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Initialization specific for modulation and initialize private ops */ + + sx127x_ops_set(dev, modulation); + + /* Update local variable */ + + dev->modulation = modulation; + + /* Initial configuration */ + + dev->ops.init(dev); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_fskook_rssi_get + ****************************************************************************/ + +static int16_t sx127x_rssi_get(FAR struct sx127x_dev_s *dev) +{ + return dev->ops.rssi_get(dev); +} + +/**************************************************************************** + * Name: sx127x_channel_scan + * + * Description: + * + ****************************************************************************/ + +static bool sx127x_channel_scan(FAR struct sx127x_dev_s *dev, + FAR struct sx127x_chanscan_ioc_s *chanscan) +{ + struct timespec tstart; + struct timespec tnow; + bool ret = true; + int16_t rssi = 0; + int16_t max = 0; + int16_t min = 0; + + /* Set frequency */ + + sx127x_frequency_set(dev, chanscan->freq); + + /* Set mode to RX */ + + dev->ops.opmode_set(dev, SX127X_OPMODE_RX); + + /* Get start time */ + + clock_gettime(CLOCK_REALTIME, &tstart); + + /* Initialize min/max */ + + max = INT16_MIN; + min = INT16_MAX; + + do + { + /* Get time now */ + + clock_gettime(CLOCK_REALTIME, &tnow); + + /* Check RSSI */ + + rssi = dev->ops.rssi_get(dev); + + /* Store maximum/minimum value */ + + if (rssi > max) + { + max = rssi; + } + else if (rssi < min) + { + min = rssi; + } + + if (rssi > chanscan->rssi_thr) + { + ret = false; + break; + } + + } while (tstart.tv_sec + chanscan->stime > tnow.tv_sec); + + /* Store limit values */ + + chanscan->rssi_max = max; + chanscan->rssi_min = min; + + /* Store return value in struct */ + + chanscan->free = ret; + + return ret; +} + +/**************************************************************************** + * Name: sx127x_random_get + * + * Description: + * + ****************************************************************************/ + +static uint32_t sx127x_random_get(FAR struct sx127x_dev_s *dev) +{ + wlerr("sx127x_random_get not implemented yet\n"); + return 0; +} + +/**************************************************************************** + * Name: sx127x_frequency_get + * + * Description: + * Get RF carrier frequency + * + ****************************************************************************/ + +static uint32_t sx127x_frequency_get(FAR struct sx127x_dev_s *dev) +{ + wlerr("sx127x_frequency_get\n"); + return 0; +} + +/**************************************************************************** + * Name: sx127x_frequency_set + * + * Description: + * Set RF carrier frequency for LORA and FSK/OOK modulation + * + ****************************************************************************/ + +static int sx127x_frequency_set(FAR struct sx127x_dev_s *dev, uint32_t freq) +{ + uint32_t frf = 0; + int ret = OK; + + if (freq == dev->freq) + { + goto errout; + } + + /* REVISIT: needs sleep/standby mode ? */ + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Get FRF value */ + + frf = SX127X_FRF_FROM_FREQ(freq); + + /* Write FRF MSB */ + + sx127x_writeregbyte(dev, SX127X_CMN_FRFMSB, SX127X_CMN_FRF_MSB(frf)); + + /* Write FRF MID */ + + sx127x_writeregbyte(dev, SX127X_CMN_FRFMID, SX127X_CMN_FRF_MID(frf)); + + /* Write FRF LSB */ + + sx127x_writeregbyte(dev, SX127X_CMN_FRFLSB, SX127X_CMN_FRF_LSB(frf)); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* Update local variable */ + + dev->freq = freq; + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_preamble_set + ****************************************************************************/ + +static void sx127x_preamble_set(FAR struct sx127x_dev_s *dev, uint32_t len) +{ + dev->ops.preamble_set(dev, len); +} + +/**************************************************************************** + * Name: sx127x_preamble_get + ****************************************************************************/ + +static int sx127x_preamble_get(FAR struct sx127x_dev_s *dev) +{ + return dev->ops.preamble_get(dev); +} + +/**************************************************************************** + * Name: sx127x_version_get + * + * Description: + * Get chip version + * + ****************************************************************************/ + +static uint8_t sx127x_version_get(FAR struct sx127x_dev_s *dev) +{ + uint8_t regval = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Get version */ + + regval = sx127x_readregbyte(dev, SX127X_CMN_VERSION); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + return regval; +} + +/**************************************************************************** + * Name: sx127x_calibration + * + * Description: + * Calibrate radio for given frequency + * + ****************************************************************************/ + +static int sx127x_calibration(FAR struct sx127x_dev_s *dev, uint32_t freq) +{ + uint8_t regval = 0; + int ret = OK; + + /* NOTE: The automatic calibration at POR and Reset is only valid at 434 MHz */ + + wlinfo("SX127X calibration for %d\n", freq); + + /* Calibration is supported only in FSK/OOK mode */ + + ret = sx127x_modulation_set(dev, SX127X_MODULATION_FSK); + if (ret < 0) + { + wlerr("ERROR: can't change modulation to FSK \n"); + goto errout; + } + + /* We need standby mode ? */ + + ret = sx127x_opmode_set(dev, SX127X_OPMODE_STANDBY); + + /* Set calibration frequency */ + + sx127x_frequency_set(dev, freq); + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Start calibration */ + + sx127x_modregbyte(dev, SX127X_FOM_IMAGECAL, SX127X_FOM_IMAGECAL_IMGCALSTART, 0); + + /* Wait for calibration done */ + + do + { + /* Wait 10ms */ + + usleep(10000); + + /* Get register */ + + regval = sx127x_readregbyte(dev, SX127X_FOM_IMAGECAL); + } + while(regval & SX127X_FOM_IMAGECAL_IMGCALRUN); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + wlinfo("Calibration done\n"); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_init + * + * Description: + * Initialize SX127X chip + * + ****************************************************************************/ + +static int sx127x_init(FAR struct sx127x_dev_s *dev) +{ + int ret = OK; + uint8_t regval = 0; + + wlinfo("Init sx127x dev\n"); + + /* Reset radio */ + + sx127x_reset(dev); + + /* Get initial modem state */ + + regval = sx127x_readregbyte(dev, SX127X_CMN_OPMODE); + + dev->opmode = (((regval >> SX127X_CMN_OPMODE_MODE_SHIFT) & + SX127X_CMN_OPMODE_MODE_MASK) + 1); + dev->modulation = ((regval & SX127X_CMN_OPMODE_LRMODE) ? SX127X_MODULATION_LORA : + (regval & SX127X_CMN_OPMODE_MODTYPE_OOK ? SX127X_MODULATION_OOK : + SX127X_MODULATION_FSK)); + + wlinfo("Init state: modulation=%d, opmode=%d\n", dev->modulation, dev->opmode); + + /* Set ops */ + + sx127x_ops_set(dev, dev->modulation); + + /* Get chip version */ + + regval = sx127x_version_get(dev); + if (regval == 0x00) + { + /* Probably sth wrong with communication */ + + wlerr("ERROR: failed to get chip version!\n"); + ret = -ENODATA; + goto errout; + } + + wlinfo("SX127X version = 0x%02x\n", regval); + + /* Calibration */ + + ret = sx127x_calibration(dev, SX127X_FREQ_CALIBRATION); + if (ret < 0) + { + wlerr("ERROR: calibration failed \n"); + } + + /* Set default modulation and configure */ + + sx127x_modulation_set(dev, SX127X_MODULATION_DEFAULT); + + /* Enter SLEEP mode */ + + sx127x_opmode_set(dev, SX127X_OPMODE_SLEEP); + + /* Set channel frequency */ + + sx127x_frequency_set(dev, SX127X_FREQ_RF_DEFAULT); + + /* Configure preamble */ + + sx127x_preamble_set(dev, SX127X_PREAMBLE_LEN_DEFAULT); + + /* TODO: Configure RF output */ + + /* TODO: Configure RF input */ + + + wlinfo("Init sx127x dev - DONE\n"); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_deinit + * + * Description: + * Deinitialize SX127X chip + * + ****************************************************************************/ + +static int sx127x_deinit(FAR struct sx127x_dev_s *dev) +{ + wlinfo("Deinit sx127x dev\n"); + + /* Enter SLEEP mode */ + + sx127x_opmode_set(dev, SX127X_OPMODE_SLEEP); + + /* Reset radio */ + + sx127x_reset(dev); + + return OK; +} + +#ifdef CONFIG_DEBUG_WIRELESS_INFO + +/**************************************************************************** + * Name: sx127x_lora_dumpregs + * + * Description: + * Dump registers for LORA modem + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_LORA +static void sx127x_lora_dumpregs(FAR struct sx127x_dev_s *dev) +{ + sx127x_lock(dev->spi); + wlinfo("LORA dump:\n"); + wlinfo("FIFO: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FIFO)); + wlinfo("OPMODE: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_OPMODE)); + wlinfo("FRFMSB: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FRFMSB)); + wlinfo("FRFMID: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FRFMID)); + wlinfo("FRFLSB: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FRFLSB)); + wlinfo("PACFG: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_PACFG)); + wlinfo("PARAMP: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_PARAMP)); + wlinfo("OCP: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_OCP)); + wlinfo("LNA: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_LNA)); + wlinfo("ADDRPTR: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_ADDRPTR)); + wlinfo("TXBASE: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_TXBASE)); + wlinfo("RXBASE: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXBASE)); + wlinfo("RXCURR: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXCURR)); + wlinfo("IRQMASK: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_IRQMASK)); + wlinfo("IRQ: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_IRQ)); + wlinfo("RXBYTES: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXBYTES)); + wlinfo("RXHDRMSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXHDRMSB)); + wlinfo("RXHDRLSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXHDRLSB)); + wlinfo("RXPKTMSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXPKTMSB)); + wlinfo("RXPKTLSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXPKTLSB)); + wlinfo("MODSTAT: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_MODSTAT)); + wlinfo("PKTSNR: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_PKTSNR)); + wlinfo("PKTRSSI: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_PKTRSSI)); + wlinfo("RSSI: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RSSIVAL)); + wlinfo("HOPCHAN: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_HOPCHAN)); + wlinfo("MDMCFG1: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_MDMCFG1)); + wlinfo("MDMCFG2: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_MDMCFG2)); + wlinfo("RXTIMEOUTLSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXTIMEOUTLSB)); + wlinfo("PREMSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_PREMSB)); + wlinfo("PRELSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_PRELSB)); + wlinfo("PAYLOADLEN: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_PAYLOADLEN)); + wlinfo("PAYLOADMAX: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_PAYLOADMAX)); + wlinfo("HOPPER: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_HOPPER)); + wlinfo("RXFIFOADDR: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RXFIFOADDR)); + wlinfo("MODEMCFG3: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_MODEMCFG3)); + wlinfo("FEIMSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_FEIMSB)); + wlinfo("FEIMID: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_FEIMID)); + wlinfo("FEILSB: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_FEILSB)); + wlinfo("RSSIWIDEBAND: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_RSSIWIDEBAND)); + wlinfo("DETECTOPT: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_DETECTOPT)); + wlinfo("INVERTIQ: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_INVERTIQ)); + wlinfo("DETECTTHR: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_DETECTTHR)); + wlinfo("SYNCWORD: %02x\n", sx127x_readregbyte(dev, SX127X_LRM_SYNCWORD)); + wlinfo("DIOMAP1: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_DIOMAP1)); + wlinfo("DIOMAP2: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_DIOMAP2)); + wlinfo("VERSION: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_VERSION)); + wlinfo("TCXO: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_TCXO)); + wlinfo("PADAC: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_PADAC)); + wlinfo("FTEMP: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FTEMP)); + wlinfo("AGCREF: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_AGCREF)); + wlinfo("AGCTHR1: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_AGCTHR1)); + wlinfo("AGCTHR2: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_AGCTHR2)); + wlinfo("AGCTHR3: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_AGCTHR3)); + wlinfo("PLL: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_PLL)); + sx127x_unlock(dev->spi); +} +#endif /* CONFIG_LPWAN_SX127X_LORA */ + +/**************************************************************************** + * Name: sx127x_fskook_dumpregs + * + * Description: + * Dump registers for FSK/OOK modem + * + ****************************************************************************/ + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK +static void sx127x_fskook_dumpregs(FAR struct sx127x_dev_s *dev) +{ + sx127x_lock(dev->spi); + wlinfo("FSK/OOK dump:\n"); + wlinfo("FIFO: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FIFO)); + wlinfo("OPMODE: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_OPMODE)); + wlinfo("FRFMSB: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FRFMSB)); + wlinfo("FRFMID: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FRFMID)); + wlinfo("FRFLSB: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FRFLSB)); + wlinfo("PACFG: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_PACFG)); + wlinfo("PARAMP: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_PARAMP)); + wlinfo("OCP: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_OCP)); + wlinfo("LNA: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_LNA)); + wlinfo("BITRATEMSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_BITRATEMSB)); + wlinfo("BITRATELSM: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_BITRATELSB)); + wlinfo("FDEVMSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_FDEVMSB)); + wlinfo("FDEVLSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_FDEVLSB)); + wlinfo("RXCFG: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RXCFG)); + wlinfo("RSSICFG: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RSSICFG)); + wlinfo("RSSICOLL: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RSSICOLL)); + wlinfo("RSSITHR: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RSSITHR)); + wlinfo("RSSIVAL: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RSSIVAL)); + wlinfo("RXBW: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RXBW)); + wlinfo("AFCBW: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_AFCBW)); + wlinfo("OOKPEAK: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_OOKPEAK)); + wlinfo("OOKFIX: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_OOKFIX)); + wlinfo("AFCFEI: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_AFCFEI)); + wlinfo("AFCMSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_AFCMSB)); + wlinfo("AFCLSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_AFCLSB)); + wlinfo("FEIMSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_FEIMSB)); + wlinfo("FEILSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_FEILSB)); + wlinfo("PREDET: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_PREDET)); + wlinfo("RXTIMEOUT1: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RXTIMEOUT1)); + wlinfo("RXTIMEOUT2: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RXTIMEOUT1)); + wlinfo("RXTIMEOUT3: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RXTIMEOUT1)); + wlinfo("RXDELAY: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_RXDELAY)); + wlinfo("OSC: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_OSC)); + wlinfo("PREMSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_PREMSB)); + wlinfo("PRELSB: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_PRELSB)); + wlinfo("SYNCCFG: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_SYNCCFG)); + wlinfo("SYNCVAL1: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_SYNCVAL1)); + wlinfo("SYNCVAL2: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_SYNCVAL2)); + wlinfo("SYNCVAL3: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_SYNCVAL3)); + wlinfo("SYNCVAL4: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_SYNCVAL4)); + wlinfo("SYNCVAL5: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_SYNCVAL5)); + wlinfo("PKTCFG1: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_PKTCFG1)); + wlinfo("PKTCFG2: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_PKTCFG2)); + wlinfo("PAYLOADLEN: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_PAYLOADLEN)); + wlinfo("NODEADDR: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_NODEADDR)); + wlinfo("BROADCAST: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_BROADCAST)); + wlinfo("FIFOTHR: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_FIFOTHR)); + wlinfo("SEQCFG1: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_SEQCFG1)); + wlinfo("SEQCFG2: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_SEQCFG2)); + wlinfo("TIMRES: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_TIMRES)); + wlinfo("TIMER1COEF: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_TIMER1COEF)); + wlinfo("TIMER2COEF: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_TIMER2COEF)); + wlinfo("IMAGECAL: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_IMAGECAL)); + wlinfo("TEMP: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_TEMP)); + wlinfo("LOWBAT: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_LOWBAT)); + wlinfo("IRQ1: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_IRQ1)); + wlinfo("IRQ2: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_IRQ2)); + wlinfo("PLLHOP: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_PLLHOP)); + wlinfo("BITRATEFRAC: %02x\n", sx127x_readregbyte(dev, SX127X_FOM_BITRATEFRAC)); + wlinfo("DIOMAP1: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_DIOMAP1)); + wlinfo("DIOMAP2: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_DIOMAP2)); + wlinfo("VERSION: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_VERSION)); + wlinfo("TCXO: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_TCXO)); + wlinfo("PADAC: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_PADAC)); + wlinfo("FTEMP: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_FTEMP)); + wlinfo("AGCREF: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_AGCREF)); + wlinfo("AGCTHR1: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_AGCTHR1)); + wlinfo("AGCTHR2: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_AGCTHR2)); + wlinfo("AGCTHR3: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_AGCTHR3)); + wlinfo("PLL: %02x\n", sx127x_readregbyte(dev, SX127X_CMN_PLL)); + sx127x_unlock(dev->spi); +} +#endif /* CONFIG_LPWAN_SX127X_FSKOOK */ + +/**************************************************************************** + * Name: sx127x_dumpregs + * + * Description: + * Dump registers according to current modulation + * + ****************************************************************************/ + +static void sx127x_dumpregs(FAR struct sx127x_dev_s *dev) +{ + switch (dev->modulation) + { +#ifdef CONFIG_LPWAN_SX127X_LORA + case SX127X_MODULATION_LORA: + { + sx127x_lora_dumpregs(dev); + break; + } +#endif +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + case SX127X_MODULATION_FSK: + case SX127X_MODULATION_OOK: + { + sx127x_fskook_dumpregs(dev); + break; + } +#endif + default: + { + wlinfo("Unknown SX127X modulation\n"); + break; + } + } +} +#endif /* CONFIG_DEBUG_WIRELESS_INFO */ + +/**************************************************************************** + * Name: sx127x_unregister + * + * Description: + * Unregister SX127X device + * + ****************************************************************************/ + +static int sx127x_unregister(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + /* Release IRQ */ + + sx127x_attachirq0(dev, NULL, NULL); + + /* Destroy semaphores */ + + nxsem_destroy(&dev->dev_sem); +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + nxsem_destroy(&dev->tx_sem); +#endif +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + nxsem_destroy(&dev->rx_sem); + nxsem_destroy(&dev->rx_buffer_sem); +#endif + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sx127x_register + * + * Description: + * Register sx127x driver + * + ****************************************************************************/ + +int sx127x_register(FAR struct spi_dev_s *spi, + FAR const struct sx127x_lower_s *lower) +{ + FAR struct sx127x_dev_s *dev = NULL; + int ret = OK; + + DEBUGASSERT(spi != NULL); + DEBUGASSERT(lower != NULL); + + /* Only one sx127x device supported for now */ + + dev = &g_sx127x_devices[0]; + + /* Reset data */ + + memset(dev, 0, sizeof(struct sx127x_dev_s)); + + /* Attach the interface, lower driver */ + + dev->spi = spi; + dev->lower = lower; + + /* Initlaize IDLE mode */ + + dev->idle = SX127X_IDLE_OPMODE; + +#ifndef CONFIG_DISABLE_POLL + dev->pfd = NULL; +#endif + + /* Initialize sem */ + + nxsem_init(&dev->dev_sem, 0, 1); +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + nxsem_init(&dev->tx_sem, 0, 0); +#endif +#ifdef CONFIG_LPWAN_SX127X_RXSUPPORT + nxsem_init(&dev->rx_sem, 0, 0); + nxsem_init(&dev->rx_buffer_sem, 0, 1); +#endif + + /* Attach irq0 - TXDONE/RXDONE/CADDONE */ + + sx127x_attachirq0(dev, sx127x_irq0handler, dev); + + /* TODO: support for irq1-5 */ + + wlinfo("Registering " SX127X_DEV_NAME "\n"); + + ret = register_driver(SX127X_DEV_NAME, &sx127x_fops, 0666, dev); + if (ret < 0) + { + wlerr("ERROR: register_driver() failed: %d\n", ret); + sx127x_unregister(dev); + } + + return ret; +} diff --git a/drivers/wireless/lpwan/sx127x/sx127x.h b/drivers/wireless/lpwan/sx127x/sx127x.h new file mode 100644 index 0000000000..2723b78cec --- /dev/null +++ b/drivers/wireless/lpwan/sx127x/sx127x.h @@ -0,0 +1,869 @@ +/**************************************************************************** + * drivers/wireless/lpwan/sx127x.h + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Authors: Mateusz Szafoni + * + * 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 NuttX 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. + * + ****************************************************************************/ + +#ifndef __DRIVERS_WIRELESS_LPWAN_SX127X_H +#define __DRIVERS_WIRELESS_LPWAN_SX127X_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Registers ****************************************************************/ + +#define SX127X_R_REGISTER 0x00 /* SX127X read register */ +#define SX127X_W_REGISTER 0x80 /* SX127X write register */ + +/* Registers common for FSK/OOK Mode and LORA Mode */ + +#define SX127X_CMN_FIFO 0x00 /* FSK/OOK/LORA: FIFO read/write access */ +#define SX127X_CMN_OPMODE 0x01 /* FSK/OOK/LORA: Operating mode & LORA/FSK selection */ +#define SX127X_CMN_FRFMSB 0x06 /* FSK/OOK/LORA: RF Carrier Frequnecy, MSB */ +#define SX127X_CMN_FRFMID 0x07 /* FSK/OOK/LORA: RF Carrier Frequency, Intermediate Bits */ +#define SX127X_CMN_FRFLSB 0x08 /* FSK/OOK/LORA: RF Carrier Frequency, LSB */ +#define SX127X_CMN_PACFG 0x09 /* FSK/OOK/LORA: PA selection and Output Power control */ +#define SX127X_CMN_PARAMP 0x0a /* FSK/OOK/LORA: Control of PA ramp time, low phase noise PLL */ +#define SX127X_CMN_OCP 0x0b /* FSK/OOK/LORA: Over Current Protection control */ +#define SX127X_CMN_LNA 0x0c /* FSK/OOK/LORA: LNA settings */ +#define SX127X_CMN_DIOMAP1 0x40 /* FSK/OOK/LORA: Mapping of pins DIO0 to DIO3 */ +#define SX127X_CMN_DIOMAP2 0x41 /* FSK/OOK/LORA: Mapping of pins DIO4 and DIO5, ClkOut freq */ +#define SX127X_CMN_VERSION 0x42 /* FSK/OOK/LORA: Semtech ID relating the silicon version */ +#define SX127X_CMN_TCXO 0x4b /* FSK/OOK/LORA: TCXO or XTAL input setting */ +#define SX127X_CMN_PADAC 0x4d /* FSK/OOK/LORA: Higher power settings of the PA */ +#define SX127X_CMN_FTEMP 0x5b /* FSK/OOK/LORA: Stored temperature during the former IQ calibration */ +#define SX127X_CMN_AGCREF 0x61 /* FSK/OOK/LORA: AGC thresholds reference */ +#define SX127X_CMN_AGCTHR1 0x62 /* FSK/OOK/LORA: AGC thresholds 1 */ +#define SX127X_CMN_AGCTHR2 0x63 /* FSK/OOK/LORA: AGC thresholds 2 */ +#define SX127X_CMN_AGCTHR3 0x64 /* FSK/OOK/LORA: AGC thresholds 3 */ +#define SX127X_CMN_PLL 0x70 /* FSK/OOK/LORA: Control of the PLL bandwidth */ + +/* Registers specific for FSK/OOK Mode */ + +#define SX127X_FOM_BITRATEMSB 0x02 /* FSK/OOK: Bit Rate setting, MSB */ +#define SX127X_FOM_BITRATELSB 0x03 /* FSK/OOK: Bit Rate setting, LSB */ +#define SX127X_FOM_FDEVMSB 0x04 /* FSK/OOK: Frequency Deviation setting, MSB */ +#define SX127X_FOM_FDEVLSB 0x05 /* FSK/OOK: Frequency Deviation setting, LSB */ +#define SX127X_FOM_RXCFG 0x0d /* FSK/OOK: AFC, AGC, ctrl */ +#define SX127X_FOM_RSSICFG 0x0e /* FSK/OOK: RSSI */ +#define SX127X_FOM_RSSICOLL 0x0f /* FSK/OOK: RSSI Collision detector */ +#define SX127X_FOM_RSSITHR 0x10 /* FSK/OOK: RSSI Threshold control */ +#define SX127X_FOM_RSSIVAL 0x11 /* FSK/OOK: RSSI value in dBm */ +#define SX127X_FOM_RXBW 0x12 /* FSK/OOK: Channel Filter BW Control */ +#define SX127X_FOM_AFCBW 0x13 /* FSK/OOK: AFC Channel Filter BW */ +#define SX127X_FOM_OOKPEAK 0x14 /* FSK/OOK: OOK demodulator */ +#define SX127X_FOM_OOKFIX 0x15 /* FSK/OOK: Threshold of the OOK demod */ +#define SX127X_FOM_OOKAVG 0x16 /* FSK/OOK: Average of the OOK demod */ +#define SX127X_FOM_AFCFEI 0x1A /* FSK/OOK: AFC and FEI control */ +#define SX127X_FOM_AFCMSB 0x1B /* FSK/OOK: Frequency correction value of the AFC MSB */ +#define SX127X_FOM_AFCLSB 0x1C /* FSK/OOK: Frequnecy correction value of the AFC LSB */ +#define SX127X_FOM_FEIMSB 0x1D /* FSK/OOK: Value of the calculated frequency error MSB */ +#define SX127X_FOM_FEILSB 0x1E /* FSK/OOK: Value of the calculated frequency error LSB */ +#define SX127X_FOM_PREDET 0x1F /* FSK/OOK: Settings of the Preamble Detector */ +#define SX127X_FOM_RXTIMEOUT1 0x20 /* FSK/OOK: Timeout RX request and RSSI */ +#define SX127X_FOM_RXTIMEOUT2 0x21 /* FSK/OOK: Timeout RSSI and PayloadReady */ +#define SX127X_FOM_RXTIMEOUT3 0x22 /* FSK/OOK: Timeout RSSI adn SyncAddress */ +#define SX127X_FOM_RXDELAY 0x23 /* FSK/OOK: Delay between RX cycles */ +#define SX127X_FOM_OSC 0x24 /* FSK/OOK: RC oscillators settings, CLKOUT frequency */ +#define SX127X_FOM_PREMSB 0x25 /* FSK/OOK: Preamble length MSB */ +#define SX127X_FOM_PRELSB 0x26 /* FSK/OOK: Preamble length LSB */ +#define SX127X_FOM_SYNCCFG 0x27 /* FSK/OOK: Sync Word Recognition control */ +#define SX127X_FOM_SYNCVAL1 0x28 /* FSK/OOK: Sync Word bytes 1 */ +#define SX127X_FOM_SYNCVAL2 0x29 /* FSK/OOK: Sync Word bytes 2 */ +#define SX127X_FOM_SYNCVAL3 0x2a /* FSK/OOK: Sync Word bytes 3 */ +#define SX127X_FOM_SYNCVAL4 0x2b /* FSK/OOK: Sync Word bytes 4 */ +#define SX127X_FOM_SYNCVAL5 0x2c /* FSK/OOK: Sync Word bytes 5 */ +#define SX127X_FOM_SYNCVAL6 0x2d /* FSK/OOK: Sync Word bytes 6 */ +#define SX127X_FOM_SYNCVAL7 0x2e /* FSK/OOK: Sync Word bytes 7 */ +#define SX127X_FOM_SYNCVAL8 0x2f /* FSK/OOK: Sync Word bytes 8 */ +#define SX127X_FOM_PKTCFG1 0x30 /* FSK/OOK: Parcket mode settings 1 */ +#define SX127X_FOM_PKTCFG2 0x31 /* FSK/OOK: Packet mode settings 2 */ +#define SX127X_FOM_PAYLOADLEN 0x32 /* FSK/OOK: Payload length setting */ +#define SX127X_FOM_NODEADDR 0x33 /* FSK/OOK: Node address */ +#define SX127X_FOM_BROADCAST 0x34 /* FSK/OOK: Broadcast address */ +#define SX127X_FOM_FIFOTHR 0x35 /* FSK/OOK: FIFO threshold, TX start condition */ +#define SX127X_FOM_SEQCFG1 0x36 /* FSK/OOK: Top level Sequencer settings 1 */ +#define SX127X_FOM_SEQCFG2 0x37 /* FSK/OOK: Top level Sequencer settings 2 */ +#define SX127X_FOM_TIMRES 0x38 /* FSK/OOK: Timer 1 and 2 resolution control */ +#define SX127X_FOM_TIMER1COEF 0x39 /* FSK/OOK: Timer 1 setting */ +#define SX127X_FOM_TIMER2COEF 0x3a /* FSK/OOK: Timer 2 setting */ +#define SX127X_FOM_IMAGECAL 0x3b /* FSK/OOK: Image callibration engine control */ +#define SX127X_FOM_TEMP 0x3c /* FSK/OOK: Temperature Sensor value */ +#define SX127X_FOM_LOWBAT 0x3d /* FSK/OOK: Low Battery Indicator settings */ +#define SX127X_FOM_IRQ1 0x3e /* FSK/OOK: Status register 1: PLL Lock state, Timeout, RSSI */ +#define SX127X_FOM_IRQ2 0x3f /* FSK/OOK: Status register 2: FIFO handling flags, Low Battery */ +#define SX127X_FOM_PLLHOP 0x44 /* FSK/OOK: Control the fast frequency hopping mode */ +#define SX127X_FOM_BITRATEFRAC 0x5d /* FSK/OOK: Fractional part in the Bit Rate division ratio */ + +/* Registers specific for LORA Mode */ + +#define SX127X_LRM_ADDRPTR 0x0d /* LORA: FIFO SPI pointer */ +#define SX127X_LRM_TXBASE 0x0e /* LORA: Start TX data */ +#define SX127X_LRM_RXBASE 0x0f /* LORA: Start RX data */ +#define SX127X_LRM_RXCURR 0x10 /* LORA: Start address of last packet received */ +#define SX127X_LRM_IRQMASK 0x11 /* LORA: Optional IRQ flag mask */ +#define SX127X_LRM_IRQ 0x12 /* LORA: IRQ flags */ +#define SX127X_LRM_RXBYTES 0x13 /* LORA: Number of received bytes */ +#define SX127X_LRM_RXHDRMSB 0x14 /* LORA: Number of valid headers received MSB */ +#define SX127X_LRM_RXHDRLSB 0x15 /* LORA: Number of valid headers received LSB */ +#define SX127X_LRM_RXPKTMSB 0x16 /* LORA: Number of valid packets received MSB */ +#define SX127X_LRM_RXPKTLSB 0x17 /* LORA: Number of valid packets received LSB */ +#define SX127X_LRM_MODSTAT 0x18 /* LORA: Live LORA modem status */ +#define SX127X_LRM_PKTSNR 0x19 /* LORA: Estimation of last packet SNR */ +#define SX127X_LRM_PKTRSSI 0x1A /* LORA: RSSI of last packet */ +#define SX127X_LRM_RSSIVAL 0x1B /* LORA: Current RSSI */ +#define SX127X_LRM_HOPCHAN 0x1C /* LORA: FHSS start channel */ +#define SX127X_LRM_MDMCFG1 0x1D /* LORA: Modem PHY config 1 */ +#define SX127X_LRM_MDMCFG2 0x1E /* LORA: Modem PHY config 2 */ +#define SX127X_LRM_RXTIMEOUTLSB 0x1F /* LORA: Receiver timeout value LSB */ +#define SX127X_LRM_PREMSB 0x20 /* LORA: Size of preamble MSB */ +#define SX127X_LRM_PRELSB 0x21 /* LORA: Size of preamble LSB */ +#define SX127X_LRM_PAYLOADLEN 0x22 /* LORA: LORA payload length */ +#define SX127X_LRM_PAYLOADMAX 0x23 /* LORA: LORA maximum payload length */ +#define SX127X_LRM_HOPPER 0x24 /* LORA: FHSS Hop period */ +#define SX127X_LRM_RXFIFOADDR 0x25 /* LORA: Address of last byte written in FIFO */ +#define SX127X_LRM_MODEMCFG3 0x26 /* LORA: Modem PHY confgi 3*/ +#define SX127X_LRM_FEIMSB 0x28 /* LORA: Estimated frequency error MSB */ +#define SX127X_LRM_FEIMID 0x29 /* LORA: Estimated frequency error, MID */ +#define SX127X_LRM_FEILSB 0x2a /* LORA: Estimated frequency error, LSB*/ +#define SX127X_LRM_RSSIWIDEBAND 0x2c /* LORA: Wideband RSSI measurement */ +#define SX127X_LRM_DETECTOPT 0x31 /* LORA: LORA detection optimize for SF6 */ +#define SX127X_LRM_INVERTIQ 0x33 /* LORA: Invert LORA I and Q signals */ +#define SX127X_LRM_DETECTTHR 0x37 /* LORA: LORA detection threshold for SF6 */ +#define SX127X_LRM_SYNCWORD 0x39 /* LORA: LORA Sync Word */ + +/* Common *******************************************************************/ + +/* FSK/OOK/LORA: FIFO read/write access */ + +#define SX127X_CMN_FIFO_MASK 0xff + +/* Operating mode & LORA/FSK selection */ + +#define SX127X_CMN_OPMODE_MODE_SHIFT (0) /* Bits 0-2: Transceiver mode */ +#define SX127X_CMN_OPMODE_MODE_MASK (7 << SX127X_CMN_OPMODE_MODE_SHIFT) +# define SX127X_CMN_OPMODE_MODE_SLEEP (0 << SX127X_CMN_OPMODE_MODE_SHIFT) /* SLEEP */ +# define SX127X_CMN_OPMODE_MODE_STBY (1 << SX127X_CMN_OPMODE_MODE_SHIFT) /* STDBY */ +# define SX127X_CMN_OPMODE_MODE_FSTX (2 << SX127X_CMN_OPMODE_MODE_SHIFT) /* FSTX */ +# define SX127X_CMN_OPMODE_MODE_TX (3 << SX127X_CMN_OPMODE_MODE_SHIFT) /* TX */ +# define SX127X_CMN_OPMODE_MODE_FSRX (4 << SX127X_CMN_OPMODE_MODE_SHIFT) /* FSRX */ +# define SX127X_CMN_OPMODE_MODE_RX (5 << SX127X_CMN_OPMODE_MODE_SHIFT) /* RX in FSK/OOK, RXCONTINOUS in LORA */ +# define SX127X_CMN_OPMODE_MODE_RXSINGLE (6 << SX127X_CMN_OPMODE_MODE_SHIFT) /* RXSINGLE (only LORA) */ +# define SX127X_CMN_OPMODE_MODE_CAD (7 << SX127X_CMN_OPMODE_MODE_SHIFT) /* CAD (only LORA) */ +#define SX127X_CMN_OPMODE_LFMODEON (1 << 3) /* Bit 3: Low Frequency Mode ON */ +#define SX127X_CMN_OPMODE_MODTYPE_SHIFT (5) /* Bits 5-6: Modulation type (only FSK/OOK) */ +# define SX127X_CMN_OPMODE_MODTYPE_MASK (3 << SX127X_CMN_OPMODE_MODTYPE_SHIFT) +# define SX127X_CMN_OPMODE_MODTYPE_FSK (0 << SX127X_CMN_OPMODE_MODTYPE_SHIFT) +# define SX127X_CMN_OPMODE_MODTYPE_OOK (1 << SX127X_CMN_OPMODE_MODTYPE_SHIFT) +#define SX127X_CMN_OPMODE_LRMODE (1 << 7) /* Bit 7: Long Range Mode 0-FSK/OOK, 1-LORA */ + +/* FSK/OOK/LORA: RF carrier frequency */ + +#define SX127X_CMN_FRF_MAX (0xffffff) +#define SX127X_FRF_FROM_FREQ(freq) (freq/SX127X_FSTEP) +#define SX127X_CMN_FRF_MSB(frf) ((frf >> 16) & 0xff) +#define SX127X_CMN_FRF_MID(frf) ((frf >> 8) & 0xff) +#define SX127X_CMN_FRF_LSB(frf) ((frf >> 0) & 0xff) + +/* FSK/OOK/LORA: PA selection and Output Power control */ + +#define SX127X_CMN_PACFG_OUTPOWER_SHIFT (0) /* Bits 0-4: Pout */ +#define SX127X_CMN_PACFG_OUTPOWER_MASK (15 << SX127X_CMN_PACFG_OUTPOWER_SHIFT) +#define SX127X_CMN_PACFG_MAXPOWER_SHIFT (4) /* Bits 4-6: Max power output: Pmax=10.8 +0.6*MaxPower */ +#define SX127X_CMN_PACFG_MAXPOWER_MASK (7 << SX127X_CMN_PACFG_MAXPOWER_SHIFT) +#define SX127X_CMN_PACFG_PASELECT (1 << 7) /* Bit 7: PA output pin */ + +/* FSK/OOK/LORA: Control of PA ramp time, low phase noise PLL */ + +#define SX127X_CMN_PARAMP_PARAMP_SHIFT (0) /* Bits 0-3: Rise/fall time of ramp up/down */ +#define SX127X_CMN_PARAMP_PARAMP_MASK (15 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_3p4ms (0 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_2ms (1 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_1ms (2 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_500us (3 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_250us (4 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_125us (5 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_100us (6 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_62us (7 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_50us (8 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_40us (9 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_31us (10 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_25us (11 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_20us (12 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_15us (13 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_12us (14 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_PARAMP_11us (15 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +#define SX127X_CMN_PARAMP_SHAPING_SHIFT (5) /* Bits 5-6: Data shaping */ +#define SX127X_CMN_PARAMP_SHAPING_MASK (3 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_SHAPING_NONE (0 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_SHAPING_1 (1 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_SHAPING_2 (2 << SX127X_CMN_PARAMP_PARAMP_SHIFT) +# define SX127X_CMN_PARAMP_SHAPING_3 (3 << SX127X_CMN_PARAMP_PARAMP_SHIFT) + +/* FSK/OOK/LORA: Over Current Protection control */ + +#define SX127X_CMN_OCP_SHIFT (0) /* Bits 0-4: Trimming of OCP current */ +#define SX127X_CMN_OCP_MASK (15 << SX127X_CMN_OCP_SHIFT) +#define SX127X_CMN_OCP_OCPON (1 << 5) /* Bit 5: Enable overload current protection for the PA */ + +/* FSK/OOK/LORA: LNA settings */ + +#define SX127X_CMN_LNA_BOOSTHF_SHIFT 0 /* Bits 0-1: HF LNA current adjustment */ +#define SX127X_CMN_LNA_BOOSTHF_MASK (3 << SX127X_CMN_LNA_BOOSTHF_SHIFT) +# define SX127X_CMN_LNA_BOOSTHF_OFF (0 << SX127X_CMN_LNA_BOOSTHF_SHIFT) +# define SX127X_CMN_LNA_BOOSTHF_ON (3 << SX127X_CMN_LNA_BOOSTHF_SHIFT) +#define SX127X_CMN_LNA_BOOSTLF_SHIFT 3 /* Bits 3-4: LF LNA current adjustment */ +#define SX127X_CMN_LNA_BOOSTLF_MASK (3 << SX127X_CMN_LNA_BOOSTLF_SHIFT) +# define SX127X_CMN_LNA_BOOSTLF_OFF (0 << SX127X_CMN_LNA_BOOSTLF_SHIFT) +#define SX127X_CMN_LNA_GAIN_SHIFT 5 /* Bits 5-7: LNA gain setting */ +#define SX127X_CMN_LNA_GAIN_MASK (7 << SX127X_CMN_LNA_GAIN_SHIFT) +# define SX127X_CMN_LNA_GAIN_1 (0 << SX127X_CMN_LNA_GAIN_SHIFT) +# define SX127X_CMN_LNA_GAIN_2 (1 << SX127X_CMN_LNA_GAIN_SHIFT) +# define SX127X_CMN_LNA_GAIN_3 (2 << SX127X_CMN_LNA_GAIN_SHIFT) +# define SX127X_CMN_LNA_GAIN_4 (3 << SX127X_CMN_LNA_GAIN_SHIFT) +# define SX127X_CMN_LNA_GAIN_5 (4 << SX127X_CMN_LNA_GAIN_SHIFT) +# define SX127X_CMN_LNA_GAIN_6 (5 << SX127X_CMN_LNA_GAIN_SHIFT) +# define SX127X_CMN_LNA_GAIN_7 (6 << SX127X_CMN_LNA_GAIN_SHIFT) + +/* FSK/OOK/LORA: Mapping of pins DIO0 to DIO3 */ + +#define SX127X_CMN_DIOMAP1_DIO3_SHIFT (0) /* Bits 0-1: */ +#define SX127X_CMN_DIOMAP1_DIO3_MASK (3 << SX127X_CMN_DIOMAP1_DIO3_SHIFT) +#define SX127X_CMN_DIOMAP1_DIO2_SHIFT (2) /* Bits 2-3: */ +#define SX127X_CMN_DIOMAP1_DIO2_MASK (3 << SX127X_CMN_DIOMAP1_DIO2_SHIFT) +#define SX127X_CMN_DIOMAP1_DIO1_SHIFT (4) /* Bits 4-5: */ +#define SX127X_CMN_DIOMAP1_DIO1_MASK (3 << SX127X_CMN_DIOMAP1_DIO1_SHIFT) +#define SX127X_CMN_DIOMAP1_DIO0_SHIFT (6) /* Bits 6-7: */ +#define SX127X_CMN_DIOMAP1_DIO0_MASK (3 << SX127X_CMN_DIOMAP1_DIO0_SHIFT) +# define SX127X_FOM_DIOMAP1_DIO0_RXTX (0 << SX127X_CMN_DIOMAP1_DIO0_SHIFT) +# define SX127X_LRM_DIOMAP1_DIO0_RXDONE (0 << SX127X_CMN_DIOMAP1_DIO0_SHIFT) +# define SX127X_LRM_DIOMAP1_DIO0_TXDONE (1 << SX127X_CMN_DIOMAP1_DIO0_SHIFT) +# define SX127X_LRM_DIOMAP1_DIO0_CADDONE (2 << SX127X_CMN_DIOMAP1_DIO0_SHIFT) + +/* FSK/OOK/LORA: Mapping of pins DIO4 and DIO5, ClkOut freq */ + +#define SX127X_CMN_DIOMAP2_MPD (1 << 0) /* Bit 0: MapPreambleDetect */ +#define SX127X_CMN_DIOMAP2_DIO5_SHIFT (4) /* Bits 4-5: */ +#define SX127X_CMN_DIOMAP2_DIO5_MASK (3 << SX127X_CMN_DIOMAP2_DIO5_SHIFT) +#define SX127X_CMN_DIOMAP2_DIO4_SHIFT (6) /* Bits 6-7: */ +#define SX127X_CMN_DIOMAP2_DIO4_MASK (3 << SX127X_CMN_DIOMAP2_DIO4_SHIFT) + +/* FSK/OOK/LORA: Semtech ID relating the silicon version */ + +#define SX127X_CMN_VERSION_MASK 0xff + +/* FSK/OOK/LORA: TCXO or XTAL input setting */ + +#define SX127X_CMN_TCXO_INPUTON (1 << 4) + +/* FSK/OOK/LORA: Higher power settings of the PA */ + +#define SX127X_CMN_PADAC_DEFAULT 0x04 +#define SX127X_CMN_PADAC_BOOST 0x07 + +/* FSK/OOK/LORA: Stored temperature during the former IQ calibration */ + +#define SX127X_CMN_FTEMP_MASK 0xff + +/* FSK/OOK/LORA: AGC thresholds reference */ + +#define SX127X_CMN_AGCREF_REF_SHIFT 0 /* Bits 0-5: */ +#define SX127X_CMN_AGCREF_REF_MASK (63 << SX127X_CMN_AGCREF_REF_SHIFT) +#define SX127X_CMN_AGCREF_REF(v) ((v << SX127X_CMN_AGCREF_REF_SHIFT) & SX127X_CMN_AGCREF_REF_MASK) + +/* FSK/OOK/LORA: AGC thresholds 1 */ + +#define SX127X_CMN_AGCTHR1_STEP1_SHIFT 0 /* Bits 0-4: 1st AGC threshold */ +#define SX127X_CMN_AGCTHR1_STEP1_MASK (31 << SX127X_CMN_AGCTHR1_STEP1_SHIFT) +#define SX127X_CMN_AGCTHR1_STEP1(v) ((v << SX127X_CMN_AGCTHR1_STEP1_SHIFT) & SX127X_CMN_AGCTHR1_STEP1_MASK) + +/* FSK/OOK/LORA: AGC thresholds 2 */ + +#define SX127X_CMN_AGCTHR2_STEP3_SHIFT 0 /* Bits 0-3: 3rd AGC threshold */ +#define SX127X_CMN_AGCTHR2_STEP3_MASK (15 << SX127X_CMN_AGCTHR1_STEP3_SHIFT) +#define SX127X_CMN_AGCTHR2_STEP3(v) ((v << SX127X_CMN_AGCTHR2_STEP3_SHIFT) & SX127X_CMN_AGCTHR2_STEP3_MASK) +#define SX127X_CMN_AGCTHR2_STEP2_SHIFT 4 /* Bits 4-7: 2nd AGC threshold */ +#define SX127X_CMN_AGCTHR2_STEP2_MASK (15 << SX127X_CMN_AGCTHR1_STEP2_SHIFT) +#define SX127X_CMN_AGCTHR2_STEP2(v) ((v << SX127X_CMN_AGCTHR2_STEP2_SHIFT) & SX127X_CMN_AGCTHR2_STEP2_MASK) + +/* FSK/OOK/LORA: AGC thresholds 3 */ + +#define SX127X_CMN_AGCTHR3_STEP5_SHIFT 0 /* Bits 0-3: 5th AGC threshold */ +#define SX127X_CMN_AGCTHR3_STEP5_MASK (15 << SX127X_CMN_AGCTHR3_STEP5_SHIFT) +#define SX127X_CMN_AGCTHR3_STEP5(v) ((v << SX127X_CMN_AGCTHR3_STEP5_SHIFT) & SX127X_CMN_AGCTHR3_STEP5_MASK) +#define SX127X_CMN_AGCTHR3_STEP4_SHIFT 4 /* Bits 4-7: 4th AGC threshold */ +#define SX127X_CMN_AGCTHR3_STEP4_MASK (15 << SX127X_CMN_AGCTHR3_STEP4_SHIFT) +#define SX127X_CMN_AGCTHR3_STEP4(v) ((v << SX127X_CMN_AGCTHR3_STEP4_SHIFT) & SX127X_CMN_AGCTHR3_STEP4_MASK) + +/* FSK/OOK/LORA: Control of the PLL bandwidth */ + +#define SX127X_CMN_PLL_BW_SHIFT (6) /* Bits 6-7: PLL bandwidth */ +#define SX127X_CMN_PLL_BW_MASK (3 << SX127X_CMN_PLL_BW_SHIFT) +# define SX127X_CMN_PLL_BW_75kHz (0 << SX127X_CMN_PLL_BW_SHIFT) +# define SX127X_CMN_PLL_BW_150kHz (1 << SX127X_CMN_PLL_BW_SHIFT) +# define SX127X_CMN_PLL_BW_225kHz (2 << SX127X_CMN_PLL_BW_SHIFT) +# define SX127X_CMN_PLL_BW_300kHz (3 << SX127X_CMN_PLL_BW_SHIFT) + +/* FSK/OOK ******************************************************************/ + +/* FSK/OOK: Bit Rate setting */ + +#define SX127X_FOM_BITRATE_DEFAULT (0x1a0b) +#define SX127X_FOM_BITRATE_MAX (0xffff) +#define SX127X_FOM_BITRATE_MSB(v) ((v >> 8) & 0xff) +#define SX127X_FOM_BITRATE_LSB(v) ((v >> 0) & 0xff) + +/* FSK/OOK: Frequency Deviation setting */ + +#define SX127X_FOM_FDEV_MSB_MASK (0x3f) +#define SX127X_FOM_FDEV_MAX (0x3fff) +#define SX127X_FDEV_FROM_FREQ(freq) (freq/SX127X_FSTEP) +#define SX127X_FOM_FDEV_MSB(v) ((v >> 8) & 0xff) +#define SX127X_FOM_FDEV_LSB(v) ((v >> 0) & 0xff) + +/* FSK/OOK: AFC, AGC, ctrl */ + +#define SX127X_FOM_RXCFG_TRG_SHIFT (0) /* Bits 0-2: RX trigger */ +#define SX127X_FOM_RXCFG_TRG_MASK (7 << SX127X_FOM_RXCFG_TRG_SHIFT) +# define SX127X_FOM_RXCFG_TRG_0 (0 << SX127X_FOM_RXCFG_TRG_SHIFT) +# define SX127X_FOM_RXCFG_TRG_1 (1 << SX127X_FOM_RXCFG_TRG_SHIFT) +# define SX127X_FOM_RXCFG_TRG_2 (2 << SX127X_FOM_RXCFG_TRG_SHIFT) +# define SX127X_FOM_RXCFG_TRG_3 (3 << SX127X_FOM_RXCFG_TRG_SHIFT) +# define SX127X_FOM_RXCFG_TRG_4 (4 << SX127X_FOM_RXCFG_TRG_SHIFT) +# define SX127X_FOM_RXCFG_TRG_5 (5 << SX127X_FOM_RXCFG_TRG_SHIFT) +# define SX127X_FOM_RXCFG_TRG_6 (6 << SX127X_FOM_RXCFG_TRG_SHIFT) +# define SX127X_FOM_RXCFG_TRG_7 (7 << SX127X_FOM_RXCFG_TRG_SHIFT) +#define SX127X_FOM_RXCFG_AGCAUTOON (1 << 3) /* Bit 3: AGC auto ON */ +#define SX127X_FOM_RXCFG_AFCAUTOON (1 << 4) /* Bit 4: AFC auto ON */ +#define SX127X_FOM_RXCFG_RESRXWITHPLL (1 << 5) /* Bit 5: Restar RX with PLL lock */ +#define SX127X_FOM_RXCFG_RESRXWITHOUTPLL (1 << 6) /* Bit 6: Restart RX without PLL lock */ +#define SX127X_FOM_RXCFG_RESRXONCOLLSION (1 << 7) /* Bit 7: Restart RX on collision */ + +/* FSK/OOK: RSSI */ + +#define SX127X_FOM_RSSICFG_SMOOTH_SHIFT (0) /* Bits 0-2: RSSI smoothing */ +#define SX127X_FOM_RSSICFG_SMOOTH_MASK (7 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +# define SX127X_FOM_RSSICFG_SMOOTH_2 (0 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +# define SX127X_FOM_RSSICFG_SMOOTH_4 (1 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +# define SX127X_FOM_RSSICFG_SMOOTH_8 (2 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +# define SX127X_FOM_RSSICFG_SMOOTH_16 (3 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +# define SX127X_FOM_RSSICFG_SMOOTH_32 (4 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +# define SX127X_FOM_RSSICFG_SMOOTH_64 (5 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +# define SX127X_FOM_RSSICFG_SMOOTH_128 (6 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +# define SX127X_FOM_RSSICFG_SMOOTH_256 (7 << SX127X_FOM_RSSICFG_SMOOTH_SHIFT) +#define SX127X_FOM_RSSICFG_OFFSET_SHIFT (3) /* Bits 3-7: RSSI offset */ +#define SX127X_FOM_RSSICFG_OFFSET_MASK (15 << SX127X_FOM_RSSICFG_OFFSET_SHIFT) + +/* FSK/OOK: RSSI value in dBm */ + +#define SX127X_FOM_RSSIVAL_GET(rssi) (-rssi/2) + +/* FSK/OOK: Channel Filter BW Control */ + +#define SX127X_FOM_RXBW_EXP_SHIFT (0) /* Bits 0-2: RXBW exp */ +#define SX127X_FOM_RXBW_EXP_MASK (7 << SX127X_FOM_RXBW_EXP_SHIFT) +#define SX127X_FOM_RXBW_MANT_SHIFT (3) /* Bits 3-4: RXBW mant */ +#define SX127X_FOM_RXBW_MANT_MASK (3 << SX127X_FOM_RXBW_MANT_SHIFT) +# define SX127X_FOM_RXBW_MANT_16 (0 << SX127X_FOM_RXBW_MANT_SHIFT) +# define SX127X_FOM_RXBW_MANT_20 (1 << SX127X_FOM_RXBW_MANT_SHIFT) +# define SX127X_FOM_RXBW_MANT_24 (2 << SX127X_FOM_RXBW_MANT_SHIFT) + +#define FSKOOK_BANDWIDTH_GET(mant, exp) (((exp << SX127X_FOM_RXBW_EXP_SHIFT) & SX127X_FOM_RXBW_EXP_MASK) | \ + ((mant << SX127X_FOM_RXBW_MANT_SHIFT) & SX127X_FOM_RXBW_MANT_MASK)) + + +/* FSK/OOK: AFC Channel Filter BW */ + +#define SX127X_FOM_AFCBW_EXP_SHIFT (0) /* Bits 0-2: AFC exp */ +#define SX127X_FOM_AFCBW_EXP_MASK (7 << SX127X_FOM_AFCBW_EXP_SHIFT) +#define SX127X_FOM_AFCBW_MANT_SHIFT (3) /* Bits 3-4: AFC mant */ +#define SX127X_FOM_AFCBW_MANT_MASK (7 << SX127X_FOM_AFCBW_MANT_SHIFT) + +/* FSK/OOK: OOK demodulator */ + +#define SX127X_FOM_OOKPEAK_THRSTEP_SHIFT (0) /* Bits 0-2: OOK peak threshold step */ +#define SX127X_FOM_OOKPEAK_THRSTEP_MASK (7 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRSTEP_0p5 (0 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRSTEP_1p0 (1 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRSTEP_1p5 (2 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRSTEP_2p0 (3 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRSTEP_3p0 (4 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRSTEP_4p0 (5 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRSTEP_5p0 (6 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRSTEP_6p0 (7 << SX127X_FOM_OOKPEAK_THRSTEP_SHIFT) +#define SX127X_FOM_OOKPEAK_THRTYPE_SHIFT (3) /* Bits 3-4: OOK threshold type */ +#define SX127X_FOM_OOKPEAK_THRTYPE_MASK (7 << SX127X_FOM_OOKPEAK_STEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRTYPE_FIX (0 << SX127X_FOM_OOKPEAK_STEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRTYPE_PEAK (1 << SX127X_FOM_OOKPEAK_STEP_SHIFT) +# define SX127X_FOM_OOKPEAK_THRTYPE_AVG (2 << SX127X_FOM_OOKPEAK_STEP_SHIFT) +#define SX127X_FOM_OOKPEAK_BITSYNCON (1 << 5) /* Bit 5: Enable bit synchronizer */ + +/* FSK/OOK: Average of the OOK demod */ + +#define SX127X_FOM_OOKAVG_THRFILT_SHIFT (0) /* Bits 0-1: OOK average threshold filter */ +#define SX127X_FOM_OOKAVG_THRFILT_MASK (3 << SX127X_FOM_OOKAVG_THRFILT_SHIFT) +# define SX127X_FOM_OOKAVG_THRFILT_32 (0 << SX127X_FOM_OOKAVG_THRFILT_SHIFT) +# define SX127X_FOM_OOKAVG_THRFILT_8 (1 << SX127X_FOM_OOKAVG_THRFILT_SHIFT) +# define SX127X_FOM_OOKAVG_THRFILT_4 (2 << SX127X_FOM_OOKAVG_THRFILT_SHIFT) +# define SX127X_FOM_OOKAVG_THRFILT_2 (3 << SX127X_FOM_OOKAVG_THRFILT_SHIFT) +#define SX127X_FOM_OOKAVG_OFFSET_SHIFT (2) /* Bits 2-3: OOK average offset */ +#define SX127X_FOM_OOKAVG_OFFSET_MASK (3 << SX127X_FOM_OOKAVG_OFFSET_SHIFT) +# define SX127X_FOM_OOKAVG_OFFSET_0 (0 << SX127X_FOM_OOKAVG_OFFSET_SHIFT) +# define SX127X_FOM_OOKAVG_OFFSET_2 (1 << SX127X_FOM_OOKAVG_OFFSET_SHIFT) +# define SX127X_FOM_OOKAVG_OFFSET_4 (2 << SX127X_FOM_OOKAVG_OFFSET_SHIFT) +# define SX127X_FOM_OOKAVG_OFFSET_6 (3 << SX127X_FOM_OOKAVG_OFFSET_SHIFT) +#define SX127X_FOM_OOKAVG_THRDEC_SHIFT (5) /* Bits 5-7: OOK peak threshold decrement */ +#define SX127X_FOM_OOKAVG_THRDEC_MASK (7 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) +# define SX127X_FOM_OOKAVG_THRDEC_1 (0 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) +# define SX127X_FOM_OOKAVG_THRDEC_2 (1 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) +# define SX127X_FOM_OOKAVG_THRDEC_3 (2 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) +# define SX127X_FOM_OOKAVG_THRDEC_4 (3 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) +# define SX127X_FOM_OOKAVG_THRDEC_5 (4 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) +# define SX127X_FOM_OOKAVG_THRDEC_6 (5 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) +# define SX127X_FOM_OOKAVG_THRDEC_7 (6 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) +# define SX127X_FOM_OOKAVG_THRDEC_8 (7 << SX127X_FOM_OOKAVG_THRDEC_SHIFT) + +/* FSK/OOK: AFC and FEI control */ + +#define SX127X_FOM_AFCFEI_AUTOCLEARON (1 << 0) /* Bit 0: AFC auto clear ON */ +#define SX127X_FOM_AFCFEI_AFCCLEAR (1 << 1) /* Bit 1: AFC clear */ +#define SX127X_FOM_AFCFEI_AGCSTART (1 << 2) /* Bit 2: AGC start */ + +/* FSK/OOK: Frequency correction value of the AFC */ + +#define SX127X_FOM_AFC_MAX (0xffff) +#define SX127X_FOM_AFC_MSB(afc) ((afc >> 8) & 0xff) +#define SX127X_FOM_AFC_LSB(afc) ((afc >> 0) & 0xff) + +/* FSK/OOK: Value of the calculated frequency error */ + +#define SX127X_FOM_FEI_MAX (0xffff) +#define SX127X_FOM_FEI_MSB(fei) ((fei >> 8) & 0xff) +#define SX127X_FOM_FEI_LSB(fei) ((fei >> 0) & 0xff) + +/* FSK/OOK: Settings of the Preamble Detector */ + +#define SX127X_FOM_PREDET_TOL_SHIFT (0) /* Bits 0-4: Preamble detector tolerance */ +#define SX127X_FOM_PREDET_TOL_MASK (31 << SX127X_FOM_PREDET_TOL_SHIFT) +#define SX127X_FOM_PREDET_TOL(tol) ((tol << SX127X_FOM_PREDET_TOL_SHIFT) & SX127X_FOM_PREDET_TOL_MASK) +#define SX127X_FOM_PREDET_SIZE_SHIFT (5) /* Bits 5-6: Preamble detector size */ +#define SX127X_FOM_PREDET_SIZE_MASK (3 << SX127X_FOM_PREDET_SIZE_SHIFT) +# define SX127X_FOM_PREDET_SIZE_1B (0 << SX127X_FOM_PREDET_SIZE_SHIFT) +# define SX127X_FOM_PREDET_SIZE_2B (1 << SX127X_FOM_PREDET_SIZE_SHIFT) +# define SX127X_FOM_PREDET_SIZE_3B (2 << SX127X_FOM_PREDET_SIZE_SHIFT) +#define SX127X_FOM_PREDET_ON (1 << 7) /* Bit 7: Preamble detector ON */ + +/* FSK/OOK: RC oscillators settings, CLKOUT frequency */ + +#define SX127X_FOM_OSC_CLKOUT_SHIFT (0) /* Bits 0-2: CLKOUT frequency */ +#define SX127X_FOM_OSC_CLKOUT_MASK (7 << SX127X_FOM_OSC_CLKOUT_SHIFT) +# define SX127X_FOM_OSC_CLKOUT_FXOSC (0 << SX127X_FOM_OSC_CLKOUT_SHIFT) +# define SX127X_FOM_OSC_CLKOUT_FXOSCd2 (1 << SX127X_FOM_OSC_CLKOUT_SHIFT) +# define SX127X_FOM_OSC_CLKOUT_FXOSCd4 (2 << SX127X_FOM_OSC_CLKOUT_SHIFT) +# define SX127X_FOM_OSC_CLKOUT_FXOSCd8 (3 << SX127X_FOM_OSC_CLKOUT_SHIFT) +# define SX127X_FOM_OSC_CLKOUT_FXOSCd16 (4 << SX127X_FOM_OSC_CLKOUT_SHIFT) +# define SX127X_FOM_OSC_CLKOUT_FXOSCd32 (5 << SX127X_FOM_OSC_CLKOUT_SHIFT) +# define SX127X_FOM_OSC_CLKOUT_RC (6 << SX127X_FOM_OSC_CLKOUT_SHIFT) +# define SX127X_FOM_OSC_CLKOUT_OFF (7 << SX127X_FOM_OSC_CLKOUT_SHIFT) +#define SX127X_FOM_OSC_CLKOUT_RCCALSTART (3 << 1) /* Bit 3: Trigger the RC oscilator calibration */ + +/* FSK/OOK: Preamble length MSB */ + +#define SX127X_FOM_PRE_MAX (0xffff) +#define SX127X_FOM_PRE_MSB(pre) ((pre >> 8) & 0xff) +#define SX127X_FOM_PRE_LSB(pre) ((pre >> 0) & 0xff) + +/* FSK/OOK: Sync Word Recognition control */ + +#define SX127X_FOM_SYNCCFG_SYNCSIZE_SHIFT (0) /* Bits 0-2: Sync word size */ +#define SX127X_FOM_SYNCCFG_SYNCSIZE_MASK (7 << SX127X_FOM_SYNCCFG_SYNCSIZE_SHIFT) +#define SX127X_FOM_SYNCCFG_SYNCSIZE(s) (((s) << SX127X_FOM_SYNCCFG_SYNCSIZE_SHIFT) & SX127X_FOM_SYNCCFG_SYNCSIZE_MASK) +#define SX127X_FOM_SYNCCFG_SYNCON (1 << 4) /* Bit 4: Sync word generation and detection enable */ +#define SX127X_FOM_SYNCCFG_PREPOL (1 << 5) /* Bit 5: Preamble Polarity */ +#define SX127X_FOM_SYNCSIZE_MAX (8) + +/* FSK/OOK: Sync Word bytes */ + +#define SX127X_FOM_SYNCVAL_1(val) ((val >> 56) & 0xff) +#define SX127X_FOM_SYNCVAL_2(val) ((val >> 48) & 0xff) +#define SX127X_FOM_SYNCVAL_3(val) ((val >> 40) & 0xff) +#define SX127X_FOM_SYNCVAL_4(val) ((val >> 32) & 0xff) +#define SX127X_FOM_SYNCVAL_5(val) ((val >> 24) & 0xff) +#define SX127X_FOM_SYNCVAL_6(val) ((val >> 16) & 0xff) +#define SX127X_FOM_SYNCVAL_7(val) ((val >> 8) & 0xff) +#define SX127X_FOM_SYNCVAL_8(val) ((val >> 0) & 0xff) + +/* FSK/OOK: Packet mode settings 1 */ + +#define SX127X_FOM_PKTCFG1_CRCTYPE (1 << 0) /* Bit 0: CRC type: 0 -> CCITT CRC, 1 -> IBM CRC with alternate whitening */ +#define SX127X_FOM_PKTCFG1_ADDRFLT_SHIFT (1) /* Bits 1-2: Address basef filtering in RX */ +#define SX127X_FOM_PKTCFG1_ADDRFLT_MASK (3 << SX127X_FOM_PKTCFG1_ADDRFLT_SHIFT) +# define SX127X_FOM_PKTCFG1_ADDRFLT_OFF (0 << SX127X_FOM_PKTCFG1_ADDRFLT_SHIFT) +# define SX127X_FOM_PKTCFG1_ADDRFLT_NA (1 << SX127X_FOM_PKTCFG1_ADDRFLT_SHIFT) +# define SX127X_FOM_PKTCFG1_ADDRFLT_NABA (2 << SX127X_FOM_PKTCFG1_ADDRFLT_SHIFT) +#define SX127X_FOM_PKTCFG1_CRCAUTOCLROFF (1 << 3) /* Bit 3: CRC auto clear OFF */ +#define SX127X_FOM_PKTCFG1_CRCON (1 << 4) /* Bit 4: TX/RX CRC enable */ +#define SX127X_FOM_PKTCFG1_DCFREE_SHIFT (5) /* Bits 5-6: DC-free encodeing/decoding */ +#define SX127X_FOM_PKTCFG1_DCFREE_MASK (3 << SX127X_FOM_PKTCFG1_DCFREE_SHIFT) +# define SX127X_FOM_PKTCFG1_DCFREE_OFF (0 << SX127X_FOM_PKTCFG1_DCFREE_SHIFT) /* 00: None */ +# define SX127X_FOM_PKTCFG1_DCFREE_M (1 << SX127X_FOM_PKTCFG1_DCFREE_SHIFT) /* 01: Manchaster */ +# define SX127X_FOM_PKTCFG1_DCFREE_W (2 << SX127X_FOM_PKTCFG1_DCFREE_SHIFT) /* 10: Whitening */ +#define SX127X_FOM_PKTCFG1_PCKFORMAT (1 << 7) /* Bit 7: 0 -> fixed length, 1 -> variable length*/ + +/* FSK/OOK: Packet mode settings 2 */ + +#define SX127X_FOM_PKTCFG2_PLENMSB_SHIFT (0) /* Bits 0-2: Packet length MSB */ +#define SX127X_FOM_PKTCFG2_PLENMSB_MASK (7 << SX127X_FOM_PKTCFG2_PLEN_SHIFT) +#define SX127X_FOM_PKTCFG2_PLENMSB(plen) ((plen >> 8) & SX127X_FOM_PKTCFG2_PLENMSB_MASK) +#define SX127X_FOM_PKTCFG2_BEACONON (1 << 3) /* Bit 3: Beacon mode in fixed packed format */ +#define SX127X_FOM_PKTCFG2_IHPF (1 << 4) /* Bit 4: reserved */ +#define SX127X_FOM_PKTCFG2_IOHOMEON (1 << 5) /* Bit 5: IO-HOMECONTROL compatibilty mode */ +#define SX127X_FOM_PKTCFG2_DATAMODE (1 << 6) /* Bit 6: 0 -> contrinous mode, 1 -> packet mode */ + +/* FSK/OOK: Payload length setting */ + +#define SX127X_FOM_PAYLOADLEN_MAX (0x7f) +#define SX127X_FOM_PAYLOADLEN_LSB(plen) ((plen >> 0) & 0xff) + +/* FSK/OOK: FIFO threshold, TX start condition */ + +#define SX127X_FOM_FIFOTHR_THR_SHIFT (0) /* Bits 0-5: FIFO threshold */ +#define SX127X_FOM_FIFOTHR_THR_MASK (63 << SX127X_FOM_FIFOTHR_THR_SHIFT) +#define SX127X_FOM_FIFOTHR_TXSTARTCOND (1 << 7) /* Bit 7: Packet transmission start condition */ + +/* FSK/OOK: Top level Sequencer settings 1 */ + +#define SX127X_FOM_SEQCFG1_TX (1 << 0) /* Bit 0: From Transmit 0 -> LowPowerSelection, 1 -> Receive */ +#define SX127X_FOM_SEQCFG1_IDLE (1 << 1) /* Bit 1: From IDLE on T1: 0 -> TX, 1 -> RX*/ +#define SX127X_FOM_SEQCFG1_LOWPOWERSEL (1 << 2) /* Bit 2: Low Power Selection */ +#define SX127X_FOM_SEQCFG1_START_SHIFT (3) /* Bits 3-4: From Start */ +#define SX127X_FOM_SEQCFG1_START_MASK (3 << SX127X_FOM_SEQCFG1_START_SHIFT) +# define SX127X_FOM_SEQCFG1_START_LPS (0 << SX127X_FOM_SEQCFG1_START_SHIFT) /* LowPowerSelection */ +# define SX127X_FOM_SEQCFG1_START_RS (1 << SX127X_FOM_SEQCFG1_START_SHIFT) /* RX */ +# define SX127X_FOM_SEQCFG1_START_TS (2 << SX127X_FOM_SEQCFG1_START_SHIFT) /* TX */ +# define SX127X_FOM_SEQCFG1_START_TSFL (3 << SX127X_FOM_SEQCFG1_START_SHIFT) /* TX on FifoLevel */ +#define SX127X_FOM_SEQCFG1_IDLEMODE (1 << 5) /* Bit 5: IDLE Mode 0 -> standby, 1 -> sleep */ +#define SX127X_FOM_SEQCFG1_SEQSTOP (1 << 6) /* Bit 6: Sequencer Stop */ +#define SX127X_FOM_SEQCFG1_SEQSTART (1 << 7) /* Bit 7: Sequencer Start */ + +/* FSK/OOK: Top level Sequencer settings 2 */ + +#define SX127X_FOM_SEQCFG2_PCKRX_SHIFT (0) /* Bits 0-2: From Packet Received */ +#define SX127X_FOM_SEQCFG2_PCKRX_MASK (7 << SX127X_FOM_SEQCFG2_PCKRX_SHIFT) +# define SX127X_FOM_SEQCFG2_PCKRX_1 (0 << SX127X_FOM_SEQCFG2_PCKRX_SHIFT) +# define SX127X_FOM_SEQCFG2_PCKRX_2 (1 << SX127X_FOM_SEQCFG2_PCKRX_SHIFT) +# define SX127X_FOM_SEQCFG2_PCKRX_3 (2 << SX127X_FOM_SEQCFG2_PCKRX_SHIFT) +# define SX127X_FOM_SEQCFG2_PCKRX_4 (3 << SX127X_FOM_SEQCFG2_PCKRX_SHIFT) +# define SX127X_FOM_SEQCFG2_PCKRX_5 (4 << SX127X_FOM_SEQCFG2_PCKRX_SHIFT) +#define SX127X_FOM_SEQCFG2_RXTOUT_SHIFT (3) /* Bits 3-4: From RX Timeout */ +#define SX127X_FOM_SEQCFG2_RXTOUT_MASK (7 << SX127X_FOM_SEQCFG2_RXTOUT_SHIFT) +# define SX127X_FOM_SEQCFG2_RXTOUT_1 (0 << SX127X_FOM_SEQCFG2_RXTOUT_SHIFT) +# define SX127X_FOM_SEQCFG2_RXTOUT_2 (1 << SX127X_FOM_SEQCFG2_RXTOUT_SHIFT) +# define SX127X_FOM_SEQCFG2_RXTOUT_3 (2 << SX127X_FOM_SEQCFG2_RXTOUT_SHIFT) +# define SX127X_FOM_SEQCFG2_RXTOUT_4 (3 << SX127X_FOM_SEQCFG2_RXTOUT_SHIFT) +#define SX127X_FOM_SEQCFG2_RX_SHIFT (5) /* Bits 5: From Receiver */ +#define SX127X_FOM_SEQCFG2_RX_MASK (7 << SX127X_FOM_SEQCFG2_RX_SHIFT) +# define SX127X_FOM_SEQCFG2_RX_1 (0 << SX127X_FOM_SEQCFG2_RX_SHIFT) +# define SX127X_FOM_SEQCFG2_RX_2 (1 << SX127X_FOM_SEQCFG2_RX_SHIFT) +# define SX127X_FOM_SEQCFG2_RX_3 (2 << SX127X_FOM_SEQCFG2_RX_SHIFT) +# define SX127X_FOM_SEQCFG2_RX_4 (3 << SX127X_FOM_SEQCFG2_RX_SHIFT) +# define SX127X_FOM_SEQCFG2_RX_5 (4 << SX127X_FOM_SEQCFG2_RX_SHIFT) +# define SX127X_FOM_SEQCFG2_RX_6 (5 << SX127X_FOM_SEQCFG2_RX_SHIFT) +# define SX127X_FOM_SEQCFG2_RX_7 (6 << SX127X_FOM_SEQCFG2_RX_SHIFT) + +/* FSK/OOK: Timer 1 and 2 resolution control */ + +#define SX127X_FOM_TIMRES_TIM1RES_SHIFT (0) /* Bits 0-1: Timer 1 Resolution */ +#define SX127X_FOM_TIMRES_TIM1RES_MASK (3 << SX127X_FOM_TIMRES_TIM1RES_SHIFT) +# define SX127X_FOM_TIMRES_TIM1RES_OFF (0 << SX127X_FOM_TIMRES_TIM1RES_SHIFT) +# define SX127X_FOM_TIMRES_TIM1RES_64us (1 << SX127X_FOM_TIMRES_TIM1RES_SHIFT) +# define SX127X_FOM_TIMRES_TIM1RES_4p1ms (2 << SX127X_FOM_TIMRES_TIM1RES_SHIFT) +# define SX127X_FOM_TIMRES_TIM1RES_262ms (3 << SX127X_FOM_TIMRES_TIM1RES_SHIFT) +#define SX127X_FOM_TIMRES_TIM2RES_SHIFT (2) /* Bits 2-3: Timer 2 Resolution */ +#define SX127X_FOM_TIMRES_TIM2RES_MASK (3 << SX127X_FOM_TIMRES_TIM2RES_SHIFT) +# define SX127X_FOM_TIMRES_TIM2RES_OFF (0 << SX127X_FOM_TIMRES_TIM2RES_SHIFT) +# define SX127X_FOM_TIMRES_TIM2RES_64us (1 << SX127X_FOM_TIMRES_TIM2RES_SHIFT) +# define SX127X_FOM_TIMRES_TIM2RES_4p1ms (2 << SX127X_FOM_TIMRES_TIM2RES_SHIFT) +# define SX127X_FOM_TIMRES_TIM2RES_262ms (3 << SX127X_FOM_TIMRES_TIM2RES_SHIFT) + +/* FSK/OOK: Image callibration engine control */ + +#define SX127X_FOM_IMAGECAL_TEMPMONOFF (1 << 0) /* Bit 0: Temperature monitor OFF */ +#define SX127X_FOM_IMAGECAL_TEMPTHR_SHIFT (1) /* Bit 1-2: Temperature threshold */ +#define SX127X_FOM_IMAGECAL_TEMPTHR_MASK (3 << SX127X_FOM_IMAGECAL_TEMPTHR_SHIFT) +# define SX127X_FOM_IMAGECAL_TEMPTHR_5C (0 << SX127X_FOM_IMAGECAL_TEMPTHR_SHIFT) +# define SX127X_FOM_IMAGECAL_TEMPTHR_10C (1 << SX127X_FOM_IMAGECAL_TEMPTHR_SHIFT) +# define SX127X_FOM_IMAGECAL_TEMPTHR_15C (2 << SX127X_FOM_IMAGECAL_TEMPTHR_SHIFT) +# define SX127X_FOM_IMAGECAL_TEMPTHR_20C (3 << SX127X_FOM_IMAGECAL_TEMPTHR_SHIFT) +#define SX127X_FOM_IMAGECAL_TEMPCHANGE (1 << 3) /* Bit 3: Temperature change */ +#define SX127X_FOM_IMAGECAL_IMGCALRUN (1 << 5) /* Bit 5: Image Callibration are running */ +#define SX127X_FOM_IMAGECAL_IMGCALSTART (1 << 6) /* Bit 6: Image Calibration start */ +#define SX127X_FOM_IMAGECAL_AUTOIMGCALON (1 << 7) /* Bit 7: Auto Image Calibration ON */ + +/* FSK/OOK: Low Battery Indicator settings */ + +#define SX127X_FOM_LOWBAT_TRIM_SHIFT (0) /* Bits 0-2: Low battery threshold */ +#define SX127X_FOM_LOWBAT_TRIM_MASK (7 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +# define SX127X_FOM_LOWBAT_TRIM_1p695V (0 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +# define SX127X_FOM_LOWBAT_TRIM_1p764V (1 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +# define SX127X_FOM_LOWBAT_TRIM_1p835V (2 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +# define SX127X_FOM_LOWBAT_TRIM_1p905V (3 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +# define SX127X_FOM_LOWBAT_TRIM_1p976V (4 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +# define SX127X_FOM_LOWBAT_TRIM_2p045V (5 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +# define SX127X_FOM_LOWBAT_TRIM_2p116V (6 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +# define SX127X_FOM_LOWBAT_TRIM_2p185V (7 << SX127X_FOM_LOWBAT_TRIM_SHIFT) +#define SX127X_FOM_LOWBAT_LOWBATON (1 << 3) /* Bit 3: Low Battery detecotr enable */ + +/* FSK/OOK: Status register 1: PLL Lock state, Timeout, RSSI */ + +#define SX127X_FOM_IRQ1_SYNCADDRMATCH (1 << 0) /* Bit 0: Sync and Address detected */ +#define SX127X_FOM_IRQ1_PREAMBE (1 << 1) /* Bit 1: Preamble detected */ +#define SX127X_FOM_IRQ1_TIMEOUT (1 << 2) /* Bit 2: Timeout */ +#define SX127X_FOM_IRQ1_RSSI (1 << 3) /* Bit 3: RssiValue exceeds RssiThreshold in RX */ +#define SX127X_FOM_IRQ1_PLLLOCK (1 << 4) /* Bit 4: PLL is locked (in FS, RX or TX) */ +#define SX127X_FOM_IRQ1_TXRDY (1 << 5) /* Bit 5: set in TX mode after PA ramp-up */ +#define SX127X_FOM_IRQ1_RXRDY (1 << 6) /* Bit 6: set in RX mode after RSSI, AGC and AFC */ +#define SX127X_FOM_IRQ1_MODERDY (1 << 7) /* Bit 7: operation mode request is ready */ + +/* FSK/OOK: Status register 2: FIFO handling flags, Low Battery */ + +#define SX127X_FOM_IRQ2_LOWBAT (1 << 0) /* Bit 0: Low Battery */ +#define SX127X_FOM_IRQ2_CRCOK (1 << 1) /* Bit 1: CRC of the payload is OK in RX */ +#define SX127X_FOM_IRQ2_PAYLOADRDY (1 << 2) /* Bit 2: payload is ready in RX */ +#define SX127X_FOM_IRQ2_PCKSENT (1 << 3) /* Bit 3: packet has been sent in TX */ +#define SX127X_FOM_IRQ2_FIFOOVR (1 << 4) /* Bit 4: FIFO overrun */ +#define SX127X_FOM_IRQ2_FIFOLVL (1 << 5) /* Bit 5: FIFO level */ +#define SX127X_FOM_IRQ2_FIFOEMPTY (1 << 6) /* Bit 6: FIFO empty */ +#define SX127X_FOM_IRQ2_FIFOFULL (1 << 7) /* Bit 7: FIFO full */ + +/* FSK/OOK: Control the fast frequency hopping mode */ + +#define SX127X_FOM_PLLHOP_FASTHOPON (1 << 7) /* Bit 7: Fast frequency hop enable */ + +/* FSK/OOK: Fractional part in the Bit Rate division ratio */ + +#define SX127X_FOM_BITRATEFRAC_SHIFT (0) +#define SX127X_FOM_BITRATEFRAC_MASK (0x0f) + +/* LORA *********************************************************************/ + +/* LORA: FIFO SPI pointer */ + +#define SX127X_LRM_ADDRPTR_DEFAULT (0x00) + +/* LORA: Start TX data */ + +#define SX127X_LRM_TXBASE_DEFAULT (0x80) + +/* LORA: Start RX data */ + +#define SX127X_LRM_RXBASE_DEFAULT (0x00) + +/* LORA: IRQ flag / IRQ Flags mask */ + +#define SX127X_LRM_IRQ_CADDETECT (1 << 0) /* Bit 0: CAD detection */ +#define SX127X_LRM_IRQ_FHSSCHANGECHAN (1 << 1) /* Bit 1: FHHS change channel */ +#define SX127X_LRM_IRQ_CADDONE (1 << 2) /* Bit 2: CAD done */ +#define SX127X_LRM_IRQ_TXDONE (1 << 3) /* Bit 3: TX done */ +#define SX127X_LRM_IRQ_VALIDHDR (1 << 4) /* Bit 4: Valid header received in RX */ +#define SX127X_LRM_IRQ_PAYLOADCRCERR (1 << 5) /* Bit 5: Payload CRC error */ +#define SX127X_LRM_IRQ_RXDONE (1 << 6) /* Bit 6: RX done */ +#define SX127X_LRM_IRQ_RXTIMEOUT (1 << 7) /* Bit 7: RX timeout */ + +/* LORA: Live LORA modem status */ + +#define SX127X_LRM_MODSTAT_SIGDETECT (1 << 0) /* Bit 0: Signal detected */ +#define SX127X_LRM_MODSTAT_SIGSYNC (1 << 1) /* Bit 1: Signal synchronized */ +#define SX127X_LRM_MODSTAT_RXONGOING (1 << 2) /* Bit 2: RX on-going */ +#define SX127X_LRM_MODSTAT_HDRVALID (1 << 3) /* Bit 3: Header info valid */ +#define SX127X_LRM_MODSTAT_MODEMCLR (1 << 4) /* Bit 4: Modem clear */ +#define SX127X_LRM_MODSTAT_RXRATE_SHIFT (5) /* Bits 5-7: Coding rate of last header received */ +#define SX127X_LRM_MODSTAT_RXRATE_MASK (7 << SX127X_LRM_MODSTAT_RXRATE_SHIFT) + +/* LORA: Current RSSI */ + +#define SX127X_LRM_RSSIVAL_HF_OFFSET (-127) +#define SX127X_LRM_RSSIVAL_LF_OFFSET (-164) + +/* LORA: FHSS start channel */ + +#define SX127X_LRM_HOPCHAN_FHHSP_SHIFT (0) /* Bits 0-5: current value of frequency hopping in use */ +#define SX127X_LRM_HOPCHAN_FHHSPN_MASK (63 << SX127X_LRM_HOPCHAN_FHHSPRESCHAN_SHIFT) +#define SX127X_LRM_HOPCHAN_CRCONPAYLOAD (1 << 6) /* Bit 6: CRC on payload */ +#define SX127X_LRM_HOPCHAN_PLLTIMEOUT (1 << 7) /* Bit 7: PLL timeout */ + +/* LORA: Modem PHY config 1 */ + +#define SX127X_LRM_MDMCFG1_IMPLHDRON (1 << 0) /* Bit 0: Implict header mode ON */ +#define SX127X_LRM_MDMCFG1_CDRATE_SHIFT (1) /* Bits 1-3: Error coding rate */ +#define SX127X_LRM_MDMCFG1_CDRATE_MASK (7 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_CDRATE_4d5 (1 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_CDRATE_4d6 (2 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_CDRATE_4d7 (3 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_CDRATE_4d8 (4 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +#define SX127X_LRM_MDMCFG1_BW_SHIFT (4) /* Bits 4-7: Signal bandwidth */ +#define SX127X_LRM_MDMCFG1_BW_MASK (31 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_7p8kHz (0 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_10p4kHz (1 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_15p6kHz (2 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_20p8kHz (3 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_31p25kHz (4 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_41p7kHz (5 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_62p5kHz (6 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_125kHz (7 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_250kHz (8 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) +# define SX127X_LRM_MDMCFG1_BW_500kHz (9 << SX127X_LRM_MDMCFG1_CDRATE_SHIFT) + +/* LORA: Modem PHY config 2 */ + +#define SX127X_LRM_MDMCFG2_RXTIMOUT_SHIFT (0) /* Bits 0-1: RX timeout MSB */ +#define SX127X_LRM_MDMCFG2_RXTIMOUT_MASK (3 << SX127X_LRM_MDMCFG2_RXTIMOUT_SHIFT) +#define SX127X_LRM_MDMCFG2_RXCRCON (1 << 2) /* Bit 2: RX payload CRC ON */ +#define SX127X_LRM_MDMCFG2_TXCONT (1 << 3) /* Bit 3: TX continous mode */ +#define SX127X_LRM_MDMCFG2_SPRFACT_SHIFT (4) /* Bits 4-7: Spreading factor */ +#define SX127X_LRM_MDMCFG2_SPRFACT_MASK (15 << SX127X_LRM_MDMCFG2_SPRFACT_SHIFT) + +/* LORA: Receiver timeout value */ + +#define SX127X_LRM_RXTIMEOUT_MAX (0x3f) +#define SX127X_LRM_RXTIMEOUT_LSB(t) ((t >> 0) & 0xff) +#define SX127X_LRM_RXTIMEOUT_MSB(t) ((t >> 8) & SX127X_LRM_MDMCFG2_RXTIMOUT_MASK) + +/* LORA: Size of preamble */ + +#define SX127X_LRM_PREMSB_DEFAULT (0x0008) +#define SX127X_LRM_PREMSB_MAX (0xffff) +#define SX127X_LRM_PRE_LSB(p) ((p >> 0) & 0xff) +#define SX127X_LRM_PRE_MSB(p) ((p >> 8) & 0xff) + +/* LORA: LORA payload length */ + +#define SX127X_LRM_PAYLOADLEN_DEFAULT (0x01) +#define SX127X_LRM_PAYLOADLEN_MAX (0xff) + +/* LORA: FHSS Hop period */ + +#define SX127X_LRM_HOPPER_DEFAULT (0x00) + +/* LORA: Modem PHY confgi 3 */ + +#define SX127X_LRM_MODEMCFG3_AGCAUTOON (1 << 2) /* Bit 2: AGC auto ON */ +#define SX127X_LRM_MODEMCFG3_LOWDRATEOPT (1 << 3) /* Bit 3: Low data rate optimize enable */ + +/* LORA: Estimated frequency error */ + +#define SX127X_LRM_FEIMSB_SHIFT (0) +#define SX127X_LRM_FEIMSB_MASK (15 << SX127X_LRM_FEIMSB_SHIFT) +#define SX127X_LRM_FEI(msb, mid, lsb) ((lsb << 0) | (mid << 8) | ((msb & SX127X_LRM_FEIMSB_MASK) << 16)) +#define SX127X_LRM_FEI_GET(fei) () /* TODO */ + +/* LORA: LORA detection optimize for SF6 */ + +#define SX127X_LRM_DETECTOPT_DO_SHIFT (0) /* Bits 0-2: Detection optimize */ +#define SX127X_LRM_DETECTOPT_DO_MASK (7 << SX127X_LRM_DETECTOPT_DO_SHIFT) +# define SX127X_LRM_DETECTOPT_DO_SF7SF12 (3 << SX127X_LRM_DETECTOPT_DO_SHIFT) /* 0x03: SF7 to SF12 */ +# define SX127X_LRM_DETECTOPT_DO_SF6 (5 << SX127X_LRM_DETECTOPT_DO_SHIFT) /* 0x05: SF6 */ + +/* LORA: Invert LORA I and Q signals */ + +#define SX127X_LRM_INVERTIQ_IIQ (1 << 6) /* Bit 6: Invert the LORA I and Q signals */ + +/* LORA: LORA detection threshold for SF6 */ + +#define SX127X_LRM_DETECTTHR_SF7SF12 (0x0a) +#define SX127X_LRM_DETECTTHR_SF6 (0x0c) + +/* LORA: LORA Sync Word */ + +#define SX127X_LRM_SYNCWORD_DEFAULT (0x12) +#define SX127X_LRM_SYNCWORD_LORAWAN (0x34) + +/* Lora data rate: + * DR = SF * BW * CR / (2**SF) + * + * DR - Data rate + * SF - Spreading factor + * BW - Bandwidth + * CR - Coding rate + */ + +#define LORA_DATARATE_GET(sf, bw, cr) (sf * bw * cr / (2< 862(779) - 1020(960) MHz + * BAND2 (LF) -> 410 - 525(480) MHz + * BAND1 (LF) -> 137 - 175(160) MHz + */ + +#define SX127X_HFBAND_THR (525000000) + +/* FXOSC is 32 MHz */ + +#define SX127X_FXOSC (32000000) + +/* FSTEP is FXOSC/(2**19) =~ 61 Hz */ + +#define SX127X_FSTEP (SX127X_FXOSC/(2<<18)) + +/* Default RF carrier frequency is 434 MHz */ + +#define SX127X_FREQ_RF_DEFAULT (434000000) + +/**************************************************************************** + * Public Data Types + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +# define EXTERN extern "C" + extern "C" + { +#else +# define EXTERN extern +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) + } +#endif + +#endif /* __DRIVERS_WIRELESS_LPWAN_SX127X_H */ diff --git a/include/nuttx/spi/spi.h b/include/nuttx/spi/spi.h index 69f9f903c4..d5428ca36e 100644 --- a/include/nuttx/spi/spi.h +++ b/include/nuttx/spi/spi.h @@ -485,6 +485,7 @@ #define SPIDEV_CONTACTLESS(n) SPIDEV_ID(SPIDEVTYPE_CONTACTLESS, (n)) #define SPIDEV_CANBUS(n) SPIDEV_ID(SPIDEVTYPE_CANBUS, (n)) #define SPIDEV_USBHOST(n) SPIDEV_ID(SPIDEVTYPE_USBHOST, (n)) +#define SPIDEV_LPWAN(n) SPIDEV_ID(SPIDEVTYPE_LPWAN, (n)) #define SPIDEV_USER(n) SPIDEV_ID(SPIDEVTYPE_USER, (n)) /**************************************************************************** @@ -522,6 +523,7 @@ enum spi_devtype_e SPIDEVTYPE_CONTACTLESS, /* Select SPI Contactless device */ SPIDEVTYPE_CANBUS, /* Select SPI CAN bus controller over SPI */ SPIDEVTYPE_USBHOST, /* Select SPI USB host controller over SPI */ + SPIDEVTYPE_LPWAN, /* Select SPI LPWAN controller over SPI */ SPIDEVTYPE_USER /* Board-specific values start here * This must always be the last definition. */ }; diff --git a/include/nuttx/wireless/ioctl.h b/include/nuttx/wireless/ioctl.h index e9bd5c816d..eb94e03075 100644 --- a/include/nuttx/wireless/ioctl.h +++ b/include/nuttx/wireless/ioctl.h @@ -92,6 +92,11 @@ #define NRF24L01_FIRST (WL_FIRST + WL_NCMDS) #define NRF24L01_NCMDS 14 +/* See include/nuttx/wireless/lpwan/sx127x.h */ + +#define SX127X_FIRST (NRF24L01_FIRST + NRF24L01_NCMDS) +#define SX127X_NCMDS 11 + /************************************************************************************ * Public Types ************************************************************************************/ diff --git a/include/nuttx/wireless/lpwan/sx127x.h b/include/nuttx/wireless/lpwan/sx127x.h new file mode 100644 index 0000000000..8a5410218b --- /dev/null +++ b/include/nuttx/wireless/lpwan/sx127x.h @@ -0,0 +1,270 @@ +/**************************************************************************** + * include/wireless/sx127x.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Authors: Mateusz Szafoni + * + * 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 NuttX 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SX127X_H +#define __INCLUDE_NUTTX_SX127X_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-Processor Declarations + ****************************************************************************/ + +/* IOCTL commands ***********************************************************/ + +/* arg: Pointer to int */ + +#define SX127XIOC_RSSIGET _WLCIOC(SX127X_FIRST+0) + +/* arg: Pointer to enum sx127x_modulation_e */ + +#define SX127XIOC_MODULATIONSET _WLCIOC(SX127X_FIRST+1) +#define SX127XIOC_MODULATIONGET _WLCIOC(SX127X_FIRST+2) + +/* arg: Pointer to enum sx127x_opmode_fskook_e */ + +#define SX127XIOC_OPMODESET _WLCIOC(SX127X_FIRST+3) +#define SX127XIOC_OPMODEGET _WLCIOC(SX127X_FIRST+4) + +/* arg: Pointer to struct sx127x_chanscan_ioc_s */ + +#define SX127XIOC_CHANSCAN _WLCIOC(SX127X_FIRST+5) + +/* arg: Pointer to uint32_t */ + +#define SX127XIOC_PREAMBLESET _WLCIOC(SX127X_FIRST+6) +#define SX127XIOC_PREAMBLEGET _WLCIOC(SX127X_FIRST+7) + +/* arg: Pointer to struct sx127x_syncword_ioc_s */ + +#define SX127XIOC_SYNCWORDSET _WLCIOC(SX127X_FIRST+8) +#define SX127XIOC_SYNCWORDGET _WLCIOC(SX127X_FIRST+9) + +/* arg: Pointer to uint32_t */ + +#define SX127XIOC_RANDOMGET _WLCIOC(SX127X_FIRST+10) + +/* TODO SX127X IOC: + * - CRC ON/OFF + * - FDEV + * - RX_BW + AFC_BW + * - BW for LORA + * - SF for LORA + * - CR for LORA + * - FREQ HOPPING + * - IMPLICT HEADER for LORA + */ + +/* RX FIFO data *************************************************************/ + +#define SX127X_READ_DATA_HEADER_LEN (sizeof(struct sx127x_read_hdr_s)-SX127X_READ_DATA_MAX) +#define SX127X_READ_DATA_MAX (CONFIG_LPWAN_SX127X_RXFIFO_DATA_LEN) +#define SX127X_RXFIFO_ITEM_SIZE (sizeof(struct sx127x_read_hdr_s)) + +/**************************************************************************** + * Public Data Types + ****************************************************************************/ + +/* RX FIFO data */ + +struct sx127x_read_hdr_s +{ + uint16_t datalen; + int8_t snr; + int16_t rssi; + uint8_t reserved[3]; + uint8_t data[SX127X_READ_DATA_MAX]; +}; + +/* Channel scan data */ + +struct sx127x_chanscan_ioc_s +{ + uint32_t freq; + int16_t rssi_thr; + int16_t rssi_max; + int16_t rssi_min; + uint16_t stime; + bool free; +}; + +/* SyncWord data */ + +struct sx127x_syncword_ioc_s +{ + uint8_t syncword[8]; + uint8_t len; +}; + +/* Modulation types */ + +enum sx127x_modulation_e +{ + SX127X_MODULATION_INVALID = 0, /* Initial state */ + SX127X_MODULATION_FSK = 1, + SX127X_MODULATION_OOK = 2, + SX127X_MODULATION_LORA = 3 +}; + +/* Operating modes */ + +enum sx127x_opmode_fskook_e +{ + SX127X_OPMODE_INVALID = 0, /* Initial state */ + SX127X_OPMODE_SLEEP = 1, + SX127X_OPMODE_STANDBY = 2, + SX127X_OPMODE_FSTX = 3, + SX127X_OPMODE_TX = 4, + SX127X_OPMODE_FSRX = 5, + SX127X_OPMODE_RX = 6, /* RXCONT for LORA */ + SX127X_OPMODE_RXSINGLE = 7, /* Only LORA */ + SX127X_OPMODE_CAD = 8, /* Only LORA */ +}; + +/* FSK bandwidth */ + +enum sx127x_fskook_bw_e +{ + FSKOOK_BANDWIDTH_2p6kHz = 0x17, + FSKOOK_BANDWIDTH_3p1kHz = 0x0f, + FSKOOK_BANDWIDTH_3p9kHz = 0x07, + FSKOOK_BANDWIDTH_5p2kHz = 0x16, + FSKOOK_BANDWIDTH_6p3kHz = 0x0e, + FSKOOK_BANDWIDTH_7p8kHz = 0x06, + FSKOOK_BANDWIDTH_10p4kHz = 0x15, + FSKOOK_BANDWIDTH_12p5kHz = 0x0d, + FSKOOK_BANDWIDTH_15p6kHz = 0x05, + FSKOOK_BANDWIDTH_20p8kHz = 0x14, + FSKOOK_BANDWIDTH_25kHz = 0x0c, + FSKOOK_BANDWIDTH_31p3kHz = 0x04, + FSKOOK_BANDWIDTH_41p7kHz = 0x13, + FSKOOK_BANDWIDTH_50kHz = 0x0b, + FSKOOK_BANDWIDTH_62p5kHz = 0x03, + FSKOOK_BANDWIDTH_83p3kHz = 0x12, + FSKOOK_BANDWIDTH_100kHz = 0x0a, + FSKOOK_BANDWIDTH_125kHz = 0x02, + FSKOOK_BANDWIDTH_166p7kHz = 0x11, + FSKOOK_BANDWIDTH_200kHz = 0x09, + FSKOOK_BANDWIDTH_250kHz = 0x01, + /* Other settings reserved */ +}; + +/* LORA bandwidth */ + +enum sx127x_lora_bw_e +{ + LORA_BANDWIDTH_7p8kHz = 0, + LORA_BANDWIDTH_10p4kHz = 1, + LORA_BANDWIDTH_15p6kHz = 2, + LORA_BANDWIDTH_20p8kHz = 3, + LORA_BANDWIDTH_31p2kHz = 4, + LORA_BANDWIDTH_41p4kHz = 5, + LORA_BANDWIDTH_62p5kHz = 6, + LORA_BANDWIDTH_125kHz = 7, + LORA_BANDWIDTH_250kHz = 9 +}; + +/* LORA SF */ + +enum sx127x_lora_sf_e +{ + LORA_SF_6 = 6, + LORA_SF_7 = 7, + LORA_SF_8 = 8, + LORA_SF_9 = 9, + LORA_SF_10 = 10, + LORA_SF_11 = 11, + LORA_SF_12 = 12 +}; + +/* LORA coding rate */ + +enum sx127x_lora_cr_e +{ + LORA_CR_4d5 = 1, + LORA_CR_4d6 = 2, + LORA_CR_4d7 = 3, + LORA_CR_4d8 = 4 +}; + +/* Only DIO0 interrupts supported for now + * + * DIO0 - RXREADY/TXREADY/CADREADY + * DIO1 - not supported yet + * DIO2 - not supported yet + * DIO3 - not supported yet + * DIO4 - not supported yet + * DIO5 - not supported yet + */ + +struct sx127x_lower_s +{ + int (*irq0attach)(xcpt_t handler, FAR void *arg); +#ifdef CONFIG_LPWAN_SX127X_DIO1 + int (*irq1attach)(xcpt_t handler, FAR void *arg); +#endif +#ifdef CONFIG_LPWAN_SX127X_DIO2 + int (*irq2attach)(xcpt_t handler, FAR void *arg); +#endif +#ifdef CONFIG_LPWAN_SX127X_DIO3 + int (*irq3attach)(xcpt_t handler, FAR void *arg); +#endif +#ifdef CONFIG_LPWAN_SX127X_DIO4 + int (*irq4attach)(xcpt_t handler, FAR void *arg); +#endif +#ifdef CONFIG_LPWAN_SX127X_DIO5 + int (*irq5attach)(xcpt_t handler, FAR void *arg); +#endif + void (*reset)(void); +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int sx127x_register(FAR struct spi_dev_s *spi, + FAR const struct sx127x_lower_s *lower); + +#endif /* __INCLUDE_NUTTX_SX127X_H */