From 32e1e2fec080143223592142254fd7e9bb46b305 Mon Sep 17 00:00:00 2001 From: wgz-code <820906721@qq.com> Date: Thu, 10 Mar 2022 19:51:41 +0800 Subject: [PATCH] aiit-arm32-board support sx127x on nuttx --- .../aiit-arm32-board/include/board.h | 2 +- .../aiit-arm32-board/src/aiit-arm32-board.h | 5 +- .../aiit-arm32-board/src/stm32_spi.c | 1 + .../aiit-arm32-board/src/stm32_sx127x.c | 2 +- .../drivers/wireless/lpwan/sx127x/sx127x.c | 4718 +++++++++++++++++ 5 files changed, 4723 insertions(+), 5 deletions(-) create mode 100644 Ubiquitous/Nuttx/app_match_nuttx/nuttx/drivers/wireless/lpwan/sx127x/sx127x.c diff --git a/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/include/board.h b/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/include/board.h index 7db852fa3..87b1b1bbe 100644 --- a/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/include/board.h +++ b/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/include/board.h @@ -336,7 +336,7 @@ #define DMACHAN_SPI1_RX DMAMAP_SPI1_RX_1 #define DMACHAN_SPI1_TX DMAMAP_SPI1_TX_1 -/* SPI2 - Test MAX31855 on SPI2 PB10 = SCK, PB14 = MISO */ +/* SPI2 - Test sx127x on SPI2 PB13 = SCK, PC2 = MISO PC3 MOSI*/ #define GPIO_SPI2_MISO GPIO_SPI2_MISO_2 #define GPIO_SPI2_MOSI GPIO_SPI2_MOSI_2 diff --git a/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/aiit-arm32-board.h b/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/aiit-arm32-board.h index dd5da8363..fc894682e 100644 --- a/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/aiit-arm32-board.h +++ b/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/aiit-arm32-board.h @@ -229,10 +229,9 @@ /* LoRa SX127x */ -#define GPIO_SX127X_DIO0 (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_PORTD|GPIO_PIN0) +#define GPIO_SX127X_DIO0 (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_PORTC|GPIO_PIN1) -#define GPIO_SX127X_RESET (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_OUTPUT_CLEAR|\ - GPIO_SPEED_50MHz|GPIO_PORTD|GPIO_PIN4) +#define GPIO_SX127X_RESET (GPIO_PORTF|GPIO_PIN11) /* PWM * diff --git a/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/stm32_spi.c b/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/stm32_spi.c index 737883466..aa6d50019 100644 --- a/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/stm32_spi.c +++ b/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/stm32_spi.c @@ -71,6 +71,7 @@ void weak_function stm32_spidev_initialize(void) stm32_configgpio(GPIO_MAX7219_CS); /* MAX7219 chip select */ #endif #ifdef CONFIG_LPWAN_SX127X + spiinfo("Configure GPIO for SX127X SPI2/CS\n"); stm32_configgpio(GPIO_SX127X_CS); /* SX127x chip select */ #endif diff --git a/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/stm32_sx127x.c b/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/stm32_sx127x.c index b6043543b..1659ec74d 100644 --- a/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/stm32_sx127x.c +++ b/Ubiquitous/Nuttx/aiit_board/aiit-arm32-board/src/stm32_sx127x.c @@ -103,7 +103,7 @@ static void sx127x_chip_reset(void) /* Configure reset as output */ - stm32_configgpio(GPIO_SX127X_RESET); + stm32_configgpio(GPIO_SX127X_RESET | GPIO_OUTPUT | GPIO_SPEED_50MHz | GPIO_OUTPUT_CLEAR); /* Set pin to zero */ diff --git a/Ubiquitous/Nuttx/app_match_nuttx/nuttx/drivers/wireless/lpwan/sx127x/sx127x.c b/Ubiquitous/Nuttx/app_match_nuttx/nuttx/drivers/wireless/lpwan/sx127x/sx127x.c new file mode 100644 index 000000000..f4cabca13 --- /dev/null +++ b/Ubiquitous/Nuttx/app_match_nuttx/nuttx/drivers/wireless/lpwan/sx127x/sx127x.c @@ -0,0 +1,4718 @@ +/**************************************************************************** + * drivers/wireless/lpwan/sx127x/sx127x.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "sx127x.h" + +/* TODO: + * - OOK communication (RX+TX) doesn't work yet + * - 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) + * - address filtering for FSK/OOK + */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if !defined(CONFIG_SCHED_HPWORK) +# error SX127X requires CONFIG_SCHED_HPWORK +#endif + +/* Configuration ************************************************************/ + +/* Device name */ + +#define SX127X_DEV_NAME "/dev/sx127x" + +/* Payload fixlen default */ + +#define SX127X_RX_FIXLEN_DEFAULT (0xff) + +/* Calibration frequency */ + +#define SX127X_FREQ_CALIBRATION (CONFIG_LPWAN_SX127X_RFFREQ_DEFAULT) + +/* FSK default frequency deviation is 5kHz */ + +#define SX127X_FDEV_DEFAULT (5000) + +/* FSK/OOK bitrate default */ + +#define SX127X_FOM_BITRATE_DEFAULT (4800) + +/* FSK/OOK bandwidth default */ + +#define SX127X_FSKOOK_RXBW_DEFAULT FSKOOK_BANDWIDTH_15P6KHZ +#define SX127X_FSKOOK_AFCBW_DEFAULT FSKOOK_BANDWIDTH_20P8KHZ + +/* Default LORA bandwidth */ + +#define SX127X_LRM_BW_DEFAULT LORA_BANDWIDTH_7P8KHZ + +/* Default SF for LORA */ + +#define SX127X_LRM_SF_DEFAULT (7) + +/* 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) + +/* 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 */ + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK +# warning OOK support is not complete, RX+TX does not work yet! +# 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 +#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 */ + + CODE void (*init)(FAR struct sx127x_dev_s *dev); + + /* Process IRQ 0 */ + + CODE int (*isr0_process)(FAR struct sx127x_dev_s *dev); + + /* Operation mode initialization */ + + CODE int (*opmode_init)(FAR struct sx127x_dev_s *dev, uint8_t opmode); + + /* Change operation mode */ + + CODE int (*opmode_set)(FAR struct sx127x_dev_s *dev, uint8_t opmode); + + /* Set preamble length */ + + CODE void (*preamble_set)(FAR struct sx127x_dev_s *dev, uint32_t len); + + /* Get preamble length */ + + CODE int (*preamble_get)(FAR struct sx127x_dev_s *dev); + + /* Get current RSSI */ + + CODE int16_t (*rssi_get)(FAR struct sx127x_dev_s *dev); + + /* Set sync word */ + + CODE int (*syncword_set)(FAR struct sx127x_dev_s *dev, FAR uint8_t *sw, + uint8_t len); + + /* Get sync word */ + + CODE void (*syncword_get)(FAR struct sx127x_dev_s *dev, FAR uint8_t *sw, + FAR uint8_t *len); + +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + /* Send packet */ + + CODE 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 */ + + CODE 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 */ + uint8_t addr_node; /* Node address used in address filtering */ + uint8_t addr_brdcast; /* Broadcast address used int address filtering */ + bool fixlen; /* Fix length */ + bool addr_fltr; /* TODO: Address filtering */ + 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; /* Implicit 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) */ + int8_t power; /* TX power */ + bool pa_force; /* Force PA BOOST pin select */ +#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 */ + FAR struct pollfd *pfd; /* Polled file descr (or NULL if any) */ +}; + +/**************************************************************************** + * 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, + FAR uint8_t *sw, uint8_t len); +static void sx127x_lora_syncword_get(FAR struct sx127x_dev_s *dev, + FAR 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 size_t 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, + FAR uint8_t *sw, uint8_t len); +static void sx127x_fskook_syncword_get(FAR struct sx127x_dev_s *dev, + FAR uint8_t *sw, FAR uint8_t *len); +# ifdef CONFIG_LPWAN_SX127X_RXSUPPORT +static size_t 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 int sx127x_power_set(FAR struct sx127x_dev_s *dev, int8_t power); +static int8_t sx127x_power_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, + FAR uint8_t *sw, uint8_t len); +static void sx127x_syncword_get(FAR struct sx127x_dev_s *dev, + FAR uint8_t *sw, FAR 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, + FAR uint8_t *buffer, size_t buflen); +static void sx127x_rxfifo_put(struct sx127x_dev_s *dev, FAR 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); +static int sx127x_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); + +/**************************************************************************** + * 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 */ + sx127x_poll /* poll */ +#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, CONFIG_LPWAN_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(FAR 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(FAR 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) + { + 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; + } + + 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) + { + 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) + { + 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) + { + return ret; + } + + /* Get RX data from fifo */ + + ret = sx127x_rxfifo_get(dev, (uint8_t *)buffer, buflen); + sx127x_writeregbyte(dev, SX127X_LRM_IRQ, 8); + + 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) + { + return ret; + } + + wlinfo("buflen=%d \n", buflen); + + /* Change mode to STANDBY */ + + sx127x_opmode_set(dev, SX127X_OPMODE_STANDBY); + + /* Initialize TX mode */ + sx127x_writeregbyte(dev, SX127X_LRM_IRQ, 8); + + ret = sx127x_opmode_init(dev, SX127X_OPMODE_TX); + if (ret < 0) + { + /* Restore IDLE mode settings */ + + sx127x_opmode_init(dev, dev->idle); + + wlerr("Failed to initialize TX mode!\n"); + + ret = -EINVAL; + goto errout; + } + + /* Call modulation specific send */ + + ret = dev->ops.send(dev, (uint8_t *)buffer, buflen); + + /* Change mode to TX to start data transfer */ + + sx127x_opmode_set(dev, SX127X_OPMODE_TX); + + /* Wait for TXDONE */ + + nxsem_wait(&dev->tx_sem); + +errout: + /* Change mode to IDLE after transfer + * NOTE: if sequencer for FSK/OOK is ON - this should be done automatically + */ + + sx127x_opmode_set(dev, dev->idle); + sx127x_writeregbyte(dev, SX127X_LRM_IRQ, 8); + + 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) + { + 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 int8_t *ptr = (FAR int8_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + *ptr = sx127x_frequency_get(dev); + break; + } + + /* Set TX power. arg: Pointer to int8_t power value */ + + case WLIOC_SETTXPOWER: + { + FAR int8_t *ptr = (FAR int8_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + sx127x_power_set(dev, *ptr); + break; + } + + /* Get current TX power. arg: Pointer to int8_t power value */ + + case WLIOC_GETTXPOWER: + { + FAR int8_t *ptr = (FAR int8_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + *ptr = sx127x_power_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 modulation */ + + case SX127XIOC_MODULATIONSET: + { + FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + + ret = sx127x_modulation_set(dev, *ptr); + break; + } + + /* Get modulation */ + + 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); + + 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. + * + ****************************************************************************/ + +static int sx127x_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ +#ifndef 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->dev_sem); + if (ret < 0) + { + 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. + */ + + 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->dev_sem); + return ret; +#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); + + switch (dev->opmode) + { +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + /* TX DONE */ + + 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: + { + /* REVISIT: Always check PAYLOADCRCERR even CRCONPAYLOAD not set */ + + if ((irq & SX127X_LRM_IRQ_PAYLOADCRCERR) != 0) + { + data_valid = false; + } + + if (data_valid) + { + ret = sx127x_lora_rxhandle(dev); + if (ret > 0) + { + if (dev->pfd) + { + /* Data available for input */ + + dev->pfd->revents |= POLLIN; + + wlinfo("Wake up polled fd\n"); + nxsem_post(dev->pfd->sem); + } + + /* 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, opmode=%d\n", + dev->opmode); + 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); + + switch (dev->opmode) + { +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + /* TX DONE */ + + 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: + { + /* RX data valid ? */ + + if (dev->crcon == true && (irq2 & SX127X_FOM_IRQ2_CRCOK) == 0) + { + data_valid = false; + } + + if (data_valid == true) + { + /* RX data valid */ + + ret = sx127x_fskook_rxhandle(dev); + if (ret > 0) + { + if (dev->pfd) + { + /* Data available for input */ + + dev->pfd->revents |= POLLIN; + + wlinfo("Wake up polled fd\n"); + nxsem_post(dev->pfd->sem); + } + + /* 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; + + /* Return immediately if isr0_process is not initialized */ + + if (dev->ops.isr0_process == NULL) + { + return; + } + + /* isr0_process depends on the current modulation scheme */ + + 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 = (FAR struct sx127x_dev_s *)arg; + + DEBUGASSERT(dev != NULL); + + DEBUGASSERT(work_available(&dev->irq0_work)); + + return work_queue(HPWORK, &dev->irq0_work, sx127x_isr0_process, arg, 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 size_t 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; + size_t len = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* 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); + } + + /* Ignore packets with unsupported data length */ + + if (datalen > SX127X_READ_DATA_MAX) + { + wlerr("Unsupported data length! %d > %d\n", + datalen, SX127X_READ_DATA_MAX); + sx127x_unlock(dev->spi); + goto errout; + } + + /* Read payload and store */ + + sx127x_readreg(dev, SX127X_CMN_FIFO, rxdata.data, datalen); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* 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); + +errout: + + /* 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 size_t sx127x_lora_rxhandle(FAR struct sx127x_dev_s *dev) +{ + DEBUGASSERT(dev->modulation == SX127X_MODULATION_LORA); + + struct sx127x_read_hdr_s rxdata; + size_t len = 0; + uint8_t datalen = 0; + uint8_t rx_ptr = 0; + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Get payload length */ + + datalen = sx127x_readregbyte(dev, SX127X_LRM_RXBYTES); + + /* Ignore packets with unsupported data length */ + + if (datalen > SX127X_READ_DATA_MAX) + { + wlerr("Unsupported data length! %d > %d\n", + datalen, SX127X_READ_DATA_MAX); + sx127x_unlock(dev->spi); + goto errout; + } + + /* 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); + + /* Unlock SPI */ + + sx127x_unlock(dev->spi); + + /* 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); + +errout: + + /* 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(FAR struct sx127x_dev_s *dev, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct sx127x_read_hdr_s *pkt = NULL; + size_t i = 0; + size_t pktlen = 0; + size_t ret = 0; + + ret = nxsem_wait(&dev->rx_buffer_sem); + if (ret < 0) + { + 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(FAR struct sx127x_dev_s *dev, + FAR uint8_t *buffer, size_t buflen) +{ + size_t i = 0; + int ret = 0; + + ret = nxsem_wait(&dev->rx_buffer_sem); + if (ret < 0) + { + 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. + * For fixlen = true, datalen <= 64 + * For fixlen = false, datalen < 64 (we support this for now) + */ + + if (datalen > 63) + { + wlerr("Not supported data len!\n"); + ret = -EINVAL; + goto errout; + } +#endif + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + if (dev->fskook.fixlen == true) + { + /* Write payload length register (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); + +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; + } + + /* 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); + +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; + } + + /* Board-specific opmode configuration */ + + ret = dev->lower->opmode_change(opmode); + if (ret < 0) + { + wlerr("Board-specific opmode_change failed %d!\n", ret); + goto errout; + } + + /* Initialize opmode */ + + ret = dev->ops.opmode_init(dev, opmode); + if (ret < 0) + { + wlerr("opmode_init failed %d!\n", ret); + goto errout; + } + +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_TX) + { + 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; +} + +/**************************************************************************** + * Name: sx127x_lora_opmode_init + * + * Description: + * Initialize operation mode for FSK/OOK. + * We need this even if FSK/OOK support is disabled + * + ****************************************************************************/ + +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; + + /* REVISIT: Configure RXCFG register: + * - AGC auto ON + * - AFC auto ON + * - RX trigger on PreableDetect + */ + + setbits = (SX127X_FOM_RXCFG_AGCAUTOON | SX127X_FOM_RXCFG_AFCAUTOON + | SX127X_FOM_RXCFG_TRG_PREDET); + + sx127x_writeregbyte(dev, SX127X_FOM_RXCFG, setbits); + + 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. + * We need this even if FSK/OOK support is disabled + * + ****************************************************************************/ + +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 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); + + sx127x_unlock(dev->spi); + +errout: + return ret; +} + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + +/**************************************************************************** + * 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 transitions between operation modes and + * thus further reduce energy consumption. + * + ****************************************************************************/ + +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, + FAR uint8_t *sw, FAR 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_fskook_syncword_set + * + * Description: + * Set SyncWord for FSK/OOK + * + ****************************************************************************/ + +static int sx127x_fskook_syncword_set(FAR struct sx127x_dev_s *dev, + FAR 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_fskook_init + * + * Description: + * Initialization specific for FSK/OOK modulation + * + ****************************************************************************/ + +static void sx127x_fskook_init(FAR struct sx127x_dev_s *dev) +{ + uint8_t setbits = 0; + uint8_t clrbits = 0; + uint8_t syncword[] = + { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 + }; + + DEBUGASSERT(dev->modulation == SX127X_MODULATION_FSK || + dev->modulation == SX127X_MODULATION_OOK); + + /* Set FDEV */ + + sx127x_fskook_fdev_set(dev, SX127X_FDEV_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 + * REVISIT: FSK communication doesn't work if syncword is disabled! + */ + + sx127x_fskook_syncword_set(dev, syncword, 8); + + /* 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; + setbits |= dev->fskook.fixlen == true ? 0 : SX127X_FOM_PKTCFG1_PCKFORMAT; + setbits |= dev->crcon == true ? SX127X_FOM_PKTCFG1_CRCON : 0; + clrbits = SX127X_FOM_PKTCFG1_PCKFORMAT | SX127X_FOM_PKTCFG1_CRCON; + + /* Write packet mode settings 1 */ + + sx127x_modregbyte(dev, SX127X_FOM_PKTCFG1, setbits, clrbits); + + /* Configure packet mode settings 2: + * - packet mode on + */ + + setbits = 0; + setbits |= SX127X_FOM_PKTCFG2_DATAMODE; + clrbits = 0; + + /* Write packet mode settings 2 */ + + sx127x_modregbyte(dev, SX127X_FOM_PKTCFG2, 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 ? */ + + nxsig_usleep(250); + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_lora_syncword_get + ****************************************************************************/ + +static void sx127x_lora_syncword_get(FAR struct sx127x_dev_s *dev, + FAR uint8_t *sw, FAR 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, + FAR 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): + * - implicit header mode ON + * - Detection optimize for SF6 + * - Detection threshold for SF6 + */ + + if (dev->lora.sf == 6) + { + if (dev->lora.implicthdr == true) + { + wlerr("SF6 needs implicit 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 implicit 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 implicit 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); + + /* 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, CONFIG_LPWAN_SX127X_LORA_IMPHEADER); + + /* Lock SPI */ + + sx127x_lock(dev->spi); + + /* Configure maximum payload */ + + sx127x_writeregbyte(dev, SX127X_LRM_PAYLOADMAX, + SX127X_LRM_PAYLOADMAX_DEFAULT); + + /* Modem PHY config 2: + * - RXCRCON + * NOTE: this works differently for implicit header and explicit header + * - packet mode + */ + + setbits = (dev->crcon == true ? SX127X_LRM_MDMCFG2_RXCRCON : 0); + clrbits = (SX127X_LRM_MDMCFG2_TXCONT | SX127X_LRM_MDMCFG2_RXCRCON); + 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 = SX127X_LRM_INVERTIQ_IIQ; + 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_syncword_get + ****************************************************************************/ + +static void sx127x_syncword_get(FAR struct sx127x_dev_s *dev, + FAR uint8_t *sw, FAR uint8_t *len) +{ + dev->ops.syncword_get(dev, sw, len); +} + +/**************************************************************************** + * Name: sx127x_syncword_set + ****************************************************************************/ + +static int sx127x_syncword_set(FAR struct sx127x_dev_s *dev, + FAR uint8_t *sw, uint8_t len) +{ + return dev->ops.syncword_set(dev, sw, len); +} + +/**************************************************************************** + * 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) +{ + if (modulation <= SX127X_MODULATION_OOK) + { + /* NOTE: we need opmode_init and opmode_set for FSK/OOK even if + * support for these modulations is disabled! + */ + + dev->ops.opmode_init = sx127x_fskook_opmode_init; + dev->ops.opmode_set = sx127x_fskook_opmode_set; +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + dev->ops.init = sx127x_fskook_init; + dev->ops.isr0_process = sx127x_fskook_isr0_process; + 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 /* CONFIG_LPWAN_SX127X_FSKOOK */ + } + +#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 /* CONFIG_LPWAN_SX127X_LORA */ +} + +/**************************************************************************** + * Name: sx127x_modulation_init + ****************************************************************************/ + +static void sx127x_modulation_init(FAR struct sx127x_dev_s *dev) +{ + dev->ops.init(dev); + + /* Configure preamble */ + + sx127x_preamble_set(dev, CONFIG_LPWAN_SX127X_PREAMBLE_DEFAULT); + + /* Dump registers after initial configuration */ + + sx127x_dumpregs(dev); +} + +/**************************************************************************** + * 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; + } + + /* Modulation 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 */ + + sx127x_modulation_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 */ + + sx127x_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; + } + + /* Wait some time */ + + nxsig_usleep(1000); + } + while (tstart.tv_sec + chanscan->stime > tnow.tv_sec); + + /* Set mode to STANDBY */ + + sx127x_opmode_set(dev, SX127X_OPMODE_STANDBY); + + /* 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; + + wlinfo("frequency %" PRId32 "->%" PRId32 "\n", dev->freq, freq); + + 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; + + /* Call board-specific LF/HF configuration */ + + ret = dev->lower->freq_select(freq); + if (ret < 0) + { + wlerr("Board-specific freq_select failed %d!\n", ret); + goto errout; + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: sx127x_power_set + ****************************************************************************/ + +static int sx127x_power_set(FAR struct sx127x_dev_s *dev, int8_t power) +{ +#ifndef CONFIG_LPWAN_SX127X_TXSUPPORT + return -ENOSYS; +#else + bool pa_select = false; + bool pa_dac = false; + uint8_t setbits = 0; + uint8_t clrbits = 0; + int ret = OK; + + if (dev->power == power) + { + goto errout; + } + + /* PA BOOST configuration */ + + if (power > SX127X_PASELECT_POWER || dev->pa_force == true) + { + pa_select = true; + } + + /* High power PA BOOST */ + + if (power >= 20) + { + pa_dac = true; + } + + /* Saturate power output */ + + if (pa_select == true) + { + if (pa_dac == true) + { + if (power < 5) + { + power = 5; + } + else if (power > 20) + { + power = 20; + } + } + else + { + if (power < 2) + { + power = 2; + } + else if (power > 17) + { + power = 17; + } + } + } + else + { + if (power < -1) + { + power = -1; + } + else if (power > 14) + { + power = 14; + } + } + + wlinfo("power %d->%d, pa=%d, dac=%d\n", + dev->power, power, pa_select, pa_dac); + + sx127x_lock(dev->spi); + + if (pa_select == true) + { + if (pa_dac == true) + { + /* Enable high power on PA_BOOST */ + + sx127x_writeregbyte(dev, SX127X_CMN_PADAC, SX127X_CMN_PADAC_BOOST); + + /* Configure output power */ + + setbits = (power - 5) << SX127X_CMN_PACFG_OUTPOWER_SHIFT; + clrbits = SX127X_CMN_PACFG_OUTPOWER_MASK; + + sx127x_modregbyte(dev, SX127X_CMN_PACFG, setbits, clrbits); + } + else + { + /* Disable high power on PA_BOOST */ + + sx127x_writeregbyte(dev, SX127X_CMN_PADAC, + SX127X_CMN_PADAC_DEFAULT); + + /* Configure output power */ + + setbits = (power - 2) << SX127X_CMN_PACFG_OUTPOWER_SHIFT; + clrbits = SX127X_CMN_PACFG_OUTPOWER_MASK; + + sx127x_modregbyte(dev, SX127X_CMN_PACFG, setbits, clrbits); + } + + /* Enable PA BOOST output */ + + sx127x_modregbyte(dev, SX127X_CMN_PACFG, SX127X_CMN_PACFG_PASELECT, 0); + } + else + { + /* Configure output power and max power to 13.8 dBm */ + + setbits = ((power + 1) << SX127X_CMN_PACFG_OUTPOWER_SHIFT); + setbits |= (5 << SX127X_CMN_PACFG_MAXPOWER_SHIFT); + clrbits = (SX127X_CMN_PACFG_OUTPOWER_MASK | + SX127X_CMN_PACFG_MAXPOWER_SHIFT); + + sx127x_modregbyte(dev, SX127X_CMN_PACFG, setbits, clrbits); + + /* Enable RFO output */ + + sx127x_modregbyte(dev, SX127X_CMN_PACFG, 0, SX127X_CMN_PACFG_PASELECT); + } + + sx127x_unlock(dev->spi); + + /* Call board-specific logic */ + + ret = dev->lower->pa_select(pa_select); + if (ret < 0) + { + wlerr("Board-specific pa_select failed %d!\n", ret); + } + + /* Update local variable */ + + dev->power = power; + +errout: + return ret; +#endif +} + +/**************************************************************************** + * Name: sx127x_power_get + ****************************************************************************/ + +static int8_t sx127x_power_get(FAR struct sx127x_dev_s *dev) +{ +#ifndef CONFIG_LPWAN_SX127X_TXSUPPORT + return 0; +#else + return dev->power; +#endif +} + +/**************************************************************************** + * 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 %" PRId32 "\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 */ + + nxsig_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); + + /* After reset - FSK mode */ + + dev->modulation = SX127X_MODULATION_FSK; + + /* Initialize modem ops */ + + sx127x_ops_set(dev, dev->modulation); + + wlinfo("Init state: modulation=%d, opmode=%d\n", + dev->modulation, dev->opmode); + +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + /* Initialize FSK/OOK modem only if support is enabled */ + + sx127x_modulation_init(dev); +#endif + + /* 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 */ + + sx127x_modulation_set(dev, CONFIG_LPWAN_SX127X_MODULATION_DEFAULT); + + /* Enter SLEEP mode */ + + sx127x_opmode_set(dev, SX127X_OPMODE_SLEEP); + + /* Set default channel frequency - common for FSK/OOK and LORA */ + + ret = sx127x_frequency_set(dev, CONFIG_LPWAN_SX127X_RFFREQ_DEFAULT); + if (ret < 0) + { + goto errout; + } + + /* Configure RF output power - common for FSK/OOK and LORA */ + + ret = sx127x_power_set(dev, CONFIG_LPWAN_SX127X_TXPOWER_DEFAULT); + if (ret < 0) + { + goto errout; + } + + 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 + +/**************************************************************************** + * 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); + DEBUGASSERT(lower->reset != NULL); + DEBUGASSERT(lower->irq0attach != NULL); + DEBUGASSERT(lower->opmode_change != NULL); + DEBUGASSERT(lower->freq_select != NULL); + DEBUGASSERT(lower->pa_select != 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 configuration */ + + dev->idle = SX127X_IDLE_OPMODE; +#ifdef CONFIG_LPWAN_SX127X_TXSUPPORT + dev->pa_force = lower->pa_force; +#endif + dev->crcon = CONFIG_LPWAN_SX127X_CRCON; +#ifdef CONFIG_LPWAN_SX127X_FSKOOK + dev->fskook.fixlen = false; +#endif +#ifdef CONFIG_LPWAN_SX127X_LORA + dev->lora.invert_iq = false; +#endif + + /* Polled file decr */ + + dev->pfd = NULL; + + /* 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; +}