This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 20ed2e689c3161c1425992cf47e1d1627b10d42d
Author: Kevin Witteveen (MartiniMarter) <kevinwit1...@gmail.com>
AuthorDate: Sun Feb 16 16:35:21 2025 +0100

    drivers/wireless/lpwan/sx126x:
    [Experimental]
    This adds a driver for the SX126x (SX1261 and SX1262) LoRa chips.
    All functions and definitions are coming directly from the DS SX1261-2 V2.1 
datasheet.
    
    Signed-off-by: Kevin Witteveen (MartiniMarter) <kevinwit1...@gmail.com>
---
 drivers/wireless/lpwan/Kconfig                |    9 +
 drivers/wireless/lpwan/Make.defs              |    1 +
 drivers/wireless/lpwan/sx126x/CMakeLists.txt  |   24 +
 drivers/wireless/lpwan/sx126x/Kconfig         |   28 +
 drivers/wireless/lpwan/{ => sx126x}/Make.defs |   16 +-
 drivers/wireless/lpwan/sx126x/sx126x.c        | 1407 +++++++++++++++++++++++++
 drivers/wireless/lpwan/sx126x/sx126x.h        |  440 ++++++++
 include/nuttx/wireless/ioctl.h                |    5 +
 include/nuttx/wireless/lpwan/sx126x.h         |  418 ++++++++
 9 files changed, 2343 insertions(+), 5 deletions(-)

diff --git a/drivers/wireless/lpwan/Kconfig b/drivers/wireless/lpwan/Kconfig
index eb8dae6258..514d6532e5 100644
--- a/drivers/wireless/lpwan/Kconfig
+++ b/drivers/wireless/lpwan/Kconfig
@@ -12,6 +12,15 @@ config LPWAN_SX127X
        ---help---
                This options adds driver support for the Samtech SX127X chip.
 
+config LPWAN_SX126X
+       bool "SX126X Low Power Long Range transceiver support"
+       default n
+       select SPI
+       ---help---
+               This options adds driver support for the Samtech SX126X chip.
+
+source "drivers/wireless/lpwan/sx126x/Kconfig"
+
 if LPWAN_SX127X
 
 config LPWAN_SX127X_RFFREQ_DEFAULT
diff --git a/drivers/wireless/lpwan/Make.defs b/drivers/wireless/lpwan/Make.defs
index 023bff806f..4a12806ac7 100644
--- a/drivers/wireless/lpwan/Make.defs
+++ b/drivers/wireless/lpwan/Make.defs
@@ -25,5 +25,6 @@
 ifeq ($(CONFIG_DRIVERS_LPWAN),y)
 
 include wireless/lpwan/sx127x/Make.defs
+include wireless/lpwan/sx126x/Make.defs
 
 endif # CONFIG_DRIVERS_LPWAN
diff --git a/drivers/wireless/lpwan/sx126x/CMakeLists.txt 
b/drivers/wireless/lpwan/sx126x/CMakeLists.txt
new file mode 100644
index 0000000000..a69a176feb
--- /dev/null
+++ b/drivers/wireless/lpwan/sx126x/CMakeLists.txt
@@ -0,0 +1,24 @@
+# 
##############################################################################
+# drivers/wireless/lpwan/sx126x/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# 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.
+#
+# 
##############################################################################
+if(CONFIG_LPWAN_SX126X)
+  target_sources(drivers PRIVATE sx126x.c)
+endif()
diff --git a/drivers/wireless/lpwan/sx126x/Kconfig 
b/drivers/wireless/lpwan/sx126x/Kconfig
new file mode 100644
index 0000000000..6a04e1952c
--- /dev/null
+++ b/drivers/wireless/lpwan/sx126x/Kconfig
@@ -0,0 +1,28 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+if LPWAN_SX126X
+
+config LPWAN_SX126X_RFFREQ_DEFAULT
+       int "SX126X default RF frequency Hz"
+       default 869252000
+
+config LPWAN_SX126X_SF_DEFAULT
+       int "SX126X default spreading factor"
+       default 10
+
+config LPWAN_SX126X_BW_DEFAULT
+       int "SX126X default bandwidth kHz"
+       default 125
+
+config LPWAN_SX126X_CR_DEFAULT
+       int "SX126X default coding rate"
+       default 8
+
+config LPWAN_SX126X_MAX_DEVICES
+       int "SX126X maximum devices"
+       default 1
+
+endif # DRIVERS_LPWAN
diff --git a/drivers/wireless/lpwan/Make.defs 
b/drivers/wireless/lpwan/sx126x/Make.defs
similarity index 71%
copy from drivers/wireless/lpwan/Make.defs
copy to drivers/wireless/lpwan/sx126x/Make.defs
index 023bff806f..bdf2143d45 100644
--- a/drivers/wireless/lpwan/Make.defs
+++ b/drivers/wireless/lpwan/sx126x/Make.defs
@@ -1,5 +1,5 @@
 ############################################################################
-# drivers/wireless/lpwan/Make.defs
+# drivers/wireless/lpwan/sx126x/Make.defs
 #
 # SPDX-License-Identifier: Apache-2.0
 #
@@ -20,10 +20,16 @@
 #
 ############################################################################
 
-# Include nothing if LPWAN is disabled
+# Include SX126X drivers into the build
 
-ifeq ($(CONFIG_DRIVERS_LPWAN),y)
+ifeq ($(CONFIG_LPWAN_SX126X),y)
 
-include wireless/lpwan/sx127x/Make.defs
+CSRCS += sx126x.c
 
