1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/rn2xx3/rn2xx3_internal.c

339 lines
8.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 Internal driver implementation for the RN2483/RN2903 devices
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include "fmt.h"
#include "timex.h"
#include "ztimer.h"
#include "rn2xx3_internal.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define RESP_TIMEOUT_SEC (5U)
static const char *closing_seq = "\r\n";
static void _uart_write_str(rn2xx3_t *dev, const char *str)
{
size_t len = strlen(str);
if (len) {
uart_write(dev->p.uart, (uint8_t *)str, len);
}
}
static void isr_resp_timeout(void *arg)
{
rn2xx3_t *dev = (rn2xx3_t *)arg;
mutex_unlock(&(dev->resp_lock));
}
static bool _wait_reply(rn2xx3_t *dev, uint8_t timeout)
{
dev->resp_done = 0;
dev->resp_size = 0;
dev->resp_buf[0] = 0;
ztimer_now_t sent_time = ztimer_now(ZTIMER_MSEC);
ztimer_t resp_timer;
resp_timer.callback = isr_resp_timeout;
resp_timer.arg = dev;
ztimer_set(ZTIMER_MSEC, &resp_timer, (uint32_t)timeout * MS_PER_SEC);
/* wait for results */
while ((!dev->resp_done) &&
((int32_t)(sent_time + (timeout * MS_PER_SEC) - ztimer_now(ZTIMER_MSEC)) > 0)) {
mutex_lock(&(dev->resp_lock));
}
ztimer_remove(ZTIMER_MSEC, &resp_timer);
if (dev->resp_done == 0) {
DEBUG("[rn2xx3] response timeout\n");
return true;
}
return false;
}
void rn2xx3_hex_to_bytes(const char *hex, uint8_t *byte_array)
{
const uint8_t charmap[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 01234567 */
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, /* @ABCDEFG */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* HIJKLMNO */
};
size_t len = strlen(hex);
for (uint8_t pos = 0; pos < len; pos += 2) {
uint8_t idx0 = ((uint8_t)hex[pos + 0] & 0x1F) ^ 0x10;
uint8_t idx1 = ((uint8_t)hex[pos + 1] & 0x1F) ^ 0x10;
byte_array[pos / 2] = (uint8_t)(charmap[idx0] << 4) | charmap[idx1];
};
}
void rn2xx3_set_internal_state(rn2xx3_t *dev, uint8_t state)
{
if ((dev->int_state == RN2XX3_INT_STATE_SLEEP) ||
(dev->int_state == state)) {
return;
}
if (IS_ACTIVE(ENABLE_DEBUG)) {
printf("[rn2xx3] new state: ");
switch(state) {
case RN2XX3_INT_STATE_CMD:
puts("CMD");
break;
case RN2XX3_INT_STATE_IDLE:
puts("IDLE");
break;
case RN2XX3_INT_STATE_MAC_JOIN:
puts("JOIN");
break;
case RN2XX3_INT_STATE_MAC_RX_MESSAGE:
puts("RX MSG");
break;
case RN2XX3_INT_STATE_MAC_RX_PORT:
puts("RX PORT");
break;
case RN2XX3_INT_STATE_MAC_TX:
puts("TX");
break;
case RN2XX3_INT_STATE_RESET:
puts("RESET");
break;
case RN2XX3_INT_STATE_SLEEP:
puts("SLEEP");
break;
default:
puts("UNKNOWN");
break;
}
}
dev->int_state = state;
}
int rn2xx3_write_cmd(rn2xx3_t *dev)
{
int ret;
DEBUG("[rn2xx3] CMD: %s\n", dev->cmd_buf);
if (dev->int_state == RN2XX3_INT_STATE_SLEEP) {
DEBUG("[rn2xx3] ABORT: device is in sleep mode\n");
return RN2XX3_ERR_SLEEP_MODE;
}
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_CMD);
mutex_lock(&(dev->cmd_lock));
_uart_write_str(dev, dev->cmd_buf);
_uart_write_str(dev, closing_seq);
ret = rn2xx3_wait_response(dev);
if (ret == RN2XX3_TIMEOUT) {
mutex_unlock(&(dev->cmd_lock));
return RN2XX3_TIMEOUT;
}
mutex_unlock(&(dev->cmd_lock));
ret = rn2xx3_process_response(dev);
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
DEBUG("[rn2xx3] RET: %d, RESP: %s\n", ret, dev->resp_buf);
return ret;
}
int rn2xx3_write_cmd_no_wait(rn2xx3_t *dev)
{
DEBUG("[rn2xx3] CMD (NO WAIT): %s\n", dev->cmd_buf);
mutex_lock(&(dev->cmd_lock));
_uart_write_str(dev, dev->cmd_buf);
_uart_write_str(dev, closing_seq);
mutex_unlock(&(dev->cmd_lock));
return RN2XX3_OK;
}
int rn2xx3_wait_response(rn2xx3_t *dev)
{
if (_wait_reply(dev, RESP_TIMEOUT_SEC)) {
return RN2XX3_TIMEOUT;
}
DEBUG("[rn2xx3] RESP: %s\n", dev->resp_buf);
return RN2XX3_OK;
}
int rn2xx3_wait_reply(rn2xx3_t *dev, uint8_t timeout)
{
if (_wait_reply(dev, timeout)) {
return RN2XX3_REPLY_TIMEOUT;
}
DEBUG("[rn2xx3] REPLY: %s\n", dev->resp_buf);
return rn2xx3_process_reply(dev);
}
void rn2xx3_cmd_start(rn2xx3_t *dev)
{
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_CMD);
DEBUG("[rn2xx3] CMD: %s", dev->cmd_buf);
mutex_lock(&(dev->cmd_lock));
_uart_write_str(dev, dev->cmd_buf);
}
void rn2xx3_cmd_append(rn2xx3_t *dev, const uint8_t *payload, uint8_t payload_len)
{
char payload_str[3] = { 0 };
for (unsigned i = 0; i < payload_len; i++) {
fmt_byte_hex(payload_str, payload[i]);
DEBUG("%s", payload_str);
_uart_write_str(dev, payload_str);
}
}
int rn2xx3_cmd_finalize(rn2xx3_t *dev)
{
DEBUG("\n");
_uart_write_str(dev, closing_seq);
uint8_t ret = rn2xx3_wait_response(dev);
mutex_unlock(&(dev->cmd_lock));
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
DEBUG("[rn2xx3] RET: %d, RESP: %s\n", ret, dev->resp_buf);
return ret;
}
void rn2xx3_mac_tx_start(rn2xx3_t *dev)
{
snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "mac tx %s %d ",
(dev->loramac.tx_mode == LORAMAC_TX_CNF) ? "cnf" : "uncnf",
dev->loramac.tx_port);
rn2xx3_cmd_start(dev);
}
int rn2xx3_mac_tx_finalize(rn2xx3_t *dev)
{
rn2xx3_cmd_finalize(dev);
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_MAC_TX);
return rn2xx3_process_response(dev);
}
int rn2xx3_process_response(rn2xx3_t *dev)
{
uint8_t ret = RN2XX3_DATA;
if (strcmp(dev->resp_buf, "ok") == 0) {
DEBUG("[rn2xx3] command succeeded: '%s'\n", dev->cmd_buf);
ret = RN2XX3_OK;
}
else if (strcmp(dev->resp_buf, "invalid_param") == 0) {
DEBUG("[rn2xx3] invalid command: '%s'\n", dev->cmd_buf);
ret = RN2XX3_ERR_INVALID_PARAM;
}
else if (strcmp(dev->resp_buf, "not_joined") == 0) {
DEBUG("[rn2xx3] failed: network is not joined\n");
ret = RN2XX3_ERR_NOT_JOINED;
}
else if (strcmp(dev->resp_buf, "no_free_ch") == 0) {
DEBUG("[rn2xx3] failed: all channels are busy\n");
ret = RN2XX3_ERR_NO_FREE_CH;
}
else if (strcmp(dev->resp_buf, "silent") == 0) {
DEBUG("[rn2xx3] failed: all channels are busy\n");
ret = RN2XX3_ERR_SILENT;
}
else if (strcmp(dev->resp_buf, "frame_counter_err_rejoin_needed") == 0) {
DEBUG("[rn2xx3] failed: Frame counter rolled over\n");
ret = RN2XX3_ERR_FR_CNT_REJOIN_NEEDED;
}
else if (strcmp(dev->resp_buf, "busy") == 0) {
DEBUG("[rn2xx3] failed: MAC state is in an Idle state\n");
ret = RN2XX3_ERR_BUSY;
}
else if (strcmp(dev->resp_buf, "invalid_data_len") == 0) {
DEBUG("[rn2xx3] failed: payload length too large\n");
ret = RN2XX3_ERR_INVALID_DATA_LENGTH;
}
return ret;
}
int rn2xx3_process_reply(rn2xx3_t *dev)
{
uint8_t ret;
if (strcmp(dev->resp_buf, "accepted") == 0) {
DEBUG("[rn2xx3] join procedure succeeded.\n");
ret = RN2XX3_REPLY_JOIN_ACCEPTED;
}
else if (strcmp(dev->resp_buf, "denied") == 0) {
DEBUG("[rn2xx3] join procedure failed.\n");
ret = RN2XX3_REPLY_JOIN_DENIED;
}
else if (strcmp(dev->resp_buf, "mac_tx_ok") == 0) {
DEBUG("[rn2xx3] uplink transmission succeeded.\n");
ret = RN2XX3_REPLY_TX_MAC_OK;
}
else if (strncmp(dev->resp_buf, "mac_rx", 6) == 0) {
DEBUG("[rn2xx3] received downlink data from server.\n");
ret = RN2XX3_REPLY_TX_MAC_RX;
}
else if (strcmp(dev->resp_buf, "mac_err") == 0) {
DEBUG("[rn2xx3] uplink transmission failed.\n");
ret = RN2XX3_REPLY_TX_MAC_ERR;
}
else if (strcmp(dev->resp_buf, "invalid_data_len") == 0) {
DEBUG("[rn2xx3] payload length too large.\n");
ret = RN2XX3_REPLY_TX_MAC_ERR;
}
else {
DEBUG("[rn2xx3] unknown reply.\n");
ret = RN2XX3_REPLY_OTHER;
}
return ret;
}