1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/rn2xx3/rn2xx3.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);
}