-endif # CONFIG_DRIVERS_LPWAN
+# Include SX126X build support
+
+DEPPATH += --dep-path wireless$(DELIM)lpwan$(DELIM)sx126x
+VPATH += :wireless$(DELIM)lpwan$(DELIM)sx126x
+CFLAGS += 
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)wireless$(DELIM)lpwan$(DELIM)sx126x
+
+endif # CONFIG_LPWAN_SX126X
diff --git a/drivers/wireless/lpwan/sx126x/sx126x.c 
b/drivers/wireless/lpwan/sx126x/sx126x.c
new file mode 100644
index 0000000000..a6b8f11aea
--- /dev/null
+++ b/drivers/wireless/lpwan/sx126x/sx126x.c
@@ -0,0 +1,1407 @@
+/****************************************************************************
+ * drivers/wireless/lpwan/sx126x/sx126x.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 "sx126x.h"
+
+#include <nuttx/arch.h>
+#include <nuttx/config.h>
+
+#include <debug.h>
+#include <errno.h>
+#include <nuttx/mutex.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/wireless/ioctl.h>
+#include <sched.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/endian.h>
+#include <unistd.h>
+#include <nuttx/wireless/lpwan/sx126x.h>
+#include <nuttx/wqueue.h>
+
+/****************************************************************************
+ * Private prototypes for file operations
+ ****************************************************************************/
+
+static int sx126x_open(FAR struct file *filep);
+
+static int sx126x_close(FAR struct file *filep);
+
+static ssize_t sx126x_read(FAR struct file *filep,
+                           FAR char *buffer,
+                           size_t buflen);
+
+static ssize_t sx126x_write(FAR struct file *filep,
+                            FAR const char *buf,
+                            size_t buflen);
+
+static int sx126x_ioctl(FAR struct file *filep,
+                        int cmd,
+                        unsigned long arg);
+
+/****************************************************************************
+ * Private data types
+ ****************************************************************************/
+
+struct sx126x_dev_s
+{
+  struct spi_dev_s *spi;
+  const struct sx126x_lower_s *lower;
+  uint8_t times_opened;
+  mutex_t lock;                           /* Only let one user in at a time */
+  sem_t rx_sem;
+  sem_t tx_sem;
+  uint16_t irqbits;
+
+  /* Hardware settings */
+
+  bool invert_iq;
+
+  /* Common settings */
+
+  uint8_t payload_len;
+  enum sx126x_packet_type_e packet_type;  /* This will decide what modulation 
to use */
+  uint32_t frequency_hz;
+  uint8_t power;
+  uint16_t preambles;
+
+  /* LoRa settings */
+
+  enum sx126x_lora_sf_e lora_sf;
+  enum sx126x_lora_bw_e lora_bw;
+  enum sx126x_lora_cr_e lora_cr;
+  bool lora_crc;
+  bool lora_fixed_header;
+  bool low_datarate_optimization;
+  uint8_t syncword[SX126X_REG_SYNCWORD_LEN];
+
+  /* Interrupt handling */
+
+  uint16_t irq_mask;
+  struct work_s irq0_work;
+};
+
+enum sx126x_cmd_status
+{
+  SX126X_STATUS_RESERVED, SX126X_STATUS_RFU, SX126X_STATUS_DATA_AVAILABLE,
+  SX126X_STATUS_TIMEOUT, SX126X_STATUS_ERROR, SX126X_STATUS_EXECUTE_FAIL,
+  SX126X_STATUS_TX_DONE
+};
+
+enum sx126x_chip_mode
+{
+  SX126X_MODE_UNUSED, SX126X_MODE_RFU, SX126X_MODE_STBY_RC,
+  SX126X_MODE_STBY_XOSC, SX126X_MODE_FS, SX126X_MODE_RX, SX126X_MODE_TX
+};
+
+struct sx126x_status_s
+{
+  enum sx126x_cmd_status cmd;
+  enum sx126x_chip_mode mode;
+};
+
+static const struct file_operations sx126x_ops =
+{
+  sx126x_open,
+  sx126x_close,
+  sx126x_read,
+  sx126x_write,
+  NULL,
+  sx126x_ioctl,
+  NULL,
+  NULL
+};
+
+/****************************************************************************
+ * Globals
+ ****************************************************************************/
+
+FAR struct sx126x_dev_s g_sx126x_devices[SX126X_MAX_DEVICES];
+
+/****************************************************************************
+ * Private prototypes
+ ****************************************************************************/
+
+/* SPI and control **********************************************************/
+
+static void sx126x_command(FAR struct sx126x_dev_s *dev,
+                           uint8_t cmd,
+                           FAR const uint8_t *params,
+                           size_t paramslen,
+                           FAR uint8_t *returns);
+
+static void sx126x_reset(FAR struct sx126x_dev_s *dev);
+
+static void sx126x_get_status(FAR struct sx126x_dev_s *dev,
+                              FAR struct sx126x_status_s *status);
+
+static void sx126x_spi_lock(FAR struct sx126x_dev_s *dev);
+
+static void sx126x_spi_unlock(FAR struct sx126x_dev_s *dev);
+
+static void sx126x_write_register(FAR struct sx126x_dev_s *dev,
+                                  uint16_t address,
+                                  uint8_t *data,
+                                  size_t data_length);
+
+/* Operational modes functions **********************************************/
+
+static void sx126x_set_standby(FAR struct sx126x_dev_s *dev,
+                               enum sx126x_standby_mode_e mode);
+
+static void sx126x_set_tx(FAR struct sx126x_dev_s *dev,
+                          uint32_t timeout);
+
+static void sx126x_set_rx(FAR struct sx126x_dev_s *dev, uint32_t timeout);
+
+static void sx126x_set_cad(struct sx126x_dev_s *dev);
+
+static void sx126x_set_tx_continuous_wave(FAR struct sx126x_dev_s *dev);
+
+static void sx126x_set_regulator_mode(FAR struct sx126x_dev_s *dev,
+                                      enum sx126x_regulator_mode_e mode);
+
+static void sx126x_set_pa_config(FAR struct sx126x_dev_s *dev,
+                                 enum sx126x_device_e model,
+                                 uint8_t hpmax,
+                                 uint8_t padutycycle);
+
+static void sx126x_set_tx_infinite_preamble(FAR struct sx126x_dev_s *dev);
+
+/* DIO and IRQ control functions ********************************************/
+
+static void sx126x_set_dio_irq_params(FAR struct sx126x_dev_s *dev,
+                                      uint16_t irq_mask,
+                                      uint16_t dio1_mask,
+                                      uint16_t dio2_mask,
+                                      uint16_t dio3_mask);
+
+static void sx126x_set_dio2_as_rf_switch(FAR struct sx126x_dev_s *dev,
+                                         bool enable);
+
+static void sx126x_set_dio3_as_tcxo(FAR struct sx126x_dev_s *dev,
+                                    enum sx126x_tcxo_voltage_e voltage,
+                                    uint32_t delay);
+
+static void sx126x_get_irq_status(FAR struct sx126x_dev_s *dev,
+                                  FAR uint16_t *irqstatus);
+
+static void sx126x_clear_irq_status(FAR struct sx126x_dev_s *dev,
+                                    uint16_t clearbits);
+
+/* RF Modulation and Packet-Related Functions *******************************/
+
+static void sx126x_set_packet_params_lora(FAR struct sx126x_dev_s *dev,
+                                          FAR struct
+                                          sx126x_packetparams_lora_s *
+                                          pktparams);
+
+static void sx126x_set_modulation_params_lora(FAR struct sx126x_dev_s *dev,
+                                              FAR struct
+                                              sx126x_modparams_lora_s *
+                                              modparams);
+
+static void sx126x_set_buffer_base_address(FAR struct sx126x_dev_s *dev,
+                                           uint8_t tx,
+                                           uint8_t rx);
+
+static void sx126x_set_tx_params(FAR struct sx126x_dev_s *dev, uint8_t power,
+                                 enum sx126x_ramp_time_e ramp_time);
+
+static void sx126x_set_packet_type(FAR struct sx126x_dev_s *dev,
+                                   enum sx126x_packet_type_e type);
+
+static void sx126x_set_rf_frequency(FAR struct sx126x_dev_s *dev,
+                                    uint32_t frequency_hz);
+
+/* Communication status information *****************************************/
+
+static void sx126x_get_rssi_inst(FAR struct sx126x_dev_s *dev,
+                                 FAR int32_t *dbm);
+
+static void sx126x_get_rx_buffer_status(FAR struct sx126x_dev_s *dev,
+                                        uint8_t *status,
+                                        uint8_t *payload_len,
+                                        uint8_t *rx_buff_offset);
+
+/* Registers and buffer *****************************************************/
+
+static void sx126x_write_register(FAR struct sx126x_dev_s *dev,
+                                  uint16_t address,
+                                  uint8_t *data,
+                                  size_t data_length);
+
+static void sx126x_write_buffer(FAR struct sx126x_dev_s *dev,
+                                uint8_t offset,
+                                FAR const uint8_t *payload,
+                                uint8_t len);
+
+static void sx126x_read_buffer(FAR struct sx126x_dev_s *dev,
+                               uint8_t offset,
+                               FAR uint8_t *payload,
+                               uint8_t len);
+
+/* Register settings ********************************************************/
+
+static void sx126x_set_syncword(FAR struct sx126x_dev_s *dev,
+                                uint8_t *syncword,
+                                uint8_t syncword_length);
+
+/* Driver specific **********************************************************/
+
+static int sx126x_init(FAR struct sx126x_dev_s *dev);
+
+static int sx126x_deinit(FAR struct sx126x_dev_s *dev);
+
+static int sx126x_setup_radio(FAR struct sx126x_dev_s *dev);
+
+static void sx126x_set_defaults(FAR struct sx126x_dev_s *dev);
+
+/* Interrupt handlers *******************************************************/
+
+static int sx126x_irq0handler(int irq, FAR void *context, FAR void *arg);
+
+static inline int sx126x_attachirq0(FAR struct sx126x_dev_s *dev, xcpt_t isr,
+  FAR void *arg);
+
+static void sx126x_isr0_process(FAR void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/* File operations **********************************************************/
+
+static int sx126x_open(FAR struct file *filep)
+{
+  int ret = 0;
+
+  /* Get device */
+
+  struct sx126x_dev_s *dev;
+  dev = filep->f_inode->i_private;
+  wlinfo("Opening SX126x %d", dev->lower->dev_number);
+
+  /* Lock dev */
+
+  ret = nxmutex_lock(&dev->lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Only one can open this dev at a time */
+
+  if (dev->times_opened > 0)
+    {
+      ret = -EBUSY;
+      goto exit_err;
+    }
+
+  /* Initialize */
+
+  ret = sx126x_init(dev);
+  if (ret != 0)
+    {
+      goto exit_err;
+    }
+
+  /* Success */
+
+  dev->times_opened++;
+  ret = OK;
+
+exit_err:
+  nxmutex_unlock(&dev->lock);
+  return ret;
+}
+
+static int sx126x_close(FAR struct file *filep)
+{
+  int ret = 0;
+
+  /* Get device */
+
+  struct sx126x_dev_s *dev;
+  dev = filep->f_inode->i_private;
+  wlinfo("Closing SX126x %d", dev->lower->dev_number);
+
+  /* Lock */
+
+  ret = nxmutex_lock(&dev->lock);
+  if (ret < 0)
+    {
+      goto exit_err;
+    }
+
+  /* De-init */
+
+  ret = sx126x_deinit(dev);
+  if (ret != 0)
+    {
+      goto exit_err;
+    }
+
+  /* Success */
+
+  if (dev->times_opened > 0)
+    {
+      dev->times_opened--; /* Do not let this wrap around. */
+      ret = OK;
+    }
+
+exit_err:
+  nxmutex_unlock(&dev->lock);
+  return ret;
+}
+
+static ssize_t sx126x_read(FAR struct file *filep,
+                           FAR char *buf,
+                           size_t buflen)
+{
+  int ret = 0;
+  if (buf == NULL || buflen < 1)
+    {
+      return -EINVAL;
+    }
+
+  /* Get device */
+
+  struct sx126x_dev_s *dev;
+  dev = filep->f_inode->i_private;
+
+  nxmutex_lock(&dev->lock);
+
+  printf("Reading\n");
+
+  /* Get header */
+
+  struct sx126x_read_header_s *header = (struct sx126x_read_header_s *)buf;
+
+  /* Pre-RX setup */
+
+  sx126x_spi_lock(dev);
+  dev->irq_mask = SX126X_IRQ_RXDONE_MASK | SX126X_IRQ_CRCERR_MASK;
+  ret = sx126x_setup_radio(dev);
+  if (ret != 0)
+    {
+      goto sx126x_rx_abort;
+    }
+
+  /* RX mode */
+
+  sx126x_set_rx(dev, SX126X_NO_TIMEOUT);
+  sx126x_spi_unlock(dev);
+
+  /* Wait for a packet */
+
+  nxsem_wait(&dev->rx_sem);
+
+  /* Get payload */
+
+  uint8_t status = 0;
+  uint8_t offset = 0;
+
+  sx126x_spi_lock(dev);
+  sx126x_get_rx_buffer_status(dev, &status,
+                              &header->payload_length,
+                              &offset);
+  sx126x_read_buffer(dev, offset, header->payload,
+                     header->payload_length);
+  sx126x_spi_unlock(dev);
+
+  /* Get CRC check */
+
+  header->crc_error = dev->irqbits & SX126X_IRQ_CRCERR_MASK;
+
+  /* Exit */
+
+  sx126x_rx_abort:
+
+  nxmutex_unlock(&dev->lock);
+
+  return 1;
+}
+
+static ssize_t sx126x_write(FAR struct file *filep,
+                            FAR const char *buf,
+                            size_t buflen)
+{
+  int ret = 0;
+
+  /* Get device */
+
+  struct sx126x_dev_s *dev;
+  dev = filep->f_inode->i_private;
+
+  if (buf == NULL || buflen < 1)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&dev->lock);
+  sx126x_spi_lock(dev);
+
+  /* Data */
+
+  dev->payload_len = buflen;
+  sx126x_write_buffer(dev, 0, (uint8_t *)buf, buflen);
+
+  /* Pre-TX setup */
+
+  dev->irq_mask = SX126X_IRQ_TXDONE_MASK;
+  ret = sx126x_setup_radio(dev);
+  if (ret != 0)
+    {
+      sx126x_spi_unlock(dev);
+      goto sx126x_tx_abort;
+    }
+
+  /* TX */
+
+  sx126x_set_tx(dev, 0);
+
+  sx126x_spi_unlock(dev);
+
+  /* Wait for transmitting operations to be finished */
+
+  wlinfo("TXing");
+  ret = nxsem_wait(&dev->tx_sem);
+
+  sx126x_tx_abort:
+
+  nxmutex_unlock(&dev->lock);
+  return ret;
+}
+
+static int sx126x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+  int ret = 0;
+
+  /* Get device */
+
+  struct sx126x_dev_s *dev;
+  dev = filep->f_inode->i_private;
+  wlinfo("IOCTL cmd %d arg %u SX126x dev_number %d",
+    cmd,
+    *(FAR uint32_t *)((uintptr_t)arg),
+    dev->lower->dev_number);
+
+  /* Lock */
+
+  ret = nxmutex_lock(&dev->lock);
+  if (ret < 0)
+    {
+      goto exit_err;
+    }
+
+  /* Do thing */
+
+  switch (cmd)
+    {
+      /* Set radio freq. Takes uint32_t *frequency in Hz */
+
+      case WLIOC_SETRADIOFREQ:
+        {
+          FAR uint32_t *freq_ptr = (FAR uint32_t *)((uintptr_t)arg);
+          DEBUGASSERT(freq_ptr != NULL);
+
+          dev->frequency_hz = *freq_ptr;
+          break;
+        }
+
+      /* Get radio freq. Sets uint32_t *frequency in Hz */
+
+      case WLIOC_GETRADIOFREQ:
+        {
+          FAR uint32_t *freq_ptr = (FAR uint32_t *)((uintptr_t)arg);
+          DEBUGASSERT(freq_ptr != NULL);
+
+           *freq_ptr = dev->frequency_hz;
+          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);
+
+          dev->power = *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 = dev->power;
+          break;
+        }
+
+      /* TODO: Integration with new common IOCTL API */
+
+      /* Driver specific IOCTL */
+
+      /* Lora config */
+
+      case SX126XIOC_LORACONFIGSET:
+        {
+          FAR struct sx126x_lora_config_s *ptr =
+            (FAR struct sx126x_lora_config_s *)((uintptr_t)arg);
+          DEBUGASSERT(ptr != NULL);
+
+          /* Modulation params */
+
+          dev->lora_sf = ptr->modulation.spreading_factor;
+          dev->lora_bw = ptr->modulation.bandwidth;
+          dev->lora_cr = ptr->modulation.coding_rate;
+          dev->low_datarate_optimization =
+            ptr->modulation.low_datarate_optimization;
+
+          /* Packet params */
+
+          dev->lora_crc = ptr->packet.crc_enable;
+          dev->lora_fixed_header =
+            ptr->packet.fixed_length_header;
+          dev->payload_len = ptr->packet.payload_length;
+          dev->invert_iq = ptr->packet.invert_iq;
+          dev->preambles = ptr->packet.preambles;
+
+          break;
+        }
+    }
+
+  /* Success */
+
+  ret = OK;
+
+exit_err:
+  nxmutex_unlock(&dev->lock);
+  return ret;
+}
+
+uint32_t sx126x_convert_freq_in_hz_to_pll_step(uint32_t freq_in_hz)
+{
+  uint32_t  steps_int;
+  uint32_t  steps_frac;
+
+  steps_int     = freq_in_hz / SX126X_PLL_STEP_SCALED;
+  steps_frac    = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
+
+  return (steps_int <<
+    SX126X_PLL_STEP_SHIFT_AMOUNT) + (((steps_frac <<
+    SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >>
+                                     1)) / SX126X_PLL_STEP_SCALED);
+}
+
+/* Operational modes functions **********************************************/
+
+static void sx126x_set_standby(FAR struct sx126x_dev_s *dev,
+                               enum sx126x_standby_mode_e mode)
+{
+  sx126x_command(dev, SX126X_SETSTANDBY, (uint8_t *)&mode,
+                 SX126X_SETSTANDBY_PARAMS, NULL);
+}
+
+static void sx126x_set_tx(FAR struct sx126x_dev_s *dev, uint32_t timeout)
+{
+  /* Convert timeout to BE24 */
+
+  timeout = htobe32(timeout << 8);
+
+  sx126x_command(dev, SX126X_SETTX, (uint8_t *)&timeout, SX126X_SETTX_PARAMS,
+                 NULL);
+}
+
+static void sx126x_set_rx(FAR struct sx126x_dev_s *dev, uint32_t timeout)
+{
+  /* Convert timeout to BE24 */
+
+  timeout = htobe32(timeout << 8);
+
+  sx126x_command(dev, SX126X_SETRX, (uint8_t *)&timeout, SX126X_SETRX_PARAMS,
+                 NULL);
+}
+
+static void sx126x_stop_timer_on_preamble(FAR struct sx126x_dev_s *dev,
+                                          bool enable)
+{
+  sx126x_command(dev, SX126X_STOPTIMERONPREAMBLE, (uint8_t *)&enable,
+                 SX126X_STOPTIMERONPREAMBLE_PARAMS, NULL);
+}
+
+static void sx126x_set_rx_duty_cycle(FAR struct sx126x_dev_s *dev,
+                                     uint32_t rx_period,
+                                     uint32_t sleep_period)
+{
+  uint8_t params[SX126X_SETRXDUTYCYCLE_PARAMS];
+
+  rx_period     = htobe32(rx_period << 8);
+  sleep_period  = htobe32(sleep_period << 8);
+
+  memcpy(params + SX126X_SETRXDUTYCYCLE_RXPERIOD_PARAM,
+         (uint8_t *)&rx_period,
+         SX126X_SETRXDUTYCYCLE_RXPERIOD_PARAMS);
+  memcpy(params + SX126X_SETRXDUTYCYCLE_SLEEPPERIOD_PARAM,
+         (uint8_t *)&sleep_period,
+         SX126X_SETRXDUTYCYCLE_SLEEPPERIOD_PARAMS);
+
+  sx126x_command(dev, SX126X_SETRXDUTYCYCLE, params,
+                 SX126X_SETRXDUTYCYCLE_PARAMS, NULL);
+}
+
+static void sx126x_set_cad(FAR struct sx126x_dev_s *dev)
+{
+  sx126x_command(dev, SX126X_SETCAD, NULL, 0, NULL);
+}
+
+static void sx126x_set_tx_continuous_wave(FAR struct sx126x_dev_s *dev)
+{
+  sx126x_command(dev, SX126X_SETTXCONTINUOUSWAVE, NULL, 0, NULL);
+}
+
+static void sx126x_set_regulator_mode(FAR struct sx126x_dev_s *dev,
+                                      enum sx126x_regulator_mode_e mode)
+{
+  sx126x_command(dev, SX126X_SETREGULATORMODE, (uint8_t *)&mode,
+                 SX126X_SETREGULATORMODE_PARAMS, NULL);
+}
+
+/* Caution! Exceeding the limits listed in DS_SX1261/2 V2.1
+ * 13.1.14.1 may cause irreversible damage to the device
+ */
+
+static void sx126x_set_pa_config(FAR struct sx126x_dev_s *dev,
+                                 enum sx126x_device_e model, uint8_t hpmax,
+                                 uint8_t padutycycle)
+{
+  uint8_t params[SX126X_SETPACONFIG_PARMS];
+
+  memset(params, 0, SX126X_SETPACONFIG_PARMS);
+
+  params[SX126X_SETPACONFIG_PADUTYCYCLE_PARAM]  = padutycycle;
+  params[SX126X_SETPACONFIG_HPMAX_PARAM]        = hpmax;
+  params[SX126X_SETPACONFIG_DEVICESEL_PARAM]    = model;
+  params[SX126X_SETPACONFIG_PALUT_PARAM]        = 0x01;
+
+  sx126x_command(dev, SX126X_SETPACONFIG, params, SX126X_SETPACONFIG_PARMS,
+                 NULL);
+}
+
+static void sx126x_set_tx_infinite_preamble(FAR struct sx126x_dev_s *dev)
+{
+  sx126x_command(dev, SX126X_SETTXINFINITEPREAMBLE, NULL, 0, NULL);
+}
+
+static void sx126x_set_rx_tx_fallback_mode(FAR struct sx126x_dev_s *dev,
+                                           enum sx126x_fallback_mode_e
+                                           fallback)
+{
+  sx126x_command(dev, SX126X_SETRXTXFALLBACKMODE, (uint8_t *)&fallback,
+                 SX126X_SETRXTXFALLBACKMODE_PARAMS, NULL);
+}
+
+/* DIO and IRQ control functions */
+
+static void sx126x_set_dio_irq_params(FAR struct sx126x_dev_s *dev,
+                                      uint16_t irq_mask, uint16_t dio1_mask,
+                                      uint16_t dio2_mask, uint16_t dio3_mask)
+{
+  irq_mask  = htobe16(irq_mask);
+  dio1_mask = htobe16(dio1_mask);
+  dio2_mask = htobe16(dio2_mask);
+  dio3_mask = htobe16(dio3_mask);
+
+  uint8_t params[SX126X_SETDIOIRQPARAMS_PARAMS];
+
+  memcpy(params + SX126X_SETDIOIRQPARAMS_IRQMASK_PARAM, &irq_mask,
+         SX126X_SETDIOIRQPARAMS_IRQMASK_PARAMS);
+
+  memcpy(params + SX126X_SETDIOIRQPARAMS_DIO1MASK_PARAM, &dio1_mask,
+         SX126X_SETDIOIRQPARAMS_DIO1MASK_PARAMS);
+
+  memcpy(params + SX126X_SETDIOIRQPARAMS_DIO2MASK_PARAM, &dio2_mask,
+         SX126X_SETDIOIRQPARAMS_DIO2MASK_PARAMS);
+
+  memcpy(params + SX126X_SETDIOIRQPARAMS_DIO3MASK_PARAM, &dio3_mask,
+         SX126X_SETDIOIRQPARAMS_DIO3MASK_PARAMS);
+
+  sx126x_command(dev, SX126X_SETDIOIRQPARAMS, params,
+                 SX126X_SETDIOIRQPARAMS_PARAMS, NULL);
+}
+
+static void sx126x_set_dio2_as_rf_switch(FAR struct sx126x_dev_s *dev,
+                                         bool enable)
+{
+  sx126x_command(dev, SX126X_SETDIO2RFSWCTRL, (uint8_t *)&enable,
+                 SX126X_SETDIO2RFSWCTRL_PARAMS, NULL);
+}
+
+static void sx126x_set_dio3_as_tcxo(FAR struct sx126x_dev_s *dev,
+                                    enum sx126x_tcxo_voltage_e voltage,
+                                    uint32_t delay)
+{
+  uint8_t params[SX126X_SETDIO3TCXOCTRL_PARAMS];
+
+  params[SX126X_SETDIO3TCXOCTRL_TCXO_V_PARAM] = voltage;
+
+  /* Convert delay to 24 bit and convert to BE */
+
+  delay = htobe32(delay << 8);
+  memcpy(params + SX126X_SETDIO3TCXOCTRL_DELAY_PARAM, &delay,
+         SX126X_SETDIO3TCXOCTRL_DELAY_PARAMS);
+
+  sx126x_command(dev, SX126X_SETDIO3TCXOCTRL, params,
+                 SX126X_SETDIO3TCXOCTRL_PARAMS, NULL);
+}
+
+static void sx126x_get_irq_status(FAR struct sx126x_dev_s *dev,
+                                  FAR uint16_t *irqstatus)
+{
+  uint8_t returns[SX126X_GETIRQSTATUS_RETURNS];
+
+  sx126x_command(dev, SX126X_GETIRQSTATUS,
+                 NULL, SX126X_GETIRQSTATUS_RETURNS,
+                 returns);
+
+  uint16_t bits;
+  memcpy(&bits, returns +
+         SX126X_GETIRQSTATUS_IRQSTATUS_RETURN,
+         SX126X_GETIRQSTATUS_IRQSTATUS_RETURNS);
+
+  *irqstatus = be16toh(bits);
+}
+
+static void sx126x_clear_irq_status(FAR struct sx126x_dev_s *dev,
+                                    uint16_t clearbits)
+{
+  uint8_t params[SX126X_CLEARIRQSTATUS_PARAMS];
+
+  clearbits = htobe16(clearbits);
+  memcpy(params + SX126X_CLEARIRQSTATUS_CLEAR_PARAM,
+         &clearbits,
+         SX126X_CLEARIRQSTATUS_CLEAR_PARAMS);
+
+  sx126x_command(dev, SX126X_CLEARIRQSTATUS, params,
+                 SX126X_CLEARIRQSTATUS_PARAMS,
+                 NULL);
+}
+
+/* RF Modulation and Packet-Related Functions *******************************/
+
+static void sx126x_set_packet_params_lora(FAR struct sx126x_dev_s *dev,
+                                          FAR struct
+                                          sx126x_packetparams_lora_s *
+                                          pktparams)
+{
+  uint8_t params[SX126X_SETPACKETPARMS_PARAMS];
+
+  memset(params, 0, SX126X_SETPACKETPARMS_PARAMS);
+
+  uint16_t preambles = htobe16(pktparams->preambles);
+  memcpy(params + SX126X_PKTPARAM1_LORA_PREAMBLELEN_PARAM, &preambles,
+         SX126X_PKTPARAM1_LORA_PREAMBLELEN_PARAMS);
+
+  params[SX126X_PKTPARAM3_LORA_HEADERTYPE_PARAM] =
+    pktparams->fixed_length_header;
+  params[SX126X_PKTPARAM4_LORA_PAYLOADLEN_PARAM] =
+    pktparams->payload_length;
+  params[SX126X_PKTPARAM5_LORA_CRCTYPE_PARAM]   = pktparams->crc_enable;
+  params[SX126X_PKTPARAM6_LORA_INVERTIQ_PARAM]  = pktparams->invert_iq;
+
+  sx126x_command(dev, SX126X_SETPACKETPARMS, params,
+                 SX126X_SETPACKETPARMS_PARAMS, NULL);
+}
+
+static void sx126x_set_modulation_params_lora(FAR struct sx126x_dev_s *dev,
+                                              FAR struct
+                                              sx126x_modparams_lora_s *
+                                              modparams)
+{
+  uint8_t params[SX126X_SETMODULATIONPARAMS_PARAMS];
+
+  memset(params, 0, SX126X_SETMODULATIONPARAMS_PARAMS);
+
+  params[SX126X_MODPARAM1_LORA_SF_PARAM] =
+    modparams->spreading_factor;
+  params[SX126X_MODPARAM2_LORA_BW_PARAM] =
+    modparams->bandwidth;
+  params[SX126X_MODPARAM3_LORA_CR_PARAM] =
+    modparams->coding_rate;
+  params[SX126X_MODPARAM4_LORA_LOWDATRATE_OPTI_PARAM] =
+    modparams->low_datarate_optimization;
+
+  sx126x_command(dev, SX126X_SETMODULATIONPARAMS, params,
+                 SX126X_SETMODULATIONPARAMS_PARAMS, NULL);
+}
+
+static void sx126x_set_buffer_base_address(FAR struct sx126x_dev_s *dev,
+                                           uint8_t tx, uint8_t rx)
+{
+  uint8_t params[SX126X_SETBUFFERBASEADDRESS_PARAMS];
+
+  memset(params, 0, SX126X_SETBUFFERBASEADDRESS_PARAMS);
+
+  params[SX126X_SETBUFFERBASEADDRESS_TX_PARAM]  = tx;
+  params[SX126X_SETBUFFERBASEADDRESS_RX_PARAM]  = rx;
+
+  sx126x_command(dev, SX126X_SETBUFFERBASEADDRESS, params,
+                 SX126X_SETBUFFERBASEADDRESS_PARAMS, NULL);
+}
+
+static void sx126x_set_tx_params(FAR struct sx126x_dev_s *dev, uint8_t power,
+                                 enum sx126x_ramp_time_e ramp_time)
+{
+  uint8_t params[SX126X_SETTXPARMS_PARAMS];
+
+  memset(params, 0, SX126X_SETTXPARMS_PARAMS);
+
+  params[SX126X_SETTXPARMS_RAMPTIME_PARAM]  = ramp_time;
+  params[SX126X_SETTXPARMS_POWER_PARAM]     = power;
+
+  sx126x_command(dev, SX126X_SETTXPARMS, params, SX126X_SETTXPARMS_PARAMS,
+                 NULL);
+}
+
+static void sx126x_set_packet_type(FAR struct sx126x_dev_s *dev,
+                                   enum sx126x_packet_type_e type)
+{
+  sx126x_command(dev, SX126X_SETPACKETTYPE, (uint8_t *)&type,
+                 SX126X_SETPACKETTYPE_PARAMS, NULL);
+}
+
+static void sx126x_set_rf_frequency(FAR struct sx126x_dev_s *dev,
+                                    uint32_t frequency_hz)
+{
+  uint32_t corrected_freq =
+    sx126x_convert_freq_in_hz_to_pll_step(frequency_hz);
+
+  corrected_freq = htobe32(corrected_freq);
+
+  sx126x_command(dev, SX126X_SETRFFREQUENCY, (uint8_t *)&corrected_freq,
+                 SX126X_SETRFFREQUENCY_PARAMS, NULL);
+}
+
+static void sx126x_set_lora_symb_num_timout(FAR struct sx126x_dev_s *dev,
+                                            uint8_t symbnum)
+{
+  sx126x_command(dev, SX126X_SETLORASYMBNUMTIMEOUT, &symbnum,
+                 SX126X_SETLORASYMBNUMTIMEOUT_PARAMS, NULL);
+}
+
+/* Communication status information *****************************************/
+
+static void sx126x_get_status(FAR struct sx126x_dev_s *dev,
+                              FAR struct sx126x_status_s *status)
+{
+  /* NOP param to shift out result */
+
+  uint8_t   parms[1] = {
+    0x00
+  };
+
+  uint8_t   rets[1];
+
+  /* Get, mask and shift result into readable mode numbers */
+
+  sx126x_command(dev, SX126X_CMD_GETSTATUS, parms, sizeof(parms), rets);
+  status->mode = (rets[0] & SX126X_STATUS_CHIPMODE_MASK) >>
+                 SX126X_STATUS_CHIPMODE_SHIFT;
+  status->cmd = (rets[0] & SX126X_STATUS_CMD_MASK) >>
+                SX126X_STATUS_CMD_SHIFT;
+}
+
+static void sx126x_get_rssi_inst(FAR struct sx126x_dev_s *dev,
+                                 FAR int32_t *dbm)
+{
+  uint8_t rets[SX126X_GETRSSIINST_RETURNS];
+
+  sx126x_command(dev, SX126X_GETRSSIINST, NULL, SX126X_GETRSSIINST_RETURNS,
+                 rets);
+
+  /* Calculate dBm from returns */
+
+  int32_t rssi = rets[SX126X_GETRSSIINST_RSSI_RETURN];
+  (*dbm) = -rssi / 2.0;
+}
+
+static void sx126x_get_rx_buffer_status(FAR struct sx126x_dev_s *dev,
+  uint8_t *status,
+  uint8_t *payload_len,
+  uint8_t *rx_buff_offset)
+{
+  uint8_t returns[SX126X_GETRXBUFFERSTATUS_RETURNS];
+
+  sx126x_command(dev, SX126X_GETRXBUFFERSTATUS,
+  NULL,
+  SX126X_GETRXBUFFERSTATUS_RETURNS,
+  returns);
+
+  *status = returns[SX126X_GETRXBUFFERSTATUS_STATUS_RETURN];
+  *payload_len = returns[SX126X_GETRXBUFFERSTATUS_PAYLOAD_LEN_RETURN];
+  *rx_buff_offset = returns[SX126X_GETRXBUFFERSTATUS_RX_START_PTR_RETURN];
+}
+
+/* Lower hardware control ***************************************************/
+
+static void sx126x_reset(FAR struct sx126x_dev_s *dev)
+{
+  dev->lower->reset();
+}
+
+/* SPI Communication ********************************************************/
+
+static void sx126x_select(FAR struct sx126x_dev_s *dev)
+{
+  SPI_SELECT(dev->spi, SPIDEV_LPWAN(dev->lower->dev_number), true);
+}
+
+static void sx126x_deselect(FAR struct sx126x_dev_s *dev)
+{
+  SPI_SELECT(dev->spi, SPIDEV_LPWAN(dev->lower->dev_number), false);
+}
+
+static void sx126x_spi_lock(FAR struct sx126x_dev_s *dev)
+{
+  struct spi_dev_s *spi = dev->spi;
+
+  SPI_LOCK(spi, true);
+  SPI_SETBITS(spi, 8);
+  SPI_SETMODE(spi, SPIDEV_MODE0);
+  SPI_SETFREQUENCY(spi, SX126X_SPI_SPEED);
+}
+
+static void sx126x_spi_unlock(FAR struct sx126x_dev_s *dev)
+{
+  SPI_LOCK(dev->spi, false);
+}
+
+static void sx126x_command(FAR struct sx126x_dev_s *dev, uint8_t cmd,
+                           const FAR uint8_t *params, size_t paramslen,
+                           FAR uint8_t *returns)
+{
+  sx126x_select(dev);
+
+  /* First send the command. This does not return anything.
+   * "RFU" according the manual
+   */
+
+  SPI_SEND(dev->spi, cmd);
+
+  /* Send all the params and record the returning bytes */
+
+  for (size_t i = 0; i < paramslen; i++)
+    {
+      uint8_t param = SX126X_NOP;
+      if (params != NULL)
+        {
+          param = params[i];
+        }
+
+      uint8_t ret = SPI_SEND(dev->spi, param);
+
+      if (returns != NULL)
+        {
+          returns[i] = ret;
+        }
+    }
+
+  sx126x_deselect(dev);
+}
+
+/* Registers and buffer *****************************************************/
+
+static void sx126x_write_register(FAR struct sx126x_dev_s *dev,
+                                  uint16_t address,
+                                  uint8_t *data,
+                                  size_t data_length)
+{
+  sx126x_select(dev);
+
+  /* Send the opcode and address */
+
+  SPI_SEND(dev->spi, SX126X_WRITEREGISTER);
+  SPI_SEND(dev->spi, (uint8_t)(address >> 8));
+  SPI_SEND(dev->spi, (uint8_t)address);
+
+  /* Send data */
+
+  for (size_t i = 0; i < data_length; i++)
+    {
+      SPI_SEND(dev->spi, data[i]);
+    }
+
+  sx126x_deselect(dev);
+}
+
+static void sx126x_read_register(FAR struct sx126x_dev_s *dev,
+                                 uint16_t address,
+                                 uint8_t *data,
+                                 size_t data_length)
+{
+  sx126x_select(dev);
+
+  /* Send the opcode and address */
+
+  SPI_SEND(dev->spi, SX126X_WRITEREGISTER);
+  SPI_SEND(dev->spi, (uint8_t)(address >> 8));
+  SPI_SEND(dev->spi, (uint8_t)address);
+
+  /* Send data */
+
+  for (size_t i = 0; i < data_length; i++)
+    {
+      data[i] = SPI_SEND(dev->spi, SX126X_NOP);
+    }
+
+  sx126x_deselect(dev);
+}
+
+static void sx126x_write_buffer(FAR struct sx126x_dev_s *dev,
+                                uint8_t offset,
+                                FAR const uint8_t *payload,
+                                uint8_t len)
+{
+  sx126x_select(dev);
+
+  /* Command */
+
+  SPI_SEND(dev->spi, SX126X_WRITEBUFFER);
+
+  /* Offset */
+
+  SPI_SEND(dev->spi, offset);
+
+  /* Data */
+
+  for (size_t i = 0; i < len; i++)
+    {
+      SPI_SEND(dev->spi, payload[i]);
+    }
+
+  sx126x_deselect(dev);
+}
+
+static void sx126x_read_buffer(FAR struct sx126x_dev_s *dev,
+                               uint8_t offset,
+                               FAR uint8_t *payload,
+                               uint8_t len)
+{
+  sx126x_select(dev);
+
+  /* Command */
+
+  SPI_SEND(dev->spi, SX126X_READBUFFER);
+
+  /* Offset */
+
+  SPI_SEND(dev->spi, offset);
+
+  /* NOP */
+
+  SPI_SEND(dev->spi, SX126X_NOP);
+
+  /* Data */
+
+  for (size_t i = 0; i < len; i++)
+    {
+      payload[i] = SPI_SEND(dev->spi, SX126X_NOP);
+    }
+
+  sx126x_deselect(dev);
+}
+
+/* Register settings ********************************************************/
+
+static void sx126x_set_syncword(FAR struct sx126x_dev_s *dev,
+                                uint8_t *syncword,
+                                uint8_t syncword_length)
+{
+  if (syncword_length > SX126X_REG_SYNCWORD_LEN)
+    {
+      syncword_length = SX126X_REG_SYNCWORD_LEN;
+      wlerr("Syncword length was limited to the maximum 8 bytes");
+    }
+
+  sx126x_write_register(dev, SX126X_REG_SYNCWORD, syncword, syncword_length);
+}
+
+/* Driver specific **********************************************************/
+
+static int sx126x_init(FAR struct sx126x_dev_s *dev)
+{
+  sx126x_reset(dev);
+  sx126x_set_defaults(dev);
+  return 0;
+}
+
+static int sx126x_deinit(FAR struct sx126x_dev_s *dev)
+{
+  return 0;
+}
+
+static void sx126x_set_defaults(FAR struct sx126x_dev_s *dev)
+{
+  /* Hardware defaults */
+
+  dev->invert_iq = SX126X_DEFAULT_INVERT_IQ;
+
+  /* Common defaults */
+
+  dev->packet_type    = SX126X_DEFAULT_PACKET_TYPE;
+  dev->frequency_hz   = SX126X_DEFAULT_FREQ;
+  dev->power          = SX126X_DEFAULT_POWER;
+  dev->preambles      = SX126X_DEFAULT_LORA_PREAMBLES;
+
+  /* LoRa defaults */
+
+  dev->lora_sf                      = SX126X_DEFAULT_LORA_SF;
+  dev->lora_bw                      = SX126X_DEFAULT_LORA_BW;
+  dev->lora_cr                      = SX126X_DEFAULT_LORA_CR;
+  dev->lora_fixed_header            = SX126X_DEFAULT_LORA_FIXED_HEADER;
+  dev->lora_crc                     = SX126X_DEFAULT_LORA_CRC_EN;
+  dev->low_datarate_optimization    = SX126X_DEFAULT_LORA_LDO;
+
+  uint8_t newsyncword[] = SX126X_DEFAULT_SYNCWORD;
+  memcpy(dev->syncword, newsyncword, sizeof(dev->syncword));
+
+  /* GFSK defaults */
+}
+
+static int sx126x_setup_radio(FAR struct sx126x_dev_s *dev)
+{
+  /* Clear IRQ status */
+
+  sx126x_clear_irq_status(dev, 0xffff);
+
+  /* Set regulator */
+
+  sx126x_set_regulator_mode(dev, dev->lower->regulator_mode);
+
+  /* Set packet type */
+
+  sx126x_set_packet_type(dev, dev->packet_type);
+
+  /* Set RF frequency */
+
+  int illegal_freq = dev->lower->check_frequency(dev->frequency_hz);
+
+  if (illegal_freq)
+    {
+      wlerr("Board does not support %dHz", dev->frequency_hz);
+      return -1;
+    }
+
+  sx126x_set_rf_frequency(dev, dev->frequency_hz);
+
+  /* Set PA settings from lower */
+
+  uint8_t hp;
+  uint8_t dc;
+  enum sx126x_device_e model;
+  dev->lower->get_pa_values(&model, &hp, &dc);
+  sx126x_set_pa_config(dev, model, hp, dc);
+
+  /* Set TX params */
+
+  dev->lower->limit_tx_power(&dev->power); /* Limited by board */
+  sx126x_set_tx_params(dev, dev->power, dev->lower->tx_ramp_time);
+
+  /* Set base */
+
+  sx126x_set_buffer_base_address(dev, 0, 0);
+
+  /* Set params depending on packet type */
+
+  switch (dev->packet_type)
+    {
+      case (SX126X_PACKETTYPE_LORA):
+        {
+          /* Mod params */
+
+          struct sx126x_modparams_lora_s modparams = {
+            .spreading_factor           = dev->lora_sf,
+            .bandwidth                  = dev->lora_bw,
+            .coding_rate                = dev->lora_cr,
+            .low_datarate_optimization  = dev->low_datarate_optimization
+          };
+
+          sx126x_set_modulation_params_lora(dev, &modparams);
+
+          /* Packet params */
+
+          struct sx126x_packetparams_lora_s pktparams = {
+            .crc_enable          = dev->lora_crc,
+            .fixed_length_header = dev->lora_fixed_header,
+            .payload_length      = dev->payload_len,
+            .invert_iq           = dev->invert_iq,
+            .preambles           = dev->preambles
+          };
+
+          sx126x_set_packet_params_lora(dev, &pktparams);
+          break;
+        }
+
+      default:
+      break;
+    }
+
+  /* Sync word */
+
+  sx126x_set_syncword(dev, dev->syncword, sizeof(dev->syncword));
+
+  /* IRQ MASK */
+
+  sx126x_set_dio_irq_params(dev, dev->irq_mask,
+    dev->lower->masks.dio1_mask,
+    dev->lower->masks.dio2_mask,
+    dev->lower->masks.dio3_mask);
+
+  /* DIO 2 */
+
+  sx126x_set_dio2_as_rf_switch(dev, dev->lower->use_dio2_as_rf_sw);
+
+  /* DIO 3 */
+
+  sx126x_set_dio3_as_tcxo(dev, dev->lower->dio3_voltage,
+                          dev->lower->dio3_delay);
+  return 0;
+}
+
+/* Interrupt handling *******************************************************/
+
+static int sx126x_irq0handler(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct sx126x_dev_s *dev = (FAR struct sx126x_dev_s *)arg;
+
+  DEBUGASSERT(dev != NULL);
+
+  DEBUGASSERT(work_available(&dev->irq0_work));
+
+  return work_queue(HPWORK, &dev->irq0_work, sx126x_isr0_process, arg, 0);
+}
+
+static inline int sx126x_attachirq0(FAR struct sx126x_dev_s *dev, xcpt_t isr,
+  FAR void *arg)
+{
+  DEBUGASSERT(dev->lower->irq0attach != NULL);
+
+  return dev->lower->irq0attach(isr, arg);
+}
+
+static void sx126x_isr0_process(FAR void *arg)
+{
+  DEBUGASSERT(arg);
+
+  FAR struct sx126x_dev_s *dev = (FAR struct sx126x_dev_s *)arg;
+
+  wlinfo("SX126x ISR0 process triggered");
+
+  /* Get and clear IRQ bits */
+
+  sx126x_spi_lock(dev);
+  sx126x_get_irq_status(dev, &dev->irqbits);
+  sx126x_spi_unlock(dev);
+
+  wlinfo("IRQ status 0x%X", dev->irqbits);
+
+  /* On TX done */
+
+  if (dev->irqbits & SX126X_IRQ_TXDONE_MASK)
+    {
+      wlinfo("TX done");
+
+      /* Release writing threads */
+
+      nxsem_post(&dev->tx_sem);
+    }
+
+  /* On RX done */
+
+  if (dev->irqbits & SX126X_IRQ_RXDONE_MASK)
+    {
+      wlinfo("RX done");
+
+      nxsem_post(&dev->rx_sem);
+    }
+
+  /* On CAD done */
+
+  if (dev->irqbits & SX126X_IRQ_CADDONE_MASK)
+    {
+      wlinfo("CAD done");
+    }
+
+  /* On CAD detect */
+
+  if (dev->irqbits & SX126X_IRQ_CADDETECTED_MASK)
+    {
+      wlinfo("CAD detect");
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void sx126x_register(FAR struct spi_dev_s *spi,
+                     FAR const struct sx126x_lower_s *lower,
+                     const char *path)
+{
+  /* Register the dev using an unique dev_number,
+   * so multiple radios can be registered at once
+   */
+
+  if (lower->dev_number >= SX126X_MAX_DEVICES)
+    {
+      wlerr("SX126x dev_number %d is greater than \
+            allowed amount of SX126x devices",
+            lower->dev_number);
+      return;
+    }
+
+  struct sx126x_dev_s *dev;
+  dev = &g_sx126x_devices[lower->dev_number];
+  dev->lower   = lower;
+  dev->spi     = spi;
+
+  /* Initialize locks and semaphores */
+
+  nxmutex_init(&dev->lock);
+  nxsem_init(&dev->rx_sem, 0, 0);
+  nxsem_init(&dev->tx_sem, 0, 0);
+
+  sx126x_attachirq0(dev, sx126x_irq0handler, dev);
+
+  (void)register_driver(path, &sx126x_ops, 0666,
+                        dev);
+}
diff --git a/drivers/wireless/lpwan/sx126x/sx126x.h 
b/drivers/wireless/lpwan/sx126x/sx126x.h
new file mode 100644
index 0000000000..7d14c1fd74
--- /dev/null
+++ b/drivers/wireless/lpwan/sx126x/sx126x.h
@@ -0,0 +1,440 @@
+/****************************************************************************
+ * drivers/wireless/lpwan/sx126x/sx126x.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/* Currently this is an experimental version.
+ * Breaking changes might happen in the near future.
+ * All functions and definitions are accurately recreated
+ * from the official datasheet "DS_SX1261-2_V2_1"
+ */
+
+#ifndef __DRIVERS_WIRELESS_LPWAN_SX126X_SX126X_H
+#define __DRIVERS_WIRELESS_LPWAN_SX126X_SX126X_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <debug.h>
+#include <nuttx/config.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/irq.h>
+#include <nuttx/wireless/ioctl.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <endian.h>
+
+/****************************************************************************
+ * Settings
+ ****************************************************************************/
+
+/* Driver settings */
+
+#define SX126X_MAX_DEVICES          2
+#define SX126X_SPI_SPEED            500000
+
+/* LoRa defaults */
+
+#define SX126X_DEFAULT_LORA_SF                 SX126X_LORA_SF10
+#define SX126X_DEFAULT_LORA_BW                 SX126X_LORA_BW_125
+#define SX126X_DEFAULT_LORA_CR                 SX126X_LORA_CR_4_8
+#define SX126X_DEFAULT_LORA_CRC_EN             true
+#define SX126X_DEFAULT_LORA_FIXED_HEADER       false
+#define SX126X_DEFAULT_LORA_PREAMBLES          12
+#define SX126X_DEFAULT_LORA_LDO                false
+
+/* Common defaults */
+
+#define SX126X_DEFAULT_FREQ               869525000
+#define SX126X_DEFAULT_POWER              0x0e
+#define SX126X_DEFAULT_PACKET_TYPE        SX126X_PACKETTYPE_LORA
+#define SX126X_DEFAULT_SYNCWORD           {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00}
+
+/* Hardware defaults */
+
+#define SX126X_DEFAULT_INVERT_IQ          false
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register addresses *******************************************************/
+
+#define SX126X_REG_SYNCWORD       0x06C0 /* Start of Byte 0 - Byte 7 */
+#define SX126X_REG_SYNCWORD_LEN   8      /* Bytes */
+#define SX126X_REG_NODEADDR       0x06CD /* Node address to filter. Default 
0x00 */      
+#define SX126X_REG_BRDCASTADDR    0x06CE /* Broadcast address to filter. 
Default 0x00 */      
+#define SX126X_REG_CRC_INIT_MSB   0x06BC /* Default 0x1D */
+#define SX126X_REG_CRC_INIT_LSB   0x06BD /* Default 0x0F */
+#define SX126X_REG_CRC_POLY_MSB   0x06BE /* Default 0x10 */
+#define SX126X_REG_CRC_POLY_LSB   0x06BF /* Default 0x21 */
+#define SX126X_REG_WHITENING_MSB  0x06B8 /* Default 0x01 */
+#define SX126X_REG_WHITENING_LSB  0x06B9 /* Default 0x00 */
+
+/* Enum and constant definitions ********************************************/
+
+/* Packet parameters    *****************************************************/
+
+#define SX126X_PKTPARAM1_GFSK_PREAMBLELEN_PARAM           0 /* Takes 0x0001 to 
0xFFFF preamble bits */
+#define SX126X_PKTPARAM1_GFSK_PREAMBLELEN_PARAMS          2
+#define SX126X_PKTPARAM3_GFSK_PREAMBLEDETECTORLEN_PARAM   2 /* Takes 
SX126X_GFSK_PREABMLE_DETECT_x */
+#define SX126X_PKTPARAM4_GFSK_SYNCWORDLEN_PARAM           3 /* 0x00 to 0x40, 0 
to 8 bytes from SX126X_SYNCWORD register */
+#define SX126X_PKTPARAM5_GFSK_ADDRCOMP_PARAM              4 /* Takes 
SX126X_ADDR_FILT_x */
+#define SX126X_PKTPARAM6_GFSK_PKTTYPE_PARAM               5 /* Packet length 
unknown? false/true */
+#define SX126X_PKTPARAM7_GFSK_PAYLOADLEN_PARAM            6 /* 0x00 to 0xFF 
size of tx&rx payload in bytes */
+#define SX126X_PKTPARAM8_GFSK_CRCTYPE_PARAM               7 /* Takes 
SX126X_GFSK_CRCTYPE_x */
+#define SX126X_PKTPARAM9_GFSK_WHITENING_PARAM             8 /* Enable 
false/true */
+
+#define SX126X_PKTPARAM1_LORA_PREAMBLELEN_PARAM        0 /* 0x0001 to 0xFFFF 
preamble symbols */
+#define SX126X_PKTPARAM1_LORA_PREAMBLELEN_PARAMS       2
+#define SX126X_PKTPARAM3_LORA_HEADERTYPE_PARAM         2 /* Variable/Fixed 
length false/true */
+#define SX126X_PKTPARAM4_LORA_PAYLOADLEN_PARAM         3 /* 0x00 to 0xFF 
number of tx and rx bytes */
+#define SX126X_PKTPARAM5_LORA_CRCTYPE_PARAM            4 /* CRC OFF/ON 
false/true */
+#define SX126X_PKTPARAM6_LORA_INVERTIQ_PARAM           5 /* Standard/Inverted 
false/true */
+
+/* Modulation parameters ****************************************************/
+
+/* GFSK */
+
+#define SX126X_MODPARAM1_GFSK_BR_PARAM           0 /* Takes br = 32 * Fxtal / 
bit_rate */
+#define SX126X_MODPARAM1_GFSK_BR_PARAMS          3
+#define SX126X_MODPARAM4_GFSK_PULSESHAPE_PARAM   3 /* Takes 
SX126X_GFSK_PULSESHAPE_x */
+#define SX126X_MODPARAM5_GFSK_BANDWIDTH_PARAM    4 /* Takes 
SX126X_GFSK_BANDWIDTH_xHZ */
+#define SX126X_MODPARAM6_GFSK_FDEV_PARAM         5 /* Takes Fdev = 
(frequency_deviation * 2^25) / Fxtal */
+#define SX126X_MODPARAM6_GFSK_FDEV_PARAMS        3
+
+/* LoRa */
+
+#define SX126X_MODPARAM1_LORA_SF_PARAM                  0 /* Takes SF between 
SX126X_LORA_SF_MIN and MAX */
+#define SX126X_MODPARAM2_LORA_BW_PARAM                  1 /* Takes 
SX126X_LORA_BW_x */
+#define SX126X_MODPARAM3_LORA_CR_PARAM                  2 /* Takes 
SX126X_LORA_CR_x */
+#define SX126X_MODPARAM4_LORA_LOWDATRATE_OPTI_PARAM     3 /* Takes true/false*/
+
+/* Operational modes functions **********************************************/
+
+/* SetSleep */
+
+#define SX126X_SETSLEEP                     0x84        /* Opcode */
+#define SX126X_SETSLEEP_PARAMS              1
+#define SX126X_SETSLEEP_CONF_PARAM          0
+#define SX126X_SETSLEEP_CONF_RTC_SHIFT      0
+#  define SX126X_SETSLEEP_CONF_RTC_DISABLE  (0<<SX126X_SETSLEEP_CONF_RTC_SHIFT)
+#  define SX126X_SETSLEEP_CONF_RTC_ENABLE   (1<<SX126X_SETSLEEP_CONF_RTC_SHIFT)
+#define SX126X_SETSLEEP_CONF_START_SHIFT    2
+#  define SX126X_SETSLEEP_CONF_START_COLD   
(0<<SX126X_SETSLEEP_CONF_START_SHIFT)
+#  define SX126X_SETSLEEP_CONF_START_WARM   
(1<<SX126X_SETSLEEP_CONF_START_SHIFT)
+
+/* SetStandby */
+
+#define SX126X_SETSTANDBY               0x80                              /* 
Opcode */
+#define SX126X_SETSTANDBY_PARAMS        1
+#define SX126X_SETSTANDBY_CONF_PARAM    0
+#define SX126X_SETSTANDBY_CONF_SHIFT    0                                 /* 
Bit 0-1: STDBY mode */
+#  define SX126X_SETSTANDBY_CONF_RC     (0<<SX126X_SETSTANDBY_CONF_SHIFT) 
+#  define SX126X_SETSTANDBY_CONF_XOSC   (1<<SX126X_SETSTANDBY_CONF_SHIFT) 
+
+/* SetFS */
+
+#define SX126X_SETFS                    0xC1        /* Opcode */
+
+/* SetTX */
+
+#define SX126X_SETTX                    0x83        /* Opcode */
+#define SX126X_SETTX_PARAMS             2
+#define SX126X_SETTX_TIMEOUT_PARAM      0
+#define SX126X_SETTX_TIMEOUT_PARAMS     2
+#define SX126X_SETTX_NO_TIMEOUT         0x000000    /* Constant */
+
+/* SetRX */
+
+#define SX126X_SETRX                    0x82        /* Opcode */
+#define SX126X_SETRX_PARAMS             2
+#define SX126X_SETRX_TIMEOUT_PARAM      0
+#define SX126X_SETRX_TIMEOUT_PARAMS     2
+#define SX126X_SETRX_NO_TIMEOUT         0x000000    /* Constant */
+#define SX126X_SETRX_CONTINUOUS         0xFFFFFF    /* Constant */
+
+/* StopTimerOnPreamble */
+
+#define SX126X_STOPTIMERONPREAMBLE          0x9F    /* Opcode */
+#define SX126X_STOPTIMERONPREAMBLE_PARAMS   1
+#define SX126X_STOPTIMERONPREAMBLE_ENABLE   (1<<0)  /* Bit 0 disabled=stop on 
syncword/header, enabled=stop on preamble */
+
+/* SetRxDutyCycle */
+
+#define SX126X_SETRXDUTYCYCLE                       0x94 /* Opcode */
+#define SX126X_SETRXDUTYCYCLE_PARAMS                6
+#define SX126X_SETRXDUTYCYCLE_RXPERIOD_PARAM        0
+#define SX126X_SETRXDUTYCYCLE_RXPERIOD_PARAMS       3  
+#define SX126X_SETRXDUTYCYCLE_SLEEPPERIOD_PARAM     3
+#define SX126X_SETRXDUTYCYCLE_SLEEPPERIOD_PARAMS    3  
+
+/* SetCAD */
+
+#define SX126X_SETCAD                   0xC5
+
+/* SetTXContinuousWave */
+
+#define SX126X_SETTXCONTINUOUSWAVE      0xD1
+
+/* SetTXInfinitePreamble */
+
+#define SX126X_SETTXINFINITEPREAMBLE    0xD2
+
+/* SetRegulatorMode */
+
+#define SX126X_SETREGULATORMODE             0x96
+#define SX126X_SETREGULATORMODE_PARAMS      1
+#define SX126X_SETREGULATORMODE_PARAM       0
+
+/* Calibrate Function */
+
+#define SX126X_CALIBRATE                    0x89
+#define SX126X_CALIBRATE_RC64K_EN           (1<<0)
+#define SX126X_CALIBRATE_RC13M_EN           (1<<1)
+#define SX126X_CALIBRATE_PLL_EN             (1<<2)
+#define SX126X_CALIBRATE_ADC_PULSE_EN       (1<<3)
+#define SX126X_CALIBRATE_ADC_BULK_N_EN      (1<<4)
+#define SX126X_CALIBRATE_ADC_BULK_P_EN      (1<<5)
+#define SX126X_CALIBRATE_IMAGE_EN           (1<<6)
+
+/* CalibrateImage */
+
+#define SX126X_CALIBRATEIMAGE               0x98
+#define SX126X_CALIBRATEIMAGE_FREQ1_PARAM   0
+#define SX126X_CALIBRATEIMAGE_FREQ2_PARAM   1
+
+/* SetPAConfig */
+
+#define SX126X_SETPACONFIG                      0x95
+#define SX126X_SETPACONFIG_PARMS                4
+#define SX126X_SETPACONFIG_PADUTYCYCLE_PARAM    0
+#define SX126X_SETPACONFIG_HPMAX_PARAM          1
+#define SX126X_SETPACONFIG_DEVICESEL_PARAM      2
+#define SX126X_SETPACONFIG_PALUT_PARAM          3
+
+/* SetRXTXFallbackMode */
+
+#define SX126X_SETRXTXFALLBACKMODE              0x93
+#define SX126X_SETRXTXFALLBACKMODE_PARAMS       1 /* Takes SX126X_FALLBACK_x*/
+
+/* Registers and buffer access **********************************************/
+
+/* WriteRegister Function */
+
+#define SX126X_WRITEREGISTER                    0x0D
+#define SX126X_WRITEREGISTER_PARAMS             2
+#define SX126X_WRITEREGISTER_ADDRESS_PARAM      0
+#define SX126X_WRITEREGISTER_ADDRESS_PARAMS     2
+#define SX126X_WRITEREGISTER_DATA_PARAM         2 /* Data extends, address is 
auto incremented */
+#define SX126X_WRITEREGISTER_STATUS_RETURN      0 /* Gets returned every byte 
sent */ 
+
+/* ReadRegister Function */
+
+#define SX126X_READREGISTER                     0x1D
+#define SX126X_READREGISTER_ADDRESS_PARAM       0
+#define SX126X_READREGISTER_ADDRESS_PARAMS      2
+#define SX126X_READREGISTER_STATUS_RETURN       0
+#define SX126X_READREGISTER_STATUS_RETURNS      3
+#define SX126X_READREGISTER_DATA_RETURN         3 /* Data extends, address is 
auto incremented */
+
+/* WriteBuffer Function */
+
+#define SX126X_WRITEBUFFER                      0x0E
+#define SX126X_WRITEBUFFER_PARAMS_MIN           2
+#define SX126X_WRITEBUFFER_OFFSET_PARAM         0
+#define SX126X_WRITEBUFFER_DATA_PARAM           1 /* Data extends, 
offset(address) is auto incremented */
+#define SX126X_WRITEBUFFER_STATUS_RETURN        0 /* Gets returned every byte 
sent */
+
+/* ReadBuffer Function */
+
+#define SX126X_READBUFFER                       0x1E
+#define SX126X_READBUFFER_OFFSET_PARAM          0
+#define SX126X_READBUFFER_STATUS_RETURN         0
+#define SX126X_READBUFFER_STATUS_RETURNS        2
+#define SX126X_READBUFFER_DATA_RETURN           2 /* Data extends, 
offset(address) is auto incremented */
+
+/* DIO and IRQ Control Functions ********************************************/
+
+/* SetDioIrqParams */
+
+#define SX126X_SETDIOIRQPARAMS                  0x08
+#define SX126X_SETDIOIRQPARAMS_PARAMS           8 /* Takes SX126X_IRQ_x bit 
masks */
+#define SX126X_SETDIOIRQPARAMS_IRQMASK_PARAM    0
+#define SX126X_SETDIOIRQPARAMS_IRQMASK_PARAMS   2
+#define SX126X_SETDIOIRQPARAMS_DIO1MASK_PARAM   2
+#define SX126X_SETDIOIRQPARAMS_DIO1MASK_PARAMS  2
+#define SX126X_SETDIOIRQPARAMS_DIO2MASK_PARAM   4
+#define SX126X_SETDIOIRQPARAMS_DIO2MASK_PARAMS  2
+#define SX126X_SETDIOIRQPARAMS_DIO3MASK_PARAM   6
+#define SX126X_SETDIOIRQPARAMS_DIO3MASK_PARAMS  2
+
+/* SetDIO2AsRfSwitchCtrl */
+
+#define SX126X_SETDIO2RFSWCTRL                  0x9D
+#define SX126X_SETDIO2RFSWCTRL_PARAMS           1
+#define SX126X_SETDIO2RFSWCTRL_ENABLE_PARAM     0 /* true/false */
+
+/* SetDIO3AsTCXOCtrl */
+
+#define SX126X_SETDIO3TCXOCTRL                  0x97
+#define SX126X_SETDIO3TCXOCTRL_PARAMS           4
+#define SX126X_SETDIO3TCXOCTRL_TCXO_V_PARAM     0 /* Takes SX126X_TCXO_xV */
+#define SX126X_SETDIO3TCXOCTRL_DELAY_PARAM      1 /* time = delay(23:0) * 
15.625 uS */
+#define SX126X_SETDIO3TCXOCTRL_DELAY_PARAMS     3
+
+/* GetIrqStatus */
+
+#define SX126X_GETIRQSTATUS                     0x12
+#define SX126X_GETIRQSTATUS_RETURNS             3
+#define SX126X_GETIRQSTATUS_STATUS_RETURN       0
+#define SX126X_GETIRQSTATUS_IRQSTATUS_RETURN    1
+#define SX126X_GETIRQSTATUS_IRQSTATUS_RETURNS   2
+
+/* ClearIrqStatus */
+
+#define SX126X_CLEARIRQSTATUS                   0x02
+#define SX126X_CLEARIRQSTATUS_PARAMS            2
+#define SX126X_CLEARIRQSTATUS_CLEAR_PARAM       0
+#define SX126X_CLEARIRQSTATUS_CLEAR_PARAMS      2
+
+/* RF Modulation and Packet-Related Functions *******************************/
+
+/* SetRfFrequency */
+
+#define SX126X_SETRFFREQUENCY                   0x86
+#define SX126X_SETRFFREQUENCY_PARAMS            4
+#define SX126X_SETRFFREQUENCY_RFFREQ_PARAM      0 /* Takes (freq * 2 ^ 25) / 
xtal */
+#define SX126X_SETRFFREQUENCY_RFFREQ_PARAMS     4
+
+/* SetPacketType */
+
+#define SX126X_SETPACKETTYPE                    0x8A
+#define SX126X_SETPACKETTYPE_PARAMS             1
+#define SX126X_SETPACKETTYPE_PACKETTYPE_PARAM   0       /* Takes 
SX126X_PACKETTYPE_x */
+
+/* GetPacketType */
+
+#define SX126X_GETPACKETTYPE                    0x11
+#define SX126X_GETPACKETTYPE_RETURNS            2
+#define SX126X_GETPACKETTYPE_STATUS_RETURN      0
+#define SX126X_GETPACKETTYPE_PACKETTYPE_RETURN  1       /* Gives 
SX126X_PACKETTYPE_x */
+
+/* SetTxParms */
+
+#define SX126X_SETTXPARMS                       0x8E
+#define SX126X_SETTXPARMS_PARAMS                2
+#define SX126X_SETTXPARMS_POWER_PARAM           0
+#define SX126X_SETTXPARMS_RAMPTIME_PARAM        1       /* Takes 
SX126X_SET_RAMP_xU*/
+
+/* SetModulationParams */
+
+#define SX126X_SETMODULATIONPARAMS              0x8B
+#define SX126X_SETMODULATIONPARAMS_PARAMS       8 /* Takes the corresponding 
SX126X_MODPARAMx_y */
+#define SX126X_SETMODULATIONPARAMS_PARAM1_PARAM 0
+#define SX126X_SETMODULATIONPARAMS_PARAM2_PARAM 1
+#define SX126X_SETMODULATIONPARAMS_PARAM3_PARAM 2
+#define SX126X_SETMODULATIONPARAMS_PARAM4_PARAM 3
+#define SX126X_SETMODULATIONPARAMS_PARAM5_PARAM 4
+#define SX126X_SETMODULATIONPARAMS_PARAM6_PARAM 5
+#define SX126X_SETMODULATIONPARAMS_PARAM7_PARAM 6
+#define SX126X_SETMODULATIONPARAMS_PARAM8_PARAM 7
+
+/* SetPacketParms */
+
+#define SX126X_SETPACKETPARMS                   0x8C
+#define SX126X_SETPACKETPARMS_PARAMS            9 /* Takes the corresponding 
SX126X_PKTPARAMx_y */
+#define SX126X_SETPACKETPARMS_PARAM1_PARAM      0
+#define SX126X_SETPACKETPARMS_PARAM2_PARAM      1
+#define SX126X_SETPACKETPARMS_PARAM3_PARAM      2
+#define SX126X_SETPACKETPARMS_PARAM4_PARAM      3
+#define SX126X_SETPACKETPARMS_PARAM5_PARAM      4
+#define SX126X_SETPACKETPARMS_PARAM6_PARAM      5
+#define SX126X_SETPACKETPARMS_PARAM7_PARAM      6
+#define SX126X_SETPACKETPARMS_PARAM8_PARAM      7
+#define SX126X_SETPACKETPARMS_PARAM9_PARAM      8
+
+/* SetCadParams */
+
+#define SX126X_SETCADPARAMS                     0x88
+#define SX126X_SETCADPARAMS_PARAMS              7
+#define SX126X_SETCADPARAMS_CADSYMNUM_PARAM     0 /* Takes 
SX126X_CAD_ON_x_SYMB */
+#define SX126X_SETCADPARAMS_CADDETPEAK_PARAM    1
+#define SX126X_SETCADPARAMS_CADDETMIN_PARAM     2
+#define SX126X_SETCADPARAMS_CADEXITMODE_PARAM   3 /* Takes SX126x_CAD_x */
+#define SX126X_SETCADPARAMS_CADTIMEOUT_PARAM    4 /* RxTimeout = cadTimeout * 
15.625 */
+#define SX126X_SETCADPARAMS_CADTIMEOUI_PARAMS   3
+
+/* SetBufferBaseAddress */
+
+#define SX126X_SETBUFFERBASEADDRESS             0x8F
+#define SX126X_SETBUFFERBASEADDRESS_PARAMS      2
+#define SX126X_SETBUFFERBASEADDRESS_TX_PARAM    0
+#define SX126X_SETBUFFERBASEADDRESS_RX_PARAM    1
+
+/* SetLoRaSymbNumTimeout */
+
+#define SX126X_SETLORASYMBNUMTIMEOUT            0xA0
+#define SX126X_SETLORASYMBNUMTIMEOUT_PARAMS     1
+#define SX126X_SETLORASYMBNUMTIMEOUT_NUM_PARAM  0
+
+/* Communication status information *****************************************/
+
+/* GetStatus */
+
+#define SX126X_CMD_GETSTATUS            0xC0 /* Opcode */
+#define SX126X_STATUS_CMD_SHIFT         (1)
+#define SX126X_STATUS_CMD_MASK          (0b111<<SX126X_STATUS_CMD_SHIFT)
+#define SX126X_STATUS_CHIPMODE_SHIFT    (4)
+#define SX126X_STATUS_CHIPMODE_MASK     (0b111<<SX126X_STATUS_CHIPMODE_SHIFT)
+
+/* GetRSSIInst */
+
+#define SX126X_GETRSSIINST               0x15
+#define SX126X_GETRSSIINST_RETURNS       2
+#define SX126X_GETRSSIINST_STAT_RETURN   0
+#define SX126X_GETRSSIINST_RSSI_RETURN   1
+
+/* GetRxBufferStatus */
+
+#define SX126X_GETRXBUFFERSTATUS                      0x13
+#define SX126X_GETRXBUFFERSTATUS_RETURNS              3
+#define SX126X_GETRXBUFFERSTATUS_STATUS_RETURN        0
+#define SX126X_GETRXBUFFERSTATUS_PAYLOAD_LEN_RETURN   1
+#define SX126X_GETRXBUFFERSTATUS_RX_START_PTR_RETURN  2
+
+/* Misc *********************************************************************/
+
+/* GetDeviceErrors */
+
+#define SX126X_GETDEVICEERRORS                  0x17
+#define SX126X_GETDEVICEERRORS_RETURNS          3
+#define SX126X_GETDEVICEERRORS_STATUS_RETURN    0
+#define SX126X_GETDEVICEERRORS_OPERROR_RETURN   1
+#define SX126X_GETDEVICEERRORS_OPERROR_RETURNS  2 
+
+/* ClearDeviceErrors */
+
+#define SX126X_CLEARDEVICEERRORS                0x07
+#define SX126X_CLEARDEVICEERRORS_NOPS           2
+
+#endif /* __DRIVERS_WIRELESS_LPWAN_SX126X_SX126X_H */
diff --git a/include/nuttx/wireless/ioctl.h b/include/nuttx/wireless/ioctl.h
index a4c2da3969..9a53eb5517 100644
--- a/include/nuttx/wireless/ioctl.h
+++ b/include/nuttx/wireless/ioctl.h
@@ -85,6 +85,11 @@
 #define SX127X_FIRST        (NRF24L01_FIRST + NRF24L01_NCMDS)
 #define SX127X_NCMDS        11
 
+/* See include/nuttx/wireless/lpwan/sx126x.h */
+
+#define SX126X_FIRST        (SX127X_FIRST + SX127X_NCMDS)
+#define SX126X_NCMDS        11
+
 /* See include/nuttx/wireless/gs2200m.h */
 
 #define GS2200M_FIRST       (SX127X_FIRST + SX127X_NCMDS)
diff --git a/include/nuttx/wireless/lpwan/sx126x.h 
b/include/nuttx/wireless/lpwan/sx126x.h
new file mode 100644
index 0000000000..e7c6792004
--- /dev/null
+++ b/include/nuttx/wireless/lpwan/sx126x.h
@@ -0,0 +1,418 @@
+/****************************************************************************
+ * include/nuttx/wireless/lpwan/sx126x.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_WIRELESS_LPWAN_SX126X_H
+#define __INCLUDE_NUTTX_WIRELESS_LPWAN_SX126X_H
+
+/* This version is currently experimental.
+ * Breaking changes might happen in the near future.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/irq.h>
+#include <nuttx/wireless/ioctl.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/****************************************************************************
+ * Defintions
+ ****************************************************************************/
+
+#define SX126X_RX_PAYLOAD_SIZE              0xff
+
+/* IOCTL commands ***********************************************************/
+
+/* arg: sx126x_packet_type_e */
+
+#define SX126XIOC_PACKETTYPESET             _WLCIOC(SX126X_FIRST+0)//
+
+/* Sets lora parameters. arg: sx126x_lora_config_s *config */
+
+#define SX126XIOC_LORACONFIGSET             _WLCIOC(SX126X_FIRST+1)
+
+/* IRQ Register bits ********************************************************/
+
+#define SX126X_IRQ_TXDONE_MASK              (1<<0)
+#define SX126X_IRQ_RXDONE_MASK              (1<<1)
+#define SX126X_IRQ_PREAMBLEDETECTED_MASK    (1<<2)
+#define SX126X_IRQ_SYNCWORDVALID_MASK       (1<<3)
+#define SX126X_IRQ_HEADERVALID_MASK         (1<<4)
+#define SX126X_IRQ_HEADERERR_MASK           (1<<5)
+#define SX126X_IRQ_CRCERR_MASK              (1<<6)
+#define SX126X_IRQ_CADDONE_MASK             (1<<7)
+#define SX126X_IRQ_CADDETECTED_MASK         (1<<8)
+#define SX126X_IRQ_TIMEOUT_MASK             (1<<9)
+#define SX126X_IRQ_LRFHSSHOP_MASK           (1<<14)
+
+/* Others */
+
+#define SX126X_NOP                   0
+#define SX126X_NO_TIMEOUT            0
+#define SX126X_NO_DELAY              0
+
+/* Oscillators and PLLs */
+
+#define SX126X_OSC_MAIN_HZ           (32000000)
+#define SX126X_FXTAL                 SX126X_OSC_MAIN_HZ
+#define SX126X_PLL_STEP_SHIFT_AMOUNT (14)
+#define SX126X_PLL_STEP_SCALED       
(SX126X_FXTAL>>(25-SX126X_PLL_STEP_SHIFT_AMOUNT))
+
+/****************************************************************************
+ * Public Data Types
+ ****************************************************************************/
+
+/* Standby config */
+
+enum sx126x_standby_mode_e
+{
+  SX126X_STDBY_RC    = 0x00,
+  SX126X_STDBY_XOSC  = 0x01
+};
+
+/* Packet Types */
+
+enum sx126x_packet_type_e
+{
+  SX126X_PACKETTYPE_GFSK    = 0x00,
+  SX126X_PACKETTYPE_LORA    = 0x01,
+  SX126X_PACKETTYPE_LR_FHSS = 0x13
+};
+
+/* Ramp times */
+
+enum sx126x_ramp_time_e
+{
+  SX126X_SET_RAMP_10U   = 0x00,
+  SX126X_SET_RAMP_20U   = 0x01,
+  SX126X_SET_RAMP_40U   = 0x02,
+  SX126X_SET_RAMP_80U   = 0x03,
+  SX126X_SET_RAMP_200U  = 0x04,
+  SX126X_SET_RAMP_800U  = 0x05,
+  SX126X_SET_RAMP_1700U = 0x06,
+  SX126X_SET_RAMP_3400U = 0x07
+};
+
+/* GFSK Pulse shapes */
+
+enum sx126x_gfsk_pulseshape_e
+{
+  SX126X_GFSK_PULSESHAPE_NONE            = 0x00,
+  SX126X_GFSK_PULSESHAPE_GAUSSIAN_BT_0_3 = 0x08,
+  SX126X_GFSK_PULSESHAPE_GAUSSIAN_BT_0_5 = 0x09,
+  SX126X_GFSK_PULSESHAPE_GAUSSIAN_BT_0_7 = 0x0a,
+  SX126X_GFSK_PULSESHAPE_GAUSSIAN_BT_1   = 0x0b
+};
+
+/* GFSK Bandwidths in Hz */
+
+enum sx126x_gfsk_bandwidth_e
+{
+  SX126X_GFSK_BANDWIDTH_4800HZ   = 0x1f,
+  SX126X_GFSK_BANDWIDTH_5800HZ   = 0x17,
+  SX126X_GFSK_BANDWIDTH_7300HZ   = 0x0f,
+  SX126X_GFSK_BANDWIDTH_9700HZ   = 0x1e,
+  SX126X_GFSK_BANDWIDTH_11700HZ  = 0x16,
+  SX126X_GFSK_BANDWIDTH_14600HZ  = 0x0e,
+  SX126X_GFSK_BANDWIDTH_19500HZ  = 0x1d,
+  SX126X_GFSK_BANDWIDTH_23400HZ  = 0x15,
+  SX126X_GFSK_BANDWIDTH_29300HZ  = 0x0d,
+  SX126X_GFSK_BANDWIDTH_39000HZ  = 0x1c,
+  SX126X_GFSK_BANDWIDTH_46900HZ  = 0x14,
+  SX126X_GFSK_BANDWIDTH_58600HZ  = 0x0c,
+  SX126X_GFSK_BANDWIDTH_78200HZ  = 0x1b,
+  SX126X_GFSK_BANDWIDTH_93800HZ  = 0x13,
+  SX126X_GFSK_BANDWIDTH_117300HZ = 0x0b,
+  SX126X_GFSK_BANDWIDTH_156200HZ = 0x1a,
+  SX126X_GFSK_BANDWIDTH_187200HZ = 0x12,
+  SX126X_GFSK_BANDWIDTH_234300HZ = 0x0a,
+  SX126X_GFSK_BANDWIDTH_312000HZ = 0x19,
+  SX126X_GFSK_BANDWIDTH_373600HZ = 0x11,
+  SX126X_GFSK_BANDWIDTH_467000HZ = 0x09
+};
+
+/* LoRa Spreading Factors */
+
+enum sx126x_lora_sf_e
+{
+  SX126X_LORA_SF5  = 0x05,
+  SX126X_LORA_SF6  = 0x06,
+  SX126X_LORA_SF7  = 0x07,
+  SX126X_LORA_SF8  = 0x08,
+  SX126X_LORA_SF9  = 0x09,
+  SX126X_LORA_SF10 = 0x0a,
+  SX126X_LORA_SF11 = 0x0b,
+  SX126X_LORA_SF12 = 0x0c
+};
+
+/* LoRa Bandwidths */
+
+enum sx126x_lora_bw_e
+{
+  SX126X_LORA_BW_7   = 0x00,
+  SX126X_LORA_BW_10  = 0x08,
+  SX126X_LORA_BW_15  = 0x01,
+  SX126X_LORA_BW_20  = 0x09,
+  SX126X_LORA_BW_31  = 0x02,
+  SX126X_LORA_BW_41  = 0x0a,
+  SX126X_LORA_BW_62  = 0x03,
+  SX126X_LORA_BW_125 = 0x04,
+  SX126X_LORA_BW_250 = 0x05,
+  SX126X_LORA_BW_500 = 0x06
+};
+
+/* LoRa Coding Rates */
+
+enum sx126x_lora_cr_e
+{
+  SX126X_LORA_CR_4_5 = 0x01,
+  SX126X_LORA_CR_4_6 = 0x02,
+  SX126X_LORA_CR_4_7 = 0x03,
+  SX126X_LORA_CR_4_8 = 0x04
+};
+
+/* CAD Exit modes */
+
+enum sx126x_cad_exit_mode_e
+{
+  SX126X_CAD_ONLY = 0x00,
+  SX126X_CAD_RX   = 0x01
+};
+
+/* TCXO voltages */
+
+enum sx126x_tcxo_voltage_e
+{
+  SX126X_TCXO_1_6V = 0x00,
+  SX126X_TCXO_1_7V = 0x01,
+  SX126X_TCXO_1_8V = 0x02,
+  SX126X_TCXO_2_2V = 0x03,
+  SX126X_TCXO_2_4V = 0x04,
+  SX126X_TCXO_2_7V = 0x05,
+  SX126X_TCXO_3_0V = 0x06,
+  SX126X_TCXO_3_3V = 0x07
+};
+
+/* Fallback modes */
+
+enum sx126x_fallback_mode_e
+{
+  SX126X_FALLBACK_FS          = 0x40,
+  SX126X_FALLBACK_STDBY_XOSC  = 0x30,
+  SX126X_FALLBACK_STDBY_RC    = 0x20
+};
+
+/* Regulator modes */
+
+enum sx126x_regulator_mode_e
+{
+  SX126X_LDO         = 0x00,
+  SX126X_DC_DC_LDO   = 0x01
+};
+
+/* Device */
+
+enum sx126x_device_e
+{
+  SX1261 =            0x01,
+  SX1262 =            0x00
+};
+
+enum sx126x_gfsk_preamble_detect_e
+{
+  SX126X_GFSK_PREAMBLE_DETECT_OFF,
+  SX126X_GFSK_PREAMBLE_DETECT_8B,
+  SX126X_GFSK_PREAMBLE_DETECT_16B,
+  SX126X_GFSK_PREAMBLE_DETECT_24B,
+  SX126X_GFSK_PREAMBLE_DETECT_32B
+};
+
+/* Addr comp */
+
+enum sx126x_address_filtering_e
+{
+  SX126X_ADDR_FILT_DISABLED,
+  SX126X_ADDR_FILT_NODE,
+  SX126X_ADDR_FILT_NODE_BROADCAST
+};
+
+/* GFSK CRC types */
+
+enum sx126x_gfsk_crc_type_e
+{
+  SX126X_GFSK_CRCTYPE_OFF         = 0x01,
+  SX126X_GFSK_CRCTYPE_1_BYTE      = 0x00,
+  SX126X_GFSK_CRCTYPE_2_BYTE      = 0x02,
+  SX126X_GFSK_CRCTYPE_1_BYTE_INV  = 0x04,
+  SX126X_GFSK_CRCTYPE_2_BYTE_INV  = 0x06
+};
+
+/* LoRa mod params */
+
+struct sx126x_modparams_lora_s
+{
+  enum sx126x_lora_sf_e spreading_factor;
+  enum sx126x_lora_bw_e bandwidth;
+  enum sx126x_lora_cr_e coding_rate;
+  bool low_datarate_optimization;
+};
+
+/* GFSK mod params */
+
+struct sx126x_modparams_gfsk_s
+{
+  uint32_t bitrate;
+  enum sx126x_gfsk_pulseshape_e pulseshape;
+  enum sx126x_gfsk_bandwidth_e bandwidth;
+  uint32_t frequency_deviation;
+};
+
+/* LoRa packet params */
+
+struct sx126x_packetparams_lora_s
+{
+  uint16_t preambles;
+  bool fixed_length_header;
+  uint8_t payload_length;
+  bool crc_enable;
+  bool invert_iq;
+};
+
+/* GFSK packet params */
+
+struct sx126x_packetparams_gfsk_s
+{
+  uint16_t preambles;
+  enum sx126x_gfsk_preamble_detect_e preamble_detect;
+  uint8_t syncword_length;
+  enum sx126x_address_filtering_e address_filtering;
+  bool include_packet_size;
+  uint8_t packet_length;
+  enum sx126x_gfsk_crc_type_e crc_type;
+  bool whitening_enable;
+};
+
+/* Config */
+
+struct sx126x_lora_config_s
+{
+  struct sx126x_modparams_lora_s modulation;
+  struct sx126x_packetparams_lora_s packet;
+};
+
+/* Lower driver *************************************************************/
+
+struct sx126x_irq_masks
+{
+  uint16_t dio1_mask;
+  uint16_t dio2_mask;
+  uint16_t dio3_mask;
+};
+
+struct sx126x_lower_s
+{
+  /* Index of radio to register.
+   * ex: 0 is the primary radio, 1 is the secondary.
+   * Must be within the maximum configured radios.
+   */
+
+  unsigned int dev_number;
+  CODE void (*reset)(void);
+
+  /* This controls which DIO reacts to interrupts
+   * Depended on the pinout of the board / module.
+   * Note that DIO 2 and DIO 3 can be already in use
+   * by the module and setting them might intefere
+   * with the operation or even damage them.
+   */
+
+  struct sx126x_irq_masks masks;
+  enum sx126x_tcxo_voltage_e dio3_voltage;
+  uint32_t dio3_delay;
+  uint8_t use_dio2_as_rf_sw;
+
+  /* Interrupt attachments. These should be
+   * connected to one of the DIOx pins
+   */
+
+  CODE int (*irq0attach)(xcpt_t handler, FAR void *arg);
+
+  /* The regulator mode is board / module depended */
+
+  enum sx126x_regulator_mode_e regulator_mode;
+
+  /* Power amplifier control. DO NOT exceeds the
+   * limits listed in SX1261-2 V2 datasheet.
+   * 13.1.14 SetPaConfig
+   * This can cause damage to the device.
+   */
+
+  CODE int (*get_pa_values)(enum sx126x_device_e *model,
+                            uint8_t *hpmax, uint8_t *padutycycle);
+
+  /* TX power control. Depending on the local RF regulations,
+   * power might have to be limited.
+   * Also depending on board or module,
+   * power values have different charactersitics.
+   * More info in sx1261-2 V2 datasheet 13.4.4 SetTxParams.
+   * uint8_t *power is set and this function may limit it.
+   */
+
+  CODE int (*limit_tx_power)(uint8_t *current_power);
+
+  enum sx126x_ramp_time_e tx_ramp_time;
+
+  /* Frequency control
+   * Typically boards have a limited range of frequencies.
+   * Exceeding these can damage the radio.
+   * Also depending on regulations, some frequencies are restricted.
+   * This must return non zero in case a frequency is denied.
+   */
+
+  CODE int (*check_frequency)(uint32_t frequency);
+};
+
+/* Upper ********************************************************************/
+
+struct sx126x_read_header_s
+{
+  uint8_t payload_length;
+  int32_t snr;
+  int16_t rssi_db;
+  uint8_t payload[SX126X_RX_PAYLOAD_SIZE];
+  uint8_t crc_error;
+};
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+void sx126x_register(FAR struct spi_dev_s *spi,
+                     FAR const struct sx126x_lower_s *lower,
+                     const char *path);
+
+#endif /* __INCLUDE_NUTTX_WIRELESS_LPWAN_SX126X_H */
\ No newline at end of file

Reply via email to