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 */