From b9b505a2b214a251901d8e15f9f23e81deff45c7 Mon Sep 17 00:00:00 2001 From: Matteo Golin Date: Tue, 4 Mar 2025 08:49:37 -0500 Subject: [PATCH] wireless/lpwan: Add support for the RN903 and RN2483 family of LoRa radio transceivers. This initial support includes transmit and receive functionality and configuration and reading of radio parameters like frequency and bandwidth. Signed-off-by: Matteo Golin --- drivers/wireless/lpwan/Kconfig | 7 + drivers/wireless/lpwan/Make.defs | 1 + drivers/wireless/lpwan/rn2xx3/CMakeLists.txt | 25 + drivers/wireless/lpwan/rn2xx3/Make.defs | 31 + drivers/wireless/lpwan/rn2xx3/rn2xx3.c | 1940 ++++++++++++++++++ include/nuttx/wireless/ioctl.h | 46 +- include/nuttx/wireless/lpwan/rn2xx3.h | 83 + 7 files changed, 2131 insertions(+), 2 deletions(-) create mode 100644 drivers/wireless/lpwan/rn2xx3/CMakeLists.txt create mode 100644 drivers/wireless/lpwan/rn2xx3/Make.defs create mode 100644 drivers/wireless/lpwan/rn2xx3/rn2xx3.c create mode 100644 include/nuttx/wireless/lpwan/rn2xx3.h diff --git a/drivers/wireless/lpwan/Kconfig b/drivers/wireless/lpwan/Kconfig index 514d6532e5..9753795dbb 100644 --- a/drivers/wireless/lpwan/Kconfig +++ b/drivers/wireless/lpwan/Kconfig @@ -5,6 +5,13 @@ if DRIVERS_LPWAN +config LPWAN_RN2XX3 + bool "Microchip RN2xx3 driver support" + default n + depends on UART + ---help--- + Enable driver support for the RN2xx3 LoRa radio transceiver family. + config LPWAN_SX127X bool "SX127X Low Power Long Range transceiver support" default n diff --git a/drivers/wireless/lpwan/Make.defs b/drivers/wireless/lpwan/Make.defs index 4a12806ac7..a6998793e5 100644 --- a/drivers/wireless/lpwan/Make.defs +++ b/drivers/wireless/lpwan/Make.defs @@ -26,5 +26,6 @@ ifeq ($(CONFIG_DRIVERS_LPWAN),y) include wireless/lpwan/sx127x/Make.defs include wireless/lpwan/sx126x/Make.defs +include wireless/lpwan/rn2xx3/Make.defs endif # CONFIG_DRIVERS_LPWAN diff --git a/drivers/wireless/lpwan/rn2xx3/CMakeLists.txt b/drivers/wireless/lpwan/rn2xx3/CMakeLists.txt new file mode 100644 index 0000000000..008dd7069f --- /dev/null +++ b/drivers/wireless/lpwan/rn2xx3/CMakeLists.txt @@ -0,0 +1,25 @@ +# ############################################################################## +# drivers/wireless/lpwan/rn2xx3/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_RN2903) + target_sources(drivers PRIVATE rn2xx3.c) +endif() diff --git a/drivers/wireless/lpwan/rn2xx3/Make.defs b/drivers/wireless/lpwan/rn2xx3/Make.defs new file mode 100644 index 0000000000..d1adf6b5af --- /dev/null +++ b/drivers/wireless/lpwan/rn2xx3/Make.defs @@ -0,0 +1,31 @@ +############################################################################ +# drivers/wireless/lpwan/rn2xx3/Make.defs +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_LPWAN_RN2XX3),y) + +CSRCS += rn2xx3.c + +DEPPATH += --dep-path wireless$(DELIM)lpwan$(DELIM)rn2xx3 +VPATH += :wireless$(DELIM)lpwan$(DELIM)rn2xx3 +CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)wireless$(DELIM)lpwan$(DELIM)rn2xx3 + +endif # CONFIG_LPWAN_RN2903 diff --git a/drivers/wireless/lpwan/rn2xx3/rn2xx3.c b/drivers/wireless/lpwan/rn2xx3/rn2xx3.c new file mode 100644 index 0000000000..32df0c42ba --- /dev/null +++ b/drivers/wireless/lpwan/rn2xx3/rn2xx3.c @@ -0,0 +1,1940 @@ +/**************************************************************************** + * drivers/wireless/lpwan/rn2xx3/rn2xx3.c + * + * NOTE: EXPERIMENTAL DRIVER + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_LIBC_LONG_LONG +#error "CONFIG_LIBC_LONG_LONG must be enabled for this driver" +#endif + +/* Duration of maximum MAC layer pause in milliseconds */ + +#define MAC_PAUSE_DUR "4294967245" + +/* Helper to get array length */ + +#define array_len(arr) ((sizeof(arr)) / sizeof((arr)[0])) + +/**************************************************************************** + * Private Data Types + ****************************************************************************/ + +/* Radio transceiver modules */ + +enum rn2xx3_type_e +{ + RN2903 = 0, /* RN2903 transceiver */ + RN2483 = 1, /* RN2483 transceiver */ +}; + +/* Transmit power level and associated dBm power */ + +struct txlvl_s +{ + int8_t lvl; /* Transmit power level */ + int32_t power; /* Transmit power in 0.01 dBm */ +}; + +/* Radio device struct */ + +struct rn2xx3_dev_s +{ + FAR struct file uart; /* UART interface */ + bool receiving; /* Currently receiving */ + mutex_t devlock; /* Exclusive access */ + enum rn2xx3_type_e model; /* Transceiver model */ + enum rn2xx3_mod_e mod; /* Modulation mode */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + int16_t crefs; /* Number of open references */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int rn2xx3_open(FAR struct file *filep); +static int rn2xx3_close(FAR struct file *filep); +#endif +static ssize_t rn2xx3_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static ssize_t rn2xx3_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static int rn2xx3_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_rn2xx3fops = +{ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + .open = rn2xx3_open, + .close = rn2xx3_close, +#else + .open = NULL, + .close = NULL, +#endif + .read = rn2xx3_read, + .write = rn2xx3_write, + .ioctl = rn2xx3_ioctl, +}; + +/* Modulation types */ + +static const char *MODULATIONS[] = +{ + "lora", /* RN2XX3_MOD_LORA */ + "fsk", /* RN2XX3_MOD_FSK */ +}; + +/* Coding rates */ + +static const char *CODING_RATES[] = +{ + "4/5", /* RN2XX3_CR_4_5 */ + "4/6", /* RN2XX3_CR_4_6 */ + "4/7", /* RN2XX3_CR_4_7 */ + "4/8", /* RN2XX3_CR_4_8 */ +}; + +/* Max packet sizes for each modulation type. */ + +static const uint8_t MAX_PKTSIZE[] = +{ + 255, /* RN2XX3_MOD_LORA */ + 64, /* RN2XX3_MOD_FSK */ +}; + +/* Transmit power levels and their output powers in dBm for the RN2903 + * module, ordered from least to greatest + */ + +static const struct txlvl_s RN2903_TXLVLS[] = +{ + {.lvl = 2, .power = 300}, + {.lvl = 3, .power = 400}, + {.lvl = 4, .power = 500}, + {.lvl = 5, .power = 600}, + {.lvl = 6, .power = 700}, + {.lvl = 7, .power = 800}, + {.lvl = 8, .power = 900}, + {.lvl = 9, .power = 1000}, + {.lvl = 10, .power = 1100}, + {.lvl = 11, .power = 1200}, + {.lvl = 12, .power = 1300}, + {.lvl = 14, .power = 1470}, + {.lvl = 15, .power = 1550}, + {.lvl = 16, .power = 1630}, + {.lvl = 17, .power = 1700}, + {.lvl = 20, .power = 1850}, +}; + +/* Transmit power levels and their output powers in dBm for the RN2483 + * module, ordered from least to greatest + */ + +static const struct txlvl_s RN2483_TXLVLS[] = +{ + {.lvl = -3, .power = -400}, + {.lvl = -2, .power = -290}, + {.lvl = -1, .power = -190}, + {.lvl = 0, .power = -170}, + {.lvl = 1, .power = -60}, + {.lvl = 2, .power = 40}, + {.lvl = 3, .power = 140}, + {.lvl = 4, .power = 250}, + {.lvl = 5, .power = 360}, + {.lvl = 6, .power = 470}, + {.lvl = 7, .power = 580}, + {.lvl = 8, .power = 690}, + {.lvl = 9, .power = 810}, + {.lvl = 10, .power = 930}, + {.lvl = 11, .power = 1040}, + {.lvl = 12, .power = 1160}, + {.lvl = 13, .power = 1250}, + {.lvl = 14, .power = 1350}, + {.lvl = 15, .power = 1410}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: radio_flush + * + * Description: + * Flushes all characters in the receive buffer from the RN2xx3 radio. + * Returns the number of bytes read. + * + ****************************************************************************/ + +static int radio_flush(FAR struct rn2xx3_dev_s *priv) +{ + int err; + int to_read; + char buf[10]; + + err = file_ioctl(&priv->uart, FIONREAD, &to_read); + if (err < 0) + { + return err; + } + + while (to_read > 0) + { + err = file_read(&priv->uart, buf, sizeof(buf)); + if (err < 0) + { + return err; + } + + to_read -= err; + } + + return 0; +} + +/**************************************************************************** + * Name: read_line + * + * Description: + * Reads a whole line of response from the RN2xx3 radio. Returns the number + * of bytes read, excluding the terminating \r\n and null terminator. + * + ****************************************************************************/ + +static ssize_t read_line(FAR struct rn2xx3_dev_s *priv, FAR char *buf, + size_t nbytes) +{ + ssize_t length = 0; + ssize_t i = 0; + bool line_complete = false; + + for (; i <= nbytes; ) + { + length = file_read(&priv->uart, &buf[i], nbytes - i); + + if (length < 0) + { + return length; + } + + i += length; + + /* Check if the character we just read was '\n'. This only occurs when + * the transceiver's response is complete. Length check ensures that + * we're not checking uninitialized memory in the case that `length` is + * 0. + */ + + if (length > 0 && buf[i - 1] == '\n') + { + line_complete = true; + break; + } + } + + /* Insufficient buffer space to handle response */ + + if (!line_complete) + { + return -ENOBUFS; + } + + /* Overwrite preceding \r with null terminator */ + + buf[i - 2] = '\0'; + return i - 2; /* Number of bytes read excluding \r\n */ +} + +/**************************************************************************** + * Name: mac_pause + * + * Description: + * Pauses the MAC layer. Required before every transmission/receive. + * + ****************************************************************************/ + +static int mac_pause(FAR struct rn2xx3_dev_s *priv) +{ + ssize_t length = 0; + char response[30]; + + /* Issue pause command */ + + length = + file_write(&priv->uart, "mac pause\r\n", sizeof("mac pause\r\n") - 1); + if (length < 0) + { + return length; + } + + /* Wait for response of watchdog timer timeout */ + + length = read_line(priv, response, sizeof(response)); + if (length < 0) + { + return length; + } + + /* Check for pause duration */ + + if (strstr(response, MAC_PAUSE_DUR) == NULL) + { + return -EIO; + } + + return 0; +}; + +/**************************************************************************** + * Name: get_command_err + * + * Description: + * Parses the command error response from the radio module and converts it + * into an error code. 0 for 'ok', -EINVAL for 'invalid_param' and -EBUSY + * for 'busy'. If an unknown error occurs, -EIO is returned. + * + ****************************************************************************/ + +static int get_command_err(FAR struct rn2xx3_dev_s *priv) +{ + char response[18]; + ssize_t length; + + length = read_line(priv, response, sizeof(response)); + if (length < 0) + { + return length; + } + + if (strstr(response, "ok")) + { + /* Do nothing, this is good */ + + return 0; + } + else if (strstr(response, "invalid_param")) + { + wlerr("RN2xx3 invalid_param\n"); + return -EINVAL; + } + else if (strstr(response, "busy")) + { + wlerr("RN2xx3 busy"); + return -EBUSY; + } + + wlerr("Unknown error"); + return -EIO; +} + +/**************************************************************************** + * Name: rn2xx3_txpwrlevel + * + * Description: + * Get the transmission power level that is greater than or equal to the + * requested transmission power in dBm. + * The true dBm level that was selected is returned in the `dbm` pointer. + * + ****************************************************************************/ + +static int8_t rn2xx3_txpwrlevel(FAR int32_t *dbm, + FAR const struct txlvl_s *arr, uint8_t len) +{ + /* Look until a power level is found that is equal to or exceeds the + * requested one. Starting from lowest power + */ + + for (uint8_t i = 0; i < len; i++) + { + if (arr[i].power >= *dbm) + { + *dbm = arr[i].power; + return arr[i].lvl; + } + } + + /* Requested power was higher than all options, return highest possible + * power + */ + + *dbm = arr[len - 1].power; + return arr[len - 1].lvl; +} + +/**************************************************************************** + * Name: rn2xx3_txpwrdbm + * + * Description: + * Get the transmission power in dBm that is equal to the passed tx power + * level. + * + * Return: `INT32_MAX` if level doesn't exist. + * + ****************************************************************************/ + +static int32_t rn2xx3_txpwrdbm(int8_t lvl, FAR const struct txlvl_s *arr, + uint8_t len) +{ + /* Look until a power level is found that is equal to this one. */ + + for (uint8_t i = 0; i < len; i++) + { + if (arr[i].lvl == lvl) + { + return arr[i].power; + } + } + + /* Requested level does not exist */ + + return INT32_MAX; +} + +/**************************************************************************** + * Name: rn2xx3_get_param + * + * Description: + * Send a command and read the response from the RN2XX3. + * + * Arguments: + * priv - Pointer to radio device struct + * cmd - Null terminated command string, including \r\n + * resp - Buffer to store radio response + * nbytes - Size of 'resp' buffer + * + * Returns: Number of bytes read in response. + * + ****************************************************************************/ + +static ssize_t rn2xx3_getparam(FAR struct rn2xx3_dev_s *priv, FAR char *cmd, + FAR char *resp, size_t nbytes) +{ + ssize_t length; + + length = file_write(&priv->uart, cmd, strlen(cmd)); + if (length < 0) + { + return length; + } + + length = read_line(priv, resp, nbytes); + return length; +} + +/**************************************************************************** + * Name: rn2xx3_reset + * + * Description: + * Reset the radio module. + * + ****************************************************************************/ + +static int rn2xx3_reset(FAR struct rn2xx3_dev_s *priv) +{ + ssize_t ret; + char response[50]; + int rxavail = 0; + + ret = file_write(&priv->uart, "sys reset\r\n", + sizeof("sys reset\r\n") - 1); + if (ret < 0) + { + return ret; + } + + /* Might take some time to reset, wait for something to appear in the + * receive buffer + */ + + while (rxavail <= 10) + { + ret = file_ioctl(&priv->uart, FIONREAD, &rxavail); + if (ret < 0) + { + return ret; + } + } + + /* Clear out the receive buffer, we've now reset */ + + ret = read_line(priv, response, sizeof(response)); + if (ret < 0) + { + return ret; + } + + return 0; +} + +/**************************************************************************** + * Name: rn2xx3_getmodel + * + * Description: + * Get the transceiver model type. + * + ****************************************************************************/ + +static int rn2xx3_getmodel(FAR struct rn2xx3_dev_s *priv, + FAR enum rn2xx3_type_e *model) +{ + ssize_t length; + char response[40]; + + length = radio_flush(priv); + if (length < 0) + { + return length; + } + + length = + rn2xx3_getparam(priv, "sys get ver\r\n", response, sizeof(response)); + if (length < 0) + { + return length; + } + + if (strstr(response, "RN2903") != NULL) + { + *model = RN2903; + } + else if (strstr(response, "RN2483") != NULL) + { + *model = RN2483; + } + else + { + /* Something went wrong, model number should have been returned */ + + return -EIO; + } + + /* Flush any leftover radio data */ + + length = radio_flush(priv); + if (length < 0) + { + return length; + } + + return 0; +} + +/**************************************************************************** + * Name: rn2xx3_getsnr + * + * Description: + * Get the signal to noise ratio of the last received packet. + * + ****************************************************************************/ + +static int rn2xx3_getsnr(FAR struct rn2xx3_dev_s *priv, FAR int8_t *snr) +{ + ssize_t length; + char response[10]; + + length = + rn2xx3_getparam(priv, "radio get snr\r\n", response, sizeof(response)); + if (length < 0) + { + return length; + } + + errno = 0; + *snr = strtol(response, NULL, 10); + return -errno; /* Set by strtol on failure */ +} + +/**************************************************************************** + * Name: rn2xx3_crc_en + * + * Description: + * Enabled/disable CRC for the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_crc_en(FAR struct rn2xx3_dev_s *priv, bool enable) +{ + char enstr[16]; + ssize_t length; + + length = file_write(&priv->uart, "radio set crc ", + sizeof("radio set crc ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(enstr, sizeof(enstr), "%s\r\n", enable ? "on" : "off"); + length = file_write(&priv->uart, enstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_iqi_en + * + * Description: + * Enabled/disable invert IQ for the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_iqi_en(FAR struct rn2xx3_dev_s *priv, bool enable) +{ + char enstr[16]; + ssize_t length; + + length = file_write(&priv->uart, "radio set iqi ", + sizeof("radio set iqi ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(enstr, sizeof(enstr), "%s\r\n", enable ? "on" : "off"); + length = file_write(&priv->uart, enstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_setfreq + * + * Description: + * Set the operating frequency of the RN2xx3 in Hz. + * + ****************************************************************************/ + +static int rn2xx3_setfreq(FAR struct rn2xx3_dev_s *priv, uint32_t freq) +{ + char freqstr[18]; + ssize_t length; + + length = file_write(&priv->uart, "radio set freq ", + sizeof("radio set freq ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(freqstr, sizeof(freqstr), "%lu\r\n", freq); + length = file_write(&priv->uart, freqstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_getfreq + * + * Description: + * Get the operating frequency of the RN2xx3 in Hz. + * + ****************************************************************************/ + +static int rn2xx3_getfreq(FAR struct rn2xx3_dev_s *priv, FAR uint32_t *freq) +{ + ssize_t length; + char response[20]; + + length = rn2xx3_getparam(priv, "radio get freq\r\n", response, + sizeof(response)); + if (length < 0) + { + return length; + } + + errno = 0; + *freq = strtoul(response, NULL, 10); + return -errno; +} + +/**************************************************************************** + * Name: rn2xx3_settxpwr + * + * Description: + * Set the transmit power of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_settxpwr(FAR struct rn2xx3_dev_s *priv, FAR int32_t *pwr) +{ + char powerstr[16]; + ssize_t length; + int8_t txpwr; + + if (priv->model == RN2903) + { + txpwr = rn2xx3_txpwrlevel(pwr, RN2903_TXLVLS, + array_len(RN2903_TXLVLS)); + } + else + { + txpwr = rn2xx3_txpwrlevel(pwr, RN2483_TXLVLS, + array_len(RN2483_TXLVLS)); + } + + length = file_write(&priv->uart, "radio set pwr ", + sizeof("radio set pwr ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(powerstr, sizeof(powerstr), "%d\r\n", txpwr); + length = file_write(&priv->uart, powerstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_gettxpwr + * + * Description: + * Get the transmission power of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_gettxpwr(FAR struct rn2xx3_dev_s *priv, FAR int32_t *txpwr) +{ + ssize_t length; + char response[20]; + int8_t txlevel; + + length = + rn2xx3_getparam(priv, "radio get pwr\r\n", response, sizeof(response)); + if (length < 0) + { + return length; + } + + errno = 0; + txlevel = strtol(response, NULL, 10); + if (errno) + { + return -errno; + } + + /* Convert transmission power level to dBm */ + + if (priv->model == RN2903) + { + *txpwr = rn2xx3_txpwrdbm(txlevel, RN2903_TXLVLS, + array_len(RN2903_TXLVLS)); + } + else + { + *txpwr = rn2xx3_txpwrdbm(txlevel, RN2483_TXLVLS, + array_len(RN2483_TXLVLS)); + } + + if (*txpwr == INT32_MAX) + { + /* Some transmission power that was not expected was returned */ + + return -EIO; + } + + return 0; +} + +/**************************************************************************** + * Name: rn2xx3_setbw + * + * Description: + * Set the bandwidth of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_setbw(FAR struct rn2xx3_dev_s *priv, uint32_t bw) +{ + char bwstr[16]; + ssize_t length; + length = + file_write(&priv->uart, "radio set bw ", sizeof("radio set bw ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(bwstr, sizeof(bwstr), "%lu\r\n", bw); + length = file_write(&priv->uart, bwstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_getbw + * + * Description: + * Get the bandwidth of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_getbw(FAR struct rn2xx3_dev_s *priv, FAR uint32_t *bw) +{ + int length; + char response[10]; + + length = + rn2xx3_getparam(priv, "radio get bw\r\n", response, sizeof(response)); + if (length < 0) + { + return length; + } + + errno = 0; + *bw = strtoul(response, NULL, 10); + return -errno; +} + +/**************************************************************************** + * Name: rn2xx3_setsf + * + * Description: + * Set the spread factor of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_setsf(FAR struct rn2xx3_dev_s *priv, uint8_t sf) +{ + char sfstr[15]; + ssize_t length; + + length = + file_write(&priv->uart, "radio set sf ", sizeof("radio set sf ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(sfstr, sizeof(sfstr), "sf%u\r\n", sf); + length = file_write(&priv->uart, sfstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_getsf + * + * Description: + * Get the spread factor of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_getsf(FAR struct rn2xx3_dev_s *priv, FAR uint8_t *sf) +{ + ssize_t length; + char response[20]; + + length = + rn2xx3_getparam(priv, "radio get sf\r\n", response, sizeof(response)); + if (length < 0) + { + return length; + } + + /* String should contain at least `sf` followed by one digit */ + + if (length < 3) + { + return -EIO; + } + + errno = 0; + *sf = strtoul(&response[2], NULL, 10); /* Skip 'sf' */ + return -errno; +} + +/**************************************************************************** + * Name: rn2xx3_setprlen + * + * Description: + * Set the preamble length of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_setprlen(FAR struct rn2xx3_dev_s *priv, uint16_t prlen) +{ + char prstr[16]; + ssize_t length; + + length = file_write(&priv->uart, "radio set prlen ", + sizeof("radio set prlen ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(prstr, sizeof(prstr), "%u\r\n", prlen); + length = file_write(&priv->uart, prstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_getprlen + * + * Description: + * Get the preamble length of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_getprlen(FAR struct rn2xx3_dev_s *priv, + FAR uint16_t *prlen) +{ + ssize_t length; + char response[20]; + + length = rn2xx3_getparam(priv, "radio get prlen\r\n", response, + sizeof(response)); + if (length < 0) + { + return length; + } + + errno = 0; + *prlen = strtoul(response, NULL, 10); + return -errno; +} + +/**************************************************************************** + * Name: rn2xx3_setmod + * + * Description: + * Set the modulation of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_setmod(FAR struct rn2xx3_dev_s *priv, + enum rn2xx3_mod_e mod) +{ + int err; + char modstr[16]; + ssize_t length; + + DEBUGASSERT(mod == RN2XX3_MOD_LORA || mod == RN2XX3_MOD_FSK); + + length = file_write(&priv->uart, "radio set mod ", + sizeof("radio set mod ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(modstr, sizeof(modstr), "%s\r\n", MODULATIONS[mod]); + length = file_write(&priv->uart, modstr, length); + if (length < 0) + { + return length; + } + + err = get_command_err(priv); + if (err < 0) + { + return err; + } + + priv->mod = mod; + return 0; +} + +/**************************************************************************** + * Name: rn2xx3_getmod + * + * Description: + * Get the modulation type of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_getmod(FAR struct rn2xx3_dev_s *priv, + FAR enum rn2xx3_mod_e *mod) +{ + ssize_t length; + char response[12]; + + length = + rn2xx3_getparam(priv, "radio get mod\r\n", response, sizeof(response)); + if (length < 0) + { + return length; + } + + /* Only 'lora' and 'fsk' are valid return values from the radio */ + + if (strstr(response, "lora")) + { + *mod = RN2XX3_MOD_LORA; + } + else if (strstr(response, "fsk")) + { + *mod = RN2XX3_MOD_FSK; + } + else + { + return -EIO; + } + + return 0; +} + +/**************************************************************************** + * Name: rn2xx3_setcr + * + * Description: + * Set the coding rate of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_setcr(FAR struct rn2xx3_dev_s *priv, enum rn2xx3_cr_e cr) +{ + char crstr[8]; + ssize_t length; + + length = file_write(&priv->uart, "radio set cr ", + sizeof("radio set cr ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(crstr, sizeof(crstr), "%s\r\n", CODING_RATES[cr]); + length = file_write(&priv->uart, crstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_getcr + * + * Description: + * Get the coding rate of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_getcr(FAR struct rn2xx3_dev_s *priv, + FAR enum rn2xx3_cr_e *cr) +{ + ssize_t length; + char response[10]; + + length = + rn2xx3_getparam(priv, "radio get cr\r\n", response, sizeof(response)); + if (length < 0) + { + return length; + } + + /* Check if the response contains any of the valid options */ + + for (int i = 0; i < array_len(CODING_RATES); i++) + { + if (strstr(response, CODING_RATES[i])) + { + *cr = i; + return 0; + } + } + + /* No valid response found */ + + return -EIO; +} + +/**************************************************************************** + * Name: rn2xx3_setsync + * + * Description: + * Set the sync word of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_setsync(FAR struct rn2xx3_dev_s *priv, uint64_t sync) +{ + char syncstr[20]; + ssize_t length; + + length = file_write(&priv->uart, "radio set sync ", + sizeof("radio set sync ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(syncstr, sizeof(syncstr), "%016llX\r\n", sync); + length = file_write(&priv->uart, syncstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_getsync + * + * Description: + * Get the sync word of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_getsync(FAR struct rn2xx3_dev_s *priv, + FAR uint64_t *sync) +{ + ssize_t length; + char response[20]; + + length = rn2xx3_getparam(priv, "radio get sync\r\n", response, + sizeof(response)); + if (length < 0) + { + return length; + } + + errno = 0; + *sync = strtoull(response, NULL, 16); + return -errno; +} + +/**************************************************************************** + * Name: rn2xx3_setbitrate + * + * Description: + * Set the bit rate of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_setbitrate(FAR struct rn2xx3_dev_s *priv, uint32_t bitrate) +{ + char bitstr[20]; + ssize_t length; + + length = file_write(&priv->uart, "radio set bitrate ", + sizeof("radio set bitrate ") - 1); + if (length < 0) + { + return length; + } + + /* Write actual parameter and end command */ + + length = snprintf(bitstr, sizeof(bitstr), "%lu\r\n", bitrate); + length = file_write(&priv->uart, bitstr, length); + if (length < 0) + { + return length; + } + + return get_command_err(priv); +} + +/**************************************************************************** + * Name: rn2xx3_getbitrate + * + * Description: + * Get the bit rate of the RN2xx3. + * + ****************************************************************************/ + +static int rn2xx3_getbitrate(FAR struct rn2xx3_dev_s *priv, + FAR uint32_t *bitrate) +{ + ssize_t length; + char response[20]; + + length = rn2xx3_getparam(priv, "radio get bitrate\r\n", response, + sizeof(response)); + if (length < 0) + { + return length; + } + + errno = 0; + *bitrate = strtoull(response, NULL, 16); + return -errno; +} + +/**************************************************************************** + * Name: rn2xx3_set_mode_recv + * + * Description: + * Put the RN2xx3 into indefinite receive mode. + * + ****************************************************************************/ + +static int rn2xx3_set_mode_recv(FAR struct rn2xx3_dev_s *priv) +{ + ssize_t ret; + static const char receive_cmd[] = "radio rx 0\r\n"; + + /* Pause the MAC layer */ + + ret = mac_pause(priv); + if (ret < 0) + { + return ret; + } + + /* Put the radio into indefinite receive mode */ + + ret = file_write(&priv->uart, receive_cmd, sizeof(receive_cmd) - 1); + if (ret < 0) + { + return ret; + } + + /* Check for okay receipt of command */ + + ret = get_command_err(priv); + if (ret == 0) + { + priv->receiving = true; + } + + return ret; +} + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + +/**************************************************************************** + * Name: rn2xx3_open + ****************************************************************************/ + +static int rn2xx3_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct rn2xx3_dev_s *priv = inode->i_private; + int err; + + err = nxmutex_lock(&priv->devlock); + if (err < 0) + { + return err; + } + + /* Only one user at a time */ + + if (priv->crefs > 0) + { + err = -EBUSY; + wlerr("Too many fds"); + goto early_ret; + } + + priv->crefs++; + +early_ret: + nxmutex_unlock(&priv->devlock); + return err; +} + +#endif + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + +/**************************************************************************** + * Name: rn2xx3_close + ****************************************************************************/ + +static int rn2xx3_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct rn2xx3_dev_s *priv = inode->i_private; + int err; + + err = nxmutex_lock(&priv->devlock); + if (err < 0) + { + return err; + } + + priv->crefs--; + nxmutex_unlock(&priv->devlock); + return err; +} + +#endif + +/**************************************************************************** + * Name: rn2xx3_read + * + * Description: + * Puts the RN2xx3 into continuous receive mode and waits for data to be + * received before returning it. + * + ****************************************************************************/ + +static ssize_t rn2xx3_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct rn2xx3_dev_s *priv = inode->i_private; + ssize_t ret = 0; + ssize_t received = 0; + char response[30]; + char hex_byte[3] = + { + 0, 0 /* Extra space for null terminator */ + }; + + /* Exclusive access */ + + ret = nxmutex_lock(&priv->devlock); + if (ret < 0) + { + return ret; + } + + /* If the file position is non-zero and we're not receiving, return 0 to + * indicate the end of the last packet. + */ + + if (filep->f_pos > 0 && !priv->receiving) + { + filep->f_pos = 0; + return nxmutex_unlock(&priv->devlock); + } + + /* If we're still marked as receiving and the file position in non-zero, we + * can skip initiating receive mode and just continue parsing the packet. + */ + + else if (filep->f_pos > 0 && priv->receiving) + { + goto parse_packet; + } + + /* Otherwise, we're not receiving and haven't been receiving, so we + * initiate a new receive mode. + */ + + /* Flush data */ + + ret = radio_flush(priv); + if (ret < 0) + { + wlerr("Could not flush RN2xx3\n"); + goto early_ret; + } + + ret = rn2xx3_set_mode_recv(priv); + if (ret < 0) + { + wlerr("Could not enter receive mode\n"); + goto early_ret; + } + + /* Begin slowly parsing the response to check if we either received data or + * had an error (timeout) + * 9 is the size of 'radio_rx ' or 'radio_err'. + */ + + for (uint8_t i = 0; i < 10; ) + { + ret = file_read(&priv->uart, &response[i], 10 - i); + if (ret < 0) + { + goto early_ret; + } + + i += ret; + } + + response[10] = '\0'; + + /* Check for either error or received message */ + + if (strstr(response, "radio_err") != NULL) + { + ret = -EAGAIN; /* Timeout */ + wlerr("Timeout during receive\n"); + goto early_ret; + } + else if (strstr(response, "radio_rx ") == NULL) + { + /* Unknown error, we didn't expect this output */ + + wlerr("Unknown error during receive\n"); + ret = -EIO; + goto early_ret; + } + + /* Here we've received some actual data, and that data is what's currently + * pending to be read. + * We read until the end of message is signalled with \r\n. + */ + +parse_packet: + + hex_byte[2] = '\0'; /* Appease strtoul */ + + while (received < buflen) + { + ret = file_read(&priv->uart, &hex_byte, 2); + if (ret < 2) + { + goto early_ret; + } + + if (hex_byte[0] == '\r' && hex_byte[1] == '\n') + { + break; + } + + /* Convert ASCII hex byte into real bytes to store in the buffer */ + + buffer[received] = strtoul(hex_byte, NULL, 16); + received++; + } + + /* We've received everything we can possibly receive, let the user know by + * returning how much of the buffer we've filled. + * If we've seen the end of packet indicator (\r\n), we'll mark the receive + * as complete. However, if we only stopped to not exceed the buffer + * length, we will leave our status as receiving to continue on the next + * read call. + */ + + if (hex_byte[0] == '\r' && hex_byte[1] == '\n') + { + priv->receiving = false; + } + + ret = received; + filep->f_pos += received; /* Record our position in received packet */ + +early_ret: + + /* We can't continue receiving if we had an issue */ + + if (ret < 0) + { + priv->receiving = false; + } + + nxmutex_unlock(&priv->devlock); + return ret; +} + +/**************************************************************************** + * Name: rn2xx3_write + * + * Description: + * Transmits the data from the user provided buffer over radio as a + * packet. + * + ****************************************************************************/ + +static ssize_t rn2xx3_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct rn2xx3_dev_s *priv = inode->i_private; + ssize_t length = 0; + size_t i; + char response[20]; + char hexbyte[3]; /* Two hex characters per byte, plus null terminator */ + + /* Exclusive access */ + + length = nxmutex_lock(&priv->devlock); + if (length < 0) + { + return length; + } + + /* Receives in progress are forfeited */ + + priv->receiving = false; + + /* Flush data */ + + length = radio_flush(priv); + if (length < 0) + { + wlerr("Could not flush RN2xx3\n"); + } + + /* Pause the MAC layer */ + + length = mac_pause(priv); + if (length < 0) + { + wlerr("Could not pause MAC layer\n"); + goto early_ret; + } + + /* Start the transmission with the initial 'radio tx', followed by all the + * data in the user buffer converted into hexadecimal characters one by + * one + */ + + length = file_write(&priv->uart, "radio tx ", sizeof("radio tx ") - 1); + if (length < 0) + { + goto early_ret; + } + + /* Print out every character up to the buffer size or the packet size limit + */ + + for (i = 0; i < buflen && i < MAX_PKTSIZE[priv->mod]; i++) + { + snprintf(hexbyte, sizeof(hexbyte), "%02X", buffer[i]); + length = file_write(&priv->uart, hexbyte, 2); + if (length < 0) + { + goto early_ret; + } + } + + /* Complete the packet */ + + length = file_write(&priv->uart, "\r\n", sizeof("\r\n") - 1); + if (length < 0) + { + goto early_ret; + } + + /* Check for okay message */ + + length = get_command_err(priv); + if (length < 0) + { + goto early_ret; + } + + /* Check for radio_tx_ok */ + + length = read_line(priv, response, sizeof(response)); + if (length < 0) + { + goto early_ret; + } + + if (strstr(response, "radio_tx_ok") == NULL) + { + length = -EIO; + goto early_ret; + } + + /* All transmissions were nominal, so we let the user know that `i` bytes + * were sent + */ + + length = i; + +early_ret: + nxmutex_unlock(&priv->devlock); + return length; +} + +/**************************************************************************** + * Name: rn2xx3_ioctl + * + * Description: + * Execute commands on the RN2xx3. + ****************************************************************************/ + +static int rn2xx3_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + int err; + FAR struct inode *inode = filep->f_inode; + FAR struct rn2xx3_dev_s *priv = inode->i_private; + + /* Exclusive access */ + + err = nxmutex_lock(&priv->devlock); + if (err < 0) + { + return err; + } + + /* Receives in progress are forfeited */ + + priv->receiving = false; + + /* Flush radio to clear any lingering messages */ + + err = radio_flush(priv); + if (err < 0) + { + wlerr("Couldn't flush radio: %d\n", err); + goto early_ret; + } + + switch (cmd) + { + case WLIOC_RESET: + { + err = rn2xx3_reset(priv); + break; + } + + case WLIOC_IQIEN: + { + err = rn2xx3_iqi_en(priv, arg); + break; + } + + case WLIOC_CRCEN: + { + err = rn2xx3_crc_en(priv, arg); + break; + } + + case WLIOC_GETSNR: + { + FAR int8_t *snr = (FAR int8_t *)(arg); + DEBUGASSERT(snr != NULL); + err = rn2xx3_getsnr(priv, snr); + break; + } + + case WLIOC_SETRADIOFREQ: + { + err = rn2xx3_setfreq(priv, arg); + break; + } + + case WLIOC_GETRADIOFREQ: + { + FAR uint32_t *freq = (FAR uint32_t *)(arg); + DEBUGASSERT(freq != NULL); + err = rn2xx3_getfreq(priv, freq); + break; + } + + case WLIOC_SETTXPOWERF: + { + FAR int32_t *txpwr = (FAR int32_t *)(arg); + DEBUGASSERT(txpwr != NULL); + err = rn2xx3_settxpwr(priv, txpwr); + break; + } + + case WLIOC_GETTXPOWERF: + { + FAR int32_t *txpwr = (FAR int32_t *)(arg); + DEBUGASSERT(txpwr != NULL); + err = rn2xx3_gettxpwr(priv, txpwr); + break; + } + + case WLIOC_SETBANDWIDTH: + { + err = rn2xx3_setbw(priv, arg); + break; + } + + case WLIOC_GETBANDWIDTH: + { + FAR uint32_t *bw = (FAR uint32_t *)(arg); + DEBUGASSERT(bw != NULL); + err = rn2xx3_getbw(priv, bw); + break; + } + + case WLIOC_SETSPREAD: + { + err = rn2xx3_setsf(priv, arg); + break; + } + + case WLIOC_GETSPREAD: + { + FAR uint8_t *sf = (FAR uint8_t *)(arg); + DEBUGASSERT(sf != NULL); + err = rn2xx3_getsf(priv, sf); + break; + } + + case WLIOC_SETPRLEN: + { + err = rn2xx3_setprlen(priv, arg); + break; + } + + case WLIOC_GETPRLEN: + { + FAR uint16_t *prlen = (FAR uint16_t *)(arg); + DEBUGASSERT(prlen != NULL); + err = rn2xx3_getprlen(priv, prlen); + break; + } + + case WLIOC_SETMOD: + { + err = rn2xx3_setmod(priv, arg); + break; + } + + case WLIOC_GETMOD: + { + FAR enum rn2xx3_mod_e *mod = (FAR enum rn2xx3_mod_e *)(arg); + DEBUGASSERT(mod != NULL); + err = rn2xx3_getmod(priv, mod); + break; + } + + case WLIOC_SETSYNC: + { + FAR uint64_t *syncword = (FAR uint64_t *)(arg); + DEBUGASSERT(syncword != NULL); + err = rn2xx3_setsync(priv, *syncword); + break; + } + + case WLIOC_GETSYNC: + { + FAR uint64_t *sync = (FAR uint64_t *)(arg); + DEBUGASSERT(sync != NULL); + err = rn2xx3_getsync(priv, sync); + break; + } + + case WLIOC_SETBITRATE: + { + err = rn2xx3_setbitrate(priv, arg); + break; + } + + case WLIOC_GETBITRATE: + { + FAR uint32_t *bitrate = (FAR uint32_t *)(arg); + DEBUGASSERT(bitrate != NULL); + err = rn2xx3_getbitrate(priv, bitrate); + break; + } + + case WLIOC_SETCODERATE: + { + err = rn2xx3_setcr(priv, arg); + break; + } + + case WLIOC_GETCODERATE: + { + FAR enum rn2xx3_cr_e *cr = (FAR enum rn2xx3_cr_e *)(arg); + DEBUGASSERT(cr != NULL); + err = rn2xx3_getcr(priv, cr); + break; + } + + default: + { + err = -EINVAL; + break; + } + } + +early_ret: + nxmutex_unlock(&priv->devlock); + return err; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rn2xx3_register + * + * Description: + * Register the RN2xx3 LoRa transceiver driver. + * + * Arguments: + * devpath - The device path to use for the driver + * uartpath - The path to the UART character driver connected to the + * transceiver + * + ****************************************************************************/ + +int rn2xx3_register(FAR const char *devpath, FAR const char *uartpath) +{ + FAR struct rn2xx3_dev_s *priv = NULL; + int err = 0; + int retries = 0; + + DEBUGASSERT(uartpath != NULL); + DEBUGASSERT(devpath != NULL); + + /* Initialize device structure */ + + priv = kmm_zalloc(sizeof(struct rn2xx3_dev_s)); + if (priv == NULL) + { + wlerr("Failed to allocate instance of RN2xx3 driver."); + return -ENOMEM; + } + + priv->receiving = false; /* Not receiving yet */ + priv->mod = RN2XX3_MOD_LORA; /* Starts in LoRa */ + + /* Initialize mutex */ + + err = nxmutex_init(&priv->devlock); + if (err < 0) + { + wlerr("Failed to initialize mutex for RN2xx3 device: %d\n", err); + goto free_mem; + } + + /* Open UART interface for use */ + + err = file_open(&priv->uart, uartpath, O_RDWR | O_CLOEXEC); + if (err < 0) + { + wlerr("Failed to open UART interface %s for RN2xx3 driver: %d\n", + uartpath, err); + goto destroy_mutex; + } + + /* Check model type of the transceiver. Try a few times since the + * transceiver has some boot time delay. + */ + + do + { + err = rn2xx3_getmodel(priv, &priv->model); + retries++; + usleep(10000); /* 10ms between tries */ + } + while (retries < 3 && err < 0); + + if (err < 0) + { + wlerr("Could not detect model: %d\n", err); + goto close_file; + } + + wlinfo("Detected model %s\n", priv->model == RN2903 ? "RN2903" : "RN2483"); + + /* Register driver */ + + err = register_driver(devpath, &g_rn2xx3fops, 0666, priv); + if (err < 0) + { + wlerr("Failed to register RN2xx3 driver: %d\n", err); + goto close_file; + } + + /* Cleanup items on error */ + + if (err < 0) + { + close_file: + file_close(&priv->uart); + destroy_mutex: + nxmutex_destroy(&priv->devlock); + free_mem: + kmm_free(priv); + } + + return err; +} diff --git a/include/nuttx/wireless/ioctl.h b/include/nuttx/wireless/ioctl.h index 9a53eb5517..65f6883e67 100644 --- a/include/nuttx/wireless/ioctl.h +++ b/include/nuttx/wireless/ioctl.h @@ -57,8 +57,50 @@ #define WLIOC_GETADDR _WLCIOC(0x0004) /* arg: Pointer to address value, format * of the address is driver specific */ #define WLIOC_SETTXPOWER _WLCIOC(0x0005) /* arg: Pointer to int32_t, */ - /* output power (in dBm) */ + /* output power (in 0.01 dBm) */ #define WLIOC_GETTXPOWER _WLCIOC(0x0006) /* arg: Pointer to int32_t, */ + /* output power (in 0.01 dBm) */ + +/* WARNING: The following WLIOC command are EXPERIMENTAL and unstable. They + * may be removed or modified in upcoming changes that introduce a common + * LoRa API. These commands are currently only used by the RN2XX3 driver. + */ + +#define WLIOC_SETBANDWIDTH _WLCIOC(0x0007) /* arg: Pointer to uint32_t, */ + /* bandwidth in Hz */ +#define WLIOC_GETBANDWIDTH _WLCIOC(0x0008) /* arg: Pointer to uint32_t, */ + /* bandwidth in Hz */ +#define WLIOC_SETSPREAD _WLCIOC(0x0009) /* arg: Pointer to uint8_t, */ + /* spread factor */ +#define WLIOC_GETSPREAD _WLCIOC(0x000a) /* arg: Pointer to uint8_t, */ + /* spread factor */ +#define WLIOC_GETSNR _WLCIOC(0x000b) /* arg: Pointer to int8_t, */ + /* signal to noise ratio */ +#define WLIOC_SETPRLEN _WLCIOC(0x000c) /* arg: uint16_t, */ + /* preamble length */ +#define WLIOC_GETPRLEN _WLCIOC(0x000d) /* arg: Pointer to uint16_t, */ + /* preamble length */ +#define WLIOC_SETMOD _WLCIOC(0x000e) /* arg: enum, */ + /* modulation type */ +#define WLIOC_GETMOD _WLCIOC(0x000f) /* arg: enum pointer, */ + /* modulation type */ +#define WLIOC_RESET _WLCIOC(0x0010) /* arg: none */ +#define WLIOC_SETSYNC _WLCIOC(0x0011) /* arg: uint64_t pointer */ + /* sync word */ +#define WLIOC_GETSYNC _WLCIOC(0x0012) /* arg: uint64_t pointer, */ + /* sync word */ +#define WLIOC_SETBITRATE _WLCIOC(0x0013) /* arg: uint32_t */ + /* sync word */ +#define WLIOC_GETBITRATE _WLCIOC(0x0014) /* arg: uint32_t pointer, */ + /* sync word */ +#define WLIOC_IQIEN _WLCIOC(0x0015) /* arg: bool, enable invert IQ */ +#define WLIOC_CRCEN _WLCIOC(0x0016) /* arg: bool, enable CRC */ +#define WLIOC_SETCODERATE _WLCIOC(0x0017) /* arg: enum, coding rate */ +#define WLIOC_GETCODERATE _WLCIOC(0x0018) /* arg: enum pointer, */ + /* coding rate */ +#define WLIOC_SETTXPOWERF _WLCIOC(0x0019) /* arg: Pointer to flaot, */ + /* output power (in dBm) */ +#define WLIOC_GETTXPOWERF _WLCIOC(0x0020) /* arg: Pointer to float, */ /* output power (in dBm) */ /**************************************************************************** @@ -66,7 +108,7 @@ ****************************************************************************/ #define WL_FIRST 0x0001 /* First common command */ -#define WL_NCMDS 0x0006 /* Number of common commands */ +#define WL_NCMDS 0x0020 /* Number of common commands */ /* User defined ioctl commands are also supported. These will be forwarded * by the upper-half driver to the lower-half driver via the ioctl() diff --git a/include/nuttx/wireless/lpwan/rn2xx3.h b/include/nuttx/wireless/lpwan/rn2xx3.h new file mode 100644 index 0000000000..6107ae6834 --- /dev/null +++ b/include/nuttx/wireless/lpwan/rn2xx3.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * include/nuttx/wireless/lpwan/rn2xx3.h + * + * NOTE: EXPERIMENTAL DRIVER + * + * 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_RN2XX3_H +#define __INCLUDE_NUTTX_WIRELESS_LPWAN_RN2XX3_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-Processor Declarations + ****************************************************************************/ + +/**************************************************************************** + * Public Data Types + ****************************************************************************/ + +/* Modulation types */ + +enum rn2xx3_mod_e +{ + RN2XX3_MOD_LORA = 0, /* LoRa modulation */ + RN2XX3_MOD_FSK = 1, /* FSK modulation */ +}; + +/* Coding rates */ + +enum rn2xx3_cr_e +{ + RN2XX3_CR_4_5 = 0, /* 4/5 coding rate */ + RN2XX3_CR_4_6 = 1, /* 4/6 coding rate */ + RN2XX3_CR_4_7 = 2, /* 4/7 coding rate */ + RN2XX3_CR_4_8 = 3, /* 4/8 coding rate */ +}; + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: rn2xx3_register + * + * Description: + * Register the RN2xx3 LoRa transceiver driver. + * + * Arguments: + * devpath - The device path to use for the driver + * uartpath - The path to the UART character driver connected to the + * transceiver + * + ****************************************************************************/ + +int rn2xx3_register(FAR const char *devpath, FAR const char *uartpath); + +#endif /* __INCLUDE_NUTTX_WIRELESS_LPWAN_RN2XX3_H */