nuttx/drivers/wireless/lpwan/sx127x/sx127x.c

4716 lines
117 KiB
C

/****************************************************************************
* drivers/wireless/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 <nuttx/config.h>
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <poll.h>
#include <debug.h>
#include <time.h>
#include <fcntl.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <nuttx/signal.h>
#include <nuttx/wqueue.h>
#include <nuttx/wireless/lpwan/sx127x.h>
#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);
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 */
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);
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;
}