mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
322 lines
9.6 KiB
C
322 lines
9.6 KiB
C
/*
|
|
* Copyright (C) 2017 Inria
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup drivers_rn2xx3
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Driver implementation for the RN2483/RN2903 devices
|
|
*
|
|
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "assert.h"
|
|
#include "ztimer.h"
|
|
#include "fmt.h"
|
|
#include "kernel_defines.h"
|
|
|
|
#include "rn2xx3_params.h"
|
|
#include "rn2xx3.h"
|
|
#include "rn2xx3_internal.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
/* Warning: to correctly display the debug message from sleep timer callback,,
|
|
add CFLAGS+=-DTHREAD_STACKSIZE_IDLE=THREAD_STACKSIZE_DEFAULT to the build
|
|
command.
|
|
*/
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* @brief Delay when resetting the device, 10ms
|
|
*/
|
|
#define RESET_DELAY (10UL)
|
|
|
|
/*
|
|
* Interrupt callbacks
|
|
*/
|
|
static void _rx_cb(void *arg, uint8_t c)
|
|
{
|
|
rn2xx3_t *dev = arg;
|
|
netdev_t *netdev = &dev->netdev;
|
|
|
|
/* Avoid overflow of module response buffer */
|
|
if (dev->resp_size >= RN2XX3_MAX_BUF) {
|
|
return;
|
|
}
|
|
|
|
/* The device closes the response with \r\n when done */
|
|
if (c == '\r') {
|
|
return;
|
|
}
|
|
else if (c == '\n') { /* response end delimiter */
|
|
dev->resp_buf[dev->resp_size++] = '\0';
|
|
if (dev->int_state == RN2XX3_INT_STATE_MAC_RX_MESSAGE) {
|
|
/* RX state: closing RX buffer */
|
|
dev->rx_buf[(dev->rx_size + 1) / 2] = 0;
|
|
netdev_trigger_event_isr(netdev);
|
|
}
|
|
else if (dev->int_state == RN2XX3_INT_STATE_MAC_TX) {
|
|
/* still in TX state: transmission complete but no data received */
|
|
netdev_trigger_event_isr(netdev);
|
|
}
|
|
dev->resp_size = 0;
|
|
dev->rx_size = 0;
|
|
dev->resp_done = 1;
|
|
mutex_unlock(&(dev->resp_lock));
|
|
}
|
|
else {
|
|
switch (dev->int_state) {
|
|
/* A successful mac TX command expects 2 kinds of replies:
|
|
* - mac_tx_ok: transmission done, no data received
|
|
* - mac_rx <port> <data>: transmission done, some data
|
|
* received on port <port>. */
|
|
case RN2XX3_INT_STATE_MAC_TX:
|
|
/* some data are available */
|
|
dev->resp_buf[dev->resp_size++] = c;
|
|
/* if module response ends with 'rx ' after 8 received chars,
|
|
the module starts receiving data */
|
|
if (dev->resp_size == 8 && dev->resp_buf[4] == 'r'
|
|
&& dev->resp_buf[5] == 'x' && dev->resp_buf[6] == ' ') {
|
|
/* next received chars correspond to the port number */
|
|
dev->int_state = RN2XX3_INT_STATE_MAC_RX_PORT;
|
|
}
|
|
break;
|
|
|
|
case RN2XX3_INT_STATE_MAC_RX_PORT:
|
|
dev->resp_buf[dev->resp_size++] = c;
|
|
if (c == ' ') {
|
|
dev->int_state = RN2XX3_INT_STATE_MAC_RX_MESSAGE;
|
|
}
|
|
break;
|
|
|
|
case RN2XX3_INT_STATE_MAC_RX_MESSAGE:
|
|
/* read and convert RX data (received in hex chars) */
|
|
if (c == ' ') {
|
|
dev->resp_buf[dev->resp_size++] = c;
|
|
break;
|
|
}
|
|
dev->rx_tmp_buf[dev->rx_size % 2] = c;
|
|
|
|
/* We convert pairs of hex character to bytes on the fly to
|
|
save space in memory */
|
|
if (dev->rx_size != 0 && dev->rx_size % 2) {
|
|
rn2xx3_hex_to_bytes(dev->rx_tmp_buf,
|
|
&dev->rx_buf[(dev->rx_size - 1) / 2]);
|
|
}
|
|
dev->rx_size++;
|
|
break;
|
|
|
|
default:
|
|
dev->resp_buf[dev->resp_size++] = c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _sleep_timer_cb(void *arg)
|
|
{
|
|
DEBUG("[rn2xx3] exit sleep\n");
|
|
rn2xx3_t *dev = arg;
|
|
dev->int_state = RN2XX3_INT_STATE_IDLE;
|
|
}
|
|
|
|
/*
|
|
* Driver's "public" functions
|
|
*/
|
|
void rn2xx3_setup(rn2xx3_t *dev, const rn2xx3_params_t *params)
|
|
{
|
|
assert(dev && (params->uart < UART_NUMOF));
|
|
|
|
/* initialize device parameters */
|
|
dev->p = *params;
|
|
|
|
/* initialize pins and perform hardware reset */
|
|
if (gpio_is_valid(dev->p.pin_reset)) {
|
|
gpio_init(dev->p.pin_reset, GPIO_OUT);
|
|
gpio_set(dev->p.pin_reset);
|
|
}
|
|
/* UART is initialized later, since interrupts cannot be handled yet */
|
|
}
|
|
|
|
int rn2xx3_init(rn2xx3_t *dev)
|
|
{
|
|
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_RESET);
|
|
|
|
/* initialize buffers and locks*/
|
|
dev->resp_size = 0;
|
|
dev->cmd_buf[0] = 0;
|
|
|
|
/* initialize UART and GPIO pins */
|
|
if (uart_init(dev->p.uart, dev->p.baudrate, _rx_cb, dev) != UART_OK) {
|
|
DEBUG("[rn2xx3] init: error initializing UART\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* if reset pin is connected, do a hardware reset */
|
|
if (gpio_is_valid(dev->p.pin_reset)) {
|
|
gpio_clear(dev->p.pin_reset);
|
|
ztimer_sleep(ZTIMER_MSEC, RESET_DELAY);
|
|
gpio_set(dev->p.pin_reset);
|
|
}
|
|
|
|
dev->sleep_timer.callback = _sleep_timer_cb;
|
|
dev->sleep_timer.arg = dev;
|
|
|
|
rn2xx3_sys_set_sleep_duration(dev, CONFIG_RN2XX3_DEFAULT_SLEEP);
|
|
|
|
/* sending empty command to clear uart buffer */
|
|
if (rn2xx3_write_cmd(dev) == RN2XX3_TIMEOUT) {
|
|
DEBUG("[rn2xx3] init: initialization failed\n");
|
|
return RN2XX3_TIMEOUT;
|
|
}
|
|
|
|
if (rn2xx3_mac_init(dev) != RN2XX3_OK) {
|
|
DEBUG("[rn2xx3] mac: initialization failed\n");
|
|
return RN2XX3_ERR_MAC_INIT;
|
|
}
|
|
|
|
DEBUG("[rn2xx3] init: initialization successful\n");
|
|
return RN2XX3_OK;
|
|
}
|
|
|
|
int rn2xx3_sys_reset(rn2xx3_t *dev)
|
|
{
|
|
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "sys reset");
|
|
dev->cmd_buf[p] = 0;
|
|
int ret = rn2xx3_write_cmd(dev);
|
|
if (ret == RN2XX3_TIMEOUT || ret == RN2XX3_ERR_SLEEP_MODE) {
|
|
DEBUG("[rn2xx3] reset: failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return RN2XX3_OK;
|
|
}
|
|
|
|
int rn2xx3_sys_factory_reset(rn2xx3_t *dev)
|
|
{
|
|
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
|
|
"sys factoryRESET");
|
|
dev->cmd_buf[p] = 0;
|
|
int ret = rn2xx3_write_cmd(dev);
|
|
if (ret == RN2XX3_TIMEOUT || ret == RN2XX3_ERR_SLEEP_MODE) {
|
|
DEBUG("[rn2xx3] factory reset: failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return RN2XX3_OK;
|
|
}
|
|
|
|
int rn2xx3_sys_sleep(rn2xx3_t *dev)
|
|
{
|
|
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
|
|
"sys sleep %" PRIu32 "", dev->sleep);
|
|
dev->cmd_buf[p] = 0;
|
|
|
|
if (dev->int_state == RN2XX3_INT_STATE_SLEEP) {
|
|
DEBUG("[rn2xx3] sleep: device already in sleep mode\n");
|
|
return RN2XX3_ERR_SLEEP_MODE;
|
|
}
|
|
|
|
/* always succeeds */
|
|
rn2xx3_write_cmd_no_wait(dev);
|
|
|
|
/* Wait a little to check if the device could go to sleep. No answer means
|
|
it worked. */
|
|
ztimer_sleep(ZTIMER_MSEC, 1);
|
|
|
|
DEBUG("[rn2xx3] RESP: %s\n", dev->resp_buf);
|
|
if (rn2xx3_process_response(dev) == RN2XX3_ERR_INVALID_PARAM) {
|
|
DEBUG("[rn2xx3] sleep: cannot put module in sleep mode\n");
|
|
return RN2XX3_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_SLEEP);
|
|
ztimer_set(ZTIMER_MSEC, &dev->sleep_timer, dev->sleep);
|
|
|
|
return RN2XX3_OK;
|
|
}
|
|
|
|
int rn2xx3_mac_init(rn2xx3_t *dev)
|
|
{
|
|
rn2xx3_mac_set_dr(dev, CONFIG_LORAMAC_DEFAULT_DR);
|
|
rn2xx3_mac_set_tx_power(dev, CONFIG_LORAMAC_DEFAULT_TX_POWER);
|
|
rn2xx3_mac_set_tx_port(dev, CONFIG_LORAMAC_DEFAULT_TX_PORT);
|
|
rn2xx3_mac_set_tx_mode(dev, CONFIG_LORAMAC_DEFAULT_TX_MODE);
|
|
rn2xx3_mac_set_adr(dev, IS_ACTIVE(CONFIG_LORAMAC_DEFAULT_ADR));
|
|
rn2xx3_mac_set_retx(dev, CONFIG_LORAMAC_DEFAULT_RETX);
|
|
rn2xx3_mac_set_linkchk_interval(dev, CONFIG_LORAMAC_DEFAULT_LINKCHK);
|
|
rn2xx3_mac_set_rx1_delay(dev, CONFIG_LORAMAC_DEFAULT_RX1_DELAY);
|
|
rn2xx3_mac_set_ar(dev, IS_ACTIVE(CONFIG_RN2XX3_DEFAULT_AR));
|
|
rn2xx3_mac_set_rx2_dr(dev, CONFIG_LORAMAC_DEFAULT_RX2_DR);
|
|
rn2xx3_mac_set_rx2_freq(dev, CONFIG_LORAMAC_DEFAULT_RX2_FREQ);
|
|
|
|
return RN2XX3_OK;
|
|
}
|
|
|
|
int rn2xx3_mac_tx(rn2xx3_t *dev, uint8_t *payload, uint8_t payload_len)
|
|
{
|
|
if (dev->int_state == RN2XX3_INT_STATE_SLEEP) {
|
|
DEBUG("[rn2xx3] ABORT: device is in sleep mode\n");
|
|
return RN2XX3_ERR_SLEEP_MODE;
|
|
}
|
|
|
|
rn2xx3_mac_tx_start(dev);
|
|
for (unsigned i = 0; i < payload_len; ++i) {
|
|
rn2xx3_cmd_append(dev, &payload[i], 1);
|
|
}
|
|
|
|
int ret = rn2xx3_mac_tx_finalize(dev);
|
|
if (ret != RN2XX3_OK) {
|
|
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
|
|
DEBUG("[rn2xx3] TX command failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = rn2xx3_wait_reply(dev, RN2XX3_REPLY_DELAY_TIMEOUT);
|
|
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int rn2xx3_mac_join_network(rn2xx3_t *dev, loramac_join_mode_t mode)
|
|
{
|
|
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
|
|
"mac join %s",
|
|
(mode == LORAMAC_JOIN_OTAA) ? "otaa" : "abp");
|
|
dev->cmd_buf[p] = 0;
|
|
|
|
int ret = rn2xx3_write_cmd(dev);
|
|
if (ret != RN2XX3_OK) {
|
|
DEBUG("[rn2xx3] join procedure command failed\n");
|
|
return ret;
|
|
}
|
|
|
|
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_MAC_JOIN);
|
|
|
|
ret = rn2xx3_wait_reply(dev,
|
|
CONFIG_LORAMAC_DEFAULT_JOIN_DELAY1 + \
|
|
CONFIG_LORAMAC_DEFAULT_JOIN_DELAY2);
|
|
|
|
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int rn2xx3_mac_save(rn2xx3_t *dev)
|
|
{
|
|
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "mac save");
|
|
dev->cmd_buf[p] = 0;
|
|
return rn2xx3_write_cmd(dev);
|
|
}
|