mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
339 lines
8.6 KiB
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;
|
|
}
